From e95f033e9287a87ad5b60b2b593735615056e078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= Date: Thu, 1 Oct 2020 12:15:56 +0200 Subject: [PATCH 001/128] [Security Solution] Fix useMatrixHistogram to refetch data when stackbyField change (#78751) --- .../containers/matrix_histogram/index.test.ts | 50 +++++++++++++++++++ .../containers/matrix_histogram/index.ts | 4 +- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts new file mode 100644 index 00000000000000..8c9c19fc3f2185 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; + +import { useKibana } from '../../../common/lib/kibana'; +import { useMatrixHistogram } from '.'; +import { MatrixHistogramType } from '../../../../common/search_strategy'; + +jest.mock('../../../common/lib/kibana'); + +describe('useMatrixHistogram', () => { + const props = { + endDate: new Date(Date.now()).toISOString(), + errorMessage: '', + filterQuery: {}, + histogramType: MatrixHistogramType.events, + indexNames: [], + stackByField: 'event.module', + startDate: new Date(Date.now()).toISOString(), + }; + + it('should update request when props has changed', async () => { + const localProps = { ...props }; + const { rerender } = renderHook(() => useMatrixHistogram(localProps)); + + localProps.stackByField = 'event.action'; + + rerender(); + + const mockCalls = (useKibana().services.data.search.search as jest.Mock).mock.calls; + + expect(mockCalls.length).toBe(2); + expect(mockCalls[0][0].stackByField).toBe('event.module'); + expect(mockCalls[1][0].stackByField).toBe('event.action'); + }); + + it('returns a memoized value', async () => { + const { result, rerender } = renderHook(() => useMatrixHistogram(props)); + + const result1 = result.current[1]; + act(() => rerender()); + const result2 = result.current[1]; + + expect(result1).toBe(result2); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts index ca8bcc637717b4..33cc86f5e97983 100644 --- a/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts +++ b/x-pack/plugins/security_solution/public/common/containers/matrix_histogram/index.ts @@ -134,18 +134,20 @@ export const useMatrixHistogram = ({ ...prevRequest, defaultIndex: indexNames, filterQuery: createFilter(filterQuery), + histogramType, timerange: { interval: '12h', from: startDate, to: endDate, }, + stackByField, }; if (!deepEqual(prevRequest, myRequest)) { return myRequest; } return prevRequest; }); - }, [indexNames, endDate, filterQuery, startDate]); + }, [indexNames, endDate, filterQuery, startDate, stackByField, histogramType]); useEffect(() => { hostsSearch(matrixHistogramRequest); From c5ab7493e5459b80a2ccf57447ed4897fdf96627 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 1 Oct 2020 12:20:33 +0200 Subject: [PATCH 002/128] [ES UI] Reintroduce import of brace/mode/json (#78571) * added brace/mode/json import everywhere json mode is being used * added brace mocks to tests * slight refactor of imports * remove import from es ui shared * Reverse adding brace/mode/json import everywhere - different approach of just adding brace/json/mode to es_ui_shared as before with a BIG comment * updated @kbn/ace readme Co-authored-by: Elastic Machine --- packages/kbn-ace/README.md | 19 +++++++++++++++++-- .../static/forms/components/index.ts | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/kbn-ace/README.md b/packages/kbn-ace/README.md index 54c422a72c6f86..c11d5cc2f24b82 100644 --- a/packages/kbn-ace/README.md +++ b/packages/kbn-ace/README.md @@ -1,5 +1,20 @@ # @kbn/ace -Contains all Kibana-specific brace related code. Excluding the code that still inside of Console because that code is only used inside of console at the moment. +This package contains the XJSON mode for brace. This is an extension of the `brace/mode/json` mode. -This package enables plugins to use this functionality and import it as needed -- behind an async import so that brace does not bloat the JS code needed for first page load of Kibana. +This package also contains an import of the entire brace editor which is used for creating the custom XJSON worker. + +## Note to plugins +_This code should not be eagerly loaded_. + +Make sure imports of this package are behind a lazy-load `import()` statement. + +Your plugin should already be loading application code this way in the `mount` function. + +## Deprecated + +This package is considered deprecated and will be removed in future. + +New and existing editor functionality should use Monaco. + +_Do not add new functionality to this package_. Build new functionality for Monaco and use it instead. diff --git a/src/plugins/es_ui_shared/static/forms/components/index.ts b/src/plugins/es_ui_shared/static/forms/components/index.ts index 6e319978e9392d..1108c56161966a 100644 --- a/src/plugins/es_ui_shared/static/forms/components/index.ts +++ b/src/plugins/es_ui_shared/static/forms/components/index.ts @@ -17,6 +17,21 @@ * under the License. */ +/* +@TODO + +The brace/mode/json import below is loaded eagerly - before this plugin is explicitly loaded by users. This makes +the brace JSON mode, used for JSON syntax highlighting and grammar checking, available across all of Kibana plugins. + +This is not ideal because we are loading JS that is not necessary for Kibana to start, but the alternative +is breaking JSON mode for an unknown number of ace editors across Kibana - not all components reference the underlying +EuiCodeEditor (for instance, explicitly). + +Importing here is a way of preventing a more sophisticated solution to this problem since we want to, eventually, +migrate all code editors over to Monaco. Once that is done, we should remove this import. + */ +import 'brace/mode/json'; + export * from './field'; export * from './form_row'; export * from './fields'; From 8f89ef571c9605c35a572ef8bb3d03455258cbcc Mon Sep 17 00:00:00 2001 From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com> Date: Thu, 1 Oct 2020 13:23:45 +0300 Subject: [PATCH 003/128] Reduce bundle size of the default editor (#78938) * Reduce bundle size * Remove useless line of code Co-authored-by: Elastic Machine --- .../public/components/sidebar/index.ts | 2 - .../public/components/sidebar/navbar.tsx | 20 ++---- .../public/components/sidebar/sidebar.tsx | 45 +++++------- .../components/sidebar/use_option_tabs.ts | 72 +++++++++++++++++++ .../public/default_editor.tsx | 11 +-- .../public/default_editor_controller.tsx | 61 ++++------------ 6 files changed, 113 insertions(+), 98 deletions(-) create mode 100644 src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts diff --git a/src/plugins/vis_default_editor/public/components/sidebar/index.ts b/src/plugins/vis_default_editor/public/components/sidebar/index.ts index 31228aad85d1ec..09b6e229d9fcc1 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/index.ts +++ b/src/plugins/vis_default_editor/public/components/sidebar/index.ts @@ -18,5 +18,3 @@ */ export { DefaultEditorSideBar } from './sidebar'; -export { DefaultEditorDataTab } from './data_tab'; -export { OptionTab } from './navbar'; diff --git a/src/plugins/vis_default_editor/public/components/sidebar/navbar.tsx b/src/plugins/vis_default_editor/public/components/sidebar/navbar.tsx index a1b5003a092f76..33ef5cc2353d75 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/navbar.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/navbar.tsx @@ -20,32 +20,20 @@ import React from 'react'; import { EuiTabs, EuiTab } from '@elastic/eui'; -import { VisOptionsProps } from '../../vis_options_props'; -import { DefaultEditorDataTabProps } from './data_tab'; - -export interface OptionTab { - editor: React.ComponentType; - name: string; - title: string; -} +import { OptionTab } from './use_option_tabs'; interface DefaultEditorNavBarProps { optionTabs: OptionTab[]; - selectedTab: string; setSelectedTab(name: string): void; } -function DefaultEditorNavBar({ - selectedTab, - setSelectedTab, - optionTabs, -}: DefaultEditorNavBarProps) { +function DefaultEditorNavBar({ setSelectedTab, optionTabs }: DefaultEditorNavBarProps) { return ( - {optionTabs.map(({ name, title }) => ( + {optionTabs.map(({ name, title, isSelected = false }) => ( setSelectedTab(name)} > diff --git a/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx index bcbc5afec1fdc2..c0a6b48794970c 100644 --- a/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx +++ b/src/plugins/vis_default_editor/public/components/sidebar/sidebar.tsx @@ -30,18 +30,18 @@ import { } from 'src/plugins/visualizations/public'; import { TimeRange } from 'src/plugins/data/public'; import { SavedObject } from 'src/plugins/saved_objects/public'; -import { DefaultEditorNavBar, OptionTab } from './navbar'; +import { DefaultEditorNavBar } from './navbar'; import { DefaultEditorControls } from './controls'; import { setStateParamValue, useEditorReducer, useEditorFormState, discardChanges } from './state'; import { DefaultEditorAggCommonProps } from '../agg_common_props'; import { SidebarTitle } from './sidebar_title'; import { Schema } from '../../schemas'; +import { useOptionTabs } from './use_option_tabs'; interface DefaultEditorSideBarProps { embeddableHandler: VisualizeEmbeddableContract; isCollapsed: boolean; onClickCollapse: () => void; - optionTabs: OptionTab[]; uiState: PersistedState; vis: Vis; isLinkedSearch: boolean; @@ -54,7 +54,6 @@ function DefaultEditorSideBar({ embeddableHandler, isCollapsed, onClickCollapse, - optionTabs, uiState, vis, isLinkedSearch, @@ -62,10 +61,10 @@ function DefaultEditorSideBar({ savedSearch, timeRange, }: DefaultEditorSideBarProps) { - const [selectedTab, setSelectedTab] = useState(optionTabs[0].name); const [isDirty, setDirty] = useState(false); const [state, dispatch] = useEditorReducer(vis, eventEmitter); const { formState, setTouched, setValidity, resetValidity } = useEditorFormState(); + const [optionTabs, setSelectedTab] = useOptionTabs(vis); const responseAggs = useMemo(() => (state.data.aggs ? state.data.aggs.getResponseAggs() : []), [ state.data.aggs, @@ -201,31 +200,23 @@ function DefaultEditorSideBar({ )} {optionTabs.length > 1 && ( - + )} - {optionTabs.map(({ editor: Editor, name }) => { - const isTabSelected = selectedTab === name; - - return ( -
- -
- ); - })} + {optionTabs.map(({ editor: Editor, name, isSelected = false }) => ( +
+ +
+ ))} diff --git a/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts b/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts new file mode 100644 index 00000000000000..337533df50fadc --- /dev/null +++ b/src/plugins/vis_default_editor/public/components/sidebar/use_option_tabs.ts @@ -0,0 +1,72 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { useCallback, useState } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { Vis } from 'src/plugins/visualizations/public'; +import { DefaultEditorDataTab, DefaultEditorDataTabProps } from './data_tab'; +import { VisOptionsProps } from '../../vis_options_props'; + +export interface OptionTab { + editor: React.ComponentType; + name: string; + title: string; + isSelected?: boolean; +} + +export const useOptionTabs = ({ type: visType }: Vis): [OptionTab[], (name: string) => void] => { + const [optionTabs, setOptionTabs] = useState(() => { + const tabs = [ + ...(visType.schemas.buckets || visType.schemas.metrics + ? [ + { + name: 'data', + title: i18n.translate('visDefaultEditor.sidebar.tabs.dataLabel', { + defaultMessage: 'Data', + }), + editor: DefaultEditorDataTab, + }, + ] + : []), + + ...(!visType.editorConfig.optionTabs && visType.editorConfig.optionsTemplate + ? [ + { + name: 'options', + title: i18n.translate('visDefaultEditor.sidebar.tabs.optionsLabel', { + defaultMessage: 'Options', + }), + editor: visType.editorConfig.optionsTemplate, + }, + ] + : visType.editorConfig.optionTabs), + ]; + // set up the first tab as selected + tabs[0].isSelected = true; + + return tabs; + }); + + const setSelectedTab = useCallback((name: string) => { + setOptionTabs((tabs) => tabs.map((tab) => ({ ...tab, isSelected: tab.name === name }))); + }, []); + + return [optionTabs, setSelectedTab]; +}; diff --git a/src/plugins/vis_default_editor/public/default_editor.tsx b/src/plugins/vis_default_editor/public/default_editor.tsx index 60b6ebab5ad8eb..ed94e52ee23999 100644 --- a/src/plugins/vis_default_editor/public/default_editor.tsx +++ b/src/plugins/vis_default_editor/public/default_editor.tsx @@ -20,13 +20,14 @@ import './index.scss'; import React, { useEffect, useRef, useState, useCallback } from 'react'; +import { EventEmitter } from 'events'; import { EditorRenderProps } from 'src/plugins/visualize/public'; +import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public'; import { KibanaContextProvider, PanelsContainer, Panel } from '../../kibana_react/public'; import { Storage } from '../../kibana_utils/public'; import { DefaultEditorSideBar } from './components/sidebar'; -import { DefaultEditorControllerState } from './default_editor_controller'; import { getInitialWidth } from './editor_size'; const localStorage = new Storage(window.localStorage); @@ -38,13 +39,16 @@ function DefaultEditor({ uiState, timeRange, filters, - optionTabs, query, embeddableHandler, eventEmitter, linked, savedSearch, -}: DefaultEditorControllerState & EditorRenderProps) { +}: EditorRenderProps & { + vis: Vis; + eventEmitter: EventEmitter; + embeddableHandler: VisualizeEmbeddableContract; +}) { const visRef = useRef(null); const [isCollapsed, setIsCollapsed] = useState(false); @@ -105,7 +109,6 @@ function DefaultEditor({ embeddableHandler={embeddableHandler} isCollapsed={isCollapsed} onClickCollapse={onClickCollapse} - optionTabs={optionTabs} vis={vis} uiState={uiState} isLinkedSearch={linked} diff --git a/src/plugins/vis_default_editor/public/default_editor_controller.tsx b/src/plugins/vis_default_editor/public/default_editor_controller.tsx index 56fb15ea8354a5..0efd6e7746fd25 100644 --- a/src/plugins/vis_default_editor/public/default_editor_controller.tsx +++ b/src/plugins/vis_default_editor/public/default_editor_controller.tsx @@ -19,63 +19,21 @@ import React, { Suspense, lazy } from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; -import { i18n } from '@kbn/i18n'; import { EventEmitter } from 'events'; import { EuiErrorBoundary, EuiLoadingChart } from '@elastic/eui'; import { EditorRenderProps } from 'src/plugins/visualize/public'; import { Vis, VisualizeEmbeddableContract } from 'src/plugins/visualizations/public'; -import { DefaultEditorDataTab, OptionTab } from './components/sidebar'; const DefaultEditor = lazy(() => import('./default_editor')); -export interface DefaultEditorControllerState { - vis: Vis; - eventEmitter: EventEmitter; - embeddableHandler: VisualizeEmbeddableContract; - optionTabs: OptionTab[]; -} - class DefaultEditorController { - private el: HTMLElement; - private state: DefaultEditorControllerState; - - constructor(el: HTMLElement, vis: Vis, eventEmitter: EventEmitter, embeddableHandler: any) { - this.el = el; - const { type: visType } = vis; - - const optionTabs = [ - ...(visType.schemas.buckets || visType.schemas.metrics - ? [ - { - name: 'data', - title: i18n.translate('visDefaultEditor.sidebar.tabs.dataLabel', { - defaultMessage: 'Data', - }), - editor: DefaultEditorDataTab, - }, - ] - : []), - - ...(!visType.editorConfig.optionTabs && visType.editorConfig.optionsTemplate - ? [ - { - name: 'options', - title: i18n.translate('visDefaultEditor.sidebar.tabs.optionsLabel', { - defaultMessage: 'Options', - }), - editor: visType.editorConfig.optionsTemplate, - }, - ] - : visType.editorConfig.optionTabs), - ]; - this.state = { - vis, - optionTabs, - eventEmitter, - embeddableHandler, - }; - } + constructor( + private el: HTMLElement, + private vis: Vis, + private eventEmitter: EventEmitter, + private embeddableHandler: VisualizeEmbeddableContract + ) {} render(props: EditorRenderProps) { render( @@ -94,7 +52,12 @@ class DefaultEditorController { } > - + , this.el From 36814aa1ef5e8b1e593bbcbfc915075f62bc38c0 Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 1 Oct 2020 13:30:10 +0300 Subject: [PATCH 004/128] Change implementation on TSVB functional when testing the indexPattern switch (#78754) Co-authored-by: Elastic Machine --- test/functional/apps/visualize/_tsvb_chart.ts | 4 ++-- test/functional/page_objects/visual_builder_page.ts | 8 ++++++++ test/functional/services/combo_box.ts | 11 +++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/visualize/_tsvb_chart.ts b/test/functional/apps/visualize/_tsvb_chart.ts index bfe0da7a5b24f8..3e325d5e6b9077 100644 --- a/test/functional/apps/visualize/_tsvb_chart.ts +++ b/test/functional/apps/visualize/_tsvb_chart.ts @@ -135,11 +135,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.visualBuilder.clickPanelOptions('metric'); const fromTime = 'Oct 22, 2018 @ 00:00:00.000'; const toTime = 'Oct 28, 2018 @ 23:59:59.999'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); // Sometimes popovers take some time to appear in Firefox (#71979) await retry.tryForTime(20000, async () => { - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); await PageObjects.visualBuilder.setIndexPatternValue('kibana_sample_data_flights'); - await PageObjects.common.sleep(3000); + await PageObjects.visualBuilder.waitForIndexPatternTimeFieldOptionsLoaded(); await PageObjects.visualBuilder.selectIndexPatternTimeField('timestamp'); }); const newValue = await PageObjects.visualBuilder.getMetricValue(); diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 6e49fd3b03494c..37634d0248b047 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -453,6 +453,14 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro await PageObjects.header.waitUntilLoadingHasFinished(); } + public async waitForIndexPatternTimeFieldOptionsLoaded() { + await retry.waitFor('combobox options loaded', async () => { + const options = await comboBox.getOptions('metricsIndexPatternFieldsSelect'); + log.debug(`-- optionsCount=${options.length}`); + return options.length > 0; + }); + } + public async selectIndexPatternTimeField(timeField: string) { await retry.try(async () => { await comboBox.clearInputField('metricsIndexPatternFieldsSelect'); diff --git a/test/functional/services/combo_box.ts b/test/functional/services/combo_box.ts index ac7a40361d065e..57e18579899501 100644 --- a/test/functional/services/combo_box.ts +++ b/test/functional/services/combo_box.ts @@ -58,6 +58,17 @@ export function ComboBoxProvider({ getService, getPageObjects }: FtrProviderCont return isMouseClick ? await element.clickMouseButton() : await element._webElement.click(); } + /** + * Finds combobox element options + * + * @param comboBoxSelector data-test-subj selector + */ + public async getOptions(comboBoxSelector: string) { + const comboBoxElement = await testSubjects.find(comboBoxSelector); + await this.openOptionsList(comboBoxElement); + return await find.allByCssSelector('.euiFilterSelectItem', WAIT_FOR_EXISTS_TIME); + } + /** * Sets value for specified combobox element * From d11da3275d0b829fa48f76df9864e4913fe0cf22 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 1 Oct 2020 12:54:40 +0200 Subject: [PATCH 005/128] [Lens] Don't allow values outside of range for number of top values (#78734) --- .../{terms.tsx => terms/index.tsx} | 46 ++++----------- .../definitions/{ => terms}/terms.test.tsx | 32 +++++------ .../terms/values_range_input.test.tsx | 56 +++++++++++++++++++ .../definitions/terms/values_range_input.tsx | 50 +++++++++++++++++ 4 files changed, 133 insertions(+), 51 deletions(-) rename x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/{terms.tsx => terms/index.tsx} (87%) rename x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/{ => terms}/terms.test.tsx (95%) create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.test.tsx create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.tsx diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx similarity index 87% rename from x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx rename to x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx index c147029bbd3c77..85deb2bac25cae 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/index.tsx @@ -6,24 +6,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiRange, EuiSelect } from '@elastic/eui'; -import { IndexPatternColumn } from '../../indexpattern'; -import { updateColumnParam } from '../../state_helpers'; -import { DataType } from '../../../types'; -import { OperationDefinition } from './index'; -import { FieldBasedIndexPatternColumn } from './column_types'; - -type PropType = C extends React.ComponentType ? P : unknown; - -// Add ticks to EuiRange component props -const FixedEuiRange = (EuiRange as unknown) as React.ComponentType< - PropType & { - ticks?: Array<{ - label: string; - value: number; - }>; - } ->; +import { EuiFormRow, EuiSelect } from '@elastic/eui'; +import { IndexPatternColumn } from '../../../indexpattern'; +import { updateColumnParam } from '../../../state_helpers'; +import { DataType } from '../../../../types'; +import { OperationDefinition } from '../index'; +import { FieldBasedIndexPatternColumn } from '../column_types'; +import { ValuesRangeInput } from './values_range_input'; function ofName(name: string) { return i18n.translate('xpack.lens.indexPattern.termsOf', { @@ -182,30 +171,19 @@ export const termsOperation: OperationDefinition - | React.MouseEvent - ) => + onChange={(value) => { setState( updateColumnParam({ state, layerId, currentColumn, paramName: 'size', - value: Number((e.target as HTMLInputElement).value), + value, }) - ) - } - aria-label={i18n.translate('xpack.lens.indexPattern.terms.size', { - defaultMessage: 'Number of values', - })} + ); + }} /> { it('should render current size value', () => { const setStateSpy = jest.fn(); - const instance = shallow( + const instance = mount( { /> ); - expect(instance.find(EuiRange).prop('value')).toEqual(3); + expect(instance.find(EuiRange).prop('value')).toEqual('3'); }); it('should update state with the size value', () => { const setStateSpy = jest.fn(); - const instance = shallow( + const instance = mount( { /> ); - instance.find(EuiRange).prop('onChange')!( - { - target: { - value: '7', - }, - } as React.ChangeEvent, - true - ); + act(() => { + instance.find(ValuesRangeInput).prop('onChange')!(7); + }); + expect(setStateSpy).toHaveBeenCalledWith({ ...state, layers: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.test.tsx new file mode 100644 index 00000000000000..c1620dd316a609 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.test.tsx @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { act } from 'react-dom/test-utils'; +import { shallow } from 'enzyme'; +import { EuiRange } from '@elastic/eui'; +import { ValuesRangeInput } from './values_range_input'; + +jest.mock('react-use', () => ({ + useDebounce: (fn: () => void) => fn(), +})); + +describe('ValuesRangeInput', () => { + it('should render EuiRange correctly', () => { + const onChangeSpy = jest.fn(); + const instance = shallow(); + + expect(instance.find(EuiRange).prop('value')).toEqual('5'); + }); + + it('should run onChange function on update', () => { + const onChangeSpy = jest.fn(); + const instance = shallow(); + act(() => { + instance.find(EuiRange).prop('onChange')!( + { currentTarget: { value: '7' } } as React.ChangeEvent, + true + ); + }); + expect(instance.find(EuiRange).prop('value')).toEqual('7'); + // useDebounce runs on initialization and on change + expect(onChangeSpy.mock.calls.length).toBe(2); + expect(onChangeSpy.mock.calls[0][0]).toBe(5); + expect(onChangeSpy.mock.calls[1][0]).toBe(7); + }); + it('should not run onChange function on update when value is out of 1-100 range', () => { + const onChangeSpy = jest.fn(); + const instance = shallow(); + act(() => { + instance.find(EuiRange).prop('onChange')!( + { currentTarget: { value: '107' } } as React.ChangeEvent, + true + ); + }); + instance.update(); + expect(instance.find(EuiRange).prop('value')).toEqual('107'); + // useDebounce only runs on initialization + expect(onChangeSpy.mock.calls.length).toBe(2); + expect(onChangeSpy.mock.calls[0][0]).toBe(5); + expect(onChangeSpy.mock.calls[1][0]).toBe(100); + }); +}); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.tsx new file mode 100644 index 00000000000000..6bfde4b652571b --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/terms/values_range_input.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { useDebounce } from 'react-use'; +import { i18n } from '@kbn/i18n'; +import { EuiRange } from '@elastic/eui'; + +export const ValuesRangeInput = ({ + value, + onChange, +}: { + value: number; + onChange: (value: number) => void; +}) => { + const MIN_NUMBER_OF_VALUES = 1; + const MAX_NUMBER_OF_VALUES = 100; + + const [inputValue, setInputValue] = useState(String(value)); + useDebounce( + () => { + if (inputValue === '') { + return; + } + const inputNumber = Number(inputValue); + onChange(Math.min(MAX_NUMBER_OF_VALUES, Math.max(inputNumber, MIN_NUMBER_OF_VALUES))); + }, + 256, + [inputValue] + ); + + return ( + setInputValue(currentTarget.value)} + aria-label={i18n.translate('xpack.lens.indexPattern.terms.size', { + defaultMessage: 'Number of values', + })} + /> + ); +}; From 8948811c634ba2e4c7a4c9881b6834f7577440a0 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 1 Oct 2020 12:54:48 +0200 Subject: [PATCH 006/128] fix: add EuiOutsideClickDetector (#78733) Co-authored-by: Elastic Machine --- .../config_panel/dimension_container.tsx | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index d6b395ac74cced..a415eb44cf196a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -13,6 +13,7 @@ import { EuiButtonEmpty, EuiFlexItem, EuiFocusTrap, + EuiOutsideClickDetector, } from '@elastic/eui'; import classNames from 'classnames'; @@ -91,37 +92,39 @@ export function DimensionContainer({ const flyout = flyoutIsVisible && ( -
- - - - {panelTitle} + +
+ + + + {panelTitle} + + + + + {panel} + + + + {i18n.translate('xpack.lens.dimensionContainer.close', { + defaultMessage: 'Close', + })} - - - - {panel} - - - - {i18n.translate('xpack.lens.dimensionContainer.close', { - defaultMessage: 'Close', - })} - - -
+ +
+
); From 3024513e107a7411a0a6b4aadc5ba362f13ce0e2 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 1 Oct 2020 14:05:59 +0300 Subject: [PATCH 007/128] [KP] instrument platform server-side code with apm agent (#70919) * instrument platform server-side code with apm agent: setup, start lifecycles and SO migration * add span type * span should have name: saved_objects.migration * remove migration reports * put migration span back --- src/core/server/server.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 5935636d54f9d1..4e5a7a328bed4c 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ - +import apm from 'elastic-apm-node'; import { config as pathConfig } from '@kbn/utils'; import { mapToObject } from '@kbn/std'; import { ConfigService, Env, RawConfigurationProvider, coreDeprecationProvider } from './config'; @@ -106,6 +106,7 @@ export class Server { public async setup() { this.log.debug('setting up server'); + const setupTransaction = apm.startTransaction('server_setup', 'kibana_platform'); const environmentSetup = await this.environment.setup(); @@ -207,20 +208,25 @@ export class Server { this.registerCoreContext(coreSetup); this.coreApp.setup(coreSetup); + setupTransaction?.end(); return coreSetup; } public async start() { this.log.debug('starting server'); + const startTransaction = apm.startTransaction('server_start', 'kibana_platform'); + const auditTrailStart = this.auditTrail.start(); const elasticsearchStart = await this.elasticsearch.start({ auditTrail: auditTrailStart, }); + const soStartSpan = startTransaction?.startSpan('saved_objects.migration', 'migration'); const savedObjectsStart = await this.savedObjects.start({ elasticsearch: elasticsearchStart, pluginsInitialized: this.#pluginsInitialized, }); + soStartSpan?.end(); const capabilitiesStart = this.capabilities.start(); const uiSettingsStart = await this.uiSettings.start(); const metricsStart = await this.metrics.start(); @@ -248,6 +254,7 @@ export class Server { await this.http.start(); + startTransaction?.end(); return this.coreStart; } From cbc83003d35a3b0016d241100fb12d357c4bbc09 Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 1 Oct 2020 14:21:34 +0300 Subject: [PATCH 008/128] [Actions][Jira] Fix bug with Jira sub-task (#79070) --- .../builtin_action_types/jira/service.test.ts | 2 +- .../builtin_action_types/jira/service.ts | 5 +- .../builtin_action_types/jira/api.test.ts | 159 ++++++++++++++++++ .../builtin_action_types/jira/api.ts | 2 +- .../jira/use_get_fields_by_issue_type.tsx | 1 + .../jira/use_get_issue_types.tsx | 1 + .../jira/use_get_issues.tsx | 1 + .../jira/use_get_single_issue.tsx | 3 +- .../resilient/use_get_incident_types.tsx | 1 + .../resilient/use_get_severity.tsx | 1 + 10 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts index 605c05e2a9f259..fe4e135c76fc37 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.test.ts @@ -965,7 +965,7 @@ describe('Jira service', () => { axios, logger, method: 'get', - url: `https://siem-kibana.atlassian.net/rest/api/2/search?jql=project=CK and summary ~"Test title"`, + url: `https://siem-kibana.atlassian.net/rest/api/2/search?jql=project%3D%22CK%22%20and%20summary%20~%22Test%20title%22`, }); }); diff --git a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts index 7429c3d36d7b0b..f52d3fa2efd376 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/jira/service.ts @@ -396,7 +396,10 @@ export const createExternalService = ( }; const getIssues = async (title: string) => { - const query = `${searchUrl}?jql=project=${projectKey} and summary ~"${title}"`; + const query = `${searchUrl}?jql=${encodeURIComponent( + `project="${projectKey}" and summary ~"${title}"` + )}`; + try { const res = await request({ axios: axiosInstance, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts new file mode 100644 index 00000000000000..d5474aaceaa480 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.test.ts @@ -0,0 +1,159 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { httpServiceMock } from '../../../../../../../../src/core/public/mocks'; +import { getIssueTypes, getFieldsByIssueType, getIssues, getIssue } from './api'; + +const issueTypesResponse = { + data: { + projects: [ + { + issuetypes: [ + { + id: '10006', + name: 'Task', + }, + { + id: '10007', + name: 'Bug', + }, + ], + }, + ], + }, +}; + +const fieldsResponse = { + data: { + projects: [ + { + issuetypes: [ + { + id: '10006', + name: 'Task', + fields: { + summary: { fieldId: 'summary' }, + priority: { + fieldId: 'priority', + allowedValues: [ + { + name: 'Highest', + id: '1', + }, + { + name: 'High', + id: '2', + }, + { + name: 'Medium', + id: '3', + }, + { + name: 'Low', + id: '4', + }, + { + name: 'Lowest', + id: '5', + }, + ], + defaultValue: { + name: 'Medium', + id: '3', + }, + }, + }, + }, + ], + }, + ], + }, +}; + +const issueResponse = { + id: '10267', + key: 'RJ-107', + fields: { summary: 'Test title' }, +}; + +const issuesResponse = [issueResponse]; + +describe('Jira API', () => { + const http = httpServiceMock.createStartContract(); + + beforeEach(() => jest.resetAllMocks()); + + describe('getIssueTypes', () => { + test('should call get issue types API', async () => { + const abortCtrl = new AbortController(); + http.post.mockResolvedValueOnce(issueTypesResponse); + const res = await getIssueTypes({ http, signal: abortCtrl.signal, connectorId: 'test' }); + + expect(res).toEqual(issueTypesResponse); + expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { + body: '{"params":{"subAction":"issueTypes","subActionParams":{}}}', + signal: abortCtrl.signal, + }); + }); + }); + + describe('getFieldsByIssueType', () => { + test('should call get fields API', async () => { + const abortCtrl = new AbortController(); + http.post.mockResolvedValueOnce(fieldsResponse); + const res = await getFieldsByIssueType({ + http, + signal: abortCtrl.signal, + connectorId: 'test', + id: '10006', + }); + + expect(res).toEqual(fieldsResponse); + expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { + body: '{"params":{"subAction":"fieldsByIssueType","subActionParams":{"id":"10006"}}}', + signal: abortCtrl.signal, + }); + }); + }); + + describe('getIssues', () => { + test('should call get fields API', async () => { + const abortCtrl = new AbortController(); + http.post.mockResolvedValueOnce(issuesResponse); + const res = await getIssues({ + http, + signal: abortCtrl.signal, + connectorId: 'test', + title: 'test issue', + }); + + expect(res).toEqual(issuesResponse); + expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { + body: '{"params":{"subAction":"issues","subActionParams":{"title":"test issue"}}}', + signal: abortCtrl.signal, + }); + }); + }); + + describe('getIssue', () => { + test('should call get fields API', async () => { + const abortCtrl = new AbortController(); + http.post.mockResolvedValueOnce(issuesResponse); + const res = await getIssue({ + http, + signal: abortCtrl.signal, + connectorId: 'test', + id: 'RJ-107', + }); + + expect(res).toEqual(issuesResponse); + expect(http.post).toHaveBeenCalledWith('/api/actions/action/test/_execute', { + body: '{"params":{"subAction":"issue","subActionParams":{"id":"RJ-107"}}}', + signal: abortCtrl.signal, + }); + }); + }); +}); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts index bc9fee042a9a67..c209244c64404a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/api.ts @@ -75,7 +75,7 @@ export async function getIssue({ }): Promise> { return await http.post(`${BASE_ACTION_API_PATH}/action/${connectorId}/_execute`, { body: JSON.stringify({ - params: { subAction: 'getIncident', subActionParams: { id } }, + params: { subAction: 'issue', subActionParams: { id } }, }), signal, }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_fields_by_issue_type.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_fields_by_issue_type.tsx index 08715822e5277b..8685ee1e615b05 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_fields_by_issue_type.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_fields_by_issue_type.tsx @@ -72,6 +72,7 @@ export const useGetFieldsByIssueType = ({ } } catch (error) { if (!didCancel) { + setIsLoading(false); toastNotifications.addDanger({ title: i18n.FIELDS_API_ERROR, text: error.message, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issue_types.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issue_types.tsx index 9ebaf5882d9b95..bdc9a575074418 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issue_types.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issue_types.tsx @@ -65,6 +65,7 @@ export const useGetIssueTypes = ({ } } catch (error) { if (!didCancel) { + setIsLoading(false); toastNotifications.addDanger({ title: i18n.ISSUE_TYPES_API_ERROR, text: error.message, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx index d6590b8c70939a..8015390d29e3c8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_issues.tsx @@ -69,6 +69,7 @@ export const useGetIssues = ({ } } catch (error) { if (!didCancel) { + setIsLoading(false); toastNotifications.addDanger({ title: i18n.ISSUES_API_ERROR, text: error.message, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx index 7df9834f1bd850..c0d2eae14beadd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/use_get_single_issue.tsx @@ -61,7 +61,7 @@ export const useGetSingleIssue = ({ if (!didCancel) { setIsLoading(false); - setIssue(res.data ?? {}); + setIssue(res.data ?? null); if (res.status && res.status === 'error') { toastNotifications.addDanger({ title: i18n.GET_ISSUE_API_ERROR(id), @@ -71,6 +71,7 @@ export const useGetSingleIssue = ({ } } catch (error) { if (!didCancel) { + setIsLoading(false); toastNotifications.addDanger({ title: i18n.GET_ISSUE_API_ERROR(id), text: error.message, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx index 219c6ac77d08dc..c2a2268ddb7364 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_incident_types.tsx @@ -65,6 +65,7 @@ export const useGetIncidentTypes = ({ } } catch (error) { if (!didCancel) { + setIsLoading(false); toastNotifications.addDanger({ title: i18n.INCIDENT_TYPES_API_ERROR, text: error.message, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx index 83689254f000fd..a06fafcf8c10e7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/use_get_severity.tsx @@ -66,6 +66,7 @@ export const useGetSeverity = ({ } } catch (error) { if (!didCancel) { + setIsLoading(false); toastNotifications.addDanger({ title: i18n.SEVERITY_API_ERROR, text: error.message, From 8d7f2d0828e463b2dadaf10bdff23910401fb134 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 1 Oct 2020 13:39:10 +0200 Subject: [PATCH 009/128] [Lens] Handle missing fields gracefully (#78173) --- .../dimension_panel/bucket_nesting_editor.tsx | 9 +- .../dimension_panel/dimension_editor.tsx | 210 ++++++++++-------- .../dimension_panel/dimension_panel.test.tsx | 30 +++ .../dimension_panel/dimension_panel.tsx | 61 ++++- .../dimension_panel/field_select.tsx | 8 +- .../indexpattern_suggestions.test.tsx | 42 +++- .../indexpattern_suggestions.ts | 4 +- .../public/indexpattern_datasource/utils.ts | 36 +++ 8 files changed, 295 insertions(+), 105 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx index 3d692b1f7f5a8a..962abd8d943db8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/bucket_nesting_editor.tsx @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { EuiFormRow, EuiHorizontalRule, EuiRadio, EuiSelect, htmlIdGenerator } from '@elastic/eui'; import { IndexPatternLayer, IndexPatternField } from '../types'; import { hasField } from '../utils'; +import { IndexPatternColumn } from '../operations'; const generator = htmlIdGenerator('lens-nesting'); @@ -21,6 +22,10 @@ function nestColumn(columnOrder: string[], outer: string, inner: string) { return result; } +function getFieldName(fieldMap: Record, column: IndexPatternColumn) { + return hasField(column) ? fieldMap[column.sourceField]?.displayName || column.sourceField : ''; +} + export function BucketNestingEditor({ columnId, layer, @@ -39,7 +44,7 @@ export function BucketNestingEditor({ .map(([value, c]) => ({ value, text: c.label, - fieldName: hasField(c) ? fieldMap[c.sourceField].displayName : '', + fieldName: getFieldName(fieldMap, c), operationType: c.operationType, })); @@ -47,7 +52,7 @@ export function BucketNestingEditor({ return null; } - const fieldName = hasField(column) ? fieldMap[column.sourceField].displayName : ''; + const fieldName = getFieldName(fieldMap, column); const prevColumn = layer.columnOrder[layer.columnOrder.indexOf(columnId) - 1]; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index bd99bd16a63a88..b0d24928b794ea 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -26,7 +26,7 @@ import { } from '../operations'; import { deleteColumn, changeColumn, updateColumnParam } from '../state_helpers'; import { FieldSelect } from './field_select'; -import { hasField } from '../utils'; +import { hasField, fieldIsInvalid } from '../utils'; import { BucketNestingEditor } from './bucket_nesting_editor'; import { IndexPattern, IndexPatternField } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; @@ -132,6 +132,15 @@ export function DimensionEditor(props: DimensionEditorProps) { }; }); + const selectedColumnSourceField = + selectedColumn && 'sourceField' in selectedColumn ? selectedColumn.sourceField : undefined; + + const currentFieldIsInvalid = useMemo( + () => + fieldIsInvalid(selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern), + [selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern] + ); + const sideNavItems: EuiListGroupItemProps[] = operationsWithCompatibility.map( ({ operationType, compatibleWithCurrentField }) => { const isActive = Boolean( @@ -271,20 +280,16 @@ export function DimensionEditor(props: DimensionEditorProps) { defaultMessage: 'Choose a field', })} fullWidth - isInvalid={Boolean(incompatibleSelectedOperationType)} - error={ - selectedColumn && incompatibleSelectedOperationType - ? selectedOperationDefinition?.input === 'field' - ? i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', { - defaultMessage: 'To use this function, select a different field.', - }) - : i18n.translate('xpack.lens.indexPattern.chooseFieldLabel', { - defaultMessage: 'To use this function, select a field.', - }) - : undefined - } + isInvalid={Boolean(incompatibleSelectedOperationType || currentFieldIsInvalid)} + error={getErrorMessage( + selectedColumn, + Boolean(incompatibleSelectedOperationType), + selectedOperationDefinition?.input, + currentFieldIsInvalid + )} > ) : null} - {!incompatibleSelectedOperationType && selectedColumn && ParamEditor && ( - <> - - - )} + {!currentFieldIsInvalid && + !incompatibleSelectedOperationType && + selectedColumn && + ParamEditor && ( + <> + + + )} -
- {!incompatibleSelectedOperationType && selectedColumn && ( - { - setState({ - ...state, - layers: { - ...state.layers, - [layerId]: { - ...state.layers[layerId], - columns: { - ...state.layers[layerId].columns, - [columnId]: { - ...selectedColumn, - label: value, - customLabel: true, + {!currentFieldIsInvalid && ( +
+ {!incompatibleSelectedOperationType && selectedColumn && ( + { + setState({ + ...state, + layers: { + ...state.layers, + [layerId]: { + ...state.layers[layerId], + columns: { + ...state.layers[layerId].columns, + [columnId]: { + ...selectedColumn, + label: value, + customLabel: true, + }, }, }, }, - }, - }); - }} - /> - )} - - {!hideGrouping && ( - { - setState({ - ...state, - layers: { - ...state.layers, - [props.layerId]: { - ...state.layers[props.layerId], - columnOrder, + }); + }} + /> + )} + + {!hideGrouping && ( + { + setState({ + ...state, + layers: { + ...state.layers, + [props.layerId]: { + ...state.layers[props.layerId], + columnOrder, + }, }, - }, - }); - }} - /> - )} - - {selectedColumn && selectedColumn.dataType === 'number' ? ( - { - setState( - updateColumnParam({ - state, - layerId, - currentColumn: selectedColumn, - paramName: 'format', - value: newFormat, - }) - ); - }} - /> - ) : null} -
+ }); + }} + /> + )} + + {selectedColumn && selectedColumn.dataType === 'number' ? ( + { + setState( + updateColumnParam({ + state, + layerId, + currentColumn: selectedColumn, + paramName: 'format', + value: newFormat, + }) + ); + }} + /> + ) : null} +
+ )} ); } +function getErrorMessage( + selectedColumn: IndexPatternColumn | undefined, + incompatibleSelectedOperationType: boolean, + input: 'none' | 'field' | undefined, + fieldInvalid: boolean +) { + if (selectedColumn && incompatibleSelectedOperationType) { + if (input === 'field') { + return i18n.translate('xpack.lens.indexPattern.invalidOperationLabel', { + defaultMessage: 'To use this function, select a different field.', + }); + } + return i18n.translate('xpack.lens.indexPattern.chooseFieldLabel', { + defaultMessage: 'To use this function, select a field.', + }); + } + if (fieldInvalid) { + return i18n.translate('xpack.lens.indexPattern.invalidFieldLabel', { + defaultMessage: 'Invalid field. Check your index pattern or pick another field.', + }); + } +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx index 270f9d9f670634..d15825718682cd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.test.tsx @@ -25,6 +25,7 @@ import { IndexPatternPrivateState } from '../types'; import { IndexPatternColumn } from '../operations'; import { documentField } from '../document_field'; import { OperationMetadata } from '../../types'; +import { DateHistogramIndexPatternColumn } from '../operations/definitions/date_histogram'; jest.mock('../loader'); jest.mock('../state_helpers'); @@ -801,6 +802,35 @@ describe('IndexPatternDimensionEditorPanel', () => { }); }); + it('should render invalid field if field reference is broken', () => { + wrapper = mount( + + ); + + expect(wrapper.find(EuiComboBox).prop('selectedOptions')).toEqual([ + { + label: 'nonexistent', + value: { type: 'field', field: 'nonexistent' }, + }, + ]); + }); + it('should support selecting the operation before the field', () => { wrapper = mount(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index c4d8300722f83c..6f0a9c2a86acd3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -5,9 +5,9 @@ */ import _ from 'lodash'; -import React, { memo } from 'react'; +import React, { memo, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiLink } from '@elastic/eui'; +import { EuiLink, EuiIcon, EuiToolTip, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { @@ -22,7 +22,7 @@ import { IndexPatternColumn, OperationType } from '../indexpattern'; import { getAvailableOperationsByMetadata, buildColumn, changeField } from '../operations'; import { DimensionEditor } from './dimension_editor'; import { changeColumn } from '../state_helpers'; -import { isDraggedField, hasField } from '../utils'; +import { isDraggedField, hasField, fieldIsInvalid } from '../utils'; import { IndexPatternPrivateState, IndexPatternField } from '../types'; import { trackUiEvent } from '../../lens_ui_telemetry'; import { DateRange } from '../../../common'; @@ -233,14 +233,63 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens props: IndexPatternDimensionTriggerProps ) { const layerId = props.layerId; - - const selectedColumn: IndexPatternColumn | null = - props.state.layers[layerId].columns[props.columnId] || null; + const layer = props.state.layers[layerId]; + const selectedColumn: IndexPatternColumn | null = layer.columns[props.columnId] || null; + const currentIndexPattern = props.state.indexPatterns[layer.indexPatternId]; + + const selectedColumnSourceField = + selectedColumn && 'sourceField' in selectedColumn ? selectedColumn.sourceField : undefined; + const currentFieldIsInvalid = useMemo( + () => + fieldIsInvalid(selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern), + [selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern] + ); const { columnId, uniqueLabel } = props; if (!selectedColumn) { return null; } + + if (currentFieldIsInvalid) { + return ( + + {i18n.translate('xpack.lens.configure.invalidConfigTooltip', { + defaultMessage: 'Invalid configuration.', + })} +
+ {i18n.translate('xpack.lens.configure.invalidConfigTooltipClick', { + defaultMessage: 'Click for more details.', + })} +

+ } + anchorClassName="lnsLayerPanel__anchor" + > + + + + + + {selectedColumn.label} + + +
+ ); + } + return ( { onChoose: (choice: FieldChoice) => void; onDeleteColumn: () => void; existingFields: IndexPatternPrivateState['existingFields']; + fieldIsInvalid: boolean; } export function FieldSelect({ @@ -53,6 +54,7 @@ export function FieldSelect({ onChoose, onDeleteColumn, existingFields, + fieldIsInvalid, ...rest }: FieldSelectProps) { const { operationByField } = operationSupportMatrix; @@ -171,12 +173,14 @@ export function FieldSelect({ defaultMessage: 'Field', })} options={(memoizedFieldOptions as unknown) as EuiComboBoxOptionOption[]} - isInvalid={Boolean(incompatibleSelectedOperationType)} + isInvalid={Boolean(incompatibleSelectedOperationType || fieldIsInvalid)} selectedOptions={ ((selectedColumnOperationType && selectedColumnSourceField ? [ { - label: fieldMap[selectedColumnSourceField].displayName, + label: fieldIsInvalid + ? selectedColumnSourceField + : fieldMap[selectedColumnSourceField]?.displayName, value: { type: 'field', field: selectedColumnSourceField }, }, ] diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx index 663d7c18bb370d..80765627c1fc29 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.test.tsx @@ -147,7 +147,7 @@ function testInitialState(): IndexPatternPrivateState { // Private operationType: 'terms', - sourceField: 'op', + sourceField: 'dest', params: { size: 5, orderBy: { type: 'alphabetical' }, @@ -1115,7 +1115,7 @@ describe('IndexPattern Data Source suggestions', () => { // Private operationType: 'terms', - sourceField: 'op', + sourceField: 'dest', params: { size: 5, orderBy: { type: 'alphabetical' }, @@ -1615,7 +1615,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, operationType: 'date_histogram', - sourceField: 'field2', + sourceField: 'timestamp', params: { interval: 'd', }, @@ -1626,7 +1626,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: true, operationType: 'terms', - sourceField: 'field1', + sourceField: 'dest', params: { size: 5, orderBy: { type: 'alphabetical' }, orderDirection: 'asc' }, }, id3: { @@ -1635,7 +1635,7 @@ describe('IndexPattern Data Source suggestions', () => { isBucketed: false, operationType: 'avg', - sourceField: 'field1', + sourceField: 'bytes', }, }, columnOrder: ['id1', 'id2', 'id3'], @@ -1652,6 +1652,38 @@ describe('IndexPattern Data Source suggestions', () => { }) ); }); + + it('does not generate suggestions if invalid fields are referenced', () => { + const initialState = testInitialState(); + const state: IndexPatternPrivateState = { + indexPatternRefs: [], + existingFields: {}, + currentIndexPatternId: '1', + indexPatterns: expectedIndexPatterns, + isFirstExistenceFetch: false, + layers: { + first: { + ...initialState.layers.first, + columns: { + ...initialState.layers.first.columns, + col2: { + label: 'Top 5', + dataType: 'string', + isBucketed: true, + + operationType: 'terms', + sourceField: 'nonExistingField', + params: { size: 5, orderBy: { type: 'alphabetical' }, orderDirection: 'asc' }, + }, + }, + columnOrder: ['col1', 'col2'], + }, + }, + }; + + const suggestions = getDatasourceSuggestionsFromCurrentState(state); + expect(suggestions).toEqual([]); + }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts index f5e64149c2c769..75945529ffb340 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern_suggestions.ts @@ -18,7 +18,7 @@ import { } from './operations'; import { operationDefinitions } from './operations/definitions'; import { TermsIndexPatternColumn } from './operations/definitions/terms'; -import { hasField } from './utils'; +import { hasField, hasInvalidReference } from './utils'; import { IndexPattern, IndexPatternPrivateState, @@ -90,6 +90,7 @@ export function getDatasourceSuggestionsForField( indexPatternId: string, field: IndexPatternField ): IndexPatternSugestion[] { + if (hasInvalidReference(state)) return []; const layers = Object.keys(state.layers); const layerIds = layers.filter((id) => state.layers[id].indexPatternId === indexPatternId); @@ -380,6 +381,7 @@ function createNewLayerWithMetricAggregation( export function getDatasourceSuggestionsFromCurrentState( state: IndexPatternPrivateState ): Array> { + if (hasInvalidReference(state)) return []; const layers = Object.entries(state.layers || {}); if (layers.length > 1) { // Return suggestions that reduce the data to each layer individually diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts index 374dbe77b4ca37..f1d2e7765d99fa 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/utils.ts @@ -5,11 +5,13 @@ */ import { DataType } from '../types'; +import { IndexPatternPrivateState, IndexPattern } from './types'; import { DraggedField } from './indexpattern'; import { BaseIndexPatternColumn, FieldBasedIndexPatternColumn, } from './operations/definitions/column_types'; +import { operationDefinitionMap, OperationType } from './operations'; /** * Normalizes the specified operation type. (e.g. document operations @@ -40,3 +42,37 @@ export function isDraggedField(fieldCandidate: unknown): fieldCandidate is Dragg 'indexPatternId' in fieldCandidate ); } + +export function hasInvalidReference(state: IndexPatternPrivateState) { + return Object.values(state.layers).some((layer) => { + return layer.columnOrder.some((columnId) => { + const column = layer.columns[columnId]; + return ( + hasField(column) && + fieldIsInvalid( + column.sourceField, + column.operationType, + state.indexPatterns[layer.indexPatternId] + ) + ); + }); + }); +} + +export function fieldIsInvalid( + sourceField: string | undefined, + operationType: OperationType | undefined, + indexPattern: IndexPattern +) { + const operationDefinition = operationType && operationDefinitionMap[operationType]; + return Boolean( + sourceField && + operationDefinition && + !indexPattern.fields.some( + (field) => + field.name === sourceField && + operationDefinition.input === 'field' && + operationDefinition.getPossibleOperationForField(field) !== undefined + ) + ); +} From 4d5a9df76c8aa37b19ace411d72d1cbf941fcb9a Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 1 Oct 2020 13:49:05 +0200 Subject: [PATCH 010/128] @testing-library remove "pure" hack (#78742) Co-authored-by: Elastic Machine --- src/dev/jest/setup/react_testing_library.js | 11 +++++------ .../lib/embeddables/embeddable_renderer.test.tsx | 4 +--- .../public/lib/embeddables/error_embeddable.test.tsx | 5 +---- .../public/markdown_vis_controller.test.tsx | 5 +---- .../actions/flyout_edit_drilldown/menu_item.test.tsx | 4 +--- .../components/action_wizard/action_wizard.test.tsx | 6 +----- .../connected_flyout_manage_drilldowns.test.tsx | 5 +---- .../components/flyout_frame/flyout_frame.test.tsx | 4 +--- .../form_drilldown_wizard.test.tsx | 4 +--- .../list_manage_drilldowns.test.tsx | 6 +----- .../url_drilldown_collect_config.test.tsx | 4 +--- 11 files changed, 15 insertions(+), 43 deletions(-) diff --git a/src/dev/jest/setup/react_testing_library.js b/src/dev/jest/setup/react_testing_library.js index 84b5b6096e79b0..90f73b04dc210f 100644 --- a/src/dev/jest/setup/react_testing_library.js +++ b/src/dev/jest/setup/react_testing_library.js @@ -19,14 +19,13 @@ import '@testing-library/jest-dom'; /** - * Have to import "/pure" here to not register afterEach() hook clean up - * in the very beginning. There are couple tests which fail with clean up hook. - * On CI they run before first test which imports '@testing-library/react' - * and registers afterEach hook so the whole suite is passing. - * This have to be fixed as we depend on test order execution + * PLEASE NOTE: + * Importing '@testing-library/react' registers an `afterEach(cleanup)` side effect. + * It has tricky code that flushes pending promises, that previously led to unpredictable test failures * https://github.com/elastic/kibana/issues/59469 + * But since newer versions it has stabilised itself */ -import { configure } from '@testing-library/react/pure'; +import { configure } from '@testing-library/react'; // instead of default 'data-testid', use kibana's 'data-test-subj' configure({ testIdAttribute: 'data-test-subj', asyncUtilTimeout: 4500 }); diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_renderer.test.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable_renderer.test.tsx index 51213288e47a75..f9be9d5bfade70 100644 --- a/src/plugins/embeddable/public/lib/embeddables/embeddable_renderer.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_renderer.test.tsx @@ -19,7 +19,7 @@ import React from 'react'; import { wait } from '@testing-library/dom'; -import { cleanup, render } from '@testing-library/react/pure'; +import { render } from '@testing-library/react'; import { HelloWorldEmbeddable, HelloWorldEmbeddableFactoryDefinition, @@ -29,8 +29,6 @@ import { EmbeddableRenderer } from './embeddable_renderer'; import { embeddablePluginMock } from '../../mocks'; describe('', () => { - afterEach(cleanup); - test('Render embeddable', () => { const embeddable = new HelloWorldEmbeddable({ id: 'hello' }); const { getByTestId } = render(); diff --git a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx index 17a2ac3b2a32b7..cb14d7ed11dc91 100644 --- a/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx +++ b/src/plugins/embeddable/public/lib/embeddables/error_embeddable.test.tsx @@ -17,13 +17,10 @@ * under the License. */ import React from 'react'; -import { wait } from '@testing-library/dom'; -import { cleanup, render } from '@testing-library/react/pure'; +import { wait, render } from '@testing-library/react'; import { ErrorEmbeddable } from './error_embeddable'; import { EmbeddableRoot } from './embeddable_root'; -afterEach(cleanup); - test('ErrorEmbeddable renders an embeddable', async () => { const embeddable = new ErrorEmbeddable('some error occurred', { id: '123', title: 'Error' }); const { getByTestId, getByText } = render(); diff --git a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx index 36850fc820ded5..7bc8cdbd141708 100644 --- a/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx +++ b/src/plugins/vis_type_markdown/public/markdown_vis_controller.test.tsx @@ -18,12 +18,9 @@ */ import React from 'react'; -import { wait } from '@testing-library/dom'; -import { render, cleanup } from '@testing-library/react/pure'; +import { wait, render } from '@testing-library/react'; import MarkdownVisComponent from './markdown_vis_controller'; -afterEach(cleanup); - describe('markdown vis controller', () => { it('should set html from markdown params', async () => { const vis = { diff --git a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/menu_item.test.tsx b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/menu_item.test.tsx index 771b15e46ad258..27a8d73f329446 100644 --- a/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/menu_item.test.tsx +++ b/x-pack/plugins/dashboard_enhanced/public/services/drilldowns/actions/flyout_edit_drilldown/menu_item.test.tsx @@ -5,14 +5,12 @@ */ import React from 'react'; -import { render, cleanup, act } from '@testing-library/react/pure'; +import { render, act } from '@testing-library/react'; import { MenuItem } from './menu_item'; import { createStateContainer } from '../../../../../../../../src/plugins/kibana_utils/public'; import { UiActionsEnhancedDynamicActionManager as DynamicActionManager } from '../../../../../../ui_actions_enhanced/public'; import { EnhancedEmbeddable } from '../../../../../../embeddable_enhanced/public'; -afterEach(cleanup); - test('', () => { const state = createStateContainer<{ events: object[] }>({ events: [] }); const { getByText, queryByText } = render( diff --git a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.test.tsx index 26033b7f020add..11ccb0d5f0c2d7 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/components/action_wizard/action_wizard.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { cleanup, fireEvent, render } from '@testing-library/react/pure'; +import { fireEvent, render } from '@testing-library/react'; import { TEST_SUBJ_ACTION_FACTORY_ITEM, TEST_SUBJ_SELECTED_ACTION_FACTORY } from './action_wizard'; import { dashboardFactory, @@ -17,10 +17,6 @@ import { import { ActionFactory } from '../../dynamic_actions'; import { licensingMock } from '../../../../licensing/public/mocks'; -// TODO: afterEach is not available for it globally during setup -// https://github.com/elastic/kibana/issues/59469 -afterEach(cleanup); - test('Pick and configure action', () => { const screen = render(); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx index a546fabfbbc014..48dbd5a8641700 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/connected_flyout_manage_drilldowns/connected_flyout_manage_drilldowns.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { cleanup, fireEvent, render, wait } from '@testing-library/react/pure'; +import { fireEvent, render, wait, cleanup } from '@testing-library/react'; import { createFlyoutManageDrilldowns } from './connected_flyout_manage_drilldowns'; import { mockGetTriggerInfo, @@ -30,9 +30,6 @@ const FlyoutManageDrilldowns = createFlyoutManageDrilldowns({ getTrigger: mockGetTriggerInfo, }); -// https://github.com/elastic/kibana/issues/59469 -afterEach(cleanup); - beforeEach(() => { storage.clear(); mockDynamicActionManager.state.set({ ...mockDynamicActionManager.state.get(), events: [] }); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.test.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.test.tsx index cdbf36d81de334..86679d393b17fc 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.test.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/flyout_frame/flyout_frame.test.tsx @@ -6,11 +6,9 @@ import React from 'react'; import { render } from 'react-dom'; -import { render as renderTestingLibrary, fireEvent, cleanup } from '@testing-library/react/pure'; +import { render as renderTestingLibrary, fireEvent } from '@testing-library/react'; import { FlyoutFrame } from './index'; -afterEach(cleanup); - describe('', () => { test('renders without crashing', () => { const div = document.createElement('div'); diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx index 0dcca84ede3bf0..614679ed02a415 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx @@ -7,12 +7,10 @@ import React from 'react'; import { render } from 'react-dom'; import { FormDrilldownWizard } from './form_drilldown_wizard'; -import { render as renderTestingLibrary, fireEvent, cleanup } from '@testing-library/react/pure'; +import { render as renderTestingLibrary, fireEvent } from '@testing-library/react'; import { txtNameOfDrilldown } from './i18n'; import { Trigger, TriggerId } from '../../../../../../../src/plugins/ui_actions/public'; -afterEach(cleanup); - const otherProps = { actionFactoryContext: { triggers: [] as TriggerId[] }, supportedTriggers: [ diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx index 889f8983254d56..5bf11e31aee890 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx @@ -5,17 +5,13 @@ */ import React from 'react'; -import { cleanup, fireEvent, render } from '@testing-library/react/pure'; +import { fireEvent, render } from '@testing-library/react'; import { DrilldownListItem, ListManageDrilldowns, TEST_SUBJ_DRILLDOWN_ITEM, } from './list_manage_drilldowns'; -// TODO: for some reason global cleanup from RTL doesn't work -// afterEach is not available for it globally during setup -afterEach(cleanup); - const drilldowns: DrilldownListItem[] = [ { id: '1', actionName: 'Dashboard', drilldownName: 'Drilldown 1' }, { id: '2', actionName: 'Dashboard', drilldownName: 'Drilldown 2' }, diff --git a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.test.tsx b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.test.tsx index f55818379ef3f2..a30c880c3d430f 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.test.tsx +++ b/x-pack/plugins/ui_actions_enhanced/public/drilldowns/url_drilldown/components/url_drilldown_collect_config/url_drilldown_collect_config.test.tsx @@ -5,11 +5,9 @@ */ import { Demo } from './test_samples/demo'; -import { cleanup, fireEvent, render } from '@testing-library/react/pure'; +import { fireEvent, render } from '@testing-library/react'; import React from 'react'; -afterEach(cleanup); - test('configure valid URL template', () => { const screen = render(); From d793040082040c02a30a12c6fcdc27d740467001 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 1 Oct 2020 14:25:59 +0200 Subject: [PATCH 011/128] [Lens] Histogram/range operation new copy (#78328) * :speech_balloon: New copy revision for histogram/range operation in lens * :ok_hand: Updated panel copy * :ok_hand: Change copy based on feedback --- .../dimension_panel/dimension_editor.tsx | 4 +-- .../definitions/ranges/advanced_editor.tsx | 12 ++++----- .../definitions/ranges/range_editor.tsx | 25 ++++++++++++++++--- .../operations/definitions/ranges/ranges.tsx | 4 +-- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index b0d24928b794ea..0e33c20faff7ea 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -257,7 +257,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
{i18n.translate('xpack.lens.indexPattern.functionsLabel', { - defaultMessage: 'Choose a function', + defaultMessage: 'Select a function', })} @@ -277,7 +277,7 @@ export function DimensionEditor(props: DimensionEditorProps) { {' '} - {i18n.translate('xpack.lens.indexPattern.ranges.customIntervalsRemoval', { - defaultMessage: 'Remove custom intervals', + {i18n.translate('xpack.lens.indexPattern.ranges.customRangesRemoval', { + defaultMessage: 'Remove custom ranges', })} @@ -286,8 +286,8 @@ export const AdvancedRangeEditor = ({ addNewRange(); setIsOpenByCreation(true); }} - label={i18n.translate('xpack.lens.indexPattern.ranges.addInterval', { - defaultMessage: 'Add interval', + label={i18n.translate('xpack.lens.indexPattern.ranges.addRange', { + defaultMessage: 'Add range', })} /> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx index 5d5acf7778973c..8ed17a813e7fd9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/range_editor.tsx @@ -15,6 +15,7 @@ import { EuiFlexGroup, EuiButtonIcon, EuiToolTip, + EuiIconTip, } from '@elastic/eui'; import { IFieldFormat } from 'src/plugins/data/public'; import { RangeColumnParams, UpdateParamsFnType, MODES_TYPES } from './ranges'; @@ -45,8 +46,14 @@ const BaseRangeEditor = ({ ); const granularityLabel = i18n.translate('xpack.lens.indexPattern.ranges.granularity', { - defaultMessage: 'Granularity', + defaultMessage: 'Intervals granularity', }); + const granularityLabelDescription = i18n.translate( + 'xpack.lens.indexPattern.ranges.granularityDescription', + { + defaultMessage: 'Divides the field into evenly spaced intervals.', + } + ); const decreaseButtonLabel = i18n.translate('xpack.lens.indexPattern.ranges.decreaseButtonLabel', { defaultMessage: 'Decrease granularity', }); @@ -57,7 +64,17 @@ const BaseRangeEditor = ({ return ( <> + {granularityLabel}{' '} + + + } data-test-subj="indexPattern-ranges-section-label" labelType="legend" fullWidth @@ -91,7 +108,7 @@ const BaseRangeEditor = ({ /> - + onToggleEditor()}> {i18n.translate('xpack.lens.indexPattern.ranges.customIntervalsToggle', { - defaultMessage: 'Create custom intervals', + defaultMessage: 'Create custom ranges', })} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index 1971fb2875bed5..a59780ef59939e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -78,8 +78,8 @@ function getEsAggsParams({ sourceField, params }: RangeIndexPatternColumn) { export const rangeOperation: OperationDefinition = { type: 'range', - displayName: i18n.translate('xpack.lens.indexPattern.ranges', { - defaultMessage: 'Ranges', + displayName: i18n.translate('xpack.lens.indexPattern.intervals', { + defaultMessage: 'Intervals', }), priority: 4, // Higher than terms, so numbers get histogram input: 'field', From 4525f0cfab2045ee9e54bff813a9614f31313602 Mon Sep 17 00:00:00 2001 From: Larry Gregory Date: Thu, 1 Oct 2020 08:26:26 -0400 Subject: [PATCH 012/128] Omit runtime fields from FLS suggestions (#78330) Co-authored-by: Aleh Zasypkin Co-authored-by: Elastic Machine --- .../server/routes/indices/get_fields.test.ts | 58 +++++++++++++++++++ .../server/routes/indices/get_fields.ts | 46 ++++++++++++--- .../apis/security/index_fields.ts | 58 +++++++++++++++++++ .../security/flstest/data/mappings.json | 7 +++ 4 files changed, 160 insertions(+), 9 deletions(-) create mode 100644 x-pack/plugins/security/server/routes/indices/get_fields.test.ts diff --git a/x-pack/plugins/security/server/routes/indices/get_fields.test.ts b/x-pack/plugins/security/server/routes/indices/get_fields.test.ts new file mode 100644 index 00000000000000..4c6182e99431d5 --- /dev/null +++ b/x-pack/plugins/security/server/routes/indices/get_fields.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { httpServerMock, elasticsearchServiceMock } from '../../../../../../src/core/server/mocks'; +import { kibanaResponseFactory } from '../../../../../../src/core/server'; + +import { routeDefinitionParamsMock } from '../index.mock'; +import { defineGetFieldsRoutes } from './get_fields'; + +const createFieldMapping = (field: string, type: string) => ({ + [field]: { mapping: { [field]: { type } } }, +}); + +const createEmptyFieldMapping = (field: string) => ({ [field]: { mapping: {} } }); + +const mockFieldMappingResponse = { + foo: { + mappings: { + ...createFieldMapping('fooField', 'keyword'), + ...createFieldMapping('commonField', 'keyword'), + ...createEmptyFieldMapping('emptyField'), + }, + }, + bar: { + mappings: { + ...createFieldMapping('commonField', 'keyword'), + ...createFieldMapping('barField', 'keyword'), + ...createFieldMapping('runtimeField', 'runtime'), + }, + }, +}; + +describe('GET /internal/security/fields/{query}', () => { + it('returns a list of deduplicated fields, omitting empty and runtime fields', async () => { + const mockRouteDefinitionParams = routeDefinitionParamsMock.create(); + + const scopedClient = elasticsearchServiceMock.createLegacyScopedClusterClient(); + scopedClient.callAsCurrentUser.mockResolvedValue(mockFieldMappingResponse); + mockRouteDefinitionParams.clusterClient.asScoped.mockReturnValue(scopedClient); + + defineGetFieldsRoutes(mockRouteDefinitionParams); + + const [[, handler]] = mockRouteDefinitionParams.router.get.mock.calls; + + const headers = { authorization: 'foo' }; + const mockRequest = httpServerMock.createKibanaRequest({ + method: 'get', + path: `/internal/security/fields/foo`, + headers, + }); + const response = await handler({} as any, mockRequest, kibanaResponseFactory); + expect(response.status).toBe(200); + expect(response.payload).toEqual(['fooField', 'commonField', 'barField']); + }); +}); diff --git a/x-pack/plugins/security/server/routes/indices/get_fields.ts b/x-pack/plugins/security/server/routes/indices/get_fields.ts index 356b78aa33879e..44b8804ed8d6e1 100644 --- a/x-pack/plugins/security/server/routes/indices/get_fields.ts +++ b/x-pack/plugins/security/server/routes/indices/get_fields.ts @@ -8,6 +8,20 @@ import { schema } from '@kbn/config-schema'; import { RouteDefinitionParams } from '../index'; import { wrapIntoCustomErrorResponse } from '../../errors'; +interface FieldMappingResponse { + [indexName: string]: { + mappings: { + [fieldName: string]: { + mapping: { + [fieldName: string]: { + type: string; + }; + }; + }; + }; + }; +} + export function defineGetFieldsRoutes({ router, clusterClient }: RouteDefinitionParams) { router.get( { @@ -23,21 +37,35 @@ export function defineGetFieldsRoutes({ router, clusterClient }: RouteDefinition fields: '*', allowNoIndices: false, includeDefaults: true, - })) as Record }>; + })) as FieldMappingResponse; // The flow is the following (see response format at https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-field-mapping.html): // 1. Iterate over all matched indices. // 2. Extract all the field names from the `mappings` field of the particular index. - // 3. Collect and flatten the list of the field names. + // 3. Collect and flatten the list of the field names, omitting any fields without mappings, and any runtime fields // 4. Use `Set` to get only unique field names. + const fields = Array.from( + new Set( + Object.values(indexMappings).flatMap((indexMapping) => { + return Object.keys(indexMapping.mappings).filter((fieldName) => { + const mappingValues = Object.values(indexMapping.mappings[fieldName].mapping); + const hasMapping = mappingValues.length > 0; + + const isRuntimeField = hasMapping && mappingValues[0]?.type === 'runtime'; + + // fields without mappings are internal fields such as `_routing` and `_index`, + // and therefore don't make sense as autocomplete suggestions for FLS. + + // Runtime fields are not securable via FLS. + // Administrators should instead secure access to the fields which derive this information. + return hasMapping && !isRuntimeField; + }); + }) + ) + ); + return response.ok({ - body: Array.from( - new Set( - Object.values(indexMappings) - .map((indexMapping) => Object.keys(indexMapping.mappings)) - .flat() - ) - ), + body: fields, }); } catch (error) { return response.customError(wrapIntoCustomErrorResponse(error)); diff --git a/x-pack/test/api_integration/apis/security/index_fields.ts b/x-pack/test/api_integration/apis/security/index_fields.ts index 795da7dbe88354..193d0eea1590e7 100644 --- a/x-pack/test/api_integration/apis/security/index_fields.ts +++ b/x-pack/test/api_integration/apis/security/index_fields.ts @@ -7,10 +7,33 @@ import expect from '@kbn/expect/expect.js'; import { FtrProviderContext } from '../../ftr_provider_context'; +interface FLSFieldMappingResponse { + flstest: { + mappings: { + [fieldName: string]: { + mapping: { + [fieldName: string]: { + type: string; + }; + }; + }; + }; + }; +} + export default function ({ getService }: FtrProviderContext) { const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + const es = getService('legacyEs'); describe('Index Fields', () => { + before(async () => { + await esArchiver.load('security/flstest/data'); + }); + after(async () => { + await esArchiver.unload('security/flstest/data'); + }); + describe('GET /internal/security/fields/{query}', () => { it('should return a list of available index mapping fields', async () => { await supertest @@ -30,6 +53,41 @@ export default function ({ getService }: FtrProviderContext) { sampleOfExpectedFields.forEach((field) => expect(response.body).to.contain(field)); }); }); + + it('should not include runtime fields', async () => { + // First, make sure the mapping actually includes a runtime field + const fieldMapping = (await es.indices.getFieldMapping({ + index: 'flstest', + fields: '*', + includeDefaults: true, + })) as FLSFieldMappingResponse; + + expect(Object.keys(fieldMapping.flstest.mappings)).to.contain('runtime_customer_ssn'); + expect( + fieldMapping.flstest.mappings.runtime_customer_ssn.mapping.runtime_customer_ssn.type + ).to.eql('runtime'); + + // Now, make sure it's not returned here + const { body: actualFields } = (await supertest + .get('/internal/security/fields/flstest') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200)) as { body: string[] }; + + const expectedFields = [ + 'customer_ssn', + 'customer_ssn.keyword', + 'customer_region', + 'customer_region.keyword', + 'customer_name', + 'customer_name.keyword', + ]; + + actualFields.sort(); + expectedFields.sort(); + + expect(actualFields).to.eql(expectedFields); + }); }); }); } diff --git a/x-pack/test/functional/es_archives/security/flstest/data/mappings.json b/x-pack/test/functional/es_archives/security/flstest/data/mappings.json index c6f11ea26f647f..3605533618a934 100644 --- a/x-pack/test/functional/es_archives/security/flstest/data/mappings.json +++ b/x-pack/test/functional/es_archives/security/flstest/data/mappings.json @@ -30,6 +30,13 @@ } }, "type": "text" + }, + "runtime_customer_ssn": { + "type": "runtime", + "runtime_type": "keyword", + "script": { + "source": "emit(doc['customer_ssn'].value + ' calculated at runtime')" + } } } }, From 97ac553d0319cde7abd63b8052b38a90bb0a44b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Thu, 1 Oct 2020 13:30:39 +0100 Subject: [PATCH 013/128] [Usage Collection] [schema] `infra` (#78581) --- x-pack/.telemetryrc.json | 1 - .../infra/server/usage/usage_collector.ts | 23 +++++++++++++++++- .../schema/xpack_plugins.json | 24 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/x-pack/.telemetryrc.json b/x-pack/.telemetryrc.json index 9140dbdaf00aea..d0e56bbed9f47d 100644 --- a/x-pack/.telemetryrc.json +++ b/x-pack/.telemetryrc.json @@ -5,7 +5,6 @@ "plugins/actions/server/usage/actions_usage_collector.ts", "plugins/alerts/server/usage/alerts_usage_collector.ts", "plugins/apm/server/lib/apm_telemetry/index.ts", - "plugins/infra/server/usage/usage_collector.ts", "plugins/maps/server/maps_telemetry/collectors/register.ts" ] } diff --git a/x-pack/plugins/infra/server/usage/usage_collector.ts b/x-pack/plugins/infra/server/usage/usage_collector.ts index 598ee21e6f2732..54f6d2f6121db2 100644 --- a/x-pack/plugins/infra/server/usage/usage_collector.ts +++ b/x-pack/plugins/infra/server/usage/usage_collector.ts @@ -14,6 +14,17 @@ interface InfraopsSum { logs: number; } +interface Usage { + last_24_hours: { + hits: { + infraops_hosts: number; + infraops_docker: number; + infraops_kubernetes: number; + logs: number; + }; + }; +} + export class UsageCollector { public static registerUsageCollector(usageCollection: UsageCollectionSetup): void { const collector = UsageCollector.getUsageCollector(usageCollection); @@ -21,12 +32,22 @@ export class UsageCollector { } public static getUsageCollector(usageCollection: UsageCollectionSetup) { - return usageCollection.makeUsageCollector({ + return usageCollection.makeUsageCollector({ type: 'infraops', isReady: () => true, fetch: async () => { return this.getReport(); }, + schema: { + last_24_hours: { + hits: { + infraops_hosts: { type: 'long' }, + infraops_docker: { type: 'long' }, + infraops_kubernetes: { type: 'long' }, + logs: { type: 'long' }, + }, + }, + }, }); } 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 9acffa1f6c78e7..816d6828381ee4 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -242,6 +242,30 @@ } } }, + "infraops": { + "properties": { + "last_24_hours": { + "properties": { + "hits": { + "properties": { + "infraops_hosts": { + "type": "long" + }, + "infraops_docker": { + "type": "long" + }, + "infraops_kubernetes": { + "type": "long" + }, + "logs": { + "type": "long" + } + } + } + } + } + } + }, "ingest_manager": { "properties": { "fleet_enabled": { From cba458e4567d32250aa1e4d9748e7b4ecefc294a Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Thu, 1 Oct 2020 08:43:12 -0400 Subject: [PATCH 014/128] [Mappings editor] Fix bug when switching between mapping tabs (#78707) --- .../mappings_editor.test.tsx | 27 +++++++++++++++++++ .../templates_form/templates_form.tsx | 9 +++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx index 68933ddc9a935d..f5fcff9f962543 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/mappings_editor.test.tsx @@ -223,6 +223,33 @@ describe('Mappings editor: core', () => { isNumericDetectionVisible = exists('advancedConfiguration.numericDetection'); expect(isNumericDetectionVisible).toBe(false); }); + + test('should keep default dynamic templates value when switching tabs', async () => { + await act(async () => { + testBed = setup({ + value: { ...defaultMappings, dynamic_templates: [] }, // by default, the UI will provide an empty array for dynamic templates + onChange: onChangeHandler, + }); + }); + testBed.component.update(); + + const { + actions: { selectTab, getJsonEditorValue }, + } = testBed; + + // Navigate to dynamic templates tab and verify empty array + await selectTab('templates'); + let templatesValue = getJsonEditorValue('dynamicTemplatesEditor'); + expect(templatesValue).toEqual([]); + + // Navigate to advanced tab + await selectTab('advanced'); + + // Navigate back to dynamic templates tab and verify empty array persists + await selectTab('templates'); + templatesValue = getJsonEditorValue('dynamicTemplatesEditor'); + expect(templatesValue).toEqual([]); + }); }); describe('component props', () => { diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx index 4b813b4edbabd6..46e7bbd5e094a2 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/templates_form/templates_form.tsx @@ -36,11 +36,10 @@ const formSerializer: SerializerFunc = (formData) // Silently swallow errors } - return Array.isArray(parsedTemplates) && parsedTemplates.length > 0 - ? { - dynamic_templates: parsedTemplates, - } - : undefined; + return { + dynamic_templates: + Array.isArray(parsedTemplates) && parsedTemplates.length > 0 ? parsedTemplates : [], + }; }; const formDeserializer = (formData: { [key: string]: any }) => { From 4fe7625f584a6d2969fa10427e5bc339b943a404 Mon Sep 17 00:00:00 2001 From: Alison Goryachev Date: Thu, 1 Oct 2020 08:44:29 -0400 Subject: [PATCH 015/128] [Mappings editor] Add support for version field type (#78206) --- .../datatypes/version_datatype.test.tsx | 95 +++++++++++++++++++ .../fields/field_types/index.ts | 2 + .../fields/field_types/version_type.tsx | 27 ++++++ .../constants/data_types_definition.tsx | 30 ++++++ .../mappings_editor/types/document_fields.ts | 1 + 5 files changed, 155 insertions(+) create mode 100644 x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/version_datatype.test.tsx create mode 100644 x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/version_type.tsx diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/version_datatype.test.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/version_datatype.test.tsx new file mode 100644 index 00000000000000..61f67b04ec3cd6 --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/__jest__/client_integration/datatypes/version_datatype.test.tsx @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { act } from 'react-dom/test-utils'; + +import { componentHelpers, MappingsEditorTestBed } from '../helpers'; + +const { setup, getMappingsEditorDataFactory } = componentHelpers.mappingsEditor; + +// Parameters automatically added to the version datatype when saved (with the default values) +export const defaultVersionParameters = { + type: 'version', +}; + +describe('Mappings editor: version datatype', () => { + /** + * Variable to store the mappings data forwarded to the consumer component + */ + let data: any; + let onChangeHandler: jest.Mock = jest.fn(); + let getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler); + let testBed: MappingsEditorTestBed; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + beforeEach(() => { + onChangeHandler = jest.fn(); + getMappingsEditorData = getMappingsEditorDataFactory(onChangeHandler); + }); + + test('supports meta parameter', async () => { + const defaultMappings = { + properties: { + myField: { + type: 'version', + }, + }, + }; + + const updatedMappings = { ...defaultMappings }; + + const metaParameter = { + meta: { + my_metadata: 'foobar', + }, + }; + + await act(async () => { + testBed = setup({ value: defaultMappings, onChange: onChangeHandler }); + }); + testBed.component.update(); + + const { + component, + actions: { + startEditField, + updateFieldAndCloseFlyout, + showAdvancedSettings, + toggleFormRow, + updateJsonEditor, + }, + } = testBed; + + // Open the flyout to edit the field + await startEditField('myField'); + await showAdvancedSettings(); + + // Enable the meta parameter and provide a valid object + toggleFormRow('metaParameter'); + await act(async () => { + updateJsonEditor('metaParameterEditor', metaParameter.meta); + }); + component.update(); + + // Save the field and close the flyout + await updateFieldAndCloseFlyout(); + + // It should have the default parameters values added, plus metadata + updatedMappings.properties.myField = { + ...defaultVersionParameters, + ...metaParameter, + }; + + ({ data } = await getMappingsEditorData(component)); + expect(data).toEqual(updatedMappings); + }); +}); diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts index 4d36b4dd2578d8..d135d1b81419cf 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/index.ts @@ -34,6 +34,7 @@ import { RankFeatureType } from './rank_feature_type'; import { RuntimeType } from './runtime_type'; import { WildcardType } from './wildcard_type'; import { PointType } from './point_type'; +import { VersionType } from './version_type'; const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { alias: AliasType, @@ -64,6 +65,7 @@ const typeToParametersFormMap: { [key in DataType]?: ComponentType } = { runtime: RuntimeType, wildcard: WildcardType, point: PointType, + version: VersionType, }; export const getParametersFormForType = ( diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/version_type.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/version_type.tsx new file mode 100644 index 00000000000000..24ee356c5db77d --- /dev/null +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/components/document_fields/fields/field_types/version_type.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; + +import { NormalizedField, Field as FieldType, ParameterName } from '../../../../types'; +import { getFieldConfig } from '../../../../lib'; +import { MetaParameter } from '../../field_parameters'; +import { AdvancedParametersSection } from '../edit_field'; + +interface Props { + field: NormalizedField; +} + +const getDefaultToggleValue = (param: ParameterName, field: FieldType) => { + return field[param] !== undefined && field[param] !== getFieldConfig(param).defaultValue; +}; + +export const VersionType = ({ field }: Props) => { + return ( + + + + ); +}; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx index 7bcd8f32f1a7d8..07ca0a69afefb3 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/constants/data_types_definition.tsx @@ -860,6 +860,35 @@ export const TYPE_DEFINITION: { [key in DataType]: DataTypeDefinition } = {

), }, + version: { + label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.versionDescription', { + defaultMessage: 'Version', + }), + value: 'version', + documentation: { + main: '/version.html', + }, + description: () => ( +

+ + {i18n.translate( + 'xpack.idxMgmt.mappingsEditor.dataType.versionLongDescription.keywordTypeLink', + { + defaultMessage: 'keyword data type', + } + )} + + ), + }} + /> +

+ ), + }, wildcard: { label: i18n.translate('xpack.idxMgmt.mappingsEditor.dataType.wildcardDescription', { defaultMessage: 'Wildcard', @@ -923,6 +952,7 @@ export const MAIN_TYPES: MainType[] = [ 'histogram', 'wildcard', 'point', + 'version', 'other', ]; diff --git a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts index 48282abd1d799b..926b4c9d12beed 100644 --- a/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts +++ b/x-pack/plugins/index_management/public/application/components/mappings_editor/types/document_fields.ts @@ -64,6 +64,7 @@ export type MainType = | 'point' | 'histogram' | 'constant_keyword' + | 'version' | 'wildcard' /** * 'other' is a special type that only exists inside of MappingsEditor as a placeholder From 9e5bf0f92f7a25df92372c52e9d7f1f9e39aa870 Mon Sep 17 00:00:00 2001 From: Corey Robertson Date: Thu, 1 Oct 2020 09:10:02 -0400 Subject: [PATCH 016/128] [Canvas] Move Handlebars and Flot dependencies out of main bundle (#78542) * Move Handlebars and Flot dependencies out of main bundle * Fix unit test Co-authored-by: Elastic Machine --- .../functions/browser/markdown.test.js | 28 +++++++++---------- .../functions/browser/markdown.ts | 8 +++--- .../canvas_plugin_src/renderers/pie/index.tsx | 6 ++-- .../canvas_plugin_src/renderers/plot/index.ts | 6 ++-- x-pack/plugins/canvas/common/lib/index.ts | 2 -- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js index 71b6af67394088..1c75f5b7e0fbc4 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.test.js @@ -12,16 +12,16 @@ import { markdown } from './markdown'; describe('markdown', () => { const fn = functionWrapper(markdown); - it('returns a render as markdown', () => { - const result = fn(null, { content: [''], font: fontStyle }); + it('returns a render as markdown', async () => { + const result = await fn(null, { content: [''], font: fontStyle }); expect(result).toHaveProperty('type', 'render'); expect(result).toHaveProperty('as', 'markdown'); }); describe('args', () => { describe('content', () => { - it('sets the content to all strings in expression concatenated', () => { - const result = fn(null, { + it('sets the content to all strings in expression concatenated', async () => { + const result = await fn(null, { content: ['# this ', 'is ', 'some ', 'markdown'], font: fontStyle, }); @@ -29,11 +29,11 @@ describe('markdown', () => { expect(result.value).toHaveProperty('content', '# this is some markdown'); }); - it('compiles and concatenates handlebars expressions using context', () => { + it('compiles and concatenates handlebars expressions using context', async () => { let expectedContent = 'Columns:'; testTable.columns.map((col) => (expectedContent += ` ${col.name}`)); - const result = fn(testTable, { + const result = await fn(testTable, { content: ['Columns:', '{{#each columns}} {{name}}{{/each}}'], }); @@ -42,8 +42,8 @@ describe('markdown', () => { }); describe('font', () => { - it('sets the font style for the markdown', () => { - const result = fn(null, { + it('sets the font style for the markdown', async () => { + const result = await fn(null, { content: ['some ', 'markdown'], font: fontStyle, }); @@ -55,8 +55,8 @@ describe('markdown', () => { // it("defaults to the expression '{font}'", () => {}); }); describe('openLinksInNewTab', () => { - it('sets the value of openLinksInNewTab to true ', () => { - const result = fn(null, { + it('sets the value of openLinksInNewTab to true ', async () => { + const result = await fn(null, { content: ['some ', 'markdown'], openLinksInNewTab: true, }); @@ -64,8 +64,8 @@ describe('markdown', () => { expect(result.value).toHaveProperty('openLinksInNewTab', true); }); - it('sets the value of openLinksInNewTab to false ', () => { - const result = fn(null, { + it('sets the value of openLinksInNewTab to false ', async () => { + const result = await fn(null, { content: ['some ', 'markdown'], openLinksInNewTab: false, }); @@ -73,8 +73,8 @@ describe('markdown', () => { expect(result.value).toHaveProperty('openLinksInNewTab', false); }); - it('defaults the value of openLinksInNewTab to false ', () => { - const result = fn(null, { + it('defaults the value of openLinksInNewTab to false ', async () => { + const result = await fn(null, { content: ['some ', 'markdown'], }); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts index 947106fd9397a5..aa73eba4564812 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/browser/markdown.ts @@ -10,8 +10,6 @@ import { Style, ExpressionFunctionDefinition, } from 'src/plugins/expressions/common'; -// @ts-expect-error untyped local -import { Handlebars } from '../../../common/lib/handlebars'; import { getFunctionHelp } from '../../../i18n'; type Context = Datatable | null; @@ -32,7 +30,7 @@ export function markdown(): ExpressionFunctionDefinition< 'markdown', Context, Arguments, - Render + Promise> > { const { help, args: argHelp } = getFunctionHelp().markdown; @@ -61,7 +59,9 @@ export function markdown(): ExpressionFunctionDefinition< default: false, }, }, - fn: (input, args) => { + fn: async (input, args) => { + // @ts-expect-error untyped local + const { Handlebars } = await import('../../../common/lib/handlebars'); const compileFunctions = args.content.map((str) => Handlebars.compile(String(str), { knownHelpersOnly: true }) ); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/pie/index.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/pie/index.tsx index 622e73ccf2223e..29e823e0a373bb 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/pie/index.tsx +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/pie/index.tsx @@ -6,7 +6,6 @@ // This bit of hackiness is required because this isn't part of the main kibana bundle import 'jquery'; -import '../../lib/flot-charts'; import { debounce, includes } from 'lodash'; import { RendererStrings } from '../../../i18n'; @@ -22,7 +21,10 @@ export const pie: RendererFactory = () => ({ displayName: strings.getDisplayName(), help: strings.getHelpDescription(), reuseDomNode: false, - render(domNode, config, handlers) { + render: async (domNode, config, handlers) => { + // @ts-expect-error + await import('../../lib/flot-charts'); + if (!includes($.plot.plugins, piePlugin)) { $.plot.plugins.push(piePlugin); } diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/plot/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/plot/index.ts index 8c84f54f8746be..9d70ca418f4914 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/plot/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/plot/index.ts @@ -6,7 +6,6 @@ // This bit of hackiness is required because this isn't part of the main kibana bundle import 'jquery'; -import '../../lib/flot-charts'; import { debounce, includes } from 'lodash'; import { RendererStrings } from '../../../i18n'; @@ -18,7 +17,10 @@ import { text } from './plugins/text'; const { plot: strings } = RendererStrings; -const render: RendererSpec['render'] = (domNode, config, handlers) => { +const render: RendererSpec['render'] = async (domNode, config, handlers) => { + // @ts-expect-error + await import('../../lib/flot-charts'); + // TODO: OH NOES if (!includes($.plot.plugins, size)) { $.plot.plugins.push(size); diff --git a/x-pack/plugins/canvas/common/lib/index.ts b/x-pack/plugins/canvas/common/lib/index.ts index 055f6ce7739b7d..c8ae53917c9e48 100644 --- a/x-pack/plugins/canvas/common/lib/index.ts +++ b/x-pack/plugins/canvas/common/lib/index.ts @@ -20,8 +20,6 @@ export * from './get_colors_from_palette'; export * from './get_field_type'; // @ts-expect-error missing local definition export * from './get_legend_config'; -// @ts-expect-error missing local definition -export * from './handlebars'; export * from './hex_to_rgb'; export * from './httpurl'; export * from './missing_asset'; From 727d62611b8f6f23ac163f0a2af2ab6f96e38f00 Mon Sep 17 00:00:00 2001 From: Gidi Meir Morris Date: Thu, 1 Oct 2020 14:24:10 +0100 Subject: [PATCH 017/128] [Actions] fixes error in UI in the Edit Flyout for PreConfigured Connectors (#78994) Ensures only User Configured Connectors can be validated and edited by the UI to avoid these kinds of errors in the future. --- .../builtin_action_types/email/email.tsx | 4 +- .../es_index/es_index.tsx | 4 +- .../builtin_action_types/jira/jira.tsx | 19 ++++---- .../jira/jira_params.test.tsx | 3 +- .../builtin_action_types/jira/types.ts | 10 ++--- .../pagerduty/pagerduty.tsx | 14 +++++- .../resilient/resilient.tsx | 13 +++++- .../builtin_action_types/resilient/types.ts | 13 +++--- .../server_log/server_log.test.tsx | 7 +-- .../server_log/server_log.tsx | 2 +- .../servicenow/servicenow.tsx | 10 ++++- .../builtin_action_types/servicenow/types.ts | 13 +++--- .../builtin_action_types/slack/slack.tsx | 4 +- .../components/builtin_action_types/types.ts | 44 +++++++------------ .../builtin_action_types/webhook/webhook.tsx | 13 +++++- .../lib/action_connector_api.test.ts | 4 +- .../lib/check_action_type_enabled.test.tsx | 8 +--- .../action_connector_form.test.tsx | 9 ++-- .../action_connector_form.tsx | 14 ++++-- .../connector_edit_flyout.tsx | 10 +++-- .../triggers_actions_ui/public/types.ts | 43 ++++++++++++++---- .../apps/triggers_actions_ui/connectors.ts | 2 +- x-pack/test/functional_with_es_ssl/config.ts | 9 ++++ 23 files changed, 171 insertions(+), 101 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx index abb102c04b0547..3e8e71991a5944 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx @@ -6,9 +6,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; -import { EmailActionParams, EmailActionConnector } from '../types'; +import { EmailActionParams, EmailConfig, EmailSecrets, EmailActionConnector } from '../types'; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { const mailformat = /^[^@\s]+@[^@\s]+$/; return { id: '.email', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx index c0255650e0f37e..de611d6a043a70 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index.tsx @@ -6,9 +6,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; -import { EsIndexActionConnector, IndexActionParams } from '../types'; +import { EsIndexActionConnector, EsIndexConfig, IndexActionParams } from '../types'; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.index', iconClass: 'indexOpen', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx index fd36bd6aeab0a9..0179cfbffdfea3 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira.tsx @@ -8,19 +8,20 @@ import { lazy } from 'react'; import { ValidationResult, ActionTypeModel } from '../../../../types'; import { connectorConfiguration } from './config'; import logo from './logo.svg'; -import { JiraActionConnector, JiraActionParams } from './types'; +import { JiraActionConnector, JiraConfig, JiraSecrets, JiraActionParams } from './types'; import * as i18n from './translations'; import { isValidUrl } from '../../../lib/value_validators'; const validateConnector = (action: JiraActionConnector): ValidationResult => { - const validationResult = { errors: {} }; - const errors = { - apiUrl: new Array(), - projectKey: new Array(), - email: new Array(), - apiToken: new Array(), + const validationResult = { + errors: { + apiUrl: new Array(), + projectKey: new Array(), + email: new Array(), + apiToken: new Array(), + }, }; - validationResult.errors = errors; + const { errors } = validationResult; if (!action.config.apiUrl) { errors.apiUrl = [...errors.apiUrl, i18n.API_URL_REQUIRED]; @@ -45,7 +46,7 @@ const validateConnector = (action: JiraActionConnector): ValidationResult => { return validationResult; }; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: connectorConfiguration.id, iconClass: logo, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx index 416f6f7b18755d..a0194ed5c81e49 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/jira_params.test.tsx @@ -11,6 +11,7 @@ import { coreMock } from 'src/core/public/mocks'; import { useGetIssueTypes } from './use_get_issue_types'; import { useGetFieldsByIssueType } from './use_get_fields_by_issue_type'; +import { ActionConnector } from '../../../../types'; jest.mock('./use_get_issue_types'); jest.mock('./use_get_fields_by_issue_type'); @@ -35,7 +36,7 @@ const actionParams = { }, }; -const connector = { +const connector: ActionConnector = { secrets: {}, config: {}, id: 'test', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts index 4c13d067913f21..e72aa1f7fc0378 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/jira/types.ts @@ -5,11 +5,9 @@ */ import { CasesConfigurationMapping } from '../case_mappings'; +import { UserConfiguredActionConnector } from '../../../../types'; -export interface JiraActionConnector { - config: JiraConfig; - secrets: JiraSecrets; -} +export type JiraActionConnector = UserConfiguredActionConnector; export interface JiraActionParams { subAction: string; @@ -30,14 +28,14 @@ interface IncidentConfiguration { mapping: CasesConfigurationMapping[]; } -interface JiraConfig { +export interface JiraConfig { apiUrl: string; projectKey: string; incidentConfiguration?: IncidentConfiguration; isCaseOwned?: boolean; } -interface JiraSecrets { +export interface JiraSecrets { email: string; apiToken: string; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx index 03bfbb38da6f2b..ed2bd39d88dc4a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty.tsx @@ -7,11 +7,20 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { ActionTypeModel, ValidationResult } from '../../../../types'; -import { PagerDutyActionParams, PagerDutyActionConnector } from '.././types'; +import { + PagerDutyActionConnector, + PagerDutyConfig, + PagerDutySecrets, + PagerDutyActionParams, +} from '.././types'; import pagerDutySvg from './pagerduty.svg'; import { hasMustacheTokens } from '../../../lib/has_mustache_tokens'; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel< + PagerDutyConfig, + PagerDutySecrets, + PagerDutyActionParams +> { return { id: '.pagerduty', iconClass: pagerDutySvg, @@ -33,6 +42,7 @@ export function getActionType(): ActionTypeModel { routingKey: new Array(), }; validationResult.errors = errors; + if (!action.secrets.routingKey) { errors.routingKey.push( i18n.translate( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx index cda6935f3b73d5..1b27968c04fd3f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/resilient.tsx @@ -8,7 +8,12 @@ import { lazy } from 'react'; import { ValidationResult, ActionTypeModel } from '../../../../types'; import { connectorConfiguration } from './config'; import logo from './logo.svg'; -import { ResilientActionConnector, ResilientActionParams } from './types'; +import { + ResilientActionConnector, + ResilientConfig, + ResilientSecrets, + ResilientActionParams, +} from './types'; import * as i18n from './translations'; import { isValidUrl } from '../../../lib/value_validators'; @@ -45,7 +50,11 @@ const validateConnector = (action: ResilientActionConnector): ValidationResult = return validationResult; }; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel< + ResilientConfig, + ResilientSecrets, + ResilientActionParams +> { return { id: connectorConfiguration.id, iconClass: logo, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts index 37516f5bac3729..38019205fbfc93 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/resilient/types.ts @@ -5,11 +5,12 @@ */ import { CasesConfigurationMapping } from '../case_mappings'; +import { UserConfiguredActionConnector } from '../../../../types'; -export interface ResilientActionConnector { - config: ResilientConfig; - secrets: ResilientSecrets; -} +export type ResilientActionConnector = UserConfiguredActionConnector< + ResilientConfig, + ResilientSecrets +>; export interface ResilientActionParams { subAction: string; @@ -28,14 +29,14 @@ interface IncidentConfiguration { mapping: CasesConfigurationMapping[]; } -interface ResilientConfig { +export interface ResilientConfig { apiUrl: string; orgId: string; incidentConfiguration?: IncidentConfiguration; isCaseOwned?: boolean; } -interface ResilientSecrets { +export interface ResilientSecrets { apiKeyId: string; apiKeySecret: string; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx index 3bb5ea68a3040a..15143eb6513ebd 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.test.tsx @@ -5,7 +5,7 @@ */ import { TypeRegistry } from '../../../type_registry'; import { registerBuiltInActionTypes } from '.././index'; -import { ActionTypeModel, ActionConnector } from '../../../../types'; +import { ActionTypeModel, UserConfiguredActionConnector } from '../../../../types'; const ACTION_TYPE_ID = '.server-log'; let actionTypeModel: ActionTypeModel; @@ -28,13 +28,14 @@ describe('actionTypeRegistry.get() works', () => { describe('server-log connector validation', () => { test('connector validation succeeds when connector config is valid', () => { - const actionConnector = { + const actionConnector: UserConfiguredActionConnector<{}, {}> = { secrets: {}, id: 'test', actionTypeId: '.server-log', name: 'server-log', config: {}, - } as ActionConnector; + isPreconfigured: false, + }; expect(actionTypeModel.validateConnector(actionConnector)).toEqual({ errors: {}, diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx index 390ccf6a494e90..057e9cf375f96d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/server_log/server_log.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; import { ServerLogActionParams } from '../types'; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.server-log', iconClass: 'logsApp', 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 0f7b83ed84fb47..8396497a6e2843 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 @@ -8,7 +8,12 @@ import { lazy } from 'react'; import { ValidationResult, ActionTypeModel } from '../../../../types'; import { connectorConfiguration } from './config'; import logo from './logo.svg'; -import { ServiceNowActionConnector, ServiceNowActionParams } from './types'; +import { + ServiceNowActionConnector, + ServiceNowConfig, + ServiceNowSecrets, + ServiceNowActionParams, +} from './types'; import * as i18n from './translations'; import { isValidUrl } from '../../../lib/value_validators'; @@ -41,7 +46,8 @@ const validateConnector = (action: ServiceNowActionConnector): ValidationResult }; export function getActionType(): ActionTypeModel< - ServiceNowActionConnector, + ServiceNowConfig, + ServiceNowSecrets, ServiceNowActionParams > { return { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/types.ts index a4f1ff2be0f69b..92753dfcba76c7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/types.ts @@ -5,11 +5,12 @@ */ import { CasesConfigurationMapping } from '../case_mappings'; +import { UserConfiguredActionConnector } from '../../../../types'; -export interface ServiceNowActionConnector { - config: ServiceNowConfig; - secrets: ServiceNowSecrets; -} +export type ServiceNowActionConnector = UserConfiguredActionConnector< + ServiceNowConfig, + ServiceNowSecrets +>; export interface ServiceNowActionParams { subAction: string; @@ -29,13 +30,13 @@ interface IncidentConfiguration { mapping: CasesConfigurationMapping[]; } -interface ServiceNowConfig { +export interface ServiceNowConfig { apiUrl: string; incidentConfiguration?: IncidentConfiguration; isCaseOwned?: boolean; } -interface ServiceNowSecrets { +export interface ServiceNowSecrets { username: string; password: string; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx index 5d39cdb5ac387b..23c76f327008bc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/slack/slack.tsx @@ -6,9 +6,9 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; -import { SlackActionParams, SlackActionConnector } from '../types'; +import { SlackActionParams, SlackSecrets, SlackActionConnector } from '../types'; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel { return { id: '.slack', iconClass: 'logoSlack', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts index 84d8b6e8caeded..f6bb08148b3cbf 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { ActionConnector } from '../../../types'; +import { UserConfiguredActionConnector } from '../../../types'; export interface EmailActionParams { to: string[]; @@ -64,66 +64,56 @@ export interface WebhookActionParams { body?: string; } -interface EmailConfig { +export interface EmailConfig { from: string; host: string; port: number; secure?: boolean; } -interface EmailSecrets { +export interface EmailSecrets { user: string | null; password: string | null; } -export interface EmailActionConnector extends ActionConnector { - config: EmailConfig; - secrets: EmailSecrets; -} +export type EmailActionConnector = UserConfiguredActionConnector; -interface EsIndexConfig { +export interface EsIndexConfig { index: string; executionTimeField?: string | null; refresh?: boolean; } -export interface EsIndexActionConnector extends ActionConnector { - config: EsIndexConfig; -} +export type EsIndexActionConnector = UserConfiguredActionConnector; -interface PagerDutyConfig { +export interface PagerDutyConfig { apiUrl?: string; } -interface PagerDutySecrets { +export interface PagerDutySecrets { routingKey: string; } -export interface PagerDutyActionConnector extends ActionConnector { - config: PagerDutyConfig; - secrets: PagerDutySecrets; -} +export type PagerDutyActionConnector = UserConfiguredActionConnector< + PagerDutyConfig, + PagerDutySecrets +>; -interface SlackSecrets { +export interface SlackSecrets { webhookUrl: string; } -export interface SlackActionConnector extends ActionConnector { - secrets: SlackSecrets; -} +export type SlackActionConnector = UserConfiguredActionConnector; -interface WebhookConfig { +export interface WebhookConfig { method: string; url: string; headers: Record; } -interface WebhookSecrets { +export interface WebhookSecrets { user: string; password: string; } -export interface WebhookActionConnector extends ActionConnector { - config: WebhookConfig; - secrets: WebhookSecrets; -} +export type WebhookActionConnector = UserConfiguredActionConnector; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx index 2c51b21d700342..04077738e6015e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/webhook/webhook.tsx @@ -6,10 +6,19 @@ import { lazy } from 'react'; import { i18n } from '@kbn/i18n'; import { ActionTypeModel, ValidationResult } from '../../../../types'; -import { WebhookActionParams, WebhookActionConnector } from '../types'; +import { + WebhookActionParams, + WebhookConfig, + WebhookSecrets, + WebhookActionConnector, +} from '../types'; import { isValidUrl } from '../../../lib/value_validators'; -export function getActionType(): ActionTypeModel { +export function getActionType(): ActionTypeModel< + WebhookConfig, + WebhookSecrets, + WebhookActionParams +> { return { id: '.webhook', iconClass: 'logoWebhook', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts index ad3a5b40bd00de..c4a09b6b8f46d1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts @@ -59,7 +59,7 @@ describe('loadAllActions', () => { describe('createActionConnector', () => { test('should call create action API', async () => { - const connector: ActionConnectorWithoutId = { + const connector: ActionConnectorWithoutId<{}, {}> = { actionTypeId: 'test', isPreconfigured: false, name: 'My test', @@ -85,7 +85,7 @@ describe('createActionConnector', () => { describe('updateActionConnector', () => { test('should call the update API', async () => { const id = '123'; - const connector: ActionConnectorWithoutId = { + const connector: ActionConnectorWithoutId<{}, {}> = { actionTypeId: 'test', isPreconfigured: false, name: 'My test', diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx index 2917be943d2768..ab44520d2954ab 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/check_action_type_enabled.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ActionType, ActionConnector } from '../../types'; +import { ActionType, PreConfiguredActionConnector } from '../../types'; import { checkActionTypeEnabled, checkActionFormActionTypeEnabled, @@ -93,23 +93,19 @@ describe('checkActionTypeEnabled', () => { }); describe('checkActionFormActionTypeEnabled', () => { - const preconfiguredConnectors: ActionConnector[] = [ + const preconfiguredConnectors: PreConfiguredActionConnector[] = [ { actionTypeId: '1', - config: {}, id: 'test1', isPreconfigured: true, name: 'test', - secrets: {}, referencedByCount: 0, }, { actionTypeId: '2', - config: {}, id: 'test2', isPreconfigured: true, name: 'test', - secrets: {}, referencedByCount: 0, }, ]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx index b7c9865cbd9d04..60ec8004983a32 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.test.tsx @@ -7,7 +7,7 @@ import * as React from 'react'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { coreMock } from '../../../../../../../src/core/public/mocks'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; -import { ValidationResult, ActionConnector } from '../../../types'; +import { ValidationResult, UserConfiguredActionConnector } from '../../../types'; import { ActionConnectorForm } from './action_connector_form'; const actionTypeRegistry = actionTypeRegistryMock.create(); @@ -46,11 +46,14 @@ describe('action_connector_form', () => { actionTypeRegistry.get.mockReturnValue(actionType); actionTypeRegistry.has.mockReturnValue(true); - const initialConnector = { + const initialConnector: UserConfiguredActionConnector<{}, {}> = { + id: '123', + name: '', actionTypeId: actionType.id, config: {}, secrets: {}, - } as ActionConnector; + isPreconfigured: false, + }; let wrapper; if (deps) { wrapper = mountWithIntl( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx index ed4edb0229c2bd..ef6621f98fac2d 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx @@ -20,7 +20,12 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { HttpSetup, ApplicationStart, DocLinksStart } from 'kibana/public'; import { ReducerAction } from './connector_reducer'; -import { ActionConnector, IErrorObject, ActionTypeModel } from '../../../types'; +import { + ActionConnector, + IErrorObject, + ActionTypeModel, + UserConfiguredActionConnector, +} from '../../../types'; import { TypeRegistry } from '../../type_registry'; import { hasSaveActionsCapability } from '../../lib/capabilities'; @@ -43,8 +48,11 @@ export function validateBaseProperties(actionObject: ActionConnector) { return validationResult; } -interface ActionConnectorProps { - connector: ActionConnector; +interface ActionConnectorProps< + ConnectorConfig = Record, + ConnectorSecrets = Record +> { + connector: UserConfiguredActionConnector; dispatch: React.Dispatch; actionTypeName: string; serverError?: { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx index 7b985ab85cd4ee..6c7a1cbdc3c706 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/connector_edit_flyout.tsx @@ -96,10 +96,12 @@ export const ConnectorEditFlyout = ({ } const actionTypeModel = actionTypeRegistry.get(connector.actionTypeId); - const errorsInConnectorConfig = { - ...actionTypeModel?.validateConnector(connector).errors, - ...validateBaseProperties(connector).errors, - } as IErrorObject; + const errorsInConnectorConfig = (!connector.isPreconfigured + ? { + ...actionTypeModel?.validateConnector(connector).errors, + ...validateBaseProperties(connector).errors, + } + : {}) as IErrorObject; const hasErrorsInConnectorConfig = !!Object.keys(errorsInConnectorConfig).find( (errorKey) => errorsInConnectorConfig[errorKey].length >= 1 ); diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index e147f035fbb86d..c551746fdec0c0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -64,15 +64,19 @@ export interface Pagination { size: number; } -export interface ActionTypeModel { +export interface ActionTypeModel { id: string; iconClass: string; selectMessage: string; actionTypeTitle?: string; - validateConnector: (connector: any) => ValidationResult; + validateConnector: ( + connector: UserConfiguredActionConnector + ) => ValidationResult; validateParams: (actionParams: any) => ValidationResult; actionConnectorFields: React.LazyExoticComponent< - ComponentType> + ComponentType< + ActionConnectorFieldsProps> + > > | null; actionParamsFields: React.LazyExoticComponent< ComponentType> @@ -83,21 +87,42 @@ export interface ValidationResult { errors: Record; } -export interface ActionConnector { - secrets: Record; +interface ActionConnectorProps { + secrets: Secrets; id: string; actionTypeId: string; name: string; referencedByCount?: number; - config: Record; + config: Config; isPreconfigured: boolean; } -export type ActionConnectorWithoutId = Omit; +export type PreConfiguredActionConnector = Omit< + ActionConnectorProps, + 'config' | 'secrets' +> & { + isPreconfigured: true; +}; + +export type UserConfiguredActionConnector = ActionConnectorProps< + Config, + Secrets +> & { + isPreconfigured: false; +}; + +export type ActionConnector, Secrets = Record> = + | PreConfiguredActionConnector + | UserConfiguredActionConnector; -export interface ActionConnectorTableItem extends ActionConnector { +export type ActionConnectorWithoutId< + Config = Record, + Secrets = Record +> = Omit, 'id'>; + +export type ActionConnectorTableItem = ActionConnector & { actionType: ActionType['name']; -} +}; export interface ActionVariable { name: string; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts index 151c8376402282..f56e0e2629d402 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts @@ -245,7 +245,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should not be able to edit a preconfigured connector', async () => { - const preconfiguredConnectorName = 'xyztest'; + const preconfiguredConnectorName = 'test-preconfigured-email'; await pageObjects.triggersActionsUI.searchConnectors(preconfiguredConnectorName); diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index 5df5a4155efd31..eedc39b09a8e4e 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -76,6 +76,15 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { actionTypeId: '.server-log', name: 'Serverlog#xyz', }, + 'my-email-connector': { + actionTypeId: '.email', + name: 'Email#test-preconfigured-email', + config: { + from: 'me@example.com', + host: 'localhost', + port: '1025', + }, + }, })}`, ], }, From af517a078a03160caa4f3498eca654d9f49e5147 Mon Sep 17 00:00:00 2001 From: Brandon Kobel Date: Thu, 1 Oct 2020 06:39:41 -0700 Subject: [PATCH 018/128] core route handler context is now lazy (#78957) * Core route handler context is now lazy * Removing `coreStart` intermediate variable * Adding unit tests for CoreRouteHandlerContext Co-authored-by: Elastic Machine --- .../server/core_route_handler_context.test.ts | 239 ++++++++++++++++++ src/core/server/core_route_handler_context.ts | 132 ++++++++++ src/core/server/server.ts | 21 +- 3 files changed, 373 insertions(+), 19 deletions(-) create mode 100644 src/core/server/core_route_handler_context.test.ts create mode 100644 src/core/server/core_route_handler_context.ts diff --git a/src/core/server/core_route_handler_context.test.ts b/src/core/server/core_route_handler_context.test.ts new file mode 100644 index 00000000000000..563e337e6c7e00 --- /dev/null +++ b/src/core/server/core_route_handler_context.test.ts @@ -0,0 +1,239 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { CoreRouteHandlerContext } from './core_route_handler_context'; +import { coreMock, httpServerMock } from './mocks'; + +describe('#auditor', () => { + test('returns the results of coreStart.audiTrail.asScoped', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const auditor = context.auditor; + expect(auditor).toBe(coreStart.auditTrail.asScoped.mock.results[0].value); + }); + + test('lazily created', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + expect(coreStart.auditTrail.asScoped).not.toHaveBeenCalled(); + const auditor = context.auditor; + expect(coreStart.auditTrail.asScoped).toHaveBeenCalled(); + expect(auditor).toBeDefined(); + }); + + test('only creates one instance', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const auditor1 = context.auditor; + const auditor2 = context.auditor; + expect(coreStart.auditTrail.asScoped.mock.calls.length).toBe(1); + const mockResult = coreStart.auditTrail.asScoped.mock.results[0].value; + expect(auditor1).toBe(mockResult); + expect(auditor2).toBe(mockResult); + }); +}); + +describe('#elasticsearch', () => { + describe('#client', () => { + test('returns the results of coreStart.elasticsearch.client.asScoped', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client = context.elasticsearch.client; + expect(client).toBe(coreStart.elasticsearch.client.asScoped.mock.results[0].value); + }); + + test('lazily created', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + expect(coreStart.elasticsearch.client.asScoped).not.toHaveBeenCalled(); + const client = context.elasticsearch.client; + expect(coreStart.elasticsearch.client.asScoped).toHaveBeenCalled(); + expect(client).toBeDefined(); + }); + + test('only creates one instance', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client1 = context.elasticsearch.client; + const client2 = context.elasticsearch.client; + expect(coreStart.elasticsearch.client.asScoped.mock.calls.length).toBe(1); + const mockResult = coreStart.elasticsearch.client.asScoped.mock.results[0].value; + expect(client1).toBe(mockResult); + expect(client2).toBe(mockResult); + }); + }); + + describe('#legacy', () => { + describe('#client', () => { + test('returns the results of coreStart.elasticsearch.legacy.client.asScoped', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client = context.elasticsearch.legacy.client; + expect(client).toBe(coreStart.elasticsearch.legacy.client.asScoped.mock.results[0].value); + }); + + test('lazily created', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + expect(coreStart.elasticsearch.legacy.client.asScoped).not.toHaveBeenCalled(); + const client = context.elasticsearch.legacy.client; + expect(coreStart.elasticsearch.legacy.client.asScoped).toHaveBeenCalled(); + expect(client).toBeDefined(); + }); + + test('only creates one instance', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client1 = context.elasticsearch.legacy.client; + const client2 = context.elasticsearch.legacy.client; + expect(coreStart.elasticsearch.legacy.client.asScoped.mock.calls.length).toBe(1); + const mockResult = coreStart.elasticsearch.legacy.client.asScoped.mock.results[0].value; + expect(client1).toBe(mockResult); + expect(client2).toBe(mockResult); + }); + }); + }); +}); + +describe('#savedObjects', () => { + describe('#client', () => { + test('returns the results of coreStart.savedObjects.getScopedClient', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client = context.savedObjects.client; + expect(client).toBe(coreStart.savedObjects.getScopedClient.mock.results[0].value); + }); + + test('lazily created', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const savedObjects = context.savedObjects; + expect(coreStart.savedObjects.getScopedClient).not.toHaveBeenCalled(); + const client = savedObjects.client; + expect(coreStart.savedObjects.getScopedClient).toHaveBeenCalled(); + expect(client).toBeDefined(); + }); + + test('only creates one instance', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client1 = context.savedObjects.client; + const client2 = context.savedObjects.client; + expect(coreStart.savedObjects.getScopedClient.mock.calls.length).toBe(1); + const mockResult = coreStart.savedObjects.getScopedClient.mock.results[0].value; + expect(client1).toBe(mockResult); + expect(client2).toBe(mockResult); + }); + }); + + describe('#typeRegistry', () => { + test('returns the results of coreStart.savedObjects.getTypeRegistry', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const typeRegistry = context.savedObjects.typeRegistry; + expect(typeRegistry).toBe(coreStart.savedObjects.getTypeRegistry.mock.results[0].value); + }); + + test('lazily created', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + expect(coreStart.savedObjects.getTypeRegistry).not.toHaveBeenCalled(); + const typeRegistry = context.savedObjects.typeRegistry; + expect(coreStart.savedObjects.getTypeRegistry).toHaveBeenCalled(); + expect(typeRegistry).toBeDefined(); + }); + + test('only creates one instance', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const typeRegistry1 = context.savedObjects.typeRegistry; + const typeRegistry2 = context.savedObjects.typeRegistry; + expect(coreStart.savedObjects.getTypeRegistry.mock.calls.length).toBe(1); + const mockResult = coreStart.savedObjects.getTypeRegistry.mock.results[0].value; + expect(typeRegistry1).toBe(mockResult); + expect(typeRegistry2).toBe(mockResult); + }); + }); +}); + +describe('#uiSettings', () => { + describe('#client', () => { + test('returns the results of coreStart.uiSettings.asScopedToClient', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client = context.uiSettings.client; + expect(client).toBe(coreStart.uiSettings.asScopedToClient.mock.results[0].value); + }); + + test('lazily created', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + expect(coreStart.uiSettings.asScopedToClient).not.toHaveBeenCalled(); + const client = context.uiSettings.client; + expect(coreStart.uiSettings.asScopedToClient).toHaveBeenCalled(); + expect(client).toBeDefined(); + }); + + test('only creates one instance', () => { + const request = httpServerMock.createKibanaRequest(); + const coreStart = coreMock.createInternalStart(); + const context = new CoreRouteHandlerContext(coreStart, request); + + const client1 = context.uiSettings.client; + const client2 = context.uiSettings.client; + expect(coreStart.uiSettings.asScopedToClient.mock.calls.length).toBe(1); + const mockResult = coreStart.uiSettings.asScopedToClient.mock.results[0].value; + expect(client1).toBe(mockResult); + expect(client2).toBe(mockResult); + }); + }); +}); diff --git a/src/core/server/core_route_handler_context.ts b/src/core/server/core_route_handler_context.ts new file mode 100644 index 00000000000000..8a182a523f52c9 --- /dev/null +++ b/src/core/server/core_route_handler_context.ts @@ -0,0 +1,132 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// eslint-disable-next-line max-classes-per-file +import { InternalCoreStart } from './internal_types'; +import { KibanaRequest } from './http/router'; +import { SavedObjectsClientContract } from './saved_objects/types'; +import { InternalSavedObjectsServiceStart, ISavedObjectTypeRegistry } from './saved_objects'; +import { + InternalElasticsearchServiceStart, + IScopedClusterClient, + LegacyScopedClusterClient, +} from './elasticsearch'; +import { Auditor } from './audit_trail'; +import { InternalUiSettingsServiceStart, IUiSettingsClient } from './ui_settings'; + +class CoreElasticsearchRouteHandlerContext { + #client?: IScopedClusterClient; + #legacy?: { + client: Pick; + }; + + constructor( + private readonly elasticsearchStart: InternalElasticsearchServiceStart, + private readonly request: KibanaRequest + ) {} + + public get client() { + if (this.#client == null) { + this.#client = this.elasticsearchStart.client.asScoped(this.request); + } + return this.#client; + } + + public get legacy() { + if (this.#legacy == null) { + this.#legacy = { + client: this.elasticsearchStart.legacy.client.asScoped(this.request), + }; + } + return this.#legacy; + } +} + +class CoreSavedObjectsRouteHandlerContext { + constructor( + private readonly savedObjectsStart: InternalSavedObjectsServiceStart, + private readonly request: KibanaRequest + ) {} + #scopedSavedObjectsClient?: SavedObjectsClientContract; + #typeRegistry?: ISavedObjectTypeRegistry; + + public get client() { + if (this.#scopedSavedObjectsClient == null) { + this.#scopedSavedObjectsClient = this.savedObjectsStart.getScopedClient(this.request); + } + return this.#scopedSavedObjectsClient; + } + + public get typeRegistry() { + if (this.#typeRegistry == null) { + this.#typeRegistry = this.savedObjectsStart.getTypeRegistry(); + } + return this.#typeRegistry; + } +} + +class CoreUiSettingsRouteHandlerContext { + #client?: IUiSettingsClient; + constructor( + private readonly uiSettingsStart: InternalUiSettingsServiceStart, + private readonly savedObjectsRouterHandlerContext: CoreSavedObjectsRouteHandlerContext + ) {} + + public get client() { + if (this.#client == null) { + this.#client = this.uiSettingsStart.asScopedToClient( + this.savedObjectsRouterHandlerContext.client + ); + } + return this.#client; + } +} + +export class CoreRouteHandlerContext { + #auditor?: Auditor; + + readonly elasticsearch: CoreElasticsearchRouteHandlerContext; + readonly savedObjects: CoreSavedObjectsRouteHandlerContext; + readonly uiSettings: CoreUiSettingsRouteHandlerContext; + + constructor( + private readonly coreStart: InternalCoreStart, + private readonly request: KibanaRequest + ) { + this.elasticsearch = new CoreElasticsearchRouteHandlerContext( + this.coreStart.elasticsearch, + this.request + ); + this.savedObjects = new CoreSavedObjectsRouteHandlerContext( + this.coreStart.savedObjects, + this.request + ); + this.uiSettings = new CoreUiSettingsRouteHandlerContext( + this.coreStart.uiSettings, + this.savedObjects + ); + } + + public get auditor() { + if (this.#auditor == null) { + this.#auditor = this.coreStart.auditTrail.asScoped(this.request); + } + return this.#auditor; + } +} diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 4e5a7a328bed4c..ece10db41962d4 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -48,6 +48,7 @@ import { config as statusConfig } from './status'; import { ContextService } from './context'; import { RequestHandlerContext } from '.'; import { InternalCoreSetup, InternalCoreStart, ServiceConfigDescriptor } from './internal_types'; +import { CoreRouteHandlerContext } from './core_route_handler_context'; const coreId = Symbol('core'); const rootConfigPath = ''; @@ -279,25 +280,7 @@ export class Server { coreId, 'core', async (context, req, res): Promise => { - const coreStart = this.coreStart!; - const savedObjectsClient = coreStart.savedObjects.getScopedClient(req); - - return { - savedObjects: { - client: savedObjectsClient, - typeRegistry: coreStart.savedObjects.getTypeRegistry(), - }, - elasticsearch: { - client: coreStart.elasticsearch.client.asScoped(req), - legacy: { - client: coreStart.elasticsearch.legacy.client.asScoped(req), - }, - }, - uiSettings: { - client: coreStart.uiSettings.asScopedToClient(savedObjectsClient), - }, - auditor: coreStart.auditTrail.asScoped(req), - }; + return new CoreRouteHandlerContext(this.coreStart!, req); } ); } From 9f5033354d2a8d8cb264827a7ab6d38b36a8a532 Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Thu, 1 Oct 2020 08:57:52 -0500 Subject: [PATCH 019/128] Remove APM-specific loading indicator (#79042) Kibana now has its own loading indicator. Remove the one we were using. Also remove the old service map loading indicator that was used back when we had background jobs. Fixes #78769. --- .../plugins/apm/public/application/csmApp.tsx | 5 +- .../plugins/apm/public/application/index.tsx | 9 +- .../app/ServiceMap/LoadingOverlay.tsx | 61 ------------ .../Delayed/index.test.tsx | 96 ------------------- .../useDelayedVisibility/Delayed/index.ts | 66 ------------- .../useDelayedVisibility/index.test.tsx | 86 ----------------- .../shared/useDelayedVisibility/index.ts | 48 ---------- .../context/LoadingIndicatorContext.tsx | 65 ------------- .../plugins/apm/public/hooks/useFetcher.tsx | 14 +-- .../apm/public/hooks/useLoadingIndicator.ts | 28 ------ .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 12 files changed, 5 insertions(+), 475 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx delete mode 100644 x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx delete mode 100644 x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts delete mode 100644 x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx delete mode 100644 x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.ts delete mode 100644 x-pack/plugins/apm/public/context/LoadingIndicatorContext.tsx delete mode 100644 x-pack/plugins/apm/public/hooks/useLoadingIndicator.ts diff --git a/x-pack/plugins/apm/public/application/csmApp.tsx b/x-pack/plugins/apm/public/application/csmApp.tsx index 5ebe14b663f562..2baddbe572a528 100644 --- a/x-pack/plugins/apm/public/application/csmApp.tsx +++ b/x-pack/plugins/apm/public/application/csmApp.tsx @@ -22,7 +22,6 @@ import { renderAsRedirectTo } from '../components/app/Main/route_config'; import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; import { RumHome, UX_LABEL } from '../components/app/RumDashboard/RumHome'; import { ApmPluginContext } from '../context/ApmPluginContext'; -import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; import { UrlParamsProvider } from '../context/UrlParamsContext'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { ConfigSchema } from '../index'; @@ -92,9 +91,7 @@ export function CsmAppRoot({ - - - + diff --git a/x-pack/plugins/apm/public/application/index.tsx b/x-pack/plugins/apm/public/application/index.tsx index 9843f73dafdbb6..24505951c9d719 100644 --- a/x-pack/plugins/apm/public/application/index.tsx +++ b/x-pack/plugins/apm/public/application/index.tsx @@ -24,7 +24,6 @@ import { routes } from '../components/app/Main/route_config'; import { ScrollToTopOnPathChange } from '../components/app/Main/ScrollToTopOnPathChange'; import { ApmPluginContext } from '../context/ApmPluginContext'; import { LicenseProvider } from '../context/LicenseContext'; -import { LoadingIndicatorProvider } from '../context/LoadingIndicatorContext'; import { UrlParamsProvider } from '../context/UrlParamsContext'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; import { ApmPluginSetupDeps } from '../plugin'; @@ -99,11 +98,9 @@ export function ApmAppRoot({ - - - - - + + + diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx b/x-pack/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx deleted file mode 100644 index 8557c3f0c0798e..00000000000000 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/LoadingOverlay.tsx +++ /dev/null @@ -1,61 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; -import { EuiProgress, EuiText, EuiSpacer } from '@elastic/eui'; -import styled from 'styled-components'; -import { i18n } from '@kbn/i18n'; - -const Container = styled.div` - position: relative; -`; - -const Overlay = styled.div` - position: absolute; - top: 0; - z-index: 1; - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - padding: ${({ theme }) => theme.eui.gutterTypes.gutterMedium}; -`; - -const ProgressBarContainer = styled.div` - width: 50%; - max-width: 600px; -`; - -interface Props { - isLoading: boolean; - percentageLoaded: number; -} - -export function LoadingOverlay({ isLoading, percentageLoaded }: Props) { - return ( - - {isLoading && ( - - - - - - - {i18n.translate('xpack.apm.loadingServiceMap', { - defaultMessage: - 'Loading service map... This might take a short while.', - })} - - - )} - - ); -} diff --git a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx deleted file mode 100644 index 47c439bdd746da..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.test.tsx +++ /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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Delayed } from './index'; - -// Advanced time like setTimeout and mocks Date.now() to stay in sync -class AdvanceTimer { - public nowTime = 0; - public advance(ms: number) { - this.nowTime += ms; - jest.spyOn(Date, 'now').mockReturnValue(this.nowTime); - jest.advanceTimersByTime(ms); - } -} - -describe('Delayed', () => { - it('should not flicker between show/hide when the hide interval is very short', async () => { - jest.useFakeTimers(); - const visibilityChanges: boolean[] = []; - const advanceTimer = new AdvanceTimer(); - const delayed = new Delayed(); - - delayed.onChange((isVisible) => visibilityChanges.push(isVisible)); - - for (let i = 1; i < 100; i += 2) { - delayed.show(); - advanceTimer.advance(1000); - delayed.hide(); - advanceTimer.advance(20); - } - advanceTimer.advance(100); - - expect(visibilityChanges).toEqual([true, false]); - }); - - it('should not be shown at all when the duration is very short', async () => { - jest.useFakeTimers(); - const advanceTimer = new AdvanceTimer(); - const visibilityChanges: boolean[] = []; - const delayed = new Delayed(); - - delayed.onChange((isVisible) => visibilityChanges.push(isVisible)); - - delayed.show(); - advanceTimer.advance(30); - delayed.hide(); - advanceTimer.advance(1000); - - expect(visibilityChanges).toEqual([]); - }); - - it('should be displayed for minimum 1000ms', async () => { - jest.useFakeTimers(); - const visibilityChanges: boolean[] = []; - const advanceTimer = new AdvanceTimer(); - const delayed = new Delayed(); - - delayed.onChange((isVisible) => visibilityChanges.push(isVisible)); - - delayed.show(); - advanceTimer.advance(200); - delayed.hide(); - advanceTimer.advance(950); - expect(visibilityChanges).toEqual([true]); - advanceTimer.advance(100); - expect(visibilityChanges).toEqual([true, false]); - delayed.show(); - advanceTimer.advance(50); - expect(visibilityChanges).toEqual([true, false, true]); - delayed.hide(); - advanceTimer.advance(950); - expect(visibilityChanges).toEqual([true, false, true]); - advanceTimer.advance(100); - expect(visibilityChanges).toEqual([true, false, true, false]); - }); - - it('should be displayed for minimum 2000ms', async () => { - jest.useFakeTimers(); - const visibilityChanges: boolean[] = []; - const advanceTimer = new AdvanceTimer(); - const delayed = new Delayed({ minimumVisibleDuration: 2000 }); - - delayed.onChange((isVisible) => visibilityChanges.push(isVisible)); - - delayed.show(); - advanceTimer.advance(200); - delayed.hide(); - advanceTimer.advance(1950); - expect(visibilityChanges).toEqual([true]); - advanceTimer.advance(100); - expect(visibilityChanges).toEqual([true, false]); - }); -}); diff --git a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts deleted file mode 100644 index daab721f64b1a0..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/Delayed/index.ts +++ /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; - * you may not use this file except in compliance with the Elastic License. - */ - -type Callback = (isVisible: boolean) => void; - -export class Delayed { - private displayedAt = 0; - private hideDelayMs: number; - private isVisible = false; - private minimumVisibleDuration: number; - private showDelayMs: number; - private timeoutId?: number; - - constructor({ - minimumVisibleDuration = 1000, - showDelayMs = 50, - hideDelayMs = 50, - } = {}) { - this.minimumVisibleDuration = minimumVisibleDuration; - this.hideDelayMs = hideDelayMs; - this.showDelayMs = showDelayMs; - } - - private onChangeCallback: Callback = () => null; - - private updateState(isVisible: boolean) { - window.clearTimeout(this.timeoutId); - const ms = !isVisible - ? Math.max( - this.displayedAt + this.minimumVisibleDuration - Date.now(), - this.hideDelayMs - ) - : this.showDelayMs; - - this.timeoutId = window.setTimeout(() => { - if (this.isVisible !== isVisible) { - this.isVisible = isVisible; - this.onChangeCallback(isVisible); - if (isVisible) { - this.displayedAt = Date.now(); - } - } - }, ms); - } - - public show() { - this.updateState(true); - } - - public hide() { - this.updateState(false); - } - - public onChange(onChangeCallback: Callback) { - this.onChangeCallback = onChangeCallback; - } - - public destroy() { - if (this.timeoutId) { - window.clearTimeout(this.timeoutId); - } - } -} diff --git a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx deleted file mode 100644 index 447e11eab5e412..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.test.tsx +++ /dev/null @@ -1,86 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { - renderHook, - act, - RenderHookResult, -} from '@testing-library/react-hooks'; -import { useDelayedVisibility } from '.'; - -// Failing: See https://github.com/elastic/kibana/issues/66389 -describe.skip('useFetcher', () => { - let hook: RenderHookResult; - - beforeEach(() => { - jest.useFakeTimers(); - }); - - it('is initially false', () => { - hook = renderHook((isLoading) => useDelayedVisibility(isLoading), { - initialProps: false, - }); - expect(hook.result.current).toEqual(false); - }); - - it('does not change to true immediately', () => { - hook = renderHook((isLoading) => useDelayedVisibility(isLoading), { - initialProps: false, - }); - - hook.rerender(true); - act(() => { - jest.advanceTimersByTime(10); - }); - - expect(hook.result.current).toEqual(false); - act(() => { - jest.advanceTimersByTime(50); - }); - - expect(hook.result.current).toEqual(true); - }); - - it('does not change to false immediately', () => { - hook = renderHook((isLoading) => useDelayedVisibility(isLoading), { - initialProps: false, - }); - - hook.rerender(true); - act(() => { - jest.advanceTimersByTime(100); - }); - hook.rerender(false); - - expect(hook.result.current).toEqual(true); - }); - - // Disabled because it's flaky: https://github.com/elastic/kibana/issues/66389 - // it('is true for minimum 1000ms', () => { - // hook = renderHook((isLoading) => useDelayedVisibility(isLoading), { - // initialProps: false, - // }); - - // hook.rerender(true); - - // act(() => { - // jest.advanceTimersByTime(100); - // }); - - // hook.rerender(false); - // act(() => { - // jest.advanceTimersByTime(900); - // }); - - // expect(hook.result.current).toEqual(true); - - // act(() => { - // jest.advanceTimersByTime(100); - // }); - - // expect(hook.result.current).toEqual(false); - // }); -}); diff --git a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.ts b/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.ts deleted file mode 100644 index bd5708494d6410..00000000000000 --- a/x-pack/plugins/apm/public/components/shared/useDelayedVisibility/index.ts +++ /dev/null @@ -1,48 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { useEffect, useRef, useState } from 'react'; -import { Delayed } from './Delayed'; - -export function useDelayedVisibility( - visible: boolean, - hideDelayMs?: number, - showDelayMs?: number, - minimumVisibleDuration?: number -) { - const [isVisible, setIsVisible] = useState(false); - const delayedRef = useRef(null); - - useEffect(() => { - const delayed = new Delayed({ - hideDelayMs, - showDelayMs, - minimumVisibleDuration, - }); - delayed.onChange((visibility) => { - setIsVisible(visibility); - }); - delayedRef.current = delayed; - - return () => { - delayed.destroy(); - }; - }, [hideDelayMs, showDelayMs, minimumVisibleDuration]); - - useEffect(() => { - if (!delayedRef.current) { - return; - } - - if (visible) { - delayedRef.current.show(); - } else { - delayedRef.current.hide(); - } - }, [visible]); - - return isVisible; -} diff --git a/x-pack/plugins/apm/public/context/LoadingIndicatorContext.tsx b/x-pack/plugins/apm/public/context/LoadingIndicatorContext.tsx deleted file mode 100644 index 99822c0bbc5cad..00000000000000 --- a/x-pack/plugins/apm/public/context/LoadingIndicatorContext.tsx +++ /dev/null @@ -1,65 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { EuiPortal, EuiProgress } from '@elastic/eui'; -import { pickBy } from 'lodash'; -import React, { Fragment, useMemo, useReducer } from 'react'; -import { useDelayedVisibility } from '../components/shared/useDelayedVisibility'; - -export const LoadingIndicatorContext = React.createContext({ - statuses: {}, - dispatchStatus: (_action: Action) => {}, -}); - -interface State { - [key: string]: boolean; -} - -interface Action { - isLoading: boolean; - id: number; -} - -function reducer(statuses: State, action: Action) { - // Return an object with only the ids with `true` as their value, so that ids - // that previously had `false` are removed and do not remain hanging around in - // the object. - return pickBy( - { ...statuses, [action.id.toString()]: action.isLoading }, - Boolean - ); -} - -function getIsAnyLoading(statuses: State) { - return Object.values(statuses).some((isLoading) => isLoading); -} - -export function LoadingIndicatorProvider({ - children, -}: { - children: React.ReactNode; -}) { - const [statuses, dispatchStatus] = useReducer(reducer, {}); - const isLoading = useMemo(() => getIsAnyLoading(statuses), [statuses]); - const shouldShowLoadingIndicator = useDelayedVisibility(isLoading); - const contextValue = React.useMemo(() => ({ statuses, dispatchStatus }), [ - statuses, - ]); - - return ( - - {shouldShowLoadingIndicator && ( - - - - )} - - - - ); -} diff --git a/x-pack/plugins/apm/public/hooks/useFetcher.tsx b/x-pack/plugins/apm/public/hooks/useFetcher.tsx index 68b197c46e8885..5d65424844c5ae 100644 --- a/x-pack/plugins/apm/public/hooks/useFetcher.tsx +++ b/x-pack/plugins/apm/public/hooks/useFetcher.tsx @@ -4,14 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useContext, useEffect, useState, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; +import React, { useEffect, useMemo, useState } from 'react'; import { IHttpFetchError } from 'src/core/public'; import { toMountPoint } from '../../../../../src/plugins/kibana_react/public'; -import { LoadingIndicatorContext } from '../context/LoadingIndicatorContext'; import { APMClient, callApmApi } from '../services/rest/createCallApmApi'; import { useApmPluginContext } from './useApmPluginContext'; -import { useLoadingIndicator } from './useLoadingIndicator'; export enum FETCH_STATUS { LOADING = 'loading', @@ -44,9 +42,6 @@ export function useFetcher( ): FetcherResult> & { refetch: () => void } { const { notifications } = useApmPluginContext().core; const { preservePreviousData = true, showToastOnError = true } = options; - const { setIsLoading } = useLoadingIndicator(); - - const { dispatchStatus } = useContext(LoadingIndicatorContext); const [result, setResult] = useState< FetcherResult> >({ @@ -67,8 +62,6 @@ export function useFetcher( return; } - setIsLoading(true); - setResult((prevResult) => ({ data: preservePreviousData ? prevResult.data : undefined, // preserve data from previous state while loading next state status: FETCH_STATUS.LOADING, @@ -78,7 +71,6 @@ export function useFetcher( try { const data = await promise; if (!didCancel) { - setIsLoading(false); setResult({ data, status: FETCH_STATUS.SUCCESS, @@ -122,7 +114,6 @@ export function useFetcher( ), }); } - setIsLoading(false); setResult({ data: undefined, status: FETCH_STATUS.FAILURE, @@ -135,7 +126,6 @@ export function useFetcher( doFetch(); return () => { - setIsLoading(false); didCancel = true; }; /* eslint-disable react-hooks/exhaustive-deps */ @@ -143,8 +133,6 @@ export function useFetcher( counter, preservePreviousData, showToastOnError, - dispatchStatus, - setIsLoading, ...fnDeps, /* eslint-enable react-hooks/exhaustive-deps */ ]); diff --git a/x-pack/plugins/apm/public/hooks/useLoadingIndicator.ts b/x-pack/plugins/apm/public/hooks/useLoadingIndicator.ts deleted file mode 100644 index 6742efb0ffbae7..00000000000000 --- a/x-pack/plugins/apm/public/hooks/useLoadingIndicator.ts +++ /dev/null @@ -1,28 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { useContext, useMemo, useEffect } from 'react'; -import { LoadingIndicatorContext } from '../context/LoadingIndicatorContext'; -import { useComponentId } from './useComponentId'; - -export function useLoadingIndicator() { - const { dispatchStatus } = useContext(LoadingIndicatorContext); - const id = useComponentId(); - - useEffect(() => { - return () => { - dispatchStatus({ id, isLoading: false }); - }; - }, [dispatchStatus, id]); - - return useMemo(() => { - return { - setIsLoading: (loading: boolean) => { - dispatchStatus({ id, isLoading: loading }); - }, - }; - }, [dispatchStatus, id]); -} diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 34ff32244035a0..0ebb10d30c0103 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4712,7 +4712,6 @@ "xpack.apm.license.betaTooltipMessage": "現在、この機能はベータです。不具合を見つけた場合やご意見がある場合、サポートに問い合わせるか、またはディスカッションフォーラムにご報告ください。", "xpack.apm.license.button": "トライアルを開始", "xpack.apm.license.title": "無料の 30 日トライアルを開始", - "xpack.apm.loadingServiceMap": "サービスマップを読み込み中...多少時間がかかる場合があります。", "xpack.apm.localFilters.titles.agentName": "エージェント名", "xpack.apm.localFilters.titles.browser": "ブラウザー", "xpack.apm.localFilters.titles.containerId": "コンテナー ID", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index db59493002987a..acd6db3b758b18 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4714,7 +4714,6 @@ "xpack.apm.license.betaTooltipMessage": "此功能当前为公测版。如果遇到任何错误或有任何反馈,请报告问题或访问我们的论坛。", "xpack.apm.license.button": "开始试用", "xpack.apm.license.title": "开始为期 30 天的免费试用", - "xpack.apm.loadingServiceMap": "正在加载服务地图......这可能需要一会儿时间。", "xpack.apm.localFilters.titles.agentName": "代理名称", "xpack.apm.localFilters.titles.browser": "浏览器", "xpack.apm.localFilters.titles.containerId": "容器 ID", From b692c374a2b0240b27d8077b3a14de435d492851 Mon Sep 17 00:00:00 2001 From: Daniil Suleiman <31325372+sulemanof@users.noreply.github.com> Date: Thu, 1 Oct 2020 17:08:12 +0300 Subject: [PATCH 020/128] Timelion visualization renderer (#78540) * Update styles * Implement toExpressionAst fn * Implement renderer * Update unit tests * Add unit tests * Update types * Remove unused vars * Fix types * Update types * Show error message when no data * Update ExpressionRenderDefinition api * Update renderer when there is no data * Make options component lazy Co-authored-by: Elastic Machine --- ....expressionrenderdefinition.displayname.md | 2 +- ....expressionrenderdefinition.displayname.md | 2 +- .../common/expression_renderers/types.ts | 2 +- src/plugins/expressions/public/public.api.md | 2 +- src/plugins/expressions/server/server.api.md | 2 +- src/plugins/timelion/public/index.scss | 6 ++ .../public/tag_cloud_vis_renderer.tsx | 1 - .../public/__snapshots__/to_ast.test.ts.snap | 21 ++++++ .../public/_timelion_editor.scss | 15 ----- .../public/_timelion_vis.scss | 12 ---- .../{_panel.scss => _timelion_vis.scss} | 8 +++ .../components/{_index.scss => index.scss} | 2 +- .../public/components/index.ts | 1 - .../public/components/timelion_vis.tsx | 50 -------------- .../{panel.tsx => timelion_vis_component.tsx} | 57 ++++++++++------ .../helpers/timelion_request_handler.ts | 4 +- .../vis_type_timelion/public/index.scss | 3 - .../vis_type_timelion/public/plugin.ts | 5 +- .../public/timelion_options.tsx | 47 +++++++++----- .../public/timelion_vis_fn.ts | 26 +++++--- .../public/timelion_vis_renderer.tsx | 65 +++++++++++++++++++ .../public/timelion_vis_type.tsx | 19 ++---- .../{components/chart.tsx => to_ast.test.ts} | 37 +++++------ .../vis_type_timelion/public/to_ast.ts | 37 +++++++++++ .../__snapshots__/build_pipeline.test.ts.snap | 2 - .../public/legacy/build_pipeline.test.ts | 6 -- .../public/legacy/build_pipeline.ts | 5 -- 27 files changed, 258 insertions(+), 181 deletions(-) create mode 100644 src/plugins/vis_type_timelion/public/__snapshots__/to_ast.test.ts.snap delete mode 100644 src/plugins/vis_type_timelion/public/_timelion_editor.scss delete mode 100644 src/plugins/vis_type_timelion/public/_timelion_vis.scss rename src/plugins/vis_type_timelion/public/components/{_panel.scss => _timelion_vis.scss} (88%) rename src/plugins/vis_type_timelion/public/components/{_index.scss => index.scss} (60%) delete mode 100644 src/plugins/vis_type_timelion/public/components/timelion_vis.tsx rename src/plugins/vis_type_timelion/public/components/{panel.tsx => timelion_vis_component.tsx} (90%) delete mode 100644 src/plugins/vis_type_timelion/public/index.scss create mode 100644 src/plugins/vis_type_timelion/public/timelion_vis_renderer.tsx rename src/plugins/vis_type_timelion/public/{components/chart.tsx => to_ast.test.ts} (60%) create mode 100644 src/plugins/vis_type_timelion/public/to_ast.ts diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md index 9d5f7609ee6cd2..a957ecd63f0430 100644 --- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md +++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.expressionrenderdefinition.displayname.md @@ -9,5 +9,5 @@ A user friendly name of the renderer as will be displayed to user in UI. Signature: ```typescript -displayName: string; +displayName?: string; ``` diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md index e936e25cee6cad..8ae5aa2f1790ec 100644 --- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md +++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.expressionrenderdefinition.displayname.md @@ -9,5 +9,5 @@ A user friendly name of the renderer as will be displayed to user in UI. Signature: ```typescript -displayName: string; +displayName?: string; ``` diff --git a/src/plugins/expressions/common/expression_renderers/types.ts b/src/plugins/expressions/common/expression_renderers/types.ts index 7b3e812eafedd4..b760e7b32a7d2b 100644 --- a/src/plugins/expressions/common/expression_renderers/types.ts +++ b/src/plugins/expressions/common/expression_renderers/types.ts @@ -28,7 +28,7 @@ export interface ExpressionRenderDefinition { /** * A user friendly name of the renderer as will be displayed to user in UI. */ - displayName: string; + displayName?: string; /** * Help text as will be displayed to user. A sentence or few about what this diff --git a/src/plugins/expressions/public/public.api.md b/src/plugins/expressions/public/public.api.md index 162f0ef6824f5b..5c0fd8ab1a5729 100644 --- a/src/plugins/expressions/public/public.api.md +++ b/src/plugins/expressions/public/public.api.md @@ -429,7 +429,7 @@ export interface ExpressionImage { // // @public (undocumented) export interface ExpressionRenderDefinition { - displayName: string; + displayName?: string; help?: string; name: string; render: (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise; diff --git a/src/plugins/expressions/server/server.api.md b/src/plugins/expressions/server/server.api.md index 6ac251ea005b44..d8872ee4160174 100644 --- a/src/plugins/expressions/server/server.api.md +++ b/src/plugins/expressions/server/server.api.md @@ -401,7 +401,7 @@ export interface ExpressionImage { // // @public (undocumented) export interface ExpressionRenderDefinition { - displayName: string; + displayName?: string; help?: string; name: string; render: (domNode: HTMLElement, config: Config, handlers: IInterpreterRenderHandlers) => void | Promise; diff --git a/src/plugins/timelion/public/index.scss b/src/plugins/timelion/public/index.scss index 6bf7133287c51c..f39e0c18a28706 100644 --- a/src/plugins/timelion/public/index.scss +++ b/src/plugins/timelion/public/index.scss @@ -10,3 +10,9 @@ @import './app'; @import './base'; @import './directives/index'; + +// these styles is needed to be loaded here explicitly if the timelion visualization was not opened in browser +// styles for timelion visualization are lazy loaded only while a vis is opened +// this will duplicate styles only if both Timelion app and timelion visualization are loaded +// could be left here as it is since the Timelion app is deprecated +@import '../../vis_type_timelion/public/components/index.scss'; diff --git a/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx b/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx index d37aa5f6fe409a..b433ed9cbed218 100644 --- a/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx +++ b/src/plugins/vis_type_tagcloud/public/tag_cloud_vis_renderer.tsx @@ -25,7 +25,6 @@ import { ExpressionRenderDefinition } from '../../expressions/common/expression_ import { TagCloudVisDependencies } from './plugin'; import { TagCloudVisRenderValue } from './tag_cloud_fn'; -// @ts-ignore const TagCloudChart = lazy(() => import('./components/tag_cloud_chart')); export const getTagCloudVisRenderer: ( diff --git a/src/plugins/vis_type_timelion/public/__snapshots__/to_ast.test.ts.snap b/src/plugins/vis_type_timelion/public/__snapshots__/to_ast.test.ts.snap new file mode 100644 index 00000000000000..9e32a6c4ae17cb --- /dev/null +++ b/src/plugins/vis_type_timelion/public/__snapshots__/to_ast.test.ts.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`timelion vis toExpressionAst function should match basic snapshot 1`] = ` +Object { + "chain": Array [ + Object { + "arguments": Object { + "expression": Array [ + ".es(*)", + ], + "interval": Array [ + "auto", + ], + }, + "function": "timelion_vis", + "type": "function", + }, + ], + "type": "expression", +} +`; diff --git a/src/plugins/vis_type_timelion/public/_timelion_editor.scss b/src/plugins/vis_type_timelion/public/_timelion_editor.scss deleted file mode 100644 index a9331930a86ffa..00000000000000 --- a/src/plugins/vis_type_timelion/public/_timelion_editor.scss +++ /dev/null @@ -1,15 +0,0 @@ -.visEditor--timelion { - vis-options-react-wrapper, - .visEditorSidebar__options, - .visEditorSidebar__timelionOptions { - flex: 1 1 auto; - display: flex; - flex-direction: column; - } - - .visEditor__sidebar { - @include euiBreakpoint('xs', 's', 'm') { - width: 100%; - } - } -} diff --git a/src/plugins/vis_type_timelion/public/_timelion_vis.scss b/src/plugins/vis_type_timelion/public/_timelion_vis.scss deleted file mode 100644 index e7175bf3c0c2aa..00000000000000 --- a/src/plugins/vis_type_timelion/public/_timelion_vis.scss +++ /dev/null @@ -1,12 +0,0 @@ -.timVis { - min-width: 100%; - display: flex; - flex-direction: column; - - .timChart { - min-width: 100%; - flex: 1; - display: flex; - flex-direction: column; - } -} diff --git a/src/plugins/vis_type_timelion/public/components/_panel.scss b/src/plugins/vis_type_timelion/public/components/_timelion_vis.scss similarity index 88% rename from src/plugins/vis_type_timelion/public/components/_panel.scss rename to src/plugins/vis_type_timelion/public/components/_timelion_vis.scss index c4d591bc82cad9..6729d400523cd8 100644 --- a/src/plugins/vis_type_timelion/public/components/_panel.scss +++ b/src/plugins/vis_type_timelion/public/components/_timelion_vis.scss @@ -58,3 +58,11 @@ white-space: nowrap; font-weight: $euiFontWeightBold; } + +.visEditor--timelion { + .visEditorSidebar__timelionOptions { + flex: 1 1 auto; + display: flex; + flex-direction: column; + } +} diff --git a/src/plugins/vis_type_timelion/public/components/_index.scss b/src/plugins/vis_type_timelion/public/components/index.scss similarity index 60% rename from src/plugins/vis_type_timelion/public/components/_index.scss rename to src/plugins/vis_type_timelion/public/components/index.scss index 707c9dafebe2b7..a541c66e6e9139 100644 --- a/src/plugins/vis_type_timelion/public/components/_index.scss +++ b/src/plugins/vis_type_timelion/public/components/index.scss @@ -1,2 +1,2 @@ -@import 'panel'; +@import 'timelion_vis'; @import 'timelion_expression_input'; diff --git a/src/plugins/vis_type_timelion/public/components/index.ts b/src/plugins/vis_type_timelion/public/components/index.ts index c70caab8dd70cf..8d7d32a3ba2627 100644 --- a/src/plugins/vis_type_timelion/public/components/index.ts +++ b/src/plugins/vis_type_timelion/public/components/index.ts @@ -19,4 +19,3 @@ export * from './timelion_expression_input'; export * from './timelion_interval'; -export * from './timelion_vis'; diff --git a/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx b/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx deleted file mode 100644 index aa594c749b600e..00000000000000 --- a/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React from 'react'; - -import { IUiSettingsClient } from 'kibana/public'; -import { ChartComponent } from './chart'; -import { VisParams } from '../timelion_vis_fn'; -import { TimelionSuccessResponse } from '../helpers/timelion_request_handler'; -import { ExprVis } from '../../../visualizations/public'; - -export interface TimelionVisComponentProp { - config: IUiSettingsClient; - renderComplete(): void; - updateStatus: object; - vis: ExprVis; - visData: TimelionSuccessResponse; - visParams: VisParams; -} - -function TimelionVisComponent(props: TimelionVisComponentProp) { - return ( -
- -
- ); -} - -export { TimelionVisComponent }; diff --git a/src/plugins/vis_type_timelion/public/components/panel.tsx b/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx similarity index 90% rename from src/plugins/vis_type_timelion/public/components/panel.tsx rename to src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx index 9c30a6b75d6dbc..a7b623ac8680c6 100644 --- a/src/plugins/vis_type_timelion/public/components/panel.tsx +++ b/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx @@ -21,7 +21,9 @@ import React, { useState, useEffect, useMemo, useCallback } from 'react'; import $ from 'jquery'; import moment from 'moment-timezone'; import { debounce, compact, get, each, cloneDeep, last, map } from 'lodash'; +import { useResizeObserver } from '@elastic/eui'; +import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; import { useKibana } from '../../../kibana_react/public'; import '../flot'; import { DEFAULT_TIME_FORMAT } from '../../common/lib'; @@ -38,18 +40,19 @@ import { Series, Sheet } from '../helpers/timelion_request_handler'; import { tickFormatters } from '../helpers/tick_formatters'; import { generateTicksProvider } from '../helpers/tick_generator'; import { TimelionVisDependencies } from '../plugin'; -import { ExprVisAPIEvents } from '../../../visualizations/public'; + +import './index.scss'; interface CrosshairPlot extends jquery.flot.plot { setCrosshair: (pos: Position) => void; clearCrosshair: () => void; } -interface PanelProps { - applyFilter: ExprVisAPIEvents['applyFilter']; +interface TimelionVisComponentProps { + fireEvent: IInterpreterRenderHandlers['event']; interval: string; seriesList: Sheet; - renderComplete(): void; + renderComplete: IInterpreterRenderHandlers['done']; } interface Position { @@ -75,11 +78,16 @@ const DEBOUNCE_DELAY = 50; // ensure legend is the same height with or without a caption so legend items do not move around const emptyCaption = '
'; -function Panel({ interval, seriesList, renderComplete, applyFilter }: PanelProps) { +function TimelionVisComponent({ + interval, + seriesList, + renderComplete, + fireEvent, +}: TimelionVisComponentProps) { const kibana = useKibana(); const [chart, setChart] = useState(() => cloneDeep(seriesList.list)); const [canvasElem, setCanvasElem] = useState(); - const [chartElem, setChartElem] = useState(); + const [chartElem, setChartElem] = useState(null); const [originalColorMap, setOriginalColorMap] = useState(() => new Map()); @@ -191,7 +199,7 @@ function Panel({ interval, seriesList, renderComplete, applyFilter }: PanelProps interval, kibana.services.timefilter, kibana.services.uiSettings, - chartElem && chartElem.clientWidth, + chartElem?.clientWidth, grid ); const updatedSeries = buildSeriesData(chartValue, options); @@ -216,12 +224,14 @@ function Panel({ interval, seriesList, renderComplete, applyFilter }: PanelProps updateCaption(newPlot.getData()); } }, - [canvasElem, chartElem, renderComplete, kibana.services, interval, updateCaption] + [canvasElem, chartElem?.clientWidth, renderComplete, kibana.services, interval, updateCaption] ); + const dimensions = useResizeObserver(chartElem); + useEffect(() => { updatePlot(chart, seriesList.render && seriesList.render.grid); - }, [chart, updatePlot, seriesList.render]); + }, [chart, updatePlot, seriesList.render, dimensions]); useEffect(() => { const colorsSet: Array<[Series, string]> = []; @@ -349,21 +359,24 @@ function Panel({ interval, seriesList, renderComplete, applyFilter }: PanelProps const plotSelectedHandler = useCallback( (event: JQuery.TriggeredEvent, ranges: Ranges) => { - applyFilter({ - timeFieldName: '*', - filters: [ - { - range: { - '*': { - gte: ranges.xaxis.from, - lte: ranges.xaxis.to, + fireEvent({ + name: 'applyFilter', + data: { + timeFieldName: '*', + filters: [ + { + range: { + '*': { + gte: ranges.xaxis.from, + lte: ranges.xaxis.to, + }, }, }, - }, - ], + ], + }, }); }, - [applyFilter] + [fireEvent] ); useEffect(() => { @@ -396,4 +409,6 @@ function Panel({ interval, seriesList, renderComplete, applyFilter }: PanelProps ); } -export { Panel }; +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { TimelionVisComponent as default }; diff --git a/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts index 3442f84599fb82..975d12a152d89d 100644 --- a/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts +++ b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts @@ -19,10 +19,10 @@ import { i18n } from '@kbn/i18n'; import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public'; -import { VisParams } from '../../../visualizations/public'; import { TimeRange, Filter, esQuery, Query } from '../../../data/public'; import { TimelionVisDependencies } from '../plugin'; import { getTimezone } from './get_timezone'; +import { TimelionVisParams } from '../timelion_vis_fn'; interface Stats { cacheCount: number; @@ -77,7 +77,7 @@ export function getTimelionRequestHandler({ timeRange: TimeRange; filters: Filter[]; query: Query; - visParams: VisParams; + visParams: TimelionVisParams; }): Promise { const expression = visParams.expression; diff --git a/src/plugins/vis_type_timelion/public/index.scss b/src/plugins/vis_type_timelion/public/index.scss deleted file mode 100644 index 00e9a885209610..00000000000000 --- a/src/plugins/vis_type_timelion/public/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import './timelion_vis'; -@import './timelion_editor'; -@import './components/index'; diff --git a/src/plugins/vis_type_timelion/public/plugin.ts b/src/plugins/vis_type_timelion/public/plugin.ts index e2c7efec34c7f3..bb8fb6b298a077 100644 --- a/src/plugins/vis_type_timelion/public/plugin.ts +++ b/src/plugins/vis_type_timelion/public/plugin.ts @@ -39,8 +39,8 @@ import { getTimelionVisDefinition } from './timelion_vis_type'; import { setIndexPatterns, setSavedObjectsClient } from './helpers/plugin_services'; import { ConfigSchema } from '../config'; -import './index.scss'; import { getArgValueSuggestions } from './helpers/arg_value_suggestions'; +import { getTimelionVisRenderer } from './timelion_vis_renderer'; /** @internal */ export interface TimelionVisDependencies extends Partial { @@ -93,7 +93,8 @@ export class TimelionVisPlugin }; expressions.registerFunction(() => getTimelionVisualizationConfig(dependencies)); - visualizations.createReactVisualization(getTimelionVisDefinition(dependencies)); + expressions.registerRenderer(getTimelionVisRenderer(dependencies)); + visualizations.createBaseVisualization(getTimelionVisDefinition(dependencies)); return { isUiEnabled: this.initializerContext.config.get().ui.enabled, diff --git a/src/plugins/vis_type_timelion/public/timelion_options.tsx b/src/plugins/vis_type_timelion/public/timelion_options.tsx index dfe017d3a273f5..1ef8088c7a7143 100644 --- a/src/plugins/vis_type_timelion/public/timelion_options.tsx +++ b/src/plugins/vis_type_timelion/public/timelion_options.tsx @@ -21,30 +21,45 @@ import React, { useCallback } from 'react'; import { EuiPanel } from '@elastic/eui'; import { VisOptionsProps } from 'src/plugins/vis_default_editor/public'; -import { VisParams } from './timelion_vis_fn'; +import { KibanaContextProvider } from '../../kibana_react/public'; + +import { TimelionVisParams } from './timelion_vis_fn'; import { TimelionInterval, TimelionExpressionInput } from './components'; +import { TimelionVisDependencies } from './plugin'; -export type TimelionOptionsProps = VisOptionsProps; +export type TimelionOptionsProps = VisOptionsProps; -function TimelionOptions({ stateParams, setValue, setValidity }: TimelionOptionsProps) { - const setInterval = useCallback((value: VisParams['interval']) => setValue('interval', value), [ - setValue, - ]); +function TimelionOptions({ + services, + stateParams, + setValue, + setValidity, +}: TimelionOptionsProps & { + services: TimelionVisDependencies; +}) { + const setInterval = useCallback( + (value: TimelionVisParams['interval']) => setValue('interval', value), + [setValue] + ); const setExpressionInput = useCallback( - (value: VisParams['expression']) => setValue('expression', value), + (value: TimelionVisParams['expression']) => setValue('expression', value), [setValue] ); return ( - - - - + + + + + + ); } -export { TimelionOptions }; +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { TimelionOptions as default }; diff --git a/src/plugins/vis_type_timelion/public/timelion_vis_fn.ts b/src/plugins/vis_type_timelion/public/timelion_vis_fn.ts index d3c6ca5d90371d..a0cd410e197ff0 100644 --- a/src/plugins/vis_type_timelion/public/timelion_vis_fn.ts +++ b/src/plugins/vis_type_timelion/public/timelion_vis_fn.ts @@ -24,29 +24,39 @@ import { KibanaContext, Render, } from 'src/plugins/expressions/public'; -import { getTimelionRequestHandler } from './helpers/timelion_request_handler'; +import { + getTimelionRequestHandler, + TimelionSuccessResponse, +} from './helpers/timelion_request_handler'; import { TIMELION_VIS_NAME } from './timelion_vis_type'; import { TimelionVisDependencies } from './plugin'; import { Filter, Query, TimeRange } from '../../data/common'; type Input = KibanaContext | null; -type Output = Promise>; +type Output = Promise>; interface Arguments { expression: string; interval: string; } -interface RenderValue { - visData: Input; +export interface TimelionRenderValue { + visData: TimelionSuccessResponse; visType: 'timelion'; - visParams: VisParams; + visParams: TimelionVisParams; } -export type VisParams = Arguments; +export type TimelionVisParams = Arguments; + +export type TimelionExpressionFunctionDefinition = ExpressionFunctionDefinition< + 'timelion_vis', + Input, + Arguments, + Output +>; export const getTimelionVisualizationConfig = ( dependencies: TimelionVisDependencies -): ExpressionFunctionDefinition<'timelion_vis', Input, Arguments, Output> => ({ +): TimelionExpressionFunctionDefinition => ({ name: 'timelion_vis', type: 'render', inputTypes: ['kibana_context', 'null'], @@ -82,7 +92,7 @@ export const getTimelionVisualizationConfig = ( return { type: 'render', - as: 'visualization', + as: 'timelion_vis', value: { visParams, visType: TIMELION_VIS_NAME, diff --git a/src/plugins/vis_type_timelion/public/timelion_vis_renderer.tsx b/src/plugins/vis_type_timelion/public/timelion_vis_renderer.tsx new file mode 100644 index 00000000000000..13a279138a8e4b --- /dev/null +++ b/src/plugins/vis_type_timelion/public/timelion_vis_renderer.tsx @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { lazy } from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; + +import { ExpressionRenderDefinition } from 'src/plugins/expressions'; +import { KibanaContextProvider } from '../../kibana_react/public'; +import { VisualizationContainer } from '../../visualizations/public'; +import { TimelionVisDependencies } from './plugin'; +import { TimelionRenderValue } from './timelion_vis_fn'; +// @ts-ignore +const TimelionVisComponent = lazy(() => import('./components/timelion_vis_component')); + +export const getTimelionVisRenderer: ( + deps: TimelionVisDependencies +) => ExpressionRenderDefinition = (deps) => ({ + name: 'timelion_vis', + displayName: 'Timelion visualization', + reuseDomNode: true, + render: (domNode, { visData, visParams }, handlers) => { + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + const [seriesList] = visData.sheet; + const showNoResult = !seriesList || !seriesList.list.length; + + if (showNoResult) { + // send the render complete event when there is no data to show + // to notify that a chart is updated + handlers.done(); + } + + render( + + + + + , + domNode + ); + }, +}); diff --git a/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx index 8fdde175708e0e..a5425478e46acf 100644 --- a/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx +++ b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx @@ -17,18 +17,19 @@ * under the License. */ -import React from 'react'; +import React, { lazy } from 'react'; import { i18n } from '@kbn/i18n'; -import { KibanaContextProvider } from '../../kibana_react/public'; import { DefaultEditorSize } from '../../vis_default_editor/public'; import { getTimelionRequestHandler } from './helpers/timelion_request_handler'; -import { TimelionVisComponent, TimelionVisComponentProp } from './components'; -import { TimelionOptions, TimelionOptionsProps } from './timelion_options'; +import { TimelionOptionsProps } from './timelion_options'; import { TimelionVisDependencies } from './plugin'; +import { toExpressionAst } from './to_ast'; import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public'; +const TimelionOptions = lazy(() => import('./timelion_options')); + export const TIMELION_VIS_NAME = 'timelion'; export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) { @@ -48,21 +49,15 @@ export function getTimelionVisDefinition(dependencies: TimelionVisDependencies) expression: '.es(*)', interval: 'auto', }, - component: (props: TimelionVisComponentProp) => ( - - - - ), }, editorConfig: { optionsTemplate: (props: TimelionOptionsProps) => ( - - - + ), defaultSize: DefaultEditorSize.MEDIUM, }, requestHandler: timelionRequestHandler, + toExpressionAst, responseHandler: 'none', inspectorAdapters: {}, getSupportedTriggers: () => { diff --git a/src/plugins/vis_type_timelion/public/components/chart.tsx b/src/plugins/vis_type_timelion/public/to_ast.test.ts similarity index 60% rename from src/plugins/vis_type_timelion/public/components/chart.tsx rename to src/plugins/vis_type_timelion/public/to_ast.test.ts index 15a376d4e96386..8a9d4b83f94d20 100644 --- a/src/plugins/vis_type_timelion/public/components/chart.tsx +++ b/src/plugins/vis_type_timelion/public/to_ast.test.ts @@ -17,25 +17,24 @@ * under the License. */ -import React from 'react'; +import { Vis } from 'src/plugins/visualizations/public'; +import { TimelionVisParams } from './timelion_vis_fn'; +import { toExpressionAst } from './to_ast'; -import { Sheet } from '../helpers/timelion_request_handler'; -import { Panel } from './panel'; -import { ExprVisAPIEvents } from '../../../visualizations/public'; +describe('timelion vis toExpressionAst function', () => { + let vis: Vis; -interface ChartComponentProp { - applyFilter: ExprVisAPIEvents['applyFilter']; - interval: string; - renderComplete(): void; - seriesList: Sheet; -} + beforeEach(() => { + vis = { + params: { + expression: '.es(*)', + interval: 'auto', + }, + } as any; + }); -function ChartComponent(props: ChartComponentProp) { - if (!props.seriesList) { - return null; - } - - return ; -} - -export { ChartComponent }; + it('should match basic snapshot', () => { + const actual = toExpressionAst(vis); + expect(actual).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/vis_type_timelion/public/to_ast.ts b/src/plugins/vis_type_timelion/public/to_ast.ts new file mode 100644 index 00000000000000..7044bbf4e58318 --- /dev/null +++ b/src/plugins/vis_type_timelion/public/to_ast.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { buildExpression, buildExpressionFunction } from '../../expressions/public'; +import { Vis } from '../../visualizations/public'; +import { TimelionExpressionFunctionDefinition, TimelionVisParams } from './timelion_vis_fn'; + +const escapeString = (data: string): string => { + return data.replace(/\\/g, `\\\\`).replace(/'/g, `\\'`); +}; + +export const toExpressionAst = (vis: Vis) => { + const timelion = buildExpressionFunction('timelion_vis', { + expression: escapeString(vis.params.expression), + interval: escapeString(vis.params.interval), + }); + + const ast = buildExpression([timelion]); + + return ast.toAst(); +}; diff --git a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap index 654ac78cdaa022..c0c37e2262f9c6 100644 --- a/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap +++ b/src/plugins/visualizations/public/legacy/__snapshots__/build_pipeline.test.ts.snap @@ -24,6 +24,4 @@ exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunct exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles tile_map function 1`] = `"tilemap visConfig='{\\"metric\\":{},\\"dimensions\\":{\\"metric\\":{\\"accessor\\":0,\\"label\\":\\"\\",\\"format\\":{},\\"params\\":{},\\"aggType\\":\\"\\"},\\"geohash\\":1,\\"geocentroid\\":3}}' "`; -exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles timelion function 1`] = `"timelion_vis expression='foo' interval='bar' "`; - exports[`visualize loader pipeline helpers: build pipeline buildPipelineVisFunction handles vega function 1`] = `"vega spec='this is a test' "`; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts index 8cac76726b13b0..a1fea45f517816 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.test.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.test.ts @@ -117,12 +117,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => { expect(actual).toMatchSnapshot(); }); - it('handles timelion function', () => { - const params = { expression: 'foo', interval: 'bar' }; - const actual = buildPipelineVisFunction.timelion(params, schemasDef, uiState); - expect(actual).toMatchSnapshot(); - }); - describe('handles table function', () => { it('without splits or buckets', () => { const params = { foo: 'bar' }; diff --git a/src/plugins/visualizations/public/legacy/build_pipeline.ts b/src/plugins/visualizations/public/legacy/build_pipeline.ts index dcc384a191858f..79e1c1cca2155a 100644 --- a/src/plugins/visualizations/public/legacy/build_pipeline.ts +++ b/src/plugins/visualizations/public/legacy/build_pipeline.ts @@ -263,11 +263,6 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = { const paramsArray = [paramsJson, uiStateJson].filter((param) => Boolean(param)); return `tsvb ${paramsArray.join(' ')}`; }, - timelion: (params) => { - const expression = prepareString('expression', params.expression); - const interval = prepareString('interval', params.interval); - return `timelion_vis ${expression}${interval}`; - }, table: (params, schemas) => { const visConfig = { ...params, From 21353403b8bc84bf84971c5b92fb2fcaa09b2f59 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 1 Oct 2020 15:16:17 +0100 Subject: [PATCH 021/128] [ML] Improve calendar ics file parsing (#78986) --- .../settings/calendars/edit/import_modal/utils.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/application/settings/calendars/edit/import_modal/utils.js b/x-pack/plugins/ml/public/application/settings/calendars/edit/import_modal/utils.js index 07bf49ea6d7db2..e93abc8eb67b5a 100644 --- a/x-pack/plugins/ml/public/application/settings/calendars/edit/import_modal/utils.js +++ b/x-pack/plugins/ml/public/application/settings/calendars/edit/import_modal/utils.js @@ -42,7 +42,10 @@ export function filterEvents(events) { } export function parseICSFile(data) { - const cal = icalendar.parse_calendar(data); + // force a new line char on the end of the data + // icalendar must split on new lines and so parsing fails + // if there isn't at least one new line at the end. + const cal = icalendar.parse_calendar(data + '\n'); return createEvents(cal); } From bad59f4fb4cc1c2c2420b6f81cc1fde18ae44721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= Date: Thu, 1 Oct 2020 15:16:49 +0100 Subject: [PATCH 022/128] [Usage Collection] [schema] `maps` (#78952) --- x-pack/.telemetryrc.json | 3 +- .../maps_telemetry/collectors/register.ts | 34 ++++++- .../collectors/register_collector.test.js | 1 + .../server/maps_telemetry/maps_telemetry.ts | 82 ++++++++++------ .../schema/xpack_plugins.json | 93 +++++++++++++++++++ 5 files changed, 180 insertions(+), 33 deletions(-) diff --git a/x-pack/.telemetryrc.json b/x-pack/.telemetryrc.json index d0e56bbed9f47d..c7430666c538f6 100644 --- a/x-pack/.telemetryrc.json +++ b/x-pack/.telemetryrc.json @@ -4,7 +4,6 @@ "exclude": [ "plugins/actions/server/usage/actions_usage_collector.ts", "plugins/alerts/server/usage/alerts_usage_collector.ts", - "plugins/apm/server/lib/apm_telemetry/index.ts", - "plugins/maps/server/maps_telemetry/collectors/register.ts" + "plugins/apm/server/lib/apm_telemetry/index.ts" ] } diff --git a/x-pack/plugins/maps/server/maps_telemetry/collectors/register.ts b/x-pack/plugins/maps/server/maps_telemetry/collectors/register.ts index f54776f5ab629f..e0ab2cf77f0843 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/collectors/register.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/collectors/register.ts @@ -5,7 +5,7 @@ */ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { getMapsTelemetry } from '../maps_telemetry'; +import { getMapsTelemetry, MapsUsage } from '../maps_telemetry'; import { MapsConfigType } from '../../../config'; export function registerMapsUsageCollector( @@ -16,10 +16,40 @@ export function registerMapsUsageCollector( return; } - const mapsUsageCollector = usageCollection.makeUsageCollector({ + const mapsUsageCollector = usageCollection.makeUsageCollector({ type: 'maps', isReady: () => true, fetch: async () => await getMapsTelemetry(config), + schema: { + settings: { + showMapVisualizationTypes: { type: 'boolean' }, + }, + indexPatternsWithGeoFieldCount: { type: 'long' }, + indexPatternsWithGeoPointFieldCount: { type: 'long' }, + indexPatternsWithGeoShapeFieldCount: { type: 'long' }, + geoShapeAggLayersCount: { type: 'long' }, + mapsTotalCount: { type: 'long' }, + timeCaptured: { type: 'date' }, + attributesPerMap: { + dataSourcesCount: { + min: { type: 'long' }, + max: { type: 'long' }, + avg: { type: 'float' }, + }, + layersCount: { + min: { type: 'long' }, + max: { type: 'long' }, + avg: { type: 'float' }, + }, + // TODO: Find out all the possible values for DYNAMIC_KEY or reformat into an array + layerTypesCount: { + DYNAMIC_KEY: { min: { type: 'long' }, max: { type: 'long' }, avg: { type: 'float' } }, + }, + emsVectorLayersCount: { + DYNAMIC_KEY: { min: { type: 'long' }, max: { type: 'long' }, avg: { type: 'float' } }, + }, + }, + }, }); usageCollection.registerCollector(mapsUsageCollector); diff --git a/x-pack/plugins/maps/server/maps_telemetry/collectors/register_collector.test.js b/x-pack/plugins/maps/server/maps_telemetry/collectors/register_collector.test.js index 33eb33100acdfa..61f4629b00712b 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/collectors/register_collector.test.js +++ b/x-pack/plugins/maps/server/maps_telemetry/collectors/register_collector.test.js @@ -33,6 +33,7 @@ describe('buildCollectorObj#fetch', () => { type: expect.any(String), isReady: expect.any(Function), fetch: expect.any(Function), + schema: expect.any(Object), }); }); }); diff --git a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts index 2af6413da039b3..56ccc7baea2838 100644 --- a/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts +++ b/x-pack/plugins/maps/server/maps_telemetry/maps_telemetry.ts @@ -5,12 +5,7 @@ */ import _ from 'lodash'; -import { - SavedObject, - SavedObjectAttribute, - SavedObjectAttributes, - SavedObjectsClientContract, -} from 'kibana/server'; +import { ISavedObjectsRepository, SavedObject, SavedObjectsClientContract } from 'kibana/server'; import { IFieldType, IndexPatternAttributes } from 'src/plugins/data/public'; import { ES_GEO_FIELD_TYPE, @@ -25,11 +20,14 @@ import { ESSearchSourceDescriptor, LayerDescriptor, } from '../../common/descriptor_types'; -import { MapSavedObject } from '../../common/map_saved_object_type'; -// @ts-ignore +import { MapSavedObject, MapSavedObjectAttributes } from '../../common/map_saved_object_type'; import { getInternalRepository } from '../kibana_server_services'; import { MapsConfigType } from '../../config'; +interface Settings { + showMapVisualizationTypes: boolean; +} + interface IStats { [key: string]: { min: number; @@ -42,6 +40,30 @@ interface ILayerTypeCount { [key: string]: number; } +export interface MapsUsage { + settings: Settings; + indexPatternsWithGeoFieldCount: number; + indexPatternsWithGeoPointFieldCount: number; + indexPatternsWithGeoShapeFieldCount: number; + geoShapeAggLayersCount: number; + mapsTotalCount: number; + timeCaptured: string; + attributesPerMap: { + dataSourcesCount: { + min: number; + max: number; + avg: number; + }; + layersCount: { + min: number; + max: number; + avg: number; + }; + layerTypesCount: IStats; + emsVectorLayersCount: IStats; + }; +} + function getUniqueLayerCounts(layerCountsList: ILayerTypeCount[], mapsCount: number) { const uniqueLayerTypes = _.uniq(_.flatten(layerCountsList.map((lTypes) => Object.keys(lTypes)))); @@ -213,8 +235,8 @@ export function buildMapsTelemetry({ }: { mapSavedObjects: MapSavedObject[]; indexPatternSavedObjects: Array>; - settings: SavedObjectAttribute; -}): SavedObjectAttributes { + settings: Settings; +}): MapsUsage { const layerLists: LayerDescriptor[][] = getLayerLists(mapSavedObjects); const mapsCount = layerLists.length; @@ -256,14 +278,14 @@ export function buildMapsTelemetry({ attributesPerMap: { // Count of data sources per map dataSourcesCount: { - min: dataSourcesCount.length ? _.min(dataSourcesCount) : 0, - max: dataSourcesCount.length ? _.max(dataSourcesCount) : 0, + min: dataSourcesCount.length ? _.min(dataSourcesCount)! : 0, + max: dataSourcesCount.length ? _.max(dataSourcesCount)! : 0, avg: dataSourcesCountSum ? layersCountSum / mapsCount : 0, }, // Total count of layers per map layersCount: { - min: layersCount.length ? _.min(layersCount) : 0, - max: layersCount.length ? _.max(layersCount) : 0, + min: layersCount.length ? _.min(layersCount)! : 0, + max: layersCount.length ? _.max(layersCount)! : 0, avg: layersCountSum ? layersCountSum / mapsCount : 0, }, // Count of layers by type @@ -277,27 +299,29 @@ export function buildMapsTelemetry({ }, }; } -async function getMapSavedObjects(savedObjectsClient: SavedObjectsClientContract) { - const mapsSavedObjects = await savedObjectsClient.find({ type: MAP_SAVED_OBJECT_TYPE }); - return _.get(mapsSavedObjects, 'saved_objects', []); +async function getMapSavedObjects( + savedObjectsClient: SavedObjectsClientContract | ISavedObjectsRepository +) { + const mapsSavedObjects = await savedObjectsClient.find({ + type: MAP_SAVED_OBJECT_TYPE, + }); + return mapsSavedObjects.saved_objects; } -async function getIndexPatternSavedObjects(savedObjectsClient: SavedObjectsClientContract) { - const indexPatternSavedObjects = await savedObjectsClient.find({ type: 'index-pattern' }); - return _.get(indexPatternSavedObjects, 'saved_objects', []); +async function getIndexPatternSavedObjects( + savedObjectsClient: SavedObjectsClientContract | ISavedObjectsRepository +) { + const indexPatternSavedObjects = await savedObjectsClient.find({ + type: 'index-pattern', + }); + return indexPatternSavedObjects.saved_objects; } export async function getMapsTelemetry(config: MapsConfigType) { const savedObjectsClient = getInternalRepository(); - // @ts-ignore - const mapSavedObjects: MapSavedObject[] = await getMapSavedObjects(savedObjectsClient); - const indexPatternSavedObjects: Array> = (await getIndexPatternSavedObjects( - // @ts-ignore - savedObjectsClient - )) as Array>; - const settings: SavedObjectAttribute = { + const mapSavedObjects = await getMapSavedObjects(savedObjectsClient); + const indexPatternSavedObjects = await getIndexPatternSavedObjects(savedObjectsClient); + const settings = { showMapVisualizationTypes: config.showMapVisualizationTypes, }; return buildMapsTelemetry({ mapSavedObjects, indexPatternSavedObjects, settings }); 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 816d6828381ee4..b08585066f100a 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -679,6 +679,99 @@ } } }, + "maps": { + "properties": { + "settings": { + "properties": { + "showMapVisualizationTypes": { + "type": "boolean" + } + } + }, + "indexPatternsWithGeoFieldCount": { + "type": "long" + }, + "indexPatternsWithGeoPointFieldCount": { + "type": "long" + }, + "indexPatternsWithGeoShapeFieldCount": { + "type": "long" + }, + "geoShapeAggLayersCount": { + "type": "long" + }, + "mapsTotalCount": { + "type": "long" + }, + "timeCaptured": { + "type": "date" + }, + "attributesPerMap": { + "properties": { + "dataSourcesCount": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + }, + "layersCount": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + }, + "layerTypesCount": { + "properties": { + "DYNAMIC_KEY": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + } + } + }, + "emsVectorLayersCount": { + "properties": { + "DYNAMIC_KEY": { + "properties": { + "min": { + "type": "long" + }, + "max": { + "type": "long" + }, + "avg": { + "type": "float" + } + } + } + } + } + } + } + } + }, "mlTelemetry": { "properties": { "file_data_visualizer": { From 31efa1ab5b4d68b681a6342708a39b2359c589e8 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Thu, 1 Oct 2020 09:39:17 -0500 Subject: [PATCH 023/128] [ML] Add feature importance summary charts (#78238) Co-authored-by: Elastic Machine --- .../ml/common/types/feature_importance.ts | 39 +++ x-pack/plugins/ml/common/types/inference.ts | 2 + .../decision_path_chart.tsx | 12 +- .../decision_path_classification.tsx | 1 + .../common/use_results_view_config.ts | 37 +++ .../classification_exploration.tsx | 3 +- .../exploration_page_wrapper.tsx | 12 + .../regression_exploration.tsx | 2 + .../feature_importance_summary.tsx | 264 ++++++++++++++++++ .../services/ml_api_service/inference.ts | 1 + .../server/routes/schemas/inference_schema.ts | 1 + 11 files changed, 370 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx diff --git a/x-pack/plugins/ml/common/types/feature_importance.ts b/x-pack/plugins/ml/common/types/feature_importance.ts index d2ab9f6c58608a..4f5619cf3ab7bd 100644 --- a/x-pack/plugins/ml/common/types/feature_importance.ts +++ b/x-pack/plugins/ml/common/types/feature_importance.ts @@ -21,3 +21,42 @@ export interface TopClass { } export type TopClasses = TopClass[]; + +export interface ClassFeatureImportanceSummary { + class_name: string; + importance: { + max: number; + min: number; + mean_magnitude: number; + }; +} +export interface ClassificationTotalFeatureImportance { + feature_name: string; + classes: ClassFeatureImportanceSummary[]; +} + +export interface RegressionFeatureImportanceSummary { + max: number; + min: number; + mean_magnitude: number; +} + +export interface RegressionTotalFeatureImportance { + feature_name: string; + importance: RegressionFeatureImportanceSummary; +} +export type TotalFeatureImportance = + | ClassificationTotalFeatureImportance + | RegressionTotalFeatureImportance; + +export function isClassificationTotalFeatureImportance( + summary: ClassificationTotalFeatureImportance | RegressionTotalFeatureImportance +): summary is ClassificationTotalFeatureImportance { + return (summary as ClassificationTotalFeatureImportance).classes !== undefined; +} + +export function isRegressionTotalFeatureImportance( + summary: ClassificationTotalFeatureImportance | RegressionTotalFeatureImportance +): summary is RegressionTotalFeatureImportance { + return (summary as RegressionTotalFeatureImportance).importance !== undefined; +} diff --git a/x-pack/plugins/ml/common/types/inference.ts b/x-pack/plugins/ml/common/types/inference.ts index c70ee264e6fc88..ce2cfb1f78fd90 100644 --- a/x-pack/plugins/ml/common/types/inference.ts +++ b/x-pack/plugins/ml/common/types/inference.ts @@ -5,6 +5,7 @@ */ import { DataFrameAnalyticsConfig } from './data_frame_analytics'; +import { TotalFeatureImportance } from './feature_importance'; export interface IngestStats { count: number; @@ -54,6 +55,7 @@ export interface ModelConfigResponse { | { analytics_config: DataFrameAnalyticsConfig; input: any; + total_feature_importance?: TotalFeatureImportance[]; } | Record; model_id: string; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx b/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx index b546ac1db57dd0..e3ab0abc18e71f 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx +++ b/x-pack/plugins/ml/public/application/components/data_grid/feature_importance/decision_path_chart.tsx @@ -74,6 +74,7 @@ interface DecisionPathChartProps { baseline?: number; minDomain: number | undefined; maxDomain: number | undefined; + showValues?: boolean; } const DECISION_PATH_MARGIN = 125; @@ -87,6 +88,7 @@ export const DecisionPathChart = ({ minDomain, maxDomain, baseline, + showValues, }: DecisionPathChartProps) => { // adjust the height so it's compact for items with more features const baselineData: LineAnnotationDatum[] = useMemo( @@ -105,9 +107,12 @@ export const DecisionPathChart = ({ ], [baseline] ); - // guarantee up to num_precision significant digits - // without having it in scientific notation - const tickFormatter = useCallback((d) => Number(d.toPrecision(NUM_PRECISION)).toString(), []); + // if regression, guarantee up to num_precision significant digits without having it in scientific notation + // if classification, hide the numeric values since we only want to show the path + const tickFormatter = useCallback( + (d) => (showValues === false ? '' : Number(d.toPrecision(NUM_PRECISION)).toString()), + [] + ); return ( = ( predictionFieldName={predictionFieldName} minDomain={domain.minDomain} maxDomain={domain.maxDomain} + showValues={false} /> ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts index b0e73edff7476a..82f431c50fb664 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/use_results_view_config.ts @@ -19,9 +19,18 @@ import { DataFrameAnalyticsConfig } from '../common'; import { isGetDataFrameAnalyticsStatsResponseOk } from '../pages/analytics_management/services/analytics_service/get_analytics'; import { DATA_FRAME_TASK_STATE } from '../pages/analytics_management/components/analytics_list/common'; +import { useInferenceApiService } from '../../services/ml_api_service/inference'; +import { TotalFeatureImportance } from '../../../../common/types/feature_importance'; +import { getToastNotificationService } from '../../services/toast_notification_service'; +import { + isClassificationAnalysis, + isRegressionAnalysis, +} from '../../../../common/util/analytics_utils'; export const useResultsViewConfig = (jobId: string) => { const mlContext = useMlContext(); + const inferenceApiService = useInferenceApiService(); + const [indexPattern, setIndexPattern] = useState(undefined); const [isInitialized, setIsInitialized] = useState(false); const [needsDestIndexPattern, setNeedsDestIndexPattern] = useState(false); @@ -33,6 +42,10 @@ export const useResultsViewConfig = (jobId: string) => { const [jobConfigErrorMessage, setJobConfigErrorMessage] = useState(undefined); const [jobStatus, setJobStatus] = useState(undefined); + const [totalFeatureImportance, setTotalFeatureImportance] = useState< + TotalFeatureImportance[] | undefined + >(undefined); + // get analytics configuration, index pattern and field caps useEffect(() => { (async function () { @@ -40,6 +53,7 @@ export const useResultsViewConfig = (jobId: string) => { try { const analyticsConfigs = await ml.dataFrameAnalytics.getDataFrameAnalytics(jobId); + const analyticsStats = await ml.dataFrameAnalytics.getDataFrameAnalyticsStats(jobId); const stats = isGetDataFrameAnalyticsStatsResponseOk(analyticsStats) ? analyticsStats.data_frame_analytics[0] @@ -54,6 +68,28 @@ export const useResultsViewConfig = (jobId: string) => { analyticsConfigs.data_frame_analytics.length > 0 ) { const jobConfigUpdate = analyticsConfigs.data_frame_analytics[0]; + // don't fetch the total feature importance if it's outlier_detection + if ( + isClassificationAnalysis(jobConfigUpdate.analysis) || + isRegressionAnalysis(jobConfigUpdate.analysis) + ) { + try { + const inferenceModels = await inferenceApiService.getInferenceModel(`${jobId}*`, { + include: 'total_feature_importance', + }); + const inferenceModel = inferenceModels.find( + (model) => model.metadata?.analytics_config?.id === jobId + ); + if ( + Array.isArray(inferenceModel?.metadata?.total_feature_importance) === true && + inferenceModel?.metadata?.total_feature_importance.length > 0 + ) { + setTotalFeatureImportance(inferenceModel?.metadata?.total_feature_importance); + } + } catch (e) { + getToastNotificationService().displayErrorToast(e); + } + } try { const destIndex = Array.isArray(jobConfigUpdate.dest.index) @@ -103,5 +139,6 @@ export const useResultsViewConfig = (jobId: string) => { jobConfigErrorMessage, jobStatus, needsDestIndexPattern, + totalFeatureImportance, }; }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx index 28ef3898cde97f..833b4a78178d41 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/classification_exploration/classification_exploration.tsx @@ -10,7 +10,7 @@ import { i18n } from '@kbn/i18n'; import { ExplorationPageWrapper } from '../exploration_page_wrapper'; import { EvaluatePanel } from './evaluate_panel'; - +import { FeatureImportanceSummaryPanel } from '../total_feature_importance_summary/feature_importance_summary'; interface Props { jobId: string; defaultIsTraining?: boolean; @@ -27,6 +27,7 @@ export const ClassificationExploration: FC = ({ jobId, defaultIsTraining } )} EvaluatePanel={EvaluatePanel} + FeatureImportanceSummaryPanel={FeatureImportanceSummaryPanel} defaultIsTraining={defaultIsTraining} /> ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx index f3fc65d264e62b..6b1b3fc1bb47fe 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/exploration_page_wrapper/exploration_page_wrapper.tsx @@ -16,6 +16,7 @@ import { DATA_FRAME_TASK_STATE } from '../../../analytics_management/components/ import { ExplorationResultsTable } from '../exploration_results_table'; import { JobConfigErrorCallout } from '../job_config_error_callout'; import { LoadingPanel } from '../loading_panel'; +import { FeatureImportanceSummaryPanelProps } from '../total_feature_importance_summary/feature_importance_summary'; export interface EvaluatePanelProps { jobConfig: DataFrameAnalyticsConfig; @@ -27,6 +28,7 @@ interface Props { jobId: string; title: string; EvaluatePanel: FC; + FeatureImportanceSummaryPanel: FC; defaultIsTraining?: boolean; } @@ -34,6 +36,7 @@ export const ExplorationPageWrapper: FC = ({ jobId, title, EvaluatePanel, + FeatureImportanceSummaryPanel, defaultIsTraining, }) => { const { @@ -45,6 +48,7 @@ export const ExplorationPageWrapper: FC = ({ jobConfigErrorMessage, jobStatus, needsDestIndexPattern, + totalFeatureImportance, } = useResultsViewConfig(jobId); const [searchQuery, setSearchQuery] = useState(defaultSearchQuery); @@ -63,6 +67,14 @@ export const ExplorationPageWrapper: FC = ({ {isLoadingJobConfig === false && jobConfig !== undefined && isInitialized === true && ( )} + {isLoadingJobConfig === true && totalFeatureImportance === undefined && } + {isLoadingJobConfig === false && totalFeatureImportance !== undefined && ( + <> + + + + )} + {isLoadingJobConfig === true && jobConfig === undefined && } {isLoadingJobConfig === false && diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx index 40279ecc6ffa47..6118207c83fcb1 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/regression_exploration/regression_exploration.tsx @@ -11,6 +11,7 @@ import { i18n } from '@kbn/i18n'; import { ExplorationPageWrapper } from '../exploration_page_wrapper'; import { EvaluatePanel } from './evaluate_panel'; +import { FeatureImportanceSummaryPanel } from '../total_feature_importance_summary/feature_importance_summary'; interface Props { jobId: string; @@ -25,6 +26,7 @@ export const RegressionExploration: FC = ({ jobId, defaultIsTraining }) = values: { jobId }, })} EvaluatePanel={EvaluatePanel} + FeatureImportanceSummaryPanel={FeatureImportanceSummaryPanel} defaultIsTraining={defaultIsTraining} /> ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx new file mode 100644 index 00000000000000..f7ac717caef2f2 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx @@ -0,0 +1,264 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useCallback, useMemo } from 'react'; +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiIconTip, + EuiPanel, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + Chart, + Settings, + Axis, + ScaleType, + Position, + BarSeries, + RecursivePartial, + AxisStyle, + PartialTheme, + BarSeriesSpec, +} from '@elastic/charts'; +import { i18n } from '@kbn/i18n'; +import euiVars from '@elastic/eui/dist/eui_theme_light.json'; +import { + TotalFeatureImportance, + isClassificationTotalFeatureImportance, + isRegressionTotalFeatureImportance, + RegressionTotalFeatureImportance, + ClassificationTotalFeatureImportance, +} from '../../../../../../../common/types/feature_importance'; + +import { useMlKibana } from '../../../../../contexts/kibana'; +const { euiColorMediumShade } = euiVars; +const axisColor = euiColorMediumShade; + +const axes: RecursivePartial = { + axisLine: { + stroke: axisColor, + }, + tickLabel: { + fontSize: 12, + fill: axisColor, + }, + tickLine: { + stroke: axisColor, + }, + gridLine: { + horizontal: { + dash: [1, 2], + }, + vertical: { + strokeWidth: 0, + }, + }, +}; +const theme: PartialTheme = { + axes, + legend: { + /** + * Added buffer between label and value. + * Smaller values render a more compact legend + */ + spacingBuffer: 100, + }, +}; + +export interface FeatureImportanceSummaryPanelProps { + totalFeatureImportance: TotalFeatureImportance[]; +} + +const tooltipContent = i18n.translate( + 'xpack.ml.dataframe.analytics.exploration.featureImportanceSummaryTooltipContent', + { + defaultMessage: + 'Total feature importance values indicate how significantly a field affects the predictions across all the training data.', + } +); + +const calculateTotalMeanImportance = (featureClass: ClassificationTotalFeatureImportance) => { + return featureClass.classes.reduce( + (runningSum, fc) => runningSum + fc.importance.mean_magnitude, + 0 + ); +}; + +export const FeatureImportanceSummaryPanel: FC = ({ + totalFeatureImportance, +}) => { + const { + services: { docLinks }, + } = useMlKibana(); + + const [plotData, barSeriesSpec, showLegend, chartHeight] = useMemo(() => { + let sortedData: Array<{ + featureName: string; + meanImportance: number; + className?: string; + }> = []; + let _barSeriesSpec: Partial = { + xAccessor: 'featureName', + yAccessors: ['meanImportance'], + name: i18n.translate( + 'xpack.ml.dataframe.analytics.exploration.featureImportanceYSeriesName', + { + defaultMessage: 'magnitude', + } + ) as string, + }; + let classificationType: + | 'binary_classification' + | 'multiclass_classification' + | 'regression' + | '' = ''; + if (totalFeatureImportance.length < 1) { + return [sortedData, _barSeriesSpec]; + } + + if (isClassificationTotalFeatureImportance(totalFeatureImportance[0])) { + // if binary classification + if (totalFeatureImportance[0].classes.length === 2) { + classificationType = 'binary_classification'; + sortedData = (totalFeatureImportance as ClassificationTotalFeatureImportance[]) + .map((d) => { + return { + featureName: d.feature_name, + // in case of binary classification, both classes will have the same mean importance + meanImportance: d.classes[0].importance.mean_magnitude, + }; + }) + .sort((a, b) => b.meanImportance - a.meanImportance); + } + + // if multiclass classification + // stack them in order of increasing importance + // so for each feature, biggest importance on the left to smallest importance on the right + if (totalFeatureImportance[0].classes.length > 2) { + classificationType = 'multiclass_classification'; + + (totalFeatureImportance as ClassificationTotalFeatureImportance[]) + .sort( + (prevFeature, currentFeature) => + calculateTotalMeanImportance(currentFeature) - + calculateTotalMeanImportance(prevFeature) + ) + .forEach((feature) => { + const sortedFeatureClass = feature.classes.sort( + (a, b) => b.importance.mean_magnitude - a.importance.mean_magnitude + ); + sortedData.push( + ...sortedFeatureClass.map((featureClass) => ({ + featureName: feature.feature_name, + meanImportance: featureClass.importance.mean_magnitude, + className: featureClass.class_name, + })) + ); + }); + + _barSeriesSpec = { + xAccessor: 'featureName', + yAccessors: ['meanImportance'], + splitSeriesAccessors: ['className'], + stackAccessors: ['featureName'], + }; + } + } + // if regression + if (isRegressionTotalFeatureImportance(totalFeatureImportance[0])) { + classificationType = 'regression'; + + sortedData = (totalFeatureImportance as RegressionTotalFeatureImportance[]) + .map((d: RegressionTotalFeatureImportance) => ({ + featureName: d.feature_name, + meanImportance: d.importance.mean_magnitude, + })) + .sort((a, b) => b.meanImportance - a.meanImportance); + } + + // only show legend if it's a multiclass + const _showLegend = classificationType === 'multiclass_classification'; + const _chartHeight = + totalFeatureImportance.length * (totalFeatureImportance.length < 5 ? 40 : 20) + 50; + return [sortedData, _barSeriesSpec, _showLegend, _chartHeight]; + }, [totalFeatureImportance]); + + const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks; + const tickFormatter = useCallback((d) => Number(d.toPrecision(3)).toString(), []); + + return ( + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/inference.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/inference.ts index 0206037b680bb3..ce211612fba668 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/inference.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/inference.ts @@ -23,6 +23,7 @@ export interface InferenceQueryParams { tags?: string; // Custom kibana endpoint query params with_pipelines?: boolean; + include?: 'total_feature_importance'; } export interface InferenceStatsQueryParams { diff --git a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts index 896449be7896a3..04cc60bf5493c5 100644 --- a/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/inference_schema.ts @@ -23,4 +23,5 @@ export const optionalModelIdSchema = schema.object({ export const getInferenceQuerySchema = schema.object({ size: schema.maybe(schema.string()), with_pipelines: schema.maybe(schema.string()), + include: schema.maybe(schema.string()), }); From 3599edbea93da33a3a72c84d41239f48b74156a9 Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Thu, 1 Oct 2020 09:42:18 -0500 Subject: [PATCH 024/128] [Logs UI] Move alerting components to public/alerting (#79035) Co-authored-by: Elastic Machine --- .../criterion_preview_chart.tsx | 4 ++-- .../group_by_expression/group_by_expression.tsx | 0 .../common}/group_by_expression/selector.tsx | 0 .../log_threshold/components}/alert_dropdown.tsx | 2 +- .../log_threshold/components}/alert_flyout.tsx | 8 ++++---- .../components}/expression_editor/criteria.tsx | 4 ++-- .../components}/expression_editor/criterion.tsx | 4 ++-- .../criterion_preview_chart.tsx | 10 +++++----- .../components}/expression_editor/editor.tsx | 16 +++++++--------- .../hooks/use_chart_preview_data.tsx | 8 ++++---- .../components}/expression_editor/index.tsx | 0 .../components}/expression_editor/threshold.tsx | 4 ++-- .../expression_editor/type_switcher.tsx | 2 +- .../infra/public/alerting/log_threshold/index.ts | 8 ++++++++ .../log_threshold/log_threshold_alert_type.ts | 7 +++---- .../log_threshold/validation.ts | 5 ++--- .../infra/public/pages/logs/page_content.tsx | 2 +- x-pack/plugins/infra/public/plugin.ts | 2 +- .../plugins/triggers_actions_ui/public/index.ts | 2 +- 19 files changed, 46 insertions(+), 42 deletions(-) rename x-pack/plugins/infra/public/{components/alerting/shared => alerting/common}/criterion_preview_chart/criterion_preview_chart.tsx (97%) rename x-pack/plugins/infra/public/{components/alerting/shared => alerting/common}/group_by_expression/group_by_expression.tsx (100%) rename x-pack/plugins/infra/public/{components/alerting/shared => alerting/common}/group_by_expression/selector.tsx (100%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/alert_dropdown.tsx (97%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/alert_flyout.tsx (84%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/criteria.tsx (98%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/criterion.tsx (98%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/criterion_preview_chart.tsx (96%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/editor.tsx (93%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/hooks/use_chart_preview_data.tsx (90%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/index.tsx (100%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/threshold.tsx (95%) rename x-pack/plugins/infra/public/{components/alerting/logs/log_threshold => alerting/log_threshold/components}/expression_editor/type_switcher.tsx (97%) create mode 100644 x-pack/plugins/infra/public/alerting/log_threshold/index.ts rename x-pack/plugins/infra/public/{components/alerting/logs => alerting}/log_threshold/log_threshold_alert_type.ts (80%) rename x-pack/plugins/infra/public/{components/alerting/logs => alerting}/log_threshold/validation.ts (94%) diff --git a/x-pack/plugins/infra/public/components/alerting/shared/criterion_preview_chart/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/common/criterion_preview_chart/criterion_preview_chart.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/alerting/shared/criterion_preview_chart/criterion_preview_chart.tsx rename to x-pack/plugins/infra/public/alerting/common/criterion_preview_chart/criterion_preview_chart.tsx index 239afd93a7a1f7..9003e13869e606 100644 --- a/x-pack/plugins/infra/public/components/alerting/shared/criterion_preview_chart/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/common/criterion_preview_chart/criterion_preview_chart.tsx @@ -11,8 +11,8 @@ import moment from 'moment'; import { i18n } from '@kbn/i18n'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { formatNumber } from '../../../../../common/formatters/number'; -import { GetLogAlertsChartPreviewDataSuccessResponsePayload } from '../../../../../common/http_api'; +import { formatNumber } from '../../../../common/formatters/number'; +import { GetLogAlertsChartPreviewDataSuccessResponsePayload } from '../../../../common/http_api'; type Series = GetLogAlertsChartPreviewDataSuccessResponsePayload['data']['series']; diff --git a/x-pack/plugins/infra/public/components/alerting/shared/group_by_expression/group_by_expression.tsx b/x-pack/plugins/infra/public/alerting/common/group_by_expression/group_by_expression.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/alerting/shared/group_by_expression/group_by_expression.tsx rename to x-pack/plugins/infra/public/alerting/common/group_by_expression/group_by_expression.tsx diff --git a/x-pack/plugins/infra/public/components/alerting/shared/group_by_expression/selector.tsx b/x-pack/plugins/infra/public/alerting/common/group_by_expression/selector.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/alerting/shared/group_by_expression/selector.tsx rename to x-pack/plugins/infra/public/alerting/common/group_by_expression/selector.tsx diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_dropdown.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_dropdown.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx index 74634bbd5d2900..b8eb73b99f45e4 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_dropdown.tsx @@ -8,7 +8,7 @@ import React, { useState, useCallback, useMemo } from 'react'; import { EuiPopover, EuiButtonEmpty, EuiContextMenuItem, EuiContextMenuPanel } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { AlertFlyout } from './alert_flyout'; -import { useLinkProps } from '../../../../hooks/use_link_props'; +import { useLinkProps } from '../../../hooks/use_link_props'; export const AlertDropdown = () => { const [popoverOpen, setPopoverOpen] = useState(false); diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_flyout.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx similarity index 84% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_flyout.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx index c6e16dcc9aaef7..cd69fe02c58461 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/alert_flyout.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_flyout.tsx @@ -6,10 +6,10 @@ import React, { useContext } from 'react'; import { ApplicationStart, DocLinksStart, HttpStart, NotificationsStart } from 'src/core/public'; -import { AlertsContextProvider, AlertAdd } from '../../../../../../triggers_actions_ui/public'; -import { TriggerActionsContext } from '../../../../utils/triggers_actions_context'; -import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; -import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../../common/alerting/logs/log_threshold/types'; +import { AlertsContextProvider, AlertAdd } from '../../../../../triggers_actions_ui/public'; +import { TriggerActionsContext } from '../../../utils/triggers_actions_context'; +import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; +import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../common/alerting/logs/log_threshold/types'; interface Props { visible?: boolean; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criteria.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx similarity index 98% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criteria.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx index a607b5ebf99758..c35e7141efc9d0 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criteria.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criteria.tsx @@ -20,10 +20,10 @@ import { isRatioAlert, getNumerator, getDenominator, -} from '../../../../../../common/alerting/logs/log_threshold/types'; +} from '../../../../../common/alerting/logs/log_threshold/types'; +import { Errors, CriterionErrors } from '../../validation'; import { AlertsContext, ExpressionLike } from './editor'; import { CriterionPreview } from './criterion_preview_chart'; -import { Errors, CriterionErrors } from '../validation'; const DEFAULT_CRITERIA = { field: 'log.level', comparator: Comparator.EQ, value: 'error' }; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx similarity index 98% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx index 8ecd172c08d24f..b2992ead3ea1b1 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion.tsx @@ -21,12 +21,12 @@ import { i18n } from '@kbn/i18n'; import { isNumber, isFinite } from 'lodash'; import { IFieldType } from 'src/plugins/data/public'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IErrorObject } from '../../../../../../../triggers_actions_ui/public/types'; +import { IErrorObject } from '../../../../../../triggers_actions_ui/public/types'; import { Comparator, Criterion as CriterionType, ComparatorToi18nMap, -} from '../../../../../../common/alerting/logs/log_threshold/types'; +} from '../../../../../common/alerting/logs/log_threshold/types'; const firstCriterionFieldPrefix = i18n.translate( 'xpack.infra.logs.alertFlyout.firstCriterionFieldPrefix', diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion_preview_chart.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx similarity index 96% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion_preview_chart.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx index 675900499e7935..478d4b879a7e11 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/criterion_preview_chart.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/criterion_preview_chart.tsx @@ -31,21 +31,21 @@ import { getChartTheme, yAxisFormatter, NUM_BUCKETS, -} from '../../../shared/criterion_preview_chart/criterion_preview_chart'; +} from '../../../common/criterion_preview_chart/criterion_preview_chart'; import { AlertParams, Threshold, Criterion, Comparator, -} from '../../../../../../common/alerting/logs/log_threshold/types'; -import { Color, colorTransformer } from '../../../../../../common/color_palette'; +} from '../../../../../common/alerting/logs/log_threshold/types'; +import { Color, colorTransformer } from '../../../../../common/color_palette'; import { GetLogAlertsChartPreviewDataAlertParamsSubset, getLogAlertsChartPreviewDataAlertParamsSubsetRT, -} from '../../../../../../common/http_api/log_alerts/'; +} from '../../../../../common/http_api/log_alerts/'; import { AlertsContext } from './editor'; import { useChartPreviewData } from './hooks/use_chart_preview_data'; -import { decodeOrThrow } from '../../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; const GROUP_LIMIT = 5; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/editor.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx similarity index 93% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/editor.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx index 0af96a7e06a2b0..c5b0ed58448526 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/editor.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/editor.tsx @@ -8,25 +8,23 @@ import React, { useCallback, useMemo, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiLoadingSpinner, EuiSpacer, EuiButton, EuiCallOut } from '@elastic/eui'; import { useMount } from 'react-use'; +import { GroupByExpression } from '../../../common/group_by_expression/group_by_expression'; import { ForLastExpression, - // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../triggers_actions_ui/public/common'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertsContextValue } from '../../../../../../../triggers_actions_ui/public/application/context/alerts_context'; + AlertsContextValue, +} from '../../../../../../triggers_actions_ui/public'; import { AlertParams, Comparator, ThresholdType, isRatioAlert, -} from '../../../../../../common/alerting/logs/log_threshold/types'; +} from '../../../../../common/alerting/logs/log_threshold/types'; import { Threshold } from './threshold'; import { Criteria } from './criteria'; import { TypeSwitcher } from './type_switcher'; -import { useSourceId } from '../../../../../containers/source_id'; -import { LogSourceProvider, useLogSourceContext } from '../../../../../containers/logs/log_source'; -import { GroupByExpression } from '../../../shared/group_by_expression/group_by_expression'; -import { Errors } from '../validation'; +import { useSourceId } from '../../../../containers/source_id'; +import { LogSourceProvider, useLogSourceContext } from '../../../../containers/logs/log_source'; +import { Errors } from '../../validation'; export interface ExpressionCriteria { field?: string; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/hooks/use_chart_preview_data.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx similarity index 90% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/hooks/use_chart_preview_data.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx index d43e291f900f23..d5ba730026b12e 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/hooks/use_chart_preview_data.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/hooks/use_chart_preview_data.tsx @@ -5,15 +5,15 @@ */ import { useState, useMemo } from 'react'; import { AlertsContext } from '../editor'; -import { useTrackedPromise } from '../../../../../../utils/use_tracked_promise'; +import { useTrackedPromise } from '../../../../../utils/use_tracked_promise'; import { GetLogAlertsChartPreviewDataSuccessResponsePayload, getLogAlertsChartPreviewDataSuccessResponsePayloadRT, getLogAlertsChartPreviewDataRequestPayloadRT, LOG_ALERTS_CHART_PREVIEW_DATA_PATH, -} from '../../../../../../../common/http_api'; -import { decodeOrThrow } from '../../../../../../../common/runtime_types'; -import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../../../common/http_api/log_alerts/'; +} from '../../../../../../common/http_api'; +import { decodeOrThrow } from '../../../../../../common/runtime_types'; +import { GetLogAlertsChartPreviewDataAlertParamsSubset } from '../../../../../../common/http_api/log_alerts/'; interface Options { sourceId: string; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/index.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/index.tsx similarity index 100% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/index.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/index.tsx diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/threshold.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/threshold.tsx similarity index 95% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/threshold.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/threshold.tsx index e2065ca25cb6f8..da0c987bac85fc 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/threshold.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/threshold.tsx @@ -18,12 +18,12 @@ import { EuiFormRow, } from '@elastic/eui'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { IErrorObject } from '../../../../../../../triggers_actions_ui/public/types'; +import { IErrorObject } from '../../../../../../triggers_actions_ui/public/types'; import { Comparator, ComparatorToi18nMap, AlertParams, -} from '../../../../../../common/alerting/logs/log_threshold/types'; +} from '../../../../../common/alerting/logs/log_threshold/types'; const thresholdPrefix = i18n.translate('xpack.infra.logs.alertFlyout.thresholdPrefix', { defaultMessage: 'is', diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/type_switcher.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/type_switcher.tsx similarity index 97% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/type_switcher.tsx rename to x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/type_switcher.tsx index 03c895dcd0f2dc..5f5078f8aa6e1e 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/expression_editor/type_switcher.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/expression_editor/type_switcher.tsx @@ -11,7 +11,7 @@ import { AlertParams, ThresholdType, isRatioAlert, -} from '../../../../../../common/alerting/logs/log_threshold/types'; +} from '../../../../../common/alerting/logs/log_threshold/types'; import { ExpressionLike } from './editor'; const typePrefix = i18n.translate('xpack.infra.logs.alertFlyout.thresholdTypePrefix', { diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/index.ts b/x-pack/plugins/infra/public/alerting/log_threshold/index.ts new file mode 100644 index 00000000000000..0974d00c4dffbb --- /dev/null +++ b/x-pack/plugins/infra/public/alerting/log_threshold/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; + * you may not use this file except in compliance with the Elastic License. + */ + +export { getAlertType } from './log_threshold_alert_type'; +export { AlertDropdown } from './components/alert_dropdown'; diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/log_threshold_alert_type.ts b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts similarity index 80% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/log_threshold_alert_type.ts rename to x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts index 15ff5844c12367..29a58fc95f2bea 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/log_threshold_alert_type.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/log_threshold_alert_type.ts @@ -5,9 +5,8 @@ */ import { i18n } from '@kbn/i18n'; import React from 'react'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { AlertTypeModel } from '../../../../../../triggers_actions_ui/public/types'; -import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../../../common/alerting/logs/log_threshold/types'; +import { AlertTypeModel } from '../../../../triggers_actions_ui/public'; +import { LOG_DOCUMENT_COUNT_ALERT_TYPE_ID } from '../../../common/alerting/logs/log_threshold/types'; import { validateExpression } from './validation'; export function getAlertType(): AlertTypeModel { @@ -17,7 +16,7 @@ export function getAlertType(): AlertTypeModel { defaultMessage: 'Log threshold', }), iconClass: 'bell', - alertParamsExpression: React.lazy(() => import('./expression_editor/editor')), + alertParamsExpression: React.lazy(() => import('./components/expression_editor/editor')), validate: validateExpression, defaultActionMessage: i18n.translate( 'xpack.infra.logs.alerting.threshold.defaultActionMessage', diff --git a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/validation.ts b/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts similarity index 94% rename from x-pack/plugins/infra/public/components/alerting/logs/log_threshold/validation.ts rename to x-pack/plugins/infra/public/alerting/log_threshold/validation.ts index a7f773c08d2b3d..6630b3d0791415 100644 --- a/x-pack/plugins/infra/public/components/alerting/logs/log_threshold/validation.ts +++ b/x-pack/plugins/infra/public/alerting/log_threshold/validation.ts @@ -6,8 +6,7 @@ import { i18n } from '@kbn/i18n'; import { isNumber, isFinite } from 'lodash'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ValidationResult } from '../../../../../../triggers_actions_ui/public/types'; +import { ValidationResult } from '../../../../triggers_actions_ui/public'; import { AlertParams, Criteria, @@ -15,7 +14,7 @@ import { isRatioAlert, getNumerator, getDenominator, -} from '../../../../../common/alerting/logs/log_threshold/types'; +} from '../../../common/alerting/logs/log_threshold/types'; export interface CriterionErrors { [id: string]: { diff --git a/x-pack/plugins/infra/public/pages/logs/page_content.tsx b/x-pack/plugins/infra/public/pages/logs/page_content.tsx index 973037af499e58..e85f0d9bf446bf 100644 --- a/x-pack/plugins/infra/public/pages/logs/page_content.tsx +++ b/x-pack/plugins/infra/public/pages/logs/page_content.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; import { useMount } from 'react-use'; +import { AlertDropdown } from '../../alerting/log_threshold'; import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; import { DocumentTitle } from '../../components/document_title'; import { Header } from '../../components/header'; @@ -23,7 +24,6 @@ import { LogEntryCategoriesPage } from './log_entry_categories'; import { LogEntryRatePage } from './log_entry_rate'; import { LogsSettingsPage } from './settings'; import { StreamPage } from './stream'; -import { AlertDropdown } from '../../components/alerting/logs/log_threshold/alert_dropdown'; export const LogsPageContent: React.FunctionComponent = () => { const uiCapabilities = useKibana().services.application?.capabilities; diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index b409c32603ffc0..3c6b1a14cfd475 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -8,7 +8,7 @@ import { AppMountParameters, PluginInitializerContext } from 'kibana/public'; import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { createMetricThresholdAlertType } from './alerting/metric_threshold'; import { createInventoryMetricAlertType } from './alerting/inventory'; -import { getAlertType as getLogsAlertType } from './components/alerting/logs/log_threshold/log_threshold_alert_type'; +import { getAlertType as getLogsAlertType } from './alerting/log_threshold'; import { registerStartSingleton } from './legacy_singletons'; import { registerFeatures } from './register_feature'; import { diff --git a/x-pack/plugins/triggers_actions_ui/public/index.ts b/x-pack/plugins/triggers_actions_ui/public/index.ts index f73fac22590671..a28b10683c28f9 100644 --- a/x-pack/plugins/triggers_actions_ui/public/index.ts +++ b/x-pack/plugins/triggers_actions_ui/public/index.ts @@ -7,7 +7,7 @@ import { PluginInitializerContext } from 'src/core/public'; import { Plugin } from './plugin'; -export { AlertsContextProvider } from './application/context/alerts_context'; +export { AlertsContextProvider, AlertsContextValue } from './application/context/alerts_context'; export { ActionsConnectorsContextProvider } from './application/context/actions_connectors_context'; export { AlertAdd } from './application/sections/alert_form'; export { AlertEdit } from './application/sections'; From 41318801d3d0b856133fcb787ec355c773f2b55e Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Thu, 1 Oct 2020 11:04:23 -0400 Subject: [PATCH 025/128] [SECURITY_SOLUTION][ENDPOINT] Add ability to view Trusted Apps from Ingest Integration Policy Edit page (#78854) * Refactor Callout shown in Ingest Edit Endpoint Integration Policy that display actions menu * Add `backComponent` to `` to allow for custom back buttons * Back button displayed on Trusted Apps List when route state is defined --- .../common/endpoint/types/trusted_apps.ts | 13 ++ .../common/components/header_page/index.tsx | 5 + .../components/administration_list_page.tsx | 4 +- .../configure_package_policy.tsx | 206 +++++++++++++----- .../trusted_apps/view/trusted_apps_page.tsx | 61 +++++- .../apps/endpoint/policy_details.ts | 44 +++- ...gest_manager_create_package_policy_page.ts | 35 +++ .../page_objects/trusted_apps_page.ts | 19 +- 8 files changed, 325 insertions(+), 62 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts index 62793388e34a67..c0afe3b612d826 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts @@ -5,6 +5,7 @@ */ import { TypeOf } from '@kbn/config-schema'; +import { ApplicationStart } from 'kibana/public'; import { DeleteTrustedAppsRequestSchema, GetTrustedAppsRequestSchema, @@ -65,3 +66,15 @@ export type TrustedApp = NewTrustedApp & { created_at: string; created_by: string; }; + +/** + * Supported React-Router state for the Trusted Apps List page + */ +export interface TrustedAppsListPageRouteState { + /** Where the user should be redirected to when the `Back` button is clicked */ + onBackButtonNavigateTo: Parameters; + /** The URL for the `Back` button */ + backButtonUrl?: string; + /** The label for the button */ + backButtonLabel?: string; +} diff --git a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx index 0cb721bb5382f2..4a5a55f30f94f0 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_page/index.tsx @@ -71,6 +71,8 @@ interface BackOptions { export interface HeaderPageProps extends HeaderProps { backOptions?: BackOptions; + /** A component to be displayed as the back button. Used only if `backOption` is not defined */ + backComponent?: React.ReactNode; badgeOptions?: BadgeOptions; children?: React.ReactNode; draggableArguments?: DraggableArguments; @@ -83,6 +85,7 @@ export interface HeaderPageProps extends HeaderProps { const HeaderPageComponent: React.FC = ({ backOptions, + backComponent, badgeOptions, border, children, @@ -123,6 +126,8 @@ const HeaderPageComponent: React.FC = ({ )} + {!backOptions && backComponent && <>{backComponent}} + {titleNode || ( = memo( - ({ beta, title, subtitle, actions, children, ...otherProps }) => { + ({ beta, title, subtitle, actions, children, headerBackComponent, ...otherProps }) => { const badgeOptions = !beta ? undefined : { beta: true, text: BETA_BADGE_LABEL }; return ( @@ -39,6 +40,7 @@ export const AdministrationListPage: FC<AdministrationListPageProps & CommonProp hideSourcerer={true} title={title} subtitle={subtitle} + backComponent={headerBackComponent} badgeOptions={badgeOptions} > {actions} diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_package_policy.tsx b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_package_policy.tsx index b03069a739e352..3cca2d9c26599b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_package_policy.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/policy/view/ingest_manager_integration/configure_package_policy.tsx @@ -4,17 +4,34 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo } from 'react'; +import React, { memo, useCallback, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiCallOut, EuiText, EuiSpacer } from '@elastic/eui'; -import { LinkToApp } from '../../../../../common/components/endpoint/link_to_app'; +import { + EuiCallOut, + EuiText, + EuiSpacer, + EuiFlexGroup, + EuiFlexItem, + EuiContextMenuPanel, + EuiPopover, + EuiButton, + EuiContextMenuItem, + EuiContextMenuPanelProps, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { CustomConfigurePackagePolicyContent, CustomConfigurePackagePolicyProps, + pagePathGetters, } from '../../../../../../../ingest_manager/public'; -import { getPolicyDetailPath } from '../../../../common/routing'; +import { getPolicyDetailPath, getTrustedAppsListPath } from '../../../../common/routing'; import { MANAGEMENT_APP_ID } from '../../../../common/constants'; -import { PolicyDetailsRouteState } from '../../../../../../common/endpoint/types'; +import { + PolicyDetailsRouteState, + TrustedAppsListPageRouteState, +} from '../../../../../../common/endpoint/types'; +import { useKibana } from '../../../../../common/lib/kibana'; +import { useNavigateToAppEventHandler } from '../../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; /** * Exports Endpoint-specific package policy instructions @@ -26,27 +43,6 @@ export const ConfigureEndpointPackagePolicy = memo<CustomConfigurePackagePolicyC packagePolicyId, packagePolicy: { policy_id: agentPolicyId }, }: CustomConfigurePackagePolicyProps) => { - let policyUrl = ''; - if (from === 'edit' && packagePolicyId) { - // Cannot use formalUrl here since the code is called in Ingest, which does not use redux - policyUrl = getPolicyDetailPath(packagePolicyId); - } - - const policyDetailRouteState = useMemo((): undefined | PolicyDetailsRouteState => { - if (from !== 'edit') { - return undefined; - } - const navigateTo: PolicyDetailsRouteState['onSaveNavigateTo'] & - PolicyDetailsRouteState['onCancelNavigateTo'] = [ - 'ingestManager', - { path: `#/policies/${agentPolicyId}/edit-integration/${packagePolicyId}` }, - ]; - return { - onSaveNavigateTo: navigateTo, - onCancelNavigateTo: navigateTo, - }; - }, [agentPolicyId, from, packagePolicyId]); - return ( <> <EuiSpacer size="m" /> @@ -55,39 +51,149 @@ export const ConfigureEndpointPackagePolicy = memo<CustomConfigurePackagePolicyC iconType="iInCircle" > <EuiText size="s"> - <p> - {from === 'edit' ? ( - <FormattedMessage - id="xpack.securitySolution.endpoint.ingestManager.editPackagePolicy.endpointConfiguration" - defaultMessage="Click {advancedConfigOptionsLink} to edit advanced configuration options." - values={{ - advancedConfigOptionsLink: ( - <LinkToApp - data-test-subj="editLinkToPolicyDetails" - appId={MANAGEMENT_APP_ID} - appPath={policyUrl} - appState={policyDetailRouteState} - > - <FormattedMessage - id="xpack.securitySolution.endpoint.ingestManager.editPackagePolicy.endpointConfigurationLink" - defaultMessage="here" - /> - </LinkToApp> - ), - }} + {from === 'edit' ? ( + <> + <EditFlowMessage + agentPolicyId={agentPolicyId} + integrationPolicyId={packagePolicyId!} /> - ) : ( + </> + ) : ( + <p> <FormattedMessage id="xpack.securitySolution.endpoint.ingestManager.createPackagePolicy.endpointConfiguration" defaultMessage="We'll save your integration with our recommended defaults. You can change this later by editing the Endpoint Security integration within your agent policy." /> - )} - </p> + </p> + )} </EuiText> </EuiCallOut> </> ); } ); - ConfigureEndpointPackagePolicy.displayName = 'ConfigureEndpointPackagePolicy'; + +const EditFlowMessage = memo<{ + agentPolicyId: string; + integrationPolicyId: string; +}>(({ agentPolicyId, integrationPolicyId }) => { + const { + services: { + application: { getUrlForApp }, + }, + } = useKibana(); + + const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false); + + const navigateBackToIngest = useMemo< + PolicyDetailsRouteState['onSaveNavigateTo'] & + PolicyDetailsRouteState['onCancelNavigateTo'] & + TrustedAppsListPageRouteState['onBackButtonNavigateTo'] + >(() => { + return [ + 'ingestManager', + { + path: `#${pagePathGetters.edit_integration({ + policyId: agentPolicyId, + packagePolicyId: integrationPolicyId!, + })}`, + }, + ]; + }, [agentPolicyId, integrationPolicyId]); + + const handleClosePopup = useCallback(() => setIsMenuOpen(false), []); + + const handleSecurityPolicyAction = useNavigateToAppEventHandler<PolicyDetailsRouteState>( + MANAGEMENT_APP_ID, + { + path: getPolicyDetailPath(integrationPolicyId), + state: { + onSaveNavigateTo: navigateBackToIngest, + onCancelNavigateTo: navigateBackToIngest, + }, + } + ); + + const handleTrustedAppsAction = useNavigateToAppEventHandler<TrustedAppsListPageRouteState>( + MANAGEMENT_APP_ID, + { + path: getTrustedAppsListPath(), + state: { + backButtonUrl: navigateBackToIngest[1]?.path + ? `${getUrlForApp('ingestManager')}${navigateBackToIngest[1].path}` + : undefined, + onBackButtonNavigateTo: navigateBackToIngest, + backButtonLabel: i18n.translate( + 'xpack.securitySolution.endpoint.ingestManager.editPackagePolicy.trustedAppsMessageReturnBackLabel', + { defaultMessage: 'Back to Edit Integration' } + ), + }, + } + ); + + const menuButton = useMemo(() => { + return ( + <EuiButton + size="s" + iconType="arrowDown" + iconSide="right" + onClick={() => setIsMenuOpen((prevState) => !prevState)} + data-test-subj="endpointActions" + > + <FormattedMessage + id="xpack.securitySolution.endpoint.ingestManager.editPackagePolicy.menuButton" + defaultMessage="Actions" + /> + </EuiButton> + ); + }, []); + + const actionItems = useMemo<EuiContextMenuPanelProps['items']>(() => { + return [ + <EuiContextMenuItem + key="policyDetails" + onClick={handleSecurityPolicyAction} + data-test-subj="securityPolicy" + > + <FormattedMessage + id="xpack.securitySolution.endpoint.ingestManager.editPackagePolicy.actionSecurityPolicy" + defaultMessage="Edit Security Policy" + /> + </EuiContextMenuItem>, + <EuiContextMenuItem + key="trustedApps" + onClick={handleTrustedAppsAction} + data-test-subj="trustedAppsAction" + > + <FormattedMessage + id="xpack.securitySolution.endpoint.ingestManager.editPackagePolicy.actionTrustedApps" + defaultMessage="View Trusted Applications" + /> + </EuiContextMenuItem>, + ]; + }, [handleSecurityPolicyAction, handleTrustedAppsAction]); + + return ( + <EuiFlexGroup> + <EuiFlexItem> + <FormattedMessage + id="xpack.securitySolution.endpoint.ingestManager.editPackagePolicy.message" + defaultMessage="More advanced configuration options can be found by selecting an action from the menu" + /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiPopover + button={menuButton} + isOpen={isMenuOpen} + closePopover={handleClosePopup} + anchorPosition="downRight" + panelPaddingSize="s" + > + <EuiContextMenuPanel data-test-subj="endpointActionsMenuPanel" items={actionItems} /> + </EuiPopover> + </EuiFlexItem> + </EuiFlexGroup> + ); +}); +EditFlowMessage.displayName = 'EditFlowMessage'; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index c1c23a39609623..878818d9b77fe6 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -3,10 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useCallback } from 'react'; +import React, { memo, useCallback, useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiButton } from '@elastic/eui'; -import { useHistory } from 'react-router-dom'; +import { EuiButton, EuiButtonEmpty } from '@elastic/eui'; +import { useHistory, useLocation } from 'react-router-dom'; +import styled from 'styled-components'; import { AdministrationListPage } from '../../../components/administration_list_page'; import { TrustedAppsList } from './trusted_apps_list'; import { TrustedAppDeletionDialog } from './trusted_app_deletion_dialog'; @@ -15,9 +16,12 @@ import { CreateTrustedAppFlyout } from './components/create_trusted_app_flyout'; import { getTrustedAppsListPath } from '../../../common/routing'; import { useTrustedAppsSelector } from './hooks'; import { getListCurrentShowValue, getListUrlSearchParams } from '../store/selectors'; +import { TrustedAppsListPageRouteState } from '../../../../../common/endpoint/types'; +import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; export const TrustedAppsPage = memo(() => { const history = useHistory(); + const { state: routeState } = useLocation<TrustedAppsListPageRouteState | undefined>(); const urlParams = useTrustedAppsSelector(getListUrlSearchParams); const showAddFlout = useTrustedAppsSelector(getListCurrentShowValue) === 'create'; const handleAddButtonClick = useCallback(() => { @@ -33,6 +37,15 @@ export const TrustedAppsPage = memo(() => { history.push(getTrustedAppsListPath(paginationParamsOnly)); }, [history, urlParams]); + const backButton = useMemo(() => { + if (routeState && routeState.onBackButtonNavigateTo) { + return <BackToExternalAppButton {...routeState} />; + } + return null; + // FIXME: Route state is being deleted by some parent component + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const addButton = ( <EuiButton fill @@ -50,6 +63,7 @@ export const TrustedAppsPage = memo(() => { return ( <AdministrationListPage + data-test-subj="trustedAppsListPage" beta={true} title={ <FormattedMessage @@ -57,6 +71,7 @@ export const TrustedAppsPage = memo(() => { defaultMessage="Trusted Applications" /> } + headerBackComponent={backButton} subtitle={ <FormattedMessage id="xpack.securitySolution.trustedapps.list.pageSubTitle" @@ -80,3 +95,43 @@ export const TrustedAppsPage = memo(() => { }); TrustedAppsPage.displayName = 'TrustedAppsPage'; + +const EuiButtonEmptyStyled = styled(EuiButtonEmpty)` + margin-bottom: ${({ theme }) => theme.eui.euiSizeS}; + + .euiIcon { + width: ${({ theme }) => theme.eui.euiIconSizes.small}; + height: ${({ theme }) => theme.eui.euiIconSizes.small}; + } + + .text { + font-size: ${({ theme }) => theme.eui.euiFontSizeXS}; + } +`; + +const BackToExternalAppButton = memo<TrustedAppsListPageRouteState>( + ({ backButtonLabel, backButtonUrl, onBackButtonNavigateTo }) => { + const handleBackOnClick = useNavigateToAppEventHandler(...onBackButtonNavigateTo!); + + return ( + <EuiButtonEmptyStyled + flush="left" + size="xs" + iconType="arrowLeft" + href={backButtonUrl!} + onClick={handleBackOnClick} + textProps={{ className: 'text' }} + data-test-subj="backToOrigin" + > + {backButtonLabel || ( + <FormattedMessage + id="xpack.securitySolution.trustedapps.list.backButton" + defaultMessage="Back" + /> + )} + </EuiButtonEmptyStyled> + ); + } +); + +BackToExternalAppButton.displayName = 'BackToExternalAppButton'; diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts index 9610144d3846df..137f24432976a1 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/policy_details.ts @@ -16,6 +16,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { 'policy', 'endpointPageUtils', 'ingestManagerCreatePackagePolicy', + 'trustedApps', ]); const testSubjects = getService('testSubjects'); const policyTestResources = getService('policyTestResources'); @@ -250,6 +251,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); }); + describe('when on Ingest Policy Edit Package Policy page', async () => { let policyInfo: PolicyTestResourceInfo; beforeEach(async () => { @@ -265,16 +267,31 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await policyInfo.cleanup(); } }); - it('should show a link to Policy Details', async () => { - await testSubjects.existOrFail('editLinkToPolicyDetails'); + + it('should show callout', async () => { + await testSubjects.existOrFail('endpointPackagePolicy_edit'); }); - it('should navigate to Policy Details when the link is clicked', async () => { - const linkToPolicy = await testSubjects.find('editLinkToPolicyDetails'); - await linkToPolicy.click(); + + it('should show actions button with expected action items', async () => { + const actionsButton = await pageObjects.ingestManagerCreatePackagePolicy.findEndpointActionsButton(); + await actionsButton.click(); + const menuPanel = await testSubjects.find('endpointActionsMenuPanel'); + const actionItems = await menuPanel.findAllByTagName<'button'>('button'); + const expectedItems = ['Edit Security Policy', 'View Trusted Applications']; + + for (const action of actionItems) { + const buttonText = await action.getVisibleText(); + expect(buttonText).to.be(expectedItems.find((item) => item === buttonText)); + } + }); + + it('should navigate to Policy Details when the edit security policy action is clicked', async () => { + await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy'); await pageObjects.policy.ensureIsOnDetailsPage(); }); + it('should allow the user to navigate, edit, save Policy Details and be redirected back to ingest', async () => { - await (await testSubjects.find('editLinkToPolicyDetails')).click(); + await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy'); await pageObjects.policy.ensureIsOnDetailsPage(); await pageObjects.endpointPageUtils.clickOnEuiCheckbox('policyWindowsEvent_dns'); await pageObjects.policy.confirmAndSave(); @@ -282,11 +299,24 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.existOrFail('policyDetailsSuccessMessage'); await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail(); }); + it('should navigate back to Ingest Policy Edit package page on click of cancel button', async () => { - await (await testSubjects.find('editLinkToPolicyDetails')).click(); + await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('policy'); await (await pageObjects.policy.findCancelButton()).click(); await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail(); }); + + it('should navigate to Trusted Apps', async () => { + await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('trustedApps'); + await pageObjects.trustedApps.ensureIsOnTrustedAppsListPage(); + }); + + it('should show the back button on Trusted Apps Page and navigate back to fleet', async () => { + await pageObjects.ingestManagerCreatePackagePolicy.selectEndpointAction('trustedApps'); + const backButton = await pageObjects.trustedApps.findTrustedAppsListPageBackButton(); + await backButton.click(); + await pageObjects.ingestManagerCreatePackagePolicy.ensureOnEditPageOrFail(); + }); }); }); } diff --git a/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts b/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts index 523b327b8de1ca..38ba50b08d5072 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/ingest_manager_create_package_policy_page.ts @@ -5,6 +5,7 @@ */ import { FtrProviderContext } from '../ftr_provider_context'; +import { WebElementWrapper } from '../../../../test/functional/services/lib/web_element_wrapper'; export function IngestManagerCreatePackagePolicy({ getService, @@ -13,6 +14,7 @@ export function IngestManagerCreatePackagePolicy({ const testSubjects = getService('testSubjects'); const find = getService('find'); const pageObjects = getPageObjects(['common']); + const browser = getService('browser'); return { /** @@ -101,5 +103,38 @@ export function IngestManagerCreatePackagePolicy({ }); await this.ensureOnEditPageOrFail(); }, + + /** + * Returns the Endpoint Callout that is displayed on the Integration Policy create/edit pages + */ + async findEndpointActionsButton() { + const button = await testSubjects.find('endpointActions'); + await this.scrollToCenterOfWindow(button); + return button; + }, + + /** + * Center a given Element on the Window viewport + * @param element + */ + async scrollToCenterOfWindow(element: WebElementWrapper) { + const [elementPosition, windowSize] = await Promise.all([ + element.getPosition(), + browser.getWindowSize(), + ]); + await browser.execute( + `document.scrollingElement.scrollTop = ${elementPosition.y - windowSize.height / 2}` + ); + }, + + /** + * Will click on the given Endpoint Action (from the Actions dropdown) + * @param action + */ + async selectEndpointAction(action: 'policy' | 'trustedApps') { + await (await this.findEndpointActionsButton()).click(); + const testSubjId = action === 'policy' ? 'securityPolicy' : 'trustedAppsAction'; + await (await testSubjects.find(testSubjId)).click(); + }, }; } diff --git a/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts b/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts index c02ac0ca9ffe0d..aac516942ad240 100644 --- a/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts +++ b/x-pack/test/security_solution_endpoint/page_objects/trusted_apps_page.ts @@ -5,8 +5,9 @@ */ import { FtrProviderContext } from '../ftr_provider_context'; -export function TrustedAppsPageProvider({ getPageObjects }: FtrProviderContext) { +export function TrustedAppsPageProvider({ getService, getPageObjects }: FtrProviderContext) { const pageObjects = getPageObjects(['common', 'header', 'endpointPageUtils']); + const testSubjects = getService('testSubjects'); return { async navigateToTrustedAppsList(searchParams?: string) { @@ -16,5 +17,21 @@ export function TrustedAppsPageProvider({ getPageObjects }: FtrProviderContext) ); await pageObjects.header.waitUntilLoadingHasFinished(); }, + + /** + * ensures that the Policy Page is the currently display view + */ + async ensureIsOnTrustedAppsListPage() { + await testSubjects.existOrFail('trustedAppsListPage'); + }, + + /** + * Returns the Back button displayed on the Trusted Apps list page when page is loaded + * with route state that triggers return button to be displayed + */ + async findTrustedAppsListPageBackButton() { + await this.ensureIsOnTrustedAppsListPage(); + return testSubjects.find('backToOrigin'); + }, }; } From 513b17ef4f4b2399d00082e3de838ca3b5a2bdad Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Thu, 1 Oct 2020 11:06:24 -0400 Subject: [PATCH 026/128] [SECURITY_SOLUTION][ENDPOINT] Additional Trusted Apps Create API Validations (#78977) * Added validations to trusted apps create schema --- .../endpoint/schema/trusted_apps.test.ts | 72 ++++++++++++++++++- .../common/endpoint/schema/trusted_apps.ts | 28 +++++++- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts index c0fbebf73ed8af..13a3fb96e10f7e 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts @@ -160,6 +160,11 @@ describe('When invoking Trusted Apps Schema', () => { }); describe('when `entries` are defined', () => { + // Some static hashes for use in validation. Some chr. are in UPPERcase on purpose + const VALID_HASH_MD5 = '741462ab431a22233C787BAAB9B653C7'; + const VALID_HASH_SHA1 = 'aedb279e378BED6C2DB3C9DC9e12ba635e0b391c'; + const VALID_HASH_SHA256 = 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476'; + const getTrustedAppItemEntryItem = () => getCreateTrustedAppItem().entries[0]; it('should validate `entry.field` is required', () => { @@ -194,13 +199,19 @@ describe('When invoking Trusted Apps Schema', () => { }; expect(() => body.validate(bodyMsg2)).toThrow(); - ['process.hash.*', 'process.path.text'].forEach((field) => { + [ + { + field: 'process.hash.*', + value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476', + }, + { field: 'process.path.text', value: '/tmp/dir1' }, + ].forEach((partialEntry) => { const bodyMsg3 = { ...getCreateTrustedAppItem(), entries: [ { ...getTrustedAppItemEntryItem(), - field, + ...partialEntry, }, ], }; @@ -280,6 +291,63 @@ describe('When invoking Trusted Apps Schema', () => { }; expect(() => body.validate(bodyMsg)).toThrow(); }); + + it('should validate that `entry.field` is used only once', () => { + const bodyMsg = { + ...getCreateTrustedAppItem(), + entries: [getTrustedAppItemEntryItem(), getTrustedAppItemEntryItem()], + }; + expect(() => body.validate(bodyMsg)).toThrow(); + }); + + it('should validate Hash field valid value', () => { + [VALID_HASH_MD5, VALID_HASH_SHA1, VALID_HASH_SHA256].forEach((value) => { + expect(() => { + body.validate({ + ...getCreateTrustedAppItem(), + entries: [ + { + ...getTrustedAppItemEntryItem(), + field: 'process.hash.*', + value, + }, + ], + }); + }).not.toThrow(); + }); + }); + + it('should validate Hash value with invalid length', () => { + ['xyz', VALID_HASH_SHA256 + VALID_HASH_MD5].forEach((value) => { + expect(() => { + body.validate({ + ...getCreateTrustedAppItem(), + entries: [ + { + ...getTrustedAppItemEntryItem(), + field: 'process.hash.*', + value, + }, + ], + }); + }).toThrow(); + }); + }); + + it('should validate Hash value with invalid characters', () => { + expect(() => { + body.validate({ + ...getCreateTrustedAppItem(), + entries: [ + { + ...getTrustedAppItemEntryItem(), + field: 'process.hash.*', + value: `G${VALID_HASH_MD5.substr(1)}`, + }, + ], + }); + }).toThrow(); + }); }); }); }); diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index 3b3bec4a478046..912468b52adc04 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -6,6 +6,13 @@ import { schema } from '@kbn/config-schema'; +const hashLengths: readonly number[] = [ + 32, // MD5 + 40, // SHA1 + 64, // SHA256 +]; +const hasInvalidCharacters = /[^0-9a-f]/i; + export const DeleteTrustedAppsRequestSchema = { params: schema.object({ id: schema.string(), @@ -34,7 +41,26 @@ export const PostTrustedAppCreateRequestSchema = { operator: schema.literal('included'), value: schema.string({ minLength: 1 }), }), - { minSize: 1 } + { + minSize: 1, + validate(entries) { + const usedFields: string[] = []; + for (const { field, value } of entries) { + if (usedFields.includes(field)) { + return `[Hash] field can only be used once`; + } + + usedFields.push(field); + + if ( + field === 'process.hash.*' && + (!hashLengths.includes(value.length) || hasInvalidCharacters.test(value)) + ) { + return `Invalid hash value [${value}]`; + } + } + }, + } ), }), }; From 4ee3677898e443e5cbd7a0640063fe80123369bb Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet <pierre.gayvallet@elastic.co> Date: Thu, 1 Oct 2020 17:10:09 +0200 Subject: [PATCH 027/128] Add relative path handling to `application.navigateToUrl` (#78565) * split application utilities and associated tests to distinct files * do not match app if path does not start with the basePath * add relative paths support to `navigateToUrl` * add null-check error * update generated doc * nits on doc --- ...re-public.applicationstart.geturlforapp.md | 6 +- ...ana-plugin-core-public.applicationstart.md | 4 +- ...e-public.applicationstart.navigatetourl.md | 11 +- packages/kbn-std/src/index.ts | 2 +- packages/kbn-std/src/url.test.ts | 26 +- packages/kbn-std/src/url.ts | 11 + src/core/public/application/types.ts | 26 +- src/core/public/application/utils.ts | 128 -------- .../application/utils/append_app_path.test.ts | 40 +++ .../application/utils/append_app_path.ts | 32 ++ .../application/utils/get_app_info.test.ts | 75 +++++ .../public/application/utils/get_app_info.ts | 36 +++ src/core/public/application/utils/index.ts | 24 ++ .../parse_app_url.test.ts} | 295 ++++++++---------- .../public/application/utils/parse_app_url.ts | 83 +++++ .../utils/relative_to_absolute.test.ts | 29 ++ .../application/utils/relative_to_absolute.ts | 35 +++ .../application/utils/remove_slashes.test.ts | 53 ++++ .../application/utils/remove_slashes.ts | 42 +++ src/core/public/http/base_path.ts | 18 -- 20 files changed, 649 insertions(+), 327 deletions(-) delete mode 100644 src/core/public/application/utils.ts create mode 100644 src/core/public/application/utils/append_app_path.test.ts create mode 100644 src/core/public/application/utils/append_app_path.ts create mode 100644 src/core/public/application/utils/get_app_info.test.ts create mode 100644 src/core/public/application/utils/get_app_info.ts create mode 100644 src/core/public/application/utils/index.ts rename src/core/public/application/{utils.test.ts => utils/parse_app_url.test.ts} (58%) create mode 100644 src/core/public/application/utils/parse_app_url.ts create mode 100644 src/core/public/application/utils/relative_to_absolute.test.ts create mode 100644 src/core/public/application/utils/relative_to_absolute.ts create mode 100644 src/core/public/application/utils/remove_slashes.test.ts create mode 100644 src/core/public/application/utils/remove_slashes.ts diff --git a/docs/development/core/public/kibana-plugin-core-public.applicationstart.geturlforapp.md b/docs/development/core/public/kibana-plugin-core-public.applicationstart.geturlforapp.md index 055ad9f37e654f..1eaf00c7a678d6 100644 --- a/docs/development/core/public/kibana-plugin-core-public.applicationstart.geturlforapp.md +++ b/docs/development/core/public/kibana-plugin-core-public.applicationstart.geturlforapp.md @@ -4,9 +4,11 @@ ## ApplicationStart.getUrlForApp() method -Returns an URL to a given app, including the global base path. By default, the URL is relative (/basePath/app/my-app). Use the `absolute` option to generate an absolute url (http://host:port/basePath/app/my-app) +Returns the absolute path (or URL) to a given app, including the global base path. -Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's location. +By default, it returns the absolute path of the application (e.g `/basePath/app/my-app`<!-- -->). Use the `absolute` option to generate an absolute url instead (e.g `http://host:port/basePath/app/my-app`<!-- -->) + +Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's current location. <b>Signature:</b> diff --git a/docs/development/core/public/kibana-plugin-core-public.applicationstart.md b/docs/development/core/public/kibana-plugin-core-public.applicationstart.md index 00318f32984e9b..ae62a7767a0e99 100644 --- a/docs/development/core/public/kibana-plugin-core-public.applicationstart.md +++ b/docs/development/core/public/kibana-plugin-core-public.applicationstart.md @@ -23,8 +23,8 @@ export interface ApplicationStart | Method | Description | | --- | --- | -| [getUrlForApp(appId, options)](./kibana-plugin-core-public.applicationstart.geturlforapp.md) | Returns an URL to a given app, including the global base path. By default, the URL is relative (/basePath/app/my-app). Use the <code>absolute</code> option to generate an absolute url (http://host:port/basePath/app/my-app)<!-- -->Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's location. | +| [getUrlForApp(appId, options)](./kibana-plugin-core-public.applicationstart.geturlforapp.md) | Returns the absolute path (or URL) to a given app, including the global base path.<!-- -->By default, it returns the absolute path of the application (e.g <code>/basePath/app/my-app</code>). Use the <code>absolute</code> option to generate an absolute url instead (e.g <code>http://host:port/basePath/app/my-app</code>)<!-- -->Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's current location. | | [navigateToApp(appId, options)](./kibana-plugin-core-public.applicationstart.navigatetoapp.md) | Navigate to a given app | -| [navigateToUrl(url)](./kibana-plugin-core-public.applicationstart.navigatetourl.md) | Navigate to given url, which can either be an absolute url or a relative path, in a SPA friendly way when possible.<!-- -->If all these criteria are true for the given url: - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location - The pathname of the URL starts with the current basePath (eg. /mybasepath/s/my-space) - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's <code>appRoute</code> configuration)<!-- -->Then a SPA navigation will be performed using <code>navigateToApp</code> using the corresponding application and path. Otherwise, fallback to a full page reload to navigate to the url using <code>window.location.assign</code> | +| [navigateToUrl(url)](./kibana-plugin-core-public.applicationstart.navigatetourl.md) | Navigate to given URL in a SPA friendly way when possible (when the URL will redirect to a valid application within the current basePath).<!-- -->The method resolves pathnames the same way browsers do when resolving a <code><a href></code> value. The provided <code>url</code> can be: - an absolute URL - an absolute path - a path relative to the current URL (window.location.href)<!-- -->If all these criteria are true for the given URL: - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location - The resolved pathname of the provided URL/path starts with the current basePath (eg. /mybasepath/s/my-space) - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's <code>appRoute</code> configuration)<!-- -->Then a SPA navigation will be performed using <code>navigateToApp</code> using the corresponding application and path. Otherwise, fallback to a full page reload to navigate to the url using <code>window.location.assign</code> | | [registerMountContext(contextName, provider)](./kibana-plugin-core-public.applicationstart.registermountcontext.md) | Register a context provider for application mounting. Will only be available to applications that depend on the plugin that registered this context. Deprecated, use [CoreSetup.getStartServices](./kibana-plugin-core-public.coresetup.getstartservices.md)<!-- -->. | diff --git a/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetourl.md b/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetourl.md index 86b86776b0b12c..8639394cbc4212 100644 --- a/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetourl.md +++ b/docs/development/core/public/kibana-plugin-core-public.applicationstart.navigatetourl.md @@ -4,9 +4,11 @@ ## ApplicationStart.navigateToUrl() method -Navigate to given url, which can either be an absolute url or a relative path, in a SPA friendly way when possible. +Navigate to given URL in a SPA friendly way when possible (when the URL will redirect to a valid application within the current basePath). -If all these criteria are true for the given url: - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location - The pathname of the URL starts with the current basePath (eg. /mybasepath/s/my-space) - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's `appRoute` configuration) +The method resolves pathnames the same way browsers do when resolving a `<a href>` value. The provided `url` can be: - an absolute URL - an absolute path - a path relative to the current URL (window.location.href) + +If all these criteria are true for the given URL: - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location - The resolved pathname of the provided URL/path starts with the current basePath (eg. /mybasepath/s/my-space) - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's `appRoute` configuration) Then a SPA navigation will be performed using `navigateToApp` using the corresponding application and path. Otherwise, fallback to a full page reload to navigate to the url using `window.location.assign` @@ -20,7 +22,7 @@ navigateToUrl(url: string): Promise<void>; | Parameter | Type | Description | | --- | --- | --- | -| url | <code>string</code> | an absolute url, or a relative path, to navigate to. | +| url | <code>string</code> | an absolute URL, an absolute path or a relative path, to navigate to. | <b>Returns:</b> @@ -35,11 +37,14 @@ navigateToUrl(url: string): Promise<void>; // will call `application.navigateToApp('discover', { path: '/some-path?foo=bar'})` application.navigateToUrl('https://kibana:8080/base-path/s/my-space/app/discover/some-path?foo=bar') application.navigateToUrl('/base-path/s/my-space/app/discover/some-path?foo=bar') +application.navigateToUrl('./discover/some-path?foo=bar') // will perform a full page reload using `window.location.assign` application.navigateToUrl('https://elsewhere:8080/base-path/s/my-space/app/discover/some-path') // origin does not match application.navigateToUrl('/app/discover/some-path') // does not include the current basePath application.navigateToUrl('/base-path/s/my-space/app/unknown-app/some-path') // unknown application +application.navigateToUrl('../discover') // resolve to `/base-path/s/my-space/discover` which is not a path of a known app. +application.navigateToUrl('../../other-space/discover') // resolve to `/base-path/s/other-space/discover` which is not within the current basePath. ``` diff --git a/packages/kbn-std/src/index.ts b/packages/kbn-std/src/index.ts index 7cf70a0e28e2cf..d9d3ec4b0d52b4 100644 --- a/packages/kbn-std/src/index.ts +++ b/packages/kbn-std/src/index.ts @@ -24,6 +24,6 @@ export { mapToObject } from './map_to_object'; export { merge } from './merge'; export { pick } from './pick'; export { withTimeout } from './promise'; -export { isRelativeUrl, modifyUrl, URLMeaningfulParts } from './url'; +export { isRelativeUrl, modifyUrl, getUrlOrigin, URLMeaningfulParts } from './url'; export { unset } from './unset'; export { getFlattenedObject } from './get_flattened_object'; diff --git a/packages/kbn-std/src/url.test.ts b/packages/kbn-std/src/url.test.ts index 7e9b6adfd3f493..4d5c5a1808c559 100644 --- a/packages/kbn-std/src/url.test.ts +++ b/packages/kbn-std/src/url.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { modifyUrl, isRelativeUrl } from './url'; +import { modifyUrl, isRelativeUrl, getUrlOrigin } from './url'; describe('modifyUrl()', () => { test('throws an error with invalid input', () => { @@ -83,3 +83,27 @@ describe('isRelativeUrl()', () => { expect(isRelativeUrl(' //evil.com')).toBe(false); }); }); + +describe('getOrigin', () => { + describe('when passing an absolute url', () => { + it('return origin without port when the url does not have a port', () => { + expect(getUrlOrigin('https://example.com/file/to/path?example')).toEqual( + 'https://example.com' + ); + }); + it('return origin with port when the url does have a port', () => { + expect(getUrlOrigin('http://example.com:80/path/to/file')).toEqual('http://example.com:80'); + }); + }); + describe('when passing a non absolute url', () => { + it('returns null for relative url', () => { + expect(getUrlOrigin('./path/to/file')).toBeNull(); + }); + it('returns null for absolute path', () => { + expect(getUrlOrigin('/path/to/file')).toBeNull(); + }); + it('returns null for empty url', () => { + expect(getUrlOrigin('')).toBeNull(); + }); + }); +}); diff --git a/packages/kbn-std/src/url.ts b/packages/kbn-std/src/url.ts index edcdebbd2bc81b..745ed05751b105 100644 --- a/packages/kbn-std/src/url.ts +++ b/packages/kbn-std/src/url.ts @@ -125,3 +125,14 @@ export function isRelativeUrl(candidatePath: string) { } return true; } + +/** + * Returns the origin (protocol + host + port) from given `url` if `url` is a valid absolute url, or null otherwise + */ +export function getUrlOrigin(url: string): string | null { + const obj = parseUrl(url); + if (!obj.protocol && !obj.hostname) { + return null; + } + return `${obj.protocol}//${obj.hostname}${obj.port ? `:${obj.port}` : ''}`; +} diff --git a/src/core/public/application/types.ts b/src/core/public/application/types.ts index df83b6e932aad9..02d2d3a52a01a5 100644 --- a/src/core/public/application/types.ts +++ b/src/core/public/application/types.ts @@ -710,11 +710,17 @@ export interface ApplicationStart { navigateToApp(appId: string, options?: NavigateToAppOptions): Promise<void>; /** - * Navigate to given url, which can either be an absolute url or a relative path, in a SPA friendly way when possible. + * Navigate to given URL in a SPA friendly way when possible (when the URL will redirect to a valid application + * within the current basePath). * - * If all these criteria are true for the given url: + * The method resolves pathnames the same way browsers do when resolving a `<a href>` value. The provided `url` can be: + * - an absolute URL + * - an absolute path + * - a path relative to the current URL (window.location.href) + * + * If all these criteria are true for the given URL: * - (only for absolute URLs) The origin of the URL matches the origin of the browser's current location - * - The pathname of the URL starts with the current basePath (eg. /mybasepath/s/my-space) + * - The resolved pathname of the provided URL/path starts with the current basePath (eg. /mybasepath/s/my-space) * - The pathname segment after the basePath matches any known application route (eg. /app/<id>/ or any application's `appRoute` configuration) * * Then a SPA navigation will be performed using `navigateToApp` using the corresponding application and path. @@ -727,23 +733,27 @@ export interface ApplicationStart { * // will call `application.navigateToApp('discover', { path: '/some-path?foo=bar'})` * application.navigateToUrl('https://kibana:8080/base-path/s/my-space/app/discover/some-path?foo=bar') * application.navigateToUrl('/base-path/s/my-space/app/discover/some-path?foo=bar') + * application.navigateToUrl('./discover/some-path?foo=bar') * * // will perform a full page reload using `window.location.assign` * application.navigateToUrl('https://elsewhere:8080/base-path/s/my-space/app/discover/some-path') // origin does not match * application.navigateToUrl('/app/discover/some-path') // does not include the current basePath * application.navigateToUrl('/base-path/s/my-space/app/unknown-app/some-path') // unknown application + * application.navigateToUrl('../discover') // resolve to `/base-path/s/my-space/discover` which is not a path of a known app. + * application.navigateToUrl('../../other-space/discover') // resolve to `/base-path/s/other-space/discover` which is not within the current basePath. * ``` * - * @param url - an absolute url, or a relative path, to navigate to. + * @param url - an absolute URL, an absolute path or a relative path, to navigate to. */ navigateToUrl(url: string): Promise<void>; /** - * Returns an URL to a given app, including the global base path. - * By default, the URL is relative (/basePath/app/my-app). - * Use the `absolute` option to generate an absolute url (http://host:port/basePath/app/my-app) + * Returns the absolute path (or URL) to a given app, including the global base path. + * + * By default, it returns the absolute path of the application (e.g `/basePath/app/my-app`). + * Use the `absolute` option to generate an absolute url instead (e.g `http://host:port/basePath/app/my-app`) * - * Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's location. + * Note that when generating absolute urls, the origin (protocol, host and port) are determined from the browser's current location. * * @param appId * @param options.path - optional path inside application to deep link to diff --git a/src/core/public/application/utils.ts b/src/core/public/application/utils.ts deleted file mode 100644 index 85760526bf5442..00000000000000 --- a/src/core/public/application/utils.ts +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { IBasePath } from '../http'; -import { App, AppNavLinkStatus, AppStatus, ParsedAppUrl, PublicAppInfo } from './types'; - -/** - * Utility to remove trailing, leading or duplicate slashes. - * By default will only remove duplicates. - */ -export const removeSlashes = ( - url: string, - { - trailing = false, - leading = false, - duplicates = true, - }: { trailing?: boolean; leading?: boolean; duplicates?: boolean } = {} -): string => { - if (duplicates) { - url = url.replace(/\/{2,}/g, '/'); - } - if (trailing) { - url = url.replace(/\/$/, ''); - } - if (leading) { - url = url.replace(/^\//, ''); - } - return url; -}; - -export const appendAppPath = (appBasePath: string, path: string = '') => { - // Only prepend slash if not a hash or query path - path = path === '' || path.startsWith('#') || path.startsWith('?') ? path : `/${path}`; - // Do not remove trailing slash when in hashbang or basePath - const removeTrailing = path.indexOf('#') === -1 && appBasePath.indexOf('#') === -1; - return removeSlashes(`${appBasePath}${path}`, { - trailing: removeTrailing, - duplicates: true, - leading: false, - }); -}; - -/** - * Converts a relative path to an absolute url. - * Implementation is based on a specified behavior of the browser to automatically convert - * a relative url to an absolute one when setting the `href` attribute of a `<a>` html element. - * - * @example - * ```ts - * // current url: `https://kibana:8000/base-path/app/my-app` - * relativeToAbsolute('/base-path/app/another-app') => `https://kibana:8000/base-path/app/another-app` - * ``` - */ -export const relativeToAbsolute = (url: string): string => { - const a = document.createElement('a'); - a.setAttribute('href', url); - return a.href; -}; - -/** - * Parse given url and return the associated app id and path if any app matches. - * Input can either be: - * - a path containing the basePath, ie `/base-path/app/my-app/some-path` - * - an absolute url matching the `origin` of the kibana instance (as seen by the browser), - * i.e `https://kibana:8080/base-path/app/my-app/some-path` - */ -export const parseAppUrl = ( - url: string, - basePath: IBasePath, - apps: Map<string, App<unknown>>, - getOrigin: () => string = () => window.location.origin -): ParsedAppUrl | undefined => { - url = removeBasePath(url, basePath, getOrigin()); - if (!url.startsWith('/')) { - return undefined; - } - - for (const app of apps.values()) { - const appPath = app.appRoute || `/app/${app.id}`; - - if (url.startsWith(appPath)) { - const path = url.substr(appPath.length); - return { - app: app.id, - path: path.length ? path : undefined, - }; - } - } -}; - -const removeBasePath = (url: string, basePath: IBasePath, origin: string): string => { - if (url.startsWith(origin)) { - url = url.substring(origin.length); - } - return basePath.remove(url); -}; - -export function getAppInfo(app: App<unknown>): PublicAppInfo { - const navLinkStatus = - app.navLinkStatus === AppNavLinkStatus.default - ? app.status === AppStatus.inaccessible - ? AppNavLinkStatus.hidden - : AppNavLinkStatus.visible - : app.navLinkStatus!; - const { updater$, mount, ...infos } = app; - return { - ...infos, - status: app.status!, - navLinkStatus, - appRoute: app.appRoute!, - }; -} diff --git a/src/core/public/application/utils/append_app_path.test.ts b/src/core/public/application/utils/append_app_path.test.ts new file mode 100644 index 00000000000000..a153b5753bbe20 --- /dev/null +++ b/src/core/public/application/utils/append_app_path.test.ts @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { appendAppPath } from './append_app_path'; + +describe('appendAppPath', () => { + it('appends the appBasePath with given path', () => { + expect(appendAppPath('/app/my-app', '/some-path')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app/', 'some-path')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app', 'some-path')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app', '')).toEqual('/app/my-app'); + }); + + it('preserves the trailing slash only if included in the hash or appPath', () => { + expect(appendAppPath('/app/my-app', '/some-path/')).toEqual('/app/my-app/some-path'); + expect(appendAppPath('/app/my-app', '/some-path#/')).toEqual('/app/my-app/some-path#/'); + expect(appendAppPath('/app/my-app#/', '')).toEqual('/app/my-app#/'); + expect(appendAppPath('/app/my-app#', '/')).toEqual('/app/my-app#/'); + expect(appendAppPath('/app/my-app', '/some-path#/hash/')).toEqual( + '/app/my-app/some-path#/hash/' + ); + expect(appendAppPath('/app/my-app', '/some-path#/hash')).toEqual('/app/my-app/some-path#/hash'); + }); +}); diff --git a/src/core/public/application/utils/append_app_path.ts b/src/core/public/application/utils/append_app_path.ts new file mode 100644 index 00000000000000..70cb4a44c648e0 --- /dev/null +++ b/src/core/public/application/utils/append_app_path.ts @@ -0,0 +1,32 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { removeSlashes } from './remove_slashes'; + +export const appendAppPath = (appBasePath: string, path: string = '') => { + // Only prepend slash if not a hash or query path + path = path === '' || path.startsWith('#') || path.startsWith('?') ? path : `/${path}`; + // Do not remove trailing slash when in hashbang or basePath + const removeTrailing = path.indexOf('#') === -1 && appBasePath.indexOf('#') === -1; + return removeSlashes(`${appBasePath}${path}`, { + trailing: removeTrailing, + duplicates: true, + leading: false, + }); +}; diff --git a/src/core/public/application/utils/get_app_info.test.ts b/src/core/public/application/utils/get_app_info.test.ts new file mode 100644 index 00000000000000..055f7d1a5ada9d --- /dev/null +++ b/src/core/public/application/utils/get_app_info.test.ts @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { of } from 'rxjs'; +import { App, AppNavLinkStatus, AppStatus } from '../types'; +import { getAppInfo } from './get_app_info'; + +describe('getAppInfo', () => { + const createApp = (props: Partial<App> = {}): App => ({ + mount: () => () => undefined, + updater$: of(() => undefined), + id: 'some-id', + title: 'some-title', + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.default, + appRoute: `/app/some-id`, + ...props, + }); + + it('converts an application and remove sensitive properties', () => { + const app = createApp(); + const info = getAppInfo(app); + + expect(info).toEqual({ + id: 'some-id', + title: 'some-title', + status: AppStatus.accessible, + navLinkStatus: AppNavLinkStatus.visible, + appRoute: `/app/some-id`, + }); + }); + + it('computes the navLinkStatus depending on the app status', () => { + expect( + getAppInfo( + createApp({ + navLinkStatus: AppNavLinkStatus.default, + status: AppStatus.inaccessible, + }) + ) + ).toEqual( + expect.objectContaining({ + navLinkStatus: AppNavLinkStatus.hidden, + }) + ); + expect( + getAppInfo( + createApp({ + navLinkStatus: AppNavLinkStatus.default, + status: AppStatus.accessible, + }) + ) + ).toEqual( + expect.objectContaining({ + navLinkStatus: AppNavLinkStatus.visible, + }) + ); + }); +}); diff --git a/src/core/public/application/utils/get_app_info.ts b/src/core/public/application/utils/get_app_info.ts new file mode 100644 index 00000000000000..71cd8a3e149290 --- /dev/null +++ b/src/core/public/application/utils/get_app_info.ts @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { App, AppNavLinkStatus, AppStatus, PublicAppInfo } from '../types'; + +export function getAppInfo(app: App<unknown>): PublicAppInfo { + const navLinkStatus = + app.navLinkStatus === AppNavLinkStatus.default + ? app.status === AppStatus.inaccessible + ? AppNavLinkStatus.hidden + : AppNavLinkStatus.visible + : app.navLinkStatus!; + const { updater$, mount, ...infos } = app; + return { + ...infos, + status: app.status!, + navLinkStatus, + appRoute: app.appRoute!, + }; +} diff --git a/src/core/public/application/utils/index.ts b/src/core/public/application/utils/index.ts new file mode 100644 index 00000000000000..3b8a34df8c50da --- /dev/null +++ b/src/core/public/application/utils/index.ts @@ -0,0 +1,24 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { appendAppPath } from './append_app_path'; +export { getAppInfo } from './get_app_info'; +export { parseAppUrl } from './parse_app_url'; +export { relativeToAbsolute } from './relative_to_absolute'; +export { removeSlashes } from './remove_slashes'; diff --git a/src/core/public/application/utils.test.ts b/src/core/public/application/utils/parse_app_url.test.ts similarity index 58% rename from src/core/public/application/utils.test.ts rename to src/core/public/application/utils/parse_app_url.test.ts index ee1d82a7a872e1..bf7e0a88a0742c 100644 --- a/src/core/public/application/utils.test.ts +++ b/src/core/public/application/utils/parse_app_url.test.ts @@ -17,78 +17,16 @@ * under the License. */ -import { of } from 'rxjs'; -import { App, AppNavLinkStatus, AppStatus } from './types'; -import { BasePath } from '../http/base_path'; -import { appendAppPath, getAppInfo, parseAppUrl, relativeToAbsolute, removeSlashes } from './utils'; - -describe('removeSlashes', () => { - it('only removes duplicates by default', () => { - expect(removeSlashes('/some//url//to//')).toEqual('/some/url/to/'); - expect(removeSlashes('some/////other//url')).toEqual('some/other/url'); - }); - - it('remove trailing slash when `trailing` is true', () => { - expect(removeSlashes('/some//url//to//', { trailing: true })).toEqual('/some/url/to'); - }); - - it('remove leading slash when `leading` is true', () => { - expect(removeSlashes('/some//url//to//', { leading: true })).toEqual('some/url/to/'); - }); - - it('does not removes duplicates when `duplicates` is false', () => { - expect(removeSlashes('/some//url//to/', { leading: true, duplicates: false })).toEqual( - 'some//url//to/' - ); - expect(removeSlashes('/some//url//to/', { trailing: true, duplicates: false })).toEqual( - '/some//url//to' - ); - }); - - it('accept mixed options', () => { - expect( - removeSlashes('/some//url//to/', { leading: true, duplicates: false, trailing: true }) - ).toEqual('some//url//to'); - expect( - removeSlashes('/some//url//to/', { leading: true, duplicates: true, trailing: true }) - ).toEqual('some/url/to'); - }); -}); - -describe('appendAppPath', () => { - it('appends the appBasePath with given path', () => { - expect(appendAppPath('/app/my-app', '/some-path')).toEqual('/app/my-app/some-path'); - expect(appendAppPath('/app/my-app/', 'some-path')).toEqual('/app/my-app/some-path'); - expect(appendAppPath('/app/my-app', 'some-path')).toEqual('/app/my-app/some-path'); - expect(appendAppPath('/app/my-app', '')).toEqual('/app/my-app'); - }); - - it('preserves the trailing slash only if included in the hash or appPath', () => { - expect(appendAppPath('/app/my-app', '/some-path/')).toEqual('/app/my-app/some-path'); - expect(appendAppPath('/app/my-app', '/some-path#/')).toEqual('/app/my-app/some-path#/'); - expect(appendAppPath('/app/my-app#/', '')).toEqual('/app/my-app#/'); - expect(appendAppPath('/app/my-app#', '/')).toEqual('/app/my-app#/'); - expect(appendAppPath('/app/my-app', '/some-path#/hash/')).toEqual( - '/app/my-app/some-path#/hash/' - ); - expect(appendAppPath('/app/my-app', '/some-path#/hash')).toEqual('/app/my-app/some-path#/hash'); - }); -}); - -describe('relativeToAbsolute', () => { - it('converts a relative path to an absolute url', () => { - const origin = window.location.origin; - expect(relativeToAbsolute('path')).toEqual(`${origin}/path`); - expect(relativeToAbsolute('/path#hash')).toEqual(`${origin}/path#hash`); - expect(relativeToAbsolute('/path?query=foo')).toEqual(`${origin}/path?query=foo`); - }); -}); +import { App } from '../types'; +import { BasePath } from '../../http/base_path'; +import { parseAppUrl } from './parse_app_url'; describe('parseAppUrl', () => { let apps: Map<string, App<any>>; let basePath: BasePath; - const getOrigin = () => 'https://kibana.local:8080'; + const currentUrl = + 'https://kibana.local:8080/base-path/app/current/current-path?current-query=true'; const createApp = (props: Partial<App>): App => { const app: App = { @@ -114,101 +52,178 @@ describe('parseAppUrl', () => { }); }); - describe('with relative paths', () => { + describe('with absolute paths', () => { it('parses the app id', () => { - expect(parseAppUrl('/base-path/app/foo', basePath, apps, getOrigin)).toEqual({ + expect(parseAppUrl('/base-path/app/foo', basePath, apps, currentUrl)).toEqual({ app: 'foo', path: undefined, }); - expect(parseAppUrl('/base-path/custom-bar', basePath, apps, getOrigin)).toEqual({ + expect(parseAppUrl('/base-path/custom-bar', basePath, apps, currentUrl)).toEqual({ app: 'bar', path: undefined, }); }); it('parses the path', () => { - expect(parseAppUrl('/base-path/app/foo/some/path', basePath, apps, getOrigin)).toEqual({ + expect(parseAppUrl('/base-path/app/foo/some/path', basePath, apps, currentUrl)).toEqual({ app: 'foo', path: '/some/path', }); - expect(parseAppUrl('/base-path/custom-bar/another/path/', basePath, apps, getOrigin)).toEqual( - { - app: 'bar', - path: '/another/path/', - } - ); + expect( + parseAppUrl('/base-path/custom-bar/another/path/', basePath, apps, currentUrl) + ).toEqual({ + app: 'bar', + path: '/another/path/', + }); }); it('includes query and hash in the path for default app route', () => { - expect(parseAppUrl('/base-path/app/foo#hash/bang', basePath, apps, getOrigin)).toEqual({ + expect(parseAppUrl('/base-path/app/foo#hash/bang', basePath, apps, currentUrl)).toEqual({ app: 'foo', path: '#hash/bang', }); - expect(parseAppUrl('/base-path/app/foo?hello=dolly', basePath, apps, getOrigin)).toEqual({ + expect(parseAppUrl('/base-path/app/foo?hello=dolly', basePath, apps, currentUrl)).toEqual({ app: 'foo', path: '?hello=dolly', }); - expect(parseAppUrl('/base-path/app/foo/path?hello=dolly', basePath, apps, getOrigin)).toEqual( - { - app: 'foo', - path: '/path?hello=dolly', - } - ); - expect(parseAppUrl('/base-path/app/foo/path#hash/bang', basePath, apps, getOrigin)).toEqual({ + expect( + parseAppUrl('/base-path/app/foo/path?hello=dolly', basePath, apps, currentUrl) + ).toEqual({ + app: 'foo', + path: '/path?hello=dolly', + }); + expect(parseAppUrl('/base-path/app/foo/path#hash/bang', basePath, apps, currentUrl)).toEqual({ app: 'foo', path: '/path#hash/bang', }); expect( - parseAppUrl('/base-path/app/foo/path#hash/bang?hello=dolly', basePath, apps, getOrigin) + parseAppUrl('/base-path/app/foo/path#hash/bang?hello=dolly', basePath, apps, currentUrl) ).toEqual({ app: 'foo', path: '/path#hash/bang?hello=dolly', }); }); it('includes query and hash in the path for custom app route', () => { - expect(parseAppUrl('/base-path/custom-bar#hash/bang', basePath, apps, getOrigin)).toEqual({ + expect(parseAppUrl('/base-path/custom-bar#hash/bang', basePath, apps, currentUrl)).toEqual({ app: 'bar', path: '#hash/bang', }); - expect(parseAppUrl('/base-path/custom-bar?hello=dolly', basePath, apps, getOrigin)).toEqual({ + expect(parseAppUrl('/base-path/custom-bar?hello=dolly', basePath, apps, currentUrl)).toEqual({ app: 'bar', path: '?hello=dolly', }); expect( - parseAppUrl('/base-path/custom-bar/path?hello=dolly', basePath, apps, getOrigin) + parseAppUrl('/base-path/custom-bar/path?hello=dolly', basePath, apps, currentUrl) ).toEqual({ app: 'bar', path: '/path?hello=dolly', }); expect( - parseAppUrl('/base-path/custom-bar/path#hash/bang', basePath, apps, getOrigin) + parseAppUrl('/base-path/custom-bar/path#hash/bang', basePath, apps, currentUrl) ).toEqual({ app: 'bar', path: '/path#hash/bang', }); expect( - parseAppUrl('/base-path/custom-bar/path#hash/bang?hello=dolly', basePath, apps, getOrigin) + parseAppUrl('/base-path/custom-bar/path#hash/bang?hello=dolly', basePath, apps, currentUrl) ).toEqual({ app: 'bar', path: '/path#hash/bang?hello=dolly', }); }); it('returns undefined when the app is not known', () => { - expect(parseAppUrl('/base-path/app/non-registered', basePath, apps, getOrigin)).toEqual( + expect(parseAppUrl('/base-path/app/non-registered', basePath, apps, currentUrl)).toEqual( undefined ); - expect(parseAppUrl('/base-path/unknown-path', basePath, apps, getOrigin)).toEqual(undefined); + expect(parseAppUrl('/base-path/unknown-path', basePath, apps, currentUrl)).toEqual(undefined); + }); + it('returns undefined when the path does not start with the base path', () => { + expect(parseAppUrl('/app/foo', basePath, apps, currentUrl)).toBeUndefined(); + }); + }); + + describe('with relative paths', () => { + it('works with sibling relative urls', () => { + expect( + parseAppUrl('./foo', basePath, apps, 'https://kibana.local:8080/base-path/app/current') + ).toEqual({ + app: 'foo', + path: undefined, + }); + }); + it('works with parent relative urls', () => { + expect( + parseAppUrl( + '../custom-bar', + basePath, + apps, + 'https://kibana.local:8080/base-path/app/current' + ) + ).toEqual({ + app: 'bar', + path: undefined, + }); + }); + it('works with nested parents', () => { + expect( + parseAppUrl( + '../../custom-bar', + basePath, + apps, + 'https://kibana.local:8080/base-path/app/current/some-path' + ) + ).toEqual({ + app: 'bar', + path: undefined, + }); + }); + it('parses the path', () => { + expect( + parseAppUrl( + './foo/path?hello=dolly', + basePath, + apps, + 'https://kibana.local:8080/base-path/app/current' + ) + ).toEqual({ + app: 'foo', + path: '/path?hello=dolly', + }); + }); + it('parses the path with query and hash', () => { + expect( + parseAppUrl( + '../custom-bar/path#hash?hello=dolly', + basePath, + apps, + 'https://kibana.local:8080/base-path/app/current' + ) + ).toEqual({ + app: 'bar', + path: '/path#hash?hello=dolly', + }); + }); + + it('returns undefined if the relative path redirect outside of the basePath', () => { + expect( + parseAppUrl( + '../../custom-bar', + basePath, + apps, + 'https://kibana.local:8080/base-path/app/current' + ) + ).toBeUndefined(); }); }); describe('with absolute urls', () => { it('parses the app id', () => { expect( - parseAppUrl('https://kibana.local:8080/base-path/app/foo', basePath, apps, getOrigin) + parseAppUrl('https://kibana.local:8080/base-path/app/foo', basePath, apps, currentUrl) ).toEqual({ app: 'foo', path: undefined, }); expect( - parseAppUrl('https://kibana.local:8080/base-path/custom-bar', basePath, apps, getOrigin) + parseAppUrl('https://kibana.local:8080/base-path/custom-bar', basePath, apps, currentUrl) ).toEqual({ app: 'bar', path: undefined, @@ -220,7 +235,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/app/foo/some/path', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'foo', @@ -231,7 +246,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/custom-bar/another/path/', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'bar', @@ -244,7 +259,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/app/foo#hash/bang', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'foo', @@ -255,7 +270,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/app/foo?hello=dolly', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'foo', @@ -266,7 +281,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/app/foo/path?hello=dolly', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'foo', @@ -277,7 +292,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/app/foo/path#hash/bang', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'foo', @@ -288,7 +303,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/app/foo/path#hash/bang?hello=dolly', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'foo', @@ -301,7 +316,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/custom-bar#hash/bang', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'bar', @@ -312,7 +327,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/custom-bar?hello=dolly', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'bar', @@ -323,7 +338,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/custom-bar/path?hello=dolly', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'bar', @@ -334,7 +349,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/custom-bar/path#hash/bang', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'bar', @@ -345,7 +360,7 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/custom-bar/path#hash/bang?hello=dolly', basePath, apps, - getOrigin + currentUrl ) ).toEqual({ app: 'bar', @@ -358,11 +373,11 @@ describe('parseAppUrl', () => { 'https://kibana.local:8080/base-path/app/non-registered', basePath, apps, - getOrigin + currentUrl ) ).toEqual(undefined); expect( - parseAppUrl('https://kibana.local:8080/base-path/unknown-path', basePath, apps, getOrigin) + parseAppUrl('https://kibana.local:8080/base-path/unknown-path', basePath, apps, currentUrl) ).toEqual(undefined); }); it('returns undefined when origin does not match', () => { @@ -371,7 +386,7 @@ describe('parseAppUrl', () => { 'https://other-kibana.external:8080/base-path/app/foo', basePath, apps, - getOrigin + currentUrl ) ).toEqual(undefined); expect( @@ -379,62 +394,14 @@ describe('parseAppUrl', () => { 'https://other-kibana.external:8080/base-path/custom-bar', basePath, apps, - getOrigin + currentUrl ) ).toEqual(undefined); }); - }); -}); - -describe('getAppInfo', () => { - const createApp = (props: Partial<App> = {}): App => ({ - mount: () => () => undefined, - updater$: of(() => undefined), - id: 'some-id', - title: 'some-title', - status: AppStatus.accessible, - navLinkStatus: AppNavLinkStatus.default, - appRoute: `/app/some-id`, - ...props, - }); - - it('converts an application and remove sensitive properties', () => { - const app = createApp(); - const info = getAppInfo(app); - - expect(info).toEqual({ - id: 'some-id', - title: 'some-title', - status: AppStatus.accessible, - navLinkStatus: AppNavLinkStatus.visible, - appRoute: `/app/some-id`, + it('returns undefined when the path does not contain the base path', () => { + expect(parseAppUrl('https://kibana.local:8080/app/foo', basePath, apps, currentUrl)).toEqual( + undefined + ); }); }); - - it('computes the navLinkStatus depending on the app status', () => { - expect( - getAppInfo( - createApp({ - navLinkStatus: AppNavLinkStatus.default, - status: AppStatus.inaccessible, - }) - ) - ).toEqual( - expect.objectContaining({ - navLinkStatus: AppNavLinkStatus.hidden, - }) - ); - expect( - getAppInfo( - createApp({ - navLinkStatus: AppNavLinkStatus.default, - status: AppStatus.accessible, - }) - ) - ).toEqual( - expect.objectContaining({ - navLinkStatus: AppNavLinkStatus.visible, - }) - ); - }); }); diff --git a/src/core/public/application/utils/parse_app_url.ts b/src/core/public/application/utils/parse_app_url.ts new file mode 100644 index 00000000000000..d253129a63ae44 --- /dev/null +++ b/src/core/public/application/utils/parse_app_url.ts @@ -0,0 +1,83 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { getUrlOrigin } from '@kbn/std'; +import { resolve } from 'url'; +import { IBasePath } from '../../http'; +import { App, ParsedAppUrl } from '../types'; + +/** + * Parse given URL and return the associated app id and path if any app matches, or undefined if none do. + * Input can either be: + * + * - an absolute path containing the basePath, + * e.g `/base-path/app/my-app/some-path` + * + * - an absolute URL matching the `origin` of the Kibana instance (as seen by the browser), + * e.g `https://kibana:8080/base-path/app/my-app/some-path` + * + * - a path relative to the provided `currentUrl`. + * e.g with `currentUrl` being `https://kibana:8080/base-path/app/current-app/some-path` + * `../other-app/other-path` will be converted to `/base-path/app/other-app/other-path` + */ +export const parseAppUrl = ( + url: string, + basePath: IBasePath, + apps: Map<string, App<unknown>>, + currentUrl: string = window.location.href +): ParsedAppUrl | undefined => { + const currentOrigin = getUrlOrigin(currentUrl); + if (!currentOrigin) { + throw new Error('when manually provided, currentUrl must be valid url with an origin'); + } + const currentPath = currentUrl.substring(currentOrigin.length); + + // remove the origin from the given url + if (url.startsWith(currentOrigin)) { + url = url.substring(currentOrigin.length); + } + + // if the path is relative (i.e `../../to/somewhere`), we convert it to absolute + if (!url.startsWith('/')) { + url = resolve(currentPath, url); + } + + // if using a basePath and the absolute path does not starts with it, it can't be a match + const basePathValue = basePath.get(); + if (basePathValue && !url.startsWith(basePathValue)) { + return undefined; + } + + url = basePath.remove(url); + if (!url.startsWith('/')) { + return undefined; + } + + for (const app of apps.values()) { + const appPath = app.appRoute || `/app/${app.id}`; + + if (url.startsWith(appPath)) { + const path = url.substr(appPath.length); + return { + app: app.id, + path: path.length ? path : undefined, + }; + } + } +}; diff --git a/src/core/public/application/utils/relative_to_absolute.test.ts b/src/core/public/application/utils/relative_to_absolute.test.ts new file mode 100644 index 00000000000000..56a33450ce902c --- /dev/null +++ b/src/core/public/application/utils/relative_to_absolute.test.ts @@ -0,0 +1,29 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { relativeToAbsolute } from './relative_to_absolute'; + +describe('relativeToAbsolute', () => { + it('converts a relative path to an absolute url', () => { + const origin = window.location.origin; + expect(relativeToAbsolute('path')).toEqual(`${origin}/path`); + expect(relativeToAbsolute('/path#hash')).toEqual(`${origin}/path#hash`); + expect(relativeToAbsolute('/path?query=foo')).toEqual(`${origin}/path?query=foo`); + }); +}); diff --git a/src/core/public/application/utils/relative_to_absolute.ts b/src/core/public/application/utils/relative_to_absolute.ts new file mode 100644 index 00000000000000..0f24f754f56cd6 --- /dev/null +++ b/src/core/public/application/utils/relative_to_absolute.ts @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Converts a relative path to an absolute url. + * Implementation is based on a specified behavior of the browser to automatically convert + * a relative url to an absolute one when setting the `href` attribute of a `<a>` html element. + * + * @example + * ```ts + * // current url: `https://kibana:8000/base-path/app/my-app` + * relativeToAbsolute('/base-path/app/another-app') => `https://kibana:8000/base-path/app/another-app` + * ``` + */ +export const relativeToAbsolute = (url: string): string => { + const a = document.createElement('a'); + a.setAttribute('href', url); + return a.href; +}; diff --git a/src/core/public/application/utils/remove_slashes.test.ts b/src/core/public/application/utils/remove_slashes.test.ts new file mode 100644 index 00000000000000..719e1ea08d109e --- /dev/null +++ b/src/core/public/application/utils/remove_slashes.test.ts @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { removeSlashes } from './remove_slashes'; + +describe('removeSlashes', () => { + it('only removes duplicates by default', () => { + expect(removeSlashes('/some//url//to//')).toEqual('/some/url/to/'); + expect(removeSlashes('some/////other//url')).toEqual('some/other/url'); + }); + + it('remove trailing slash when `trailing` is true', () => { + expect(removeSlashes('/some//url//to//', { trailing: true })).toEqual('/some/url/to'); + }); + + it('remove leading slash when `leading` is true', () => { + expect(removeSlashes('/some//url//to//', { leading: true })).toEqual('some/url/to/'); + }); + + it('does not removes duplicates when `duplicates` is false', () => { + expect(removeSlashes('/some//url//to/', { leading: true, duplicates: false })).toEqual( + 'some//url//to/' + ); + expect(removeSlashes('/some//url//to/', { trailing: true, duplicates: false })).toEqual( + '/some//url//to' + ); + }); + + it('accept mixed options', () => { + expect( + removeSlashes('/some//url//to/', { leading: true, duplicates: false, trailing: true }) + ).toEqual('some//url//to'); + expect( + removeSlashes('/some//url//to/', { leading: true, duplicates: true, trailing: true }) + ).toEqual('some/url/to'); + }); +}); diff --git a/src/core/public/application/utils/remove_slashes.ts b/src/core/public/application/utils/remove_slashes.ts new file mode 100644 index 00000000000000..641d7bc4164f48 --- /dev/null +++ b/src/core/public/application/utils/remove_slashes.ts @@ -0,0 +1,42 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Utility to remove trailing, leading or duplicate slashes. + * By default will only remove duplicates. + */ +export const removeSlashes = ( + url: string, + { + trailing = false, + leading = false, + duplicates = true, + }: { trailing?: boolean; leading?: boolean; duplicates?: boolean } = {} +): string => { + if (duplicates) { + url = url.replace(/\/{2,}/g, '/'); + } + if (trailing) { + url = url.replace(/\/$/, ''); + } + if (leading) { + url = url.replace(/^\//, ''); + } + return url; +}; diff --git a/src/core/public/http/base_path.ts b/src/core/public/http/base_path.ts index 5d9eb51023b78f..78e9cf75ff8060 100644 --- a/src/core/public/http/base_path.ts +++ b/src/core/public/http/base_path.ts @@ -16,24 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ import { modifyUrl } from '@kbn/std'; From e4b2b610a6d10df5239a9830c8fdec66456e9175 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Oct 2020 18:20:19 +0300 Subject: [PATCH 028/128] Update dependency vega to ^5.17.0 (#79088) Co-authored-by: Renovate Bot <bot@renovateapp.com> --- package.json | 2 +- yarn.lock | 32 ++++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index e70b0386bf5e78..864954e63f7e1f 100644 --- a/package.json +++ b/package.json @@ -478,7 +478,7 @@ "tree-kill": "^1.2.2", "typescript": "4.0.2", "ui-select": "0.19.8", - "vega": "^5.16.1", + "vega": "^5.17.0", "vega-lite": "^4.16.8", "vega-schema-url-parser": "^2.1.0", "vega-tooltip": "^0.24.2", diff --git a/yarn.lock b/yarn.lock index c84e43942e3cdb..399cc80a557d12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -30355,7 +30355,7 @@ vega-lite@^4.16.8: vega-util "~1.15.3" yargs "~16.0.3" -vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.3.3: +vega-loader@^4.3.2, vega-loader@^4.3.3: version "4.3.3" resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.3.3.tgz#1432dabae4dd7ab344f84c5ae6e250234e9c1005" integrity sha512-ZcAMi6C7yfbA3gpxDnFe3PvsP/jcDwUjgPIpZ2IYsaQS+JijZAj5g3i4mpQCao0Atc+C/g7htg0Ir3twFLPjkQ== @@ -30366,6 +30366,17 @@ vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.3.3: vega-format "^1.0.4" vega-util "^1.15.2" +vega-loader@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.4.0.tgz#fc515b7368c46b2be8df1fcf3c35c696c13c453d" + integrity sha512-e5enQECdau7rJob0NFB5pGumh3RaaSWWm90+boxMy3ay2b4Ki/3XIvo+C4F1Lx04qSxvQF7tO2LJcklRm6nqRA== + dependencies: + d3-dsv "^2.0.0" + node-fetch "^2.6.1" + topojson-client "^3.1.0" + vega-format "^1.0.4" + vega-util "^1.16.0" + vega-parser@~6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/vega-parser/-/vega-parser-6.1.0.tgz#485fb6fcd79d14b09efee340e2b55fb510e57e20" @@ -30485,11 +30496,16 @@ vega-typings@~0.19.0: dependencies: vega-util "^1.15.2" -vega-util@^1.15.2, vega-util@~1.15.2, vega-util@~1.15.3: +vega-util@^1.15.2, vega-util@~1.15.3: version "1.15.3" resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.15.3.tgz#b42b4fb11f32fbb57fb5cd116d4d3e1827d177aa" integrity sha512-NCbfCPMVgdP4geLrFtCDN9PTEXrgZgJBBLvpyos7HGv2xSe9bGjDCysv6qcueHrc1myEeCQzrHDFaShny6wXDg== +vega-util@^1.16.0, vega-util@~1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.16.0.tgz#77405d8df0a94944d106bdc36015f0d43aa2caa3" + integrity sha512-6mmz6mI+oU4zDMeKjgvE2Fjz0Oh6zo6WGATcvCfxH2gXBzhBHmy5d25uW5Zjnkc6QBXSWPLV9Xa6SiqMsrsKog== + vega-view-transforms@~4.5.8: version "4.5.8" resolved "https://registry.yarnpkg.com/vega-view-transforms/-/vega-view-transforms-4.5.8.tgz#c8dc42c3c7d4aa725d40b8775180c9f23bc98f4e" @@ -30533,10 +30549,10 @@ vega-wordcloud@~4.1.3: vega-statistics "^1.7.9" vega-util "^1.15.2" -vega@^5.16.1: - version "5.16.1" - resolved "https://registry.yarnpkg.com/vega/-/vega-5.16.1.tgz#76a794c28118b685e8578a8f9944c5e239dbe1cd" - integrity sha512-bqovD23iKvIgFnTkP9WqapJbaejhgdED6YjAVWIwG8hsDq5Lf/ZNstJJWPiSDu3j785GQU/o6ZU3RXywdH4dJA== +vega@^5.17.0: + version "5.17.0" + resolved "https://registry.yarnpkg.com/vega/-/vega-5.17.0.tgz#2b33296e257c97b79ee6501d4d1905fb1414d080" + integrity sha512-2Rm9aS3cSMXE55YgjfkuOmvSBMtiM/85/qX/WHLc+YiJacKGiwY9yzeC+w2Ft50JUs3nKZc1KB90ePgf5mfo0Q== dependencies: vega-crossfilter "~4.0.5" vega-dataflow "~5.7.3" @@ -30549,7 +30565,7 @@ vega@^5.16.1: vega-geo "~4.3.7" vega-hierarchy "~4.0.9" vega-label "~1.0.0" - vega-loader "~4.3.3" + vega-loader "~4.4.0" vega-parser "~6.1.0" vega-projection "~1.4.5" vega-regression "~1.0.9" @@ -30560,7 +30576,7 @@ vega@^5.16.1: vega-time "~2.0.4" vega-transforms "~4.9.3" vega-typings "~0.19.0" - vega-util "~1.15.2" + vega-util "~1.16.0" vega-view "~5.9.0" vega-view-transforms "~4.5.8" vega-voronoi "~4.1.5" From 72a41ab03d19f885792b874706589819f2f73f73 Mon Sep 17 00:00:00 2001 From: Nick Peihl <nick.peihl@elastic.co> Date: Thu, 1 Oct 2020 08:20:45 -0700 Subject: [PATCH 029/128] Update EMS landing page link (#78515) Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- src/plugins/maps_legacy/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/maps_legacy/config.ts b/src/plugins/maps_legacy/config.ts index 46d4a8fb6cb900..f49d56dedd45f5 100644 --- a/src/plugins/maps_legacy/config.ts +++ b/src/plugins/maps_legacy/config.ts @@ -29,7 +29,7 @@ export const configSchema = schema.object({ manifestServiceUrl: schema.string({ defaultValue: '' }), emsFileApiUrl: schema.string({ defaultValue: 'https://vector.maps.elastic.co' }), emsTileApiUrl: schema.string({ defaultValue: 'https://tiles.maps.elastic.co' }), - emsLandingPageUrl: schema.string({ defaultValue: 'https://maps.elastic.co/v7.7' }), + emsLandingPageUrl: schema.string({ defaultValue: 'https://maps.elastic.co/v7.10' }), emsFontLibraryUrl: schema.string({ defaultValue: 'https://tiles.maps.elastic.co/fonts/{fontstack}/{range}.pbf', }), From e83144ee405517ae089b785ea0a351ee66b8cf56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= <mikecote@users.noreply.github.com> Date: Thu, 1 Oct 2020 11:22:08 -0400 Subject: [PATCH 030/128] Fix alert add and edit flyout to not close when user clicks outside (#78860) --- .../public/application/sections/alert_form/alert_add.tsx | 1 - .../public/application/sections/alert_form/alert_edit.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx index 97dcfec5ed3c69..7b81298e8e4b69 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.tsx @@ -138,7 +138,6 @@ export const AlertAdd = ({ aria-labelledby="flyoutAlertAddTitle" size="m" maxWidth={620} - ownFocus > <EuiFlyoutHeader hasBorder> <EuiTitle size="s" data-test-subj="addAlertFlyoutTitle"> diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx index f991cea9c009c2..999873a650f07b 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.tsx @@ -107,7 +107,6 @@ export const AlertEdit = ({ initialAlert, onClose }: AlertEditProps) => { aria-labelledby="flyoutAlertEditTitle" size="m" maxWidth={620} - ownFocus > <EuiFlyoutHeader hasBorder> <EuiTitle size="s" data-test-subj="editAlertFlyoutTitle"> From 758f537708d96a870286850f526a5e94e9e68eee Mon Sep 17 00:00:00 2001 From: Tyler Smalley <tyler.smalley@elastic.co> Date: Thu, 1 Oct 2020 08:24:31 -0700 Subject: [PATCH 031/128] [docs] Fixes wording in backport policy (#79132) Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co> --- docs/developer/contributing/development-github.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer/contributing/development-github.asciidoc b/docs/developer/contributing/development-github.asciidoc index 84f51843098a70..c5a3d942f2af36 100644 --- a/docs/developer/contributing/development-github.asciidoc +++ b/docs/developer/contributing/development-github.asciidoc @@ -25,7 +25,7 @@ Pull requests are made into the `master` branch and then backported when it is s * Breaking changes do not get backported and only go into `master`. * All non-breaking changes can be backported to the `<major>.x` branch. * Features should not be backported to a `<major>.<minor>` branch. -* Bugs can be backported to a `<major>.<minor>` branch if the changes are safe and appropriate. Safety is a judgment call you make based on factors like the bug's severity, test coverage, confidence in the changes, etc. Your reasoning should be included in the pull request description. +* Bug fixes can be backported to a `<major>.<minor>` branch if the changes are safe and appropriate. Safety is a judgment call you make based on factors like the bug's severity, test coverage, confidence in the changes, etc. Your reasoning should be included in the pull request description. * Documentation changes can be backported to any branch at any time. [discrete] From e59c78c2b1f13058821a3f565359f5f3b7c9c73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= <mikecote@users.noreply.github.com> Date: Thu, 1 Oct 2020 11:24:54 -0400 Subject: [PATCH 032/128] Fix scenario where orphaned API keys can exist when SO operations fail (#78843) * Fix scenario where orphaned API keys can exist * Add test for enable API --- .../alerts/server/alerts_client.test.ts | 67 +++++++++- x-pack/plugins/alerts/server/alerts_client.ts | 125 ++++++++++-------- 2 files changed, 135 insertions(+), 57 deletions(-) diff --git a/x-pack/plugins/alerts/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts index d4817eab64acbe..a5846cd1060c59 100644 --- a/x-pack/plugins/alerts/server/alerts_client.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client.test.ts @@ -778,8 +778,12 @@ describe('create()', () => { expect(taskManager.schedule).not.toHaveBeenCalled(); }); - test('throws error if create saved object fails', async () => { + test('throws error and invalidates API key when create saved object fails', async () => { const data = getMockData(); + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '123', name: '123', api_key: 'abc' }, + }); unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ saved_objects: [ { @@ -798,6 +802,7 @@ describe('create()', () => { `"Test failure"` ); expect(taskManager.schedule).not.toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); }); test('attempts to remove saved object if scheduling failed', async () => { @@ -1422,6 +1427,10 @@ describe('enable()', () => { }); test('throws error when failing to update the first time', async () => { + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '123', name: '123', api_key: 'abc' }, + }); unsecuredSavedObjectsClient.update.mockReset(); unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Fail to update')); @@ -1430,6 +1439,7 @@ describe('enable()', () => { ); expect(alertsClientParams.getUserName).toHaveBeenCalled(); expect(alertsClientParams.createAPIKey).toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '123' }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1); expect(taskManager.schedule).not.toHaveBeenCalled(); }); @@ -3926,6 +3936,52 @@ describe('update()', () => { ); }); + test('throws when unsecuredSavedObjectsClient update fails and invalidates newly created API key', async () => { + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '234', name: '234', api_key: 'abc' }, + }); + unsecuredSavedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '1', + type: 'action', + attributes: { + actions: [], + actionTypeId: 'test', + }, + references: [], + }, + ], + }); + unsecuredSavedObjectsClient.create.mockRejectedValue(new Error('Fail')); + await expect( + alertsClient.update({ + id: '1', + data: { + schedule: { interval: '10s' }, + name: 'abc', + tags: ['foo'], + params: { + bar: true, + }, + throttle: null, + actions: [ + { + group: 'default', + id: '1', + params: { + foo: true, + }, + }, + ], + }, + }) + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Fail"`); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalledWith({ id: '123' }); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '234' }); + }); + describe('updating an alert schedule', () => { function mockApiCalls( alertId: string, @@ -4360,13 +4416,18 @@ describe('updateApiKey()', () => { expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); }); - test('throws when unsecuredSavedObjectsClient update fails', async () => { + test('throws when unsecuredSavedObjectsClient update fails and invalidates newly created API key', async () => { + alertsClientParams.createAPIKey.mockResolvedValueOnce({ + apiKeysEnabled: true, + result: { id: '234', name: '234', api_key: 'abc' }, + }); unsecuredSavedObjectsClient.update.mockRejectedValueOnce(new Error('Fail')); await expect(alertsClient.updateApiKey({ id: '1' })).rejects.toThrowErrorMatchingInlineSnapshot( `"Fail"` ); - expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalled(); + expect(alertsClientParams.invalidateAPIKey).not.toHaveBeenCalledWith({ id: '123' }); + expect(alertsClientParams.invalidateAPIKey).toHaveBeenCalledWith({ id: '234' }); }); describe('authorization', () => { diff --git a/x-pack/plugins/alerts/server/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client.ts index 033fdd752c6958..bcc3c9bcf7e555 100644 --- a/x-pack/plugins/alerts/server/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client.ts @@ -229,14 +229,21 @@ export class AlertsClient { muteAll: false, mutedInstanceIds: [], }; - const createdAlert = await this.unsecuredSavedObjectsClient.create( - 'alert', - this.updateMeta(rawAlert), - { - ...options, - references, - } - ); + let createdAlert: SavedObject<RawAlert>; + try { + createdAlert = await this.unsecuredSavedObjectsClient.create( + 'alert', + this.updateMeta(rawAlert), + { + ...options, + references, + } + ); + } catch (e) { + // Avoid unused API key + this.invalidateApiKey({ apiKey: rawAlert.apiKey }); + throw e; + } if (data.enabled) { let scheduledTask; try { @@ -498,23 +505,31 @@ export class AlertsClient { : null; const apiKeyAttributes = this.apiKeyAsAlertAttributes(createdAPIKey, username); - const updatedObject = await this.unsecuredSavedObjectsClient.create<RawAlert>( - 'alert', - this.updateMeta({ - ...attributes, - ...data, - ...apiKeyAttributes, - params: validatedAlertTypeParams as RawAlert['params'], - actions, - updatedBy: username, - }), - { - id, - overwrite: true, - version, - references, - } - ); + let updatedObject: SavedObject<RawAlert>; + const createAttributes = this.updateMeta({ + ...attributes, + ...data, + ...apiKeyAttributes, + params: validatedAlertTypeParams as RawAlert['params'], + actions, + updatedBy: username, + }); + try { + updatedObject = await this.unsecuredSavedObjectsClient.create<RawAlert>( + 'alert', + createAttributes, + { + id, + overwrite: true, + version, + references, + } + ); + } catch (e) { + // Avoid unused API key + this.invalidateApiKey({ apiKey: createAttributes.apiKey }); + throw e; + } return this.getPartialAlertFromRaw( id, @@ -580,19 +595,21 @@ export class AlertsClient { } const username = await this.getUserName(); - await this.unsecuredSavedObjectsClient.update( - 'alert', - id, - this.updateMeta({ - ...attributes, - ...this.apiKeyAsAlertAttributes( - await this.createAPIKey(this.generateAPIKeyName(attributes.alertTypeId, attributes.name)), - username - ), - updatedBy: username, - }), - { version } - ); + const updateAttributes = this.updateMeta({ + ...attributes, + ...this.apiKeyAsAlertAttributes( + await this.createAPIKey(this.generateAPIKeyName(attributes.alertTypeId, attributes.name)), + username + ), + updatedBy: username, + }); + try { + await this.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { version }); + } catch (e) { + // Avoid unused API key + this.invalidateApiKey({ apiKey: updateAttributes.apiKey }); + throw e; + } if (apiKeyToInvalidate) { await this.invalidateApiKey({ apiKey: apiKeyToInvalidate }); @@ -658,22 +675,22 @@ export class AlertsClient { if (attributes.enabled === false) { const username = await this.getUserName(); - await this.unsecuredSavedObjectsClient.update( - 'alert', - id, - this.updateMeta({ - ...attributes, - enabled: true, - ...this.apiKeyAsAlertAttributes( - await this.createAPIKey( - this.generateAPIKeyName(attributes.alertTypeId, attributes.name) - ), - username - ), - updatedBy: username, - }), - { version } - ); + const updateAttributes = this.updateMeta({ + ...attributes, + enabled: true, + ...this.apiKeyAsAlertAttributes( + await this.createAPIKey(this.generateAPIKeyName(attributes.alertTypeId, attributes.name)), + username + ), + updatedBy: username, + }); + try { + await this.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { version }); + } catch (e) { + // Avoid unused API key + this.invalidateApiKey({ apiKey: updateAttributes.apiKey }); + throw e; + } const scheduledTask = await this.scheduleAlert(id, attributes.alertTypeId); await this.unsecuredSavedObjectsClient.update('alert', id, { scheduledTaskId: scheduledTask.id, From d4a9b4bc0b806655f5299e065550564ef91015c8 Mon Sep 17 00:00:00 2001 From: Wylie Conlon <william.conlon@elastic.co> Date: Thu, 1 Oct 2020 11:30:21 -0400 Subject: [PATCH 033/128] [Vega] Fix unexpected change in autosizing behavior post upgrade (#77408) * [Vega] Fix unexpected change in autosizing behavior post upgrade * Add docs * Fix type issues * Fix i18n and snapshot * Fix snapshot? * Fix time in snapshot * Update style of sizing function --- docs/user/dashboard/vega-reference.asciidoc | 35 ++++- .../vega_visualization.test.js.snap | 4 +- .../vis_type_vega/public/data_model/types.ts | 19 ++- .../public/data_model/vega_parser.test.js | 100 ++++++++++--- .../public/data_model/vega_parser.ts | 141 ++++++++++-------- .../public/vega_view/vega_base_view.js | 5 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 8 files changed, 199 insertions(+), 107 deletions(-) diff --git a/docs/user/dashboard/vega-reference.asciidoc b/docs/user/dashboard/vega-reference.asciidoc index eed8d9a35b8748..0bc77ab0a417e5 100644 --- a/docs/user/dashboard/vega-reference.asciidoc +++ b/docs/user/dashboard/vega-reference.asciidoc @@ -11,7 +11,7 @@ For additional *Vega* and *Vega-Lite* information, refer to the reference sectio {kib} has extended Vega and Vega-Lite with extensions that support: -* Default height and width +* Automatic sizing * Default theme to match {kib} * Writing {es} queries using the time range and filters from dashboards * Using the Elastic Map Service in Vega maps @@ -22,12 +22,35 @@ For additional *Vega* and *Vega-Lite* information, refer to the reference sectio [float] [[vega-sizing-and-positioning]] -==== Default height and width +==== Automatic sizing -By default, Vega visualizations use the `autosize = { type: 'fit', contains: 'padding' }` layout. -`fit` uses all available space, ignores `width` and `height` values, -and respects the padding values. To override this behavior, change the -`autosize` value. +Most users will want their Vega visualizations to take the full available space, so unlike +Vega examples, `width` and `height` are not required parameters in {kib}. To set the width +or height manually, set `autosize: none`. For example, to set the height to a specific pixel value: + +``` +autosize: none +width: container +height: 200 +``` + +The default {kib} settings which are inherited by your visualizations are: + +``` +autosize: { + type: fit + contains: padding +} +width: container +height: container +``` + +{kib} is able to merge your custom `autosize` settings with the defaults. The options `fit-x` +and `fit-y` are supported but not recommended over the default `fit` setting. + +To learn more, read about +https://vega.github.io/vega/docs/specification/#autosize[autosize] +in the Vega documentation. [float] [[vega-theme]] diff --git a/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap b/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap index 0d38d9775a4cd4..8b813ee06b1b3d 100644 --- a/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap +++ b/src/plugins/vis_type_vega/public/__snapshots__/vega_visualization.test.js.snap @@ -4,6 +4,6 @@ exports[`VegaVisualizations VegaVisualization - basics should show vega blank re exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"<div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"512\\" height=\\"512\\" viewBox=\\"0 0 512 512\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(0,0)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h512v512h-512Z\\"></path><g><g class=\\"mark-group role-scope\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h0v0h0Z\\"></path><g><g class=\\"mark-area role-mark\\" role=\\"graphics-symbol\\" aria-roledescription=\\"area mark container\\"><path d=\\"M0,512C18.962962962962962,512,37.925925925925924,512,56.888888888888886,512C75.85185185185185,512,94.81481481481481,512,113.77777777777777,512C132.74074074074073,512,151.7037037037037,512,170.66666666666666,512C189.62962962962962,512,208.59259259259258,512,227.55555555555554,512C246.5185185185185,512,265.48148148148147,512,284.44444444444446,512C303.4074074074074,512,322.3703703703704,512,341.3333333333333,512C360.29629629629625,512,379.25925925925924,512,398.2222222222222,512C417.18518518518516,512,436.1481481481481,512,455.1111111111111,512C474.0740740740741,512,493.037037037037,512,512,512L512,355.2C493.037037037037,324.79999999999995,474.0740740740741,294.4,455.1111111111111,294.4C436.1481481481481,294.4,417.18518518518516,457.6,398.2222222222222,457.6C379.25925925925924,457.6,360.29629629629625,233.60000000000002,341.3333333333333,233.60000000000002C322.3703703703704,233.60000000000002,303.4074074074074,435.2,284.44444444444446,435.2C265.48148148148147,435.2,246.5185185185185,345.6,227.55555555555554,345.6C208.59259259259258,345.6,189.62962962962962,451.2,170.66666666666666,451.2C151.7037037037037,451.2,132.74074074074073,252.8,113.77777777777777,252.8C94.81481481481481,252.8,75.85185185185185,346.1333333333333,56.888888888888886,374.4C37.925925925925924,402.66666666666663,18.962962962962962,412.5333333333333,0,422.4Z\\" fill=\\"#54B399\\" fill-opacity=\\"1\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0,0h0v0h0Z\\"></path><g><g class=\\"mark-area role-mark\\" role=\\"graphics-symbol\\" aria-roledescription=\\"area mark container\\"><path d=\\"M0,422.4C18.962962962962962,412.5333333333333,37.925925925925924,402.66666666666663,56.888888888888886,374.4C75.85185185185185,346.1333333333333,94.81481481481481,252.8,113.77777777777777,252.8C132.74074074074073,252.8,151.7037037037037,451.2,170.66666666666666,451.2C189.62962962962962,451.2,208.59259259259258,345.6,227.55555555555554,345.6C246.5185185185185,345.6,265.48148148148147,435.2,284.44444444444446,435.2C303.4074074074074,435.2,322.3703703703704,233.60000000000002,341.3333333333333,233.60000000000002C360.29629629629625,233.60000000000002,379.25925925925924,457.6,398.2222222222222,457.6C417.18518518518516,457.6,436.1481481481481,294.4,455.1111111111111,294.4C474.0740740740741,294.4,493.037037037037,324.79999999999995,512,355.2L512,307.2C493.037037037037,275.2,474.0740740740741,243.2,455.1111111111111,243.2C436.1481481481481,243.2,417.18518518518516,371.2,398.2222222222222,371.2C379.25925925925924,371.2,360.29629629629625,22.399999999999977,341.3333333333333,22.399999999999977C322.3703703703704,22.399999999999977,303.4074074074074,278.4,284.44444444444446,278.4C265.48148148148147,278.4,246.5185185185185,204.8,227.55555555555554,192C208.59259259259258,179.20000000000002,189.62962962962962,185.6,170.66666666666666,172.8C151.7037037037037,160.00000000000003,132.74074074074073,83.19999999999999,113.77777777777777,83.19999999999999C94.81481481481481,83.19999999999999,75.85185185185185,83.19999999999999,56.888888888888886,83.19999999999999C37.925925925925924,83.19999999999999,18.962962962962962,164.79999999999998,0,246.39999999999998Z\\" fill=\\"#6092C0\\" fill-opacity=\\"1\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"<div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"512\\" height=\\"506\\" viewBox=\\"0 0 512 506\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(7,5)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0.5,0.5h498v494h-498Z\\" fill=\\"transparent\\" stroke=\\"#ddd\\"></path><g><g class=\\"mark-line role-mark marks\\" role=\\"graphics-object\\" aria-roledescription=\\"line mark container\\"><path aria-label=\\"key: Dec 11, 2017; doc_count: 0\\" role=\\"graphics-symbol\\" aria-roledescription=\\"line mark\\" d=\\"M0,494L55.33333333333333,494L110.66666666666666,494L166,44.953999999999986L221.33333333333331,32.9004L276.6666666666667,33.59199999999998L332,480.8596L387.3333333333333,494L442.66666666666663,494L498,494\\" stroke=\\"#54B399\\" stroke-width=\\"2\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--warn\\"><pre class=\\"vgaVis__messageCode\\">\\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable</pre></li></ul><div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"0\\" height=\\"0\\" viewBox=\\"0 0 0 0\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(7,7)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0.5,0.5h0v0h0Z\\" fill=\\"transparent\\" stroke=\\"#ddd\\"></path><g><g class=\\"mark-line role-mark marks\\" role=\\"graphics-object\\" aria-roledescription=\\"line mark container\\"><path aria-label=\\"key: Dec 11, 2017; doc_count: 0\\" role=\\"graphics-symbol\\" aria-roledescription=\\"line mark\\" d=\\"M0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0L0,0\\" stroke=\\"#54B399\\" stroke-width=\\"2\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"<div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"256\\" height=\\"250\\" viewBox=\\"0 0 256 250\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(7,5)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0.5,0.5h242v238h-242Z\\" fill=\\"transparent\\" stroke=\\"#ddd\\"></path><g><g class=\\"mark-line role-mark marks\\" role=\\"graphics-object\\" aria-roledescription=\\"line mark container\\"><path aria-label=\\"key: Dec 11, 2017; doc_count: 0\\" role=\\"graphics-symbol\\" aria-roledescription=\\"line mark\\" d=\\"M0,238L26.888888888888886,238L53.77777777777777,238L80.66666666666666,21.657999999999994L107.55555555555554,15.850799999999998L134.44444444444446,16.183999999999987L161.33333333333331,231.66920000000002L188.22222222222223,238L215.1111111111111,238L242,238\\" stroke=\\"#54B399\\" stroke-width=\\"2\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"<ul class=\\"vgaVis__messages\\"><li class=\\"vgaVis__message vgaVis__message--warn\\"><pre class=\\"vgaVis__messageCode\\">\\"width\\" and \\"height\\" params are ignored because \\"autosize\\" is enabled. Set \\"autosize\\": \\"none\\" to disable</pre></li></ul><div class=\\"vgaVis__view\\" style=\\"height: 100%; cursor: default;\\" role=\\"graphics-document\\" aria-roledescription=\\"visualization\\" aria-label=\\"Vega visualization\\"><svg xmlns=\\"http://www.w3.org/2000/svg\\" xmlns:xlink=\\"http://www.w3.org/1999/xlink\\" version=\\"1.1\\" class=\\"marks\\" width=\\"256\\" height=\\"250\\" viewBox=\\"0 0 256 250\\" style=\\"background-color: transparent;\\"><g fill=\\"none\\" stroke-miterlimit=\\"10\\" transform=\\"translate(7,5)\\"><g class=\\"mark-group role-frame root\\" role=\\"graphics-object\\" aria-roledescription=\\"group mark container\\"><g transform=\\"translate(0,0)\\"><path class=\\"background\\" aria-hidden=\\"true\\" d=\\"M0.5,0.5h242v238h-242Z\\" fill=\\"transparent\\" stroke=\\"#ddd\\"></path><g><g class=\\"mark-line role-mark marks\\" role=\\"graphics-object\\" aria-roledescription=\\"line mark container\\"><path aria-label=\\"key: Dec 11, 2017; doc_count: 0\\" role=\\"graphics-symbol\\" aria-roledescription=\\"line mark\\" d=\\"M0,238L26.888888888888886,238L53.77777777777777,238L80.66666666666666,21.657999999999994L107.55555555555554,15.850799999999998L134.44444444444446,16.183999999999987L161.33333333333331,231.66920000000002L188.22222222222223,238L215.1111111111111,238L242,238\\" stroke=\\"#54B399\\" stroke-width=\\"2\\"></path></g></g><path class=\\"foreground\\" aria-hidden=\\"true\\" d=\\"\\" display=\\"none\\"></path></g></g></g></svg></div><div class=\\"vgaVis__controls vgaVis__controls--column\\"></div>"`; diff --git a/src/plugins/vis_type_vega/public/data_model/types.ts b/src/plugins/vis_type_vega/public/data_model/types.ts index 14848bf5e8739c..11bdf4f0640236 100644 --- a/src/plugins/vis_type_vega/public/data_model/types.ts +++ b/src/plugins/vis_type_vega/public/data_model/types.ts @@ -43,10 +43,17 @@ interface Encoding { y: Coordinate; } -interface AutoSize { - type: string; - contains: string; -} +type AutoSize = + | 'pad' + | 'fit' + | 'fit-x' + | 'fit-y' + | 'none' + | { + type: string; + contains: string; + } + | { signal: string }; interface Padding { left: number; @@ -105,8 +112,8 @@ export interface VegaSpec { title?: string; autosize?: AutoSize; projections?: Projection[]; - width?: number; - height?: number; + width?: number | 'container'; + height?: number | 'container'; padding?: number | Padding; _hostConfig?: KibanaConfig; config: VegaSpecConfig; diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js index 5c7656efe925b0..c9f8e0a4394ec4 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.test.js @@ -371,43 +371,95 @@ describe('VegaParser._parseConfig', () => { test('_hostConfig', check({ _hostConfig: { a: 1 } }, { a: 1 }, {}, 1)); }); -describe('VegaParser._calcSizing', () => { - function check( - spec, - useResize, - paddingWidth, - paddingHeight, - isVegaLite, - expectedSpec, - warnCount - ) { +describe('VegaParser._compileWithAutosize', () => { + function check(spec, useResize, expectedSpec, warnCount) { return async () => { expectedSpec = expectedSpec || cloneDeep(spec); const vp = new VegaParser(spec); - vp.isVegaLite = !!isVegaLite; - vp._calcSizing(); + vp._compileWithAutosize(); expect(vp.useResize).toEqual(useResize); - expect(vp.paddingWidth).toEqual(paddingWidth); - expect(vp.paddingHeight).toEqual(paddingHeight); expect(vp.spec).toEqual(expectedSpec); expect(vp.warnings).toHaveLength(warnCount || 0); }; } - test('no size', check({ autosize: {} }, false, 0, 0)); - test('fit', check({ autosize: 'fit' }, true, 0, 0)); - test('fit obj', check({ autosize: { type: 'fit' } }, true, 0, 0)); - test('padding const', check({ autosize: 'fit', padding: 10 }, true, 20, 20)); test( - 'padding obj', - check({ autosize: 'fit', padding: { left: 5, bottom: 7, right: 6, top: 8 } }, true, 11, 15) + 'empty config', + check({}, true, { + autosize: { type: 'fit', contains: 'padding' }, + width: 'container', + height: 'container', + }) + ); + test( + 'no warnings for default config', + check({ width: 'container', height: 'container' }, true, { + autosize: { type: 'fit', contains: 'padding' }, + width: 'container', + height: 'container', + }) + ); + test( + 'warning when attempting to use invalid setting', + check( + { width: '300', height: '300' }, + true, + { + autosize: { type: 'fit', contains: 'padding' }, + width: 'container', + height: 'container', + }, + 1 + ) ); test( - 'width height', - check({ autosize: 'fit', width: 1, height: 2 }, true, 0, 0, false, false, 1) + 'autosize none', + check({ autosize: 'none' }, false, { autosize: { type: 'none', contains: 'padding' } }) + ); + test( + 'autosize=fit', + check({ autosize: 'fit' }, true, { + autosize: { type: 'fit', contains: 'padding' }, + width: 'container', + height: 'container', + }) ); test( - 'VL width height', - check({ autosize: 'fit', width: 1, height: 2 }, true, 0, 0, true, { autosize: 'fit' }, 0) + 'autosize=pad', + check({ autosize: 'pad' }, true, { + autosize: { type: 'pad', contains: 'padding' }, + width: 'container', + height: 'container', + }) + ); + test( + 'empty autosize object', + check({ autosize: {} }, true, { + autosize: { type: 'fit', contains: 'padding' }, + height: 'container', + width: 'container', + }) + ); + test( + 'warning on falsy arguments', + check( + { autosize: false }, + true, + { + autosize: { type: 'fit', contains: 'padding' }, + height: 'container', + width: 'container', + }, + 1 + ) + ); + test( + 'partial autosize object', + check({ autosize: { contains: 'content' } }, true, { + autosize: { contains: 'content', type: 'fit' }, + height: 'container', + width: 'container', + }) ); + test('autosize signals are ignored', check({ autosize: { signal: 'asdf' } }, undefined)); }); diff --git a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts index ccb89207e222fc..894c34c494c16e 100644 --- a/src/plugins/vis_type_vega/public/data_model/vega_parser.ts +++ b/src/plugins/vis_type_vega/public/data_model/vega_parser.ts @@ -75,8 +75,6 @@ export class VegaParser { mapConfig?: object; vlspec?: VegaSpec; useResize?: boolean; - paddingWidth?: number; - paddingHeight?: number; containerDir?: ControlsLocation | ControlsDirection; controlsDir?: ControlsLocation; searchAPI: SearchAPI; @@ -157,9 +155,9 @@ The URL is an identifier only. Kibana and your browser will never access this UR this._parseControlPlacement(); if (this.useMap) { this.mapConfig = this._parseMapConfig(); - } else if (this.spec && this.spec.autosize === undefined) { - // Default autosize should be fit, unless it's a map (leaflet-vega handles that) - this.spec.autosize = { type: 'fit', contains: 'padding' }; + this.useResize = false; + } else if (this.spec) { + this._compileWithAutosize(); } await this._resolveDataUrls(); @@ -167,15 +165,86 @@ The URL is an identifier only. Kibana and your browser will never access this UR if (this.isVegaLite) { this._compileVegaLite(); } + } + + /** + * Ensure that Vega and Vega-Lite will take the full width of the container unless + * the user has explicitly disabled this setting by setting it to "none". + * Also sets the default width to include the padding. This creates the least configuration + * needed for most cases, with the option to do more. + */ + private _compileWithAutosize() { + const defaultAutosize = { + type: 'fit', + contains: 'padding', + }; + + let autosize = this.spec.autosize; + let useResize = true; + + if (!this.isVegaLite && autosize && typeof autosize === 'object' && 'signal' in autosize) { + // Vega supports dynamic autosize information, so we ignore it + return; + } + + if (!autosize && typeof autosize !== 'undefined') { + this._onWarning( + i18n.translate('visTypeVega.vegaParser.autoSizeDoesNotAllowFalse', { + defaultMessage: + '{autoSizeParam} is enabled, it can only be disabled by setting {autoSizeParam} to {noneParam}', + values: { + autoSizeParam: '"autosize"', + noneParam: '"none"', + }, + }) + ); + } + + if (typeof autosize === 'string') { + useResize = autosize !== 'none'; + autosize = { ...defaultAutosize, type: autosize }; + } else if (typeof autosize === 'object') { + autosize = { ...defaultAutosize, ...autosize } as { + type: string; + contains: string; + }; + useResize = Boolean(autosize?.type && autosize?.type !== 'none'); + } else { + autosize = defaultAutosize; + } + + if ( + useResize && + ((this.spec.width && this.spec.width !== 'container') || + (this.spec.height && this.spec.height !== 'container')) + ) { + this._onWarning( + i18n.translate('visTypeVega.vegaParser.widthAndHeightParamsAreIgnored', { + defaultMessage: + '{widthParam} and {heightParam} params are ignored because {autoSizeParam} is enabled. Set {autoSizeParam}: {noneParam} to disable', + values: { + widthParam: '"width"', + heightParam: '"height"', + autoSizeParam: '"autosize"', + noneParam: '"none"', + }, + }) + ); + } - this._calcSizing(); + if (useResize) { + this.spec.width = 'container'; + this.spec.height = 'container'; + } + + this.spec.autosize = autosize; + this.useResize = useResize; } /** * Convert VegaLite to Vega spec - * @private */ - _compileVegaLite() { + private _compileVegaLite() { this.vlspec = this.spec; const logger = vega.logger(vega.Warn); // note: eslint has a false positive here logger.warn = this._onWarning.bind(this); @@ -226,62 +295,6 @@ The URL is an identifier only. Kibana and your browser will never access this UR } } - /** - * Process graph size and padding - * @private - */ - _calcSizing() { - this.useResize = false; - - // Padding is not included in the width/height by default - this.paddingWidth = 0; - this.paddingHeight = 0; - if (this.spec) { - if (!this.useMap) { - // when useResize is true, vega's canvas size will be set based on the size of the container, - // and will be automatically updated on resize events. - // We delete width & height if the autosize is set to "fit" - // We also set useResize=true in case autosize=none, and width & height are not set - const autosize = this.spec.autosize?.type || this.spec.autosize; - if (autosize === 'fit' || (autosize === 'none' && !this.spec.width && !this.spec.height)) { - this.useResize = true; - } - } - - if (this.useResize && this.spec.padding && this.spec.autosize?.contains !== 'padding') { - if (typeof this.spec.padding === 'object') { - this.paddingWidth += (+this.spec.padding.left || 0) + (+this.spec.padding.right || 0); - this.paddingHeight += (+this.spec.padding.top || 0) + (+this.spec.padding.bottom || 0); - } else { - this.paddingWidth += 2 * (+this.spec.padding || 0); - this.paddingHeight += 2 * (+this.spec.padding || 0); - } - } - - if (this.useResize && (this.spec.width || this.spec.height)) { - if (this.isVegaLite) { - delete this.spec.width; - delete this.spec.height; - } else { - this._onWarning( - i18n.translate( - 'visTypeVega.vegaParser.widthAndHeightParamsAreIgnoredWithAutosizeFitWarningMessage', - { - defaultMessage: - 'The {widthParam} and {heightParam} params are ignored with {autosizeParam}', - values: { - autosizeParam: 'autosize=fit', - widthParam: '"width"', - heightParam: '"height"', - }, - } - ) - ); - } - } - } - } - /** * Calculate container-direction CSS property for binding placement * @private diff --git a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js index 9b51b68e93bb49..979432b2aed2a4 100644 --- a/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_type_vega/public/vega_view/vega_base_view.js @@ -193,9 +193,8 @@ export class VegaBaseView { // This might be due to https://github.com/jquery/jquery/issues/3808 // Which is being fixed as part of jQuery 3.3.0 const heightExtraPadding = 6; - const width = Math.max(0, this._$container.width() - this._parser.paddingWidth); - const height = - Math.max(0, this._$container.height() - this._parser.paddingHeight) - heightExtraPadding; + const width = Math.max(0, this._$container.width()); + const height = Math.max(0, this._$container.height()) - heightExtraPadding; if (view.width() !== width || view.height() !== height) { view.width(width).height(height); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 0ebb10d30c0103..ba9d8e364bd173 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -4145,7 +4145,6 @@ "visTypeVega.vegaParser.unrecognizedControlsLocationValueErrorMessage": "認識されない {controlsLocationParam} 値[{locToDirMap}] のいずれかである必要があります", "visTypeVega.vegaParser.unrecognizedDirValueErrorMessage": "認識されない {dirParam} 値[{expectedValues}] のいずれかである必要があります", "visTypeVega.vegaParser.VLCompilerShouldHaveGeneratedSingleProtectionObjectErrorMessage": "内部エラー:Vega-Lite コンパイラーがシングルプロジェクションオブジェクトを生成したはずです", - "visTypeVega.vegaParser.widthAndHeightParamsAreIgnoredWithAutosizeFitWarningMessage": "{widthParam} と {heightParam} パラメーターは {autosizeParam} で無視されます", "visTypeVega.visualization.indexNotFoundErrorMessage": "インデックス {index} が見つかりません", "visTypeVega.visualization.renderErrorTitle": "Vega エラー", "visTypeVega.visualization.unableToFindDefaultIndexErrorMessage": "デフォルトのインデックスが見つかりません", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index acd6db3b758b18..5d44e0c635bee0 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -4146,7 +4146,6 @@ "visTypeVega.vegaParser.unrecognizedControlsLocationValueErrorMessage": "无法识别的 {controlsLocationParam} 值。应为 [{locToDirMap}] 之一", "visTypeVega.vegaParser.unrecognizedDirValueErrorMessage": "{dirParam} 值无法识别。应为 [{expectedValues}] 之一", "visTypeVega.vegaParser.VLCompilerShouldHaveGeneratedSingleProtectionObjectErrorMessage": "内部错误:Vega-Lite 编译器应已生成单个投影对象", - "visTypeVega.vegaParser.widthAndHeightParamsAreIgnoredWithAutosizeFitWarningMessage": "使用 {autosizeParam} 时,将忽略 {widthParam} 和 {heightParam} 参数", "visTypeVega.visualization.indexNotFoundErrorMessage": "找不到索引 {index}", "visTypeVega.visualization.renderErrorTitle": "Vega 错误", "visTypeVega.visualization.unableToFindDefaultIndexErrorMessage": "找不到默认索引", From c1a3002c1656a7e21f6719ea4066a0c599ee69a1 Mon Sep 17 00:00:00 2001 From: Shahzad <shahzad.muhammad@elastic.co> Date: Thu, 1 Oct 2020 17:37:02 +0200 Subject: [PATCH 034/128] update rum agent version which contains longtasks (#79105) --- package.json | 2 +- x-pack/package.json | 2 +- yarn.lock | 28 ++++++++++++++-------------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 864954e63f7e1f..26a7fef9cfc187 100644 --- a/package.json +++ b/package.json @@ -229,7 +229,7 @@ "devDependencies": { "@babel/parser": "^7.11.2", "@babel/types": "^7.11.0", - "@elastic/apm-rum": "^5.6.0", + "@elastic/apm-rum": "^5.6.1", "@elastic/charts": "21.1.2", "@elastic/ems-client": "7.10.0", "@elastic/eslint-config-kibana": "0.15.0", diff --git a/x-pack/package.json b/x-pack/package.json index 97d673290abd13..6c18902c773669 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@cypress/webpack-preprocessor": "^4.1.0", - "@elastic/apm-rum-react": "^1.2.4", + "@elastic/apm-rum-react": "^1.2.5", "@elastic/maki": "6.3.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 399cc80a557d12..608207400ec714 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1190,29 +1190,29 @@ enabled "2.0.x" kuler "^2.0.0" -"@elastic/apm-rum-core@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.6.1.tgz#0870e654e84e1f2ffea7c8a247a2da1b72918bcd" - integrity sha512-UtWj8UNN1sfSjav1kQK2NFhHtrH++4FzhtY0g80aSfHrDdBKVXaecWswoGmK3aiGJ9LAVlAXNfF3tPMT6JN23g== +"@elastic/apm-rum-core@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum-core/-/apm-rum-core-5.7.0.tgz#2213987285324781e2ebeca607f3a71245da5a84" + integrity sha512-YxfyDwlPDRy05ERb8h79eXq2ebDamlyII3sdc8zsfL6Hc1wOHK3uBGelDQjQzkUkRJqJL1Sy6LJqok2mpxQJyw== dependencies: error-stack-parser "^1.3.5" opentracing "^0.14.3" promise-polyfill "^8.1.3" -"@elastic/apm-rum-react@^1.2.4": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.2.4.tgz#f5b908f69f2696af10d19250226559ceb33dc1e9" - integrity sha512-zjig55n4/maU+kAEePS+DxgD12t4J0X9t3tB9YuO0gUIJhgT7KTL1Nv93ZmJ3u2tCJSpdYVfKQ0GBgSfjt1vVQ== +"@elastic/apm-rum-react@^1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum-react/-/apm-rum-react-1.2.5.tgz#ac715a192808e14e62e537e41b70cc8296854051" + integrity sha512-5+5Q2ztOQT0EbWFZqV2N78tcuA9qPuO5QAtSTQIYgb5lH27Sfa9G4xlTgCbJs9DzCKmhuu27E4DTArrU3tyNzA== dependencies: - "@elastic/apm-rum" "^5.6.0" + "@elastic/apm-rum" "^5.6.1" hoist-non-react-statics "^3.3.0" -"@elastic/apm-rum@^5.6.0": - version "5.6.0" - resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.6.0.tgz#0af2acb55091b9eb315cf38c6422a83cddfecb6f" - integrity sha512-6CuODbt7dBXoqsKoqhshQQC4GyqsGMPOR1FXZCWbnq55UZq1TWqra6zNCtEEFinz8rPaww7bzmNciXKRvGjIzQ== +"@elastic/apm-rum@^5.6.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@elastic/apm-rum/-/apm-rum-5.6.1.tgz#0d1bbef774866064795f7a9c6db0c951a900de35" + integrity sha512-q6ZkDb+m2z29h6/JKqBL/nBf6/x5yYmW1vUpdW3zy03jTQp+A7LpVaPI1HNquyGryqqT/BQl4QivFcNC28pr4w== dependencies: - "@elastic/apm-rum-core" "^5.6.1" + "@elastic/apm-rum-core" "^5.7.0" "@elastic/charts@21.1.2": version "21.1.2" From f4c5ebca9d3c7de2b6788ec133267a4d94e03cc9 Mon Sep 17 00:00:00 2001 From: Jeffrey Chu <56368296+achuguy@users.noreply.github.com> Date: Thu, 1 Oct 2020 11:47:56 -0400 Subject: [PATCH 035/128] [Security Solution]Fix basepath used by endpoint telemetry tests (#79027) * Fix basepath used by endpoint telemetry tests * Linting --- .../services/endpoint_telemetry.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/services/endpoint_telemetry.ts b/x-pack/test/security_solution_endpoint/services/endpoint_telemetry.ts index 0f158da5d2f8cd..6098404201966c 100644 --- a/x-pack/test/security_solution_endpoint/services/endpoint_telemetry.ts +++ b/x-pack/test/security_solution_endpoint/services/endpoint_telemetry.ts @@ -5,10 +5,14 @@ */ import fs from 'fs'; import Path from 'path'; +import { KIBANA_ROOT } from '@kbn/test'; import { FtrProviderContext } from '../ftr_provider_context'; const TELEMETRY_API_ROOT = '/api/stats?extended=true'; -const TELEMETRY_DATA_ROOT = 'test/functional/es_archives/endpoint/telemetry/'; +const TELEMETRY_DATA_ROOT = Path.join( + KIBANA_ROOT, + 'x-pack/test/functional/es_archives/endpoint/telemetry/' +); interface EndpointTelemetry { total_installed: number; From ee7672aaf074dc4ebaf0ffb88d95d5f1bf9e1d18 Mon Sep 17 00:00:00 2001 From: Phillip Burch <phillip.burch@live.com> Date: Thu, 1 Oct 2020 10:51:55 -0500 Subject: [PATCH 036/128] [Metrics UI] Add ability to override datafeeds and job config for partition field (#78875) * Add ability to override datafeeds and job config for partition field * Remove debug * UX cleanup * Fix types, delete dead code --- .../containers/ml/infra_ml_module_types.ts | 4 +- .../containers/ml/infra_ml_setup_state.ts | 289 ------------------ .../metrics_hosts/module_descriptor.ts | 135 +++++--- .../modules/metrics_k8s/module_descriptor.ts | 143 ++++++--- .../anomoly_detection_flyout.tsx | 4 +- .../ml/anomaly_detection/flyout_home.tsx | 113 +++---- .../ml/anomaly_detection/job_setup_screen.tsx | 3 +- 7 files changed, 247 insertions(+), 444 deletions(-) delete mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts index a9f2671de82598..e36f38add641aa 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts @@ -33,11 +33,11 @@ export interface ModuleDescriptor<JobType extends string> { partitionField?: string ) => Promise<SetupMlModuleResponsePayload>; cleanUpModule: (spaceId: string, sourceId: string) => Promise<DeleteJobsResponsePayload>; - validateSetupIndices: ( + validateSetupIndices?: ( indices: string[], timestampField: string ) => Promise<ValidationIndicesResponsePayload>; - validateSetupDatasets: ( + validateSetupDatasets?: ( indices: string[], timestampField: string, startTime: number, diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts deleted file mode 100644 index 0dfe3b301f2400..00000000000000 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts +++ /dev/null @@ -1,289 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isEqual } from 'lodash'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { usePrevious } from 'react-use'; -import { - combineDatasetFilters, - DatasetFilter, - filterDatasetFilter, - isExampleDataIndex, -} from '../../../common/infra_ml'; -import { - AvailableIndex, - ValidationIndicesError, - ValidationUIError, -} from '../../components/logging/log_analysis_setup/initial_configuration_step'; -import { useTrackedPromise } from '../../utils/use_tracked_promise'; -import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; - -type SetupHandler = ( - indices: string[], - startTime: number | undefined, - endTime: number | undefined, - datasetFilter: DatasetFilter -) => void; - -interface AnalysisSetupStateArguments<JobType extends string> { - cleanUpAndSetUpModule: SetupHandler; - moduleDescriptor: ModuleDescriptor<JobType>; - setUpModule: SetupHandler; - sourceConfiguration: ModuleSourceConfiguration; -} - -const fourWeeksInMs = 86400000 * 7 * 4; - -export const useAnalysisSetupState = <JobType extends string>({ - cleanUpAndSetUpModule, - moduleDescriptor: { validateSetupDatasets, validateSetupIndices }, - setUpModule, - sourceConfiguration, -}: AnalysisSetupStateArguments<JobType>) => { - const [startTime, setStartTime] = useState<number | undefined>(Date.now() - fourWeeksInMs); - const [endTime, setEndTime] = useState<number | undefined>(undefined); - - const isTimeRangeValid = useMemo( - () => (startTime != null && endTime != null ? startTime < endTime : true), - [endTime, startTime] - ); - - const [validatedIndices, setValidatedIndices] = useState<AvailableIndex[]>( - sourceConfiguration.indices.map((indexName) => ({ - name: indexName, - validity: 'unknown' as const, - })) - ); - - const updateIndicesWithValidationErrors = useCallback( - (validationErrors: ValidationIndicesError[]) => - setValidatedIndices((availableIndices) => - availableIndices.map((previousAvailableIndex) => { - const indexValiationErrors = validationErrors.filter( - ({ index }) => index === previousAvailableIndex.name - ); - - if (indexValiationErrors.length > 0) { - return { - validity: 'invalid', - name: previousAvailableIndex.name, - errors: indexValiationErrors, - }; - } else if (previousAvailableIndex.validity === 'valid') { - return { - ...previousAvailableIndex, - validity: 'valid', - errors: [], - }; - } else { - return { - validity: 'valid', - name: previousAvailableIndex.name, - isSelected: !isExampleDataIndex(previousAvailableIndex.name), - availableDatasets: [], - datasetFilter: { - type: 'includeAll' as const, - }, - }; - } - }) - ), - [] - ); - - const updateIndicesWithAvailableDatasets = useCallback( - (availableDatasets: Array<{ indexName: string; datasets: string[] }>) => - setValidatedIndices((availableIndices) => - availableIndices.map((previousAvailableIndex) => { - if (previousAvailableIndex.validity !== 'valid') { - return previousAvailableIndex; - } - - const availableDatasetsForIndex = availableDatasets.filter( - ({ indexName }) => indexName === previousAvailableIndex.name - ); - const newAvailableDatasets = availableDatasetsForIndex.flatMap( - ({ datasets }) => datasets - ); - - // filter out datasets that have disappeared if this index' datasets were updated - const newDatasetFilter: DatasetFilter = - availableDatasetsForIndex.length > 0 - ? filterDatasetFilter(previousAvailableIndex.datasetFilter, (dataset) => - newAvailableDatasets.includes(dataset) - ) - : previousAvailableIndex.datasetFilter; - - return { - ...previousAvailableIndex, - availableDatasets: newAvailableDatasets, - datasetFilter: newDatasetFilter, - }; - }) - ), - [] - ); - - const validIndexNames = useMemo( - () => validatedIndices.filter((index) => index.validity === 'valid').map((index) => index.name), - [validatedIndices] - ); - - const selectedIndexNames = useMemo( - () => - validatedIndices - .filter((index) => index.validity === 'valid' && index.isSelected) - .map((i) => i.name), - [validatedIndices] - ); - - const datasetFilter = useMemo( - () => - validatedIndices - .flatMap((validatedIndex) => - validatedIndex.validity === 'valid' - ? validatedIndex.datasetFilter - : { type: 'includeAll' as const } - ) - .reduce(combineDatasetFilters, { type: 'includeAll' as const }), - [validatedIndices] - ); - - const [validateIndicesRequest, validateIndices] = useTrackedPromise( - { - cancelPreviousOn: 'resolution', - createPromise: async () => { - return await validateSetupIndices( - sourceConfiguration.indices, - sourceConfiguration.timestampField - ); - }, - onResolve: ({ data: { errors } }) => { - updateIndicesWithValidationErrors(errors); - }, - onReject: () => { - setValidatedIndices([]); - }, - }, - [sourceConfiguration.indices, sourceConfiguration.timestampField] - ); - - const [validateDatasetsRequest, validateDatasets] = useTrackedPromise( - { - cancelPreviousOn: 'resolution', - createPromise: async () => { - if (validIndexNames.length === 0) { - return { data: { datasets: [] } }; - } - - return await validateSetupDatasets( - validIndexNames, - sourceConfiguration.timestampField, - startTime ?? 0, - endTime ?? Date.now() - ); - }, - onResolve: ({ data: { datasets } }) => { - updateIndicesWithAvailableDatasets(datasets); - }, - }, - [validIndexNames, sourceConfiguration.timestampField, startTime, endTime] - ); - - const setUp = useCallback(() => { - return setUpModule(selectedIndexNames, startTime, endTime, datasetFilter); - }, [setUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); - - const cleanUpAndSetUp = useCallback(() => { - return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime, datasetFilter); - }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); - - const isValidating = useMemo( - () => validateIndicesRequest.state === 'pending' || validateDatasetsRequest.state === 'pending', - [validateDatasetsRequest.state, validateIndicesRequest.state] - ); - - const validationErrors = useMemo<ValidationUIError[]>(() => { - if (isValidating) { - return []; - } - - return [ - // validate request status - ...(validateIndicesRequest.state === 'rejected' || - validateDatasetsRequest.state === 'rejected' - ? [{ error: 'NETWORK_ERROR' as const }] - : []), - // validation request results - ...validatedIndices.reduce<ValidationUIError[]>((errors, index) => { - return index.validity === 'invalid' && selectedIndexNames.includes(index.name) - ? [...errors, ...index.errors] - : errors; - }, []), - // index count - ...(selectedIndexNames.length === 0 ? [{ error: 'TOO_FEW_SELECTED_INDICES' as const }] : []), - // time range - ...(!isTimeRangeValid ? [{ error: 'INVALID_TIME_RANGE' as const }] : []), - ]; - }, [ - isValidating, - validateIndicesRequest.state, - validateDatasetsRequest.state, - validatedIndices, - selectedIndexNames, - isTimeRangeValid, - ]); - - const prevStartTime = usePrevious(startTime); - const prevEndTime = usePrevious(endTime); - const prevValidIndexNames = usePrevious(validIndexNames); - - useEffect(() => { - if (!isTimeRangeValid) { - return; - } - - validateIndices(); - }, [isTimeRangeValid, validateIndices]); - - useEffect(() => { - if (!isTimeRangeValid) { - return; - } - - if ( - startTime !== prevStartTime || - endTime !== prevEndTime || - !isEqual(validIndexNames, prevValidIndexNames) - ) { - validateDatasets(); - } - }, [ - endTime, - isTimeRangeValid, - prevEndTime, - prevStartTime, - prevValidIndexNames, - startTime, - validIndexNames, - validateDatasets, - ]); - - return { - cleanUpAndSetUp, - datasetFilter, - endTime, - isValidating, - selectedIndexNames, - setEndTime, - setStartTime, - setUp, - startTime, - validatedIndices, - setValidatedIndices, - validationErrors, - }; -}; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts index cec87fb1144e33..711ee76d42a64f 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts @@ -10,17 +10,27 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; -import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; -import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; import { metricsHostsJobTypes, getJobId, MetricsHostsJobType, DatasetFilter, bucketSpan, - partitionField, } from '../../../../../common/infra_ml'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_out.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_out.json'; +type JobType = 'hosts_memory_usage' | 'hosts_network_in' | 'hosts_network_out'; const moduleId = 'metrics_ui_hosts'; const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { defaultMessage: 'Metrics anomanly detection', @@ -54,23 +64,68 @@ const setUpModule = async ( end: number | undefined, datasetFilter: DatasetFilter, { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - pField?: string + partitionField?: string ) => { const indexNamePattern = indices.join(','); - const jobIds = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; - const jobOverrides = jobIds.map((id) => ({ - job_id: id, - data_description: { - time_field: timestampField, - }, - custom_settings: { - metrics_source_config: { - indexPattern: indexNamePattern, - timestampField, - bucketSpan, + const jobIds: JobType[] = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; + + const jobOverrides = jobIds.map((id) => { + const { job: defaultJobConfig } = getDefaultJobConfigs(id); + + // eslint-disable-next-line @typescript-eslint/naming-convention + const analysis_config = { + ...defaultJobConfig.analysis_config, + }; + + if (partitionField) { + analysis_config.detectors[0].partition_field_name = partitionField; + if (analysis_config.influencers.indexOf(partitionField) === -1) { + analysis_config.influencers.push(partitionField); + } + } + + return { + job_id: id, + data_description: { + time_field: timestampField, + }, + analysis_config, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + }; + }); + + const datafeedOverrides = jobIds.map((id) => { + const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id); + + if (!partitionField || id === 'hosts_memory_usage') { + // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field + return defaultDatafeedConfig; + } + + // If we have a partition field, we need to change the aggregation to do a terms agg at the top level + const aggregations = { + [partitionField]: { + terms: { + field: partitionField, + }, + aggregations: { + ...defaultDatafeedConfig.aggregations, + }, }, - }, - })); + }; + + return { + ...defaultDatafeedConfig, + job_id: id, + aggregations, + }; + }); return callSetupMlModuleAPI( moduleId, @@ -80,34 +135,32 @@ const setUpModule = async ( sourceId, indexNamePattern, jobOverrides, - [] + datafeedOverrides ); }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); -}; - -const validateSetupIndices = async (indices: string[], timestampField: string) => { - return await callValidateIndicesAPI(indices, [ - { - name: timestampField, - validTypes: ['date'], - }, - { - name: partitionField, - validTypes: ['keyword'], - }, - ]); +const getDefaultJobConfigs = (jobId: JobType) => { + switch (jobId) { + case 'hosts_memory_usage': + return { + datafeed: MemoryDatafeed, + job: MemoryJob, + }; + case 'hosts_network_in': + return { + datafeed: NetworkInDatafeed, + job: NetworkInJob, + }; + case 'hosts_network_out': + return { + datafeed: NetworkOutDatafeed, + job: NetworkOutJob, + }; + } }; -const validateSetupDatasets = async ( - indices: string[], - timestampField: string, - startTime: number, - endTime: number -) => { - return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); }; export const metricHostsModule: ModuleDescriptor<MetricsHostsJobType> = { @@ -121,6 +174,4 @@ export const metricHostsModule: ModuleDescriptor<MetricsHostsJobType> = { getModuleDefinition, setUpModule, cleanUpModule, - validateSetupDatasets, - validateSetupIndices, }; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts index cbcff1c307af6e..41c6df92fb379c 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts @@ -10,17 +10,28 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; -import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; -import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; import { metricsK8SJobTypes, getJobId, MetricK8sJobType, DatasetFilter, bucketSpan, - partitionField, } from '../../../../../common/infra_ml'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_out.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_out.json'; +type JobType = 'k8s_memory_usage' | 'k8s_network_in' | 'k8s_network_out'; +export const DEFAULT_K8S_PARTITION_FIELD = 'kubernetes.namespace'; const moduleId = 'metrics_ui_k8s'; const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { defaultMessage: 'Metrics anomanly detection', @@ -54,26 +65,72 @@ const setUpModule = async ( end: number | undefined, datasetFilter: DatasetFilter, { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - pField?: string + partitionField?: string ) => { const indexNamePattern = indices.join(','); - const jobIds = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; - const jobOverrides = jobIds.map((id) => ({ - job_id: id, - analysis_config: { - bucket_span: `${bucketSpan}ms`, - }, - data_description: { - time_field: timestampField, - }, - custom_settings: { - metrics_source_config: { - indexPattern: indexNamePattern, - timestampField, - bucketSpan, + const jobIds: JobType[] = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; + const jobOverrides = jobIds.map((id) => { + const { job: defaultJobConfig } = getDefaultJobConfigs(id); + + // eslint-disable-next-line @typescript-eslint/naming-convention + const analysis_config = { + ...defaultJobConfig.analysis_config, + }; + + if (partitionField) { + analysis_config.detectors[0].partition_field_name = partitionField; + if (analysis_config.influencers.indexOf(partitionField) === -1) { + analysis_config.influencers.push(partitionField); + } + } + + return { + job_id: id, + data_description: { + time_field: timestampField, + }, + analysis_config, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + }; + }); + + const datafeedOverrides = jobIds.map((id) => { + const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id); + + if (!partitionField || id === 'k8s_memory_usage') { + // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field + return defaultDatafeedConfig; + } + + // Because the ML K8s jobs ship with a default partition field of {kubernetes.namespace}, ignore that agg and wrap it in our own agg. + const innerAggregation = + defaultDatafeedConfig.aggregations[DEFAULT_K8S_PARTITION_FIELD].aggregations; + + // If we have a partition field, we need to change the aggregation to do a terms agg to partition the data at the top level + const aggregations = { + [partitionField]: { + terms: { + field: partitionField, + size: 25, // 25 is arbitratry and only used to keep the number of buckets to a managable level in the event that the user choose a high cardinality partition field. + }, + aggregations: { + ...innerAggregation, + }, }, - }, - })); + }; + + return { + ...defaultDatafeedConfig, + job_id: id, + aggregations, + }; + }); return callSetupMlModuleAPI( moduleId, @@ -83,34 +140,32 @@ const setUpModule = async ( sourceId, indexNamePattern, jobOverrides, - [] + datafeedOverrides ); }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); -}; - -const validateSetupIndices = async (indices: string[], timestampField: string) => { - return await callValidateIndicesAPI(indices, [ - { - name: timestampField, - validTypes: ['date'], - }, - { - name: partitionField, - validTypes: ['keyword'], - }, - ]); +const getDefaultJobConfigs = (jobId: JobType) => { + switch (jobId) { + case 'k8s_memory_usage': + return { + datafeed: MemoryDatafeed, + job: MemoryJob, + }; + case 'k8s_network_in': + return { + datafeed: NetworkInDatafeed, + job: NetworkInJob, + }; + case 'k8s_network_out': + return { + datafeed: NetworkOutDatafeed, + job: NetworkOutJob, + }; + } }; -const validateSetupDatasets = async ( - indices: string[], - timestampField: string, - startTime: number, - endTime: number -) => { - return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); }; export const metricHostsModule: ModuleDescriptor<MetricK8sJobType> = { @@ -124,6 +179,4 @@ export const metricHostsModule: ModuleDescriptor<MetricK8sJobType> = { getModuleDefinition, setUpModule, cleanUpModule, - validateSetupDatasets, - validateSetupIndices, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx index b063713fa2c971..b5d224910e819d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx @@ -50,10 +50,10 @@ export const AnomalyDetectionFlyout = () => { return ( <> - <EuiButtonEmpty iconSide={'right'} onClick={openFlyout}> + <EuiButtonEmpty iconSide={'left'} iconType={'inspect'} onClick={openFlyout}> <FormattedMessage id="xpack.infra.ml.anomalyDetectionButton" - defaultMessage="Anomaly Detection" + defaultMessage="Anomaly detection" /> </EuiButtonEmpty> {showFlyout && ( diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx index 801dff9c4a17a5..5b520084ebb744 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx @@ -5,7 +5,7 @@ */ import React, { useState, useCallback, useEffect } from 'react'; -import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiText, EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -30,7 +30,7 @@ interface Props { } export const FlyoutHome = (props: Props) => { - const [tab, setTab] = useState<'jobs' | 'anomalies'>('jobs'); + const [tab] = useState<'jobs' | 'anomalies'>('jobs'); const { goToSetup } = props; const { fetchJobStatus: fetchHostJobStatus, @@ -56,18 +56,10 @@ export const FlyoutHome = (props: Props) => { goToSetup('kubernetes'); }, [goToSetup]); - const goToJobs = useCallback(() => { - setTab('jobs'); - }, []); - const jobIds = [ ...(k8sJobSummaries || []).map((k) => k.id), ...(hostJobSummaries || []).map((h) => h.id), ]; - const anomaliesUrl = useLinkProps({ - app: 'ml', - pathname: `/explorer?_g=${createResultsUrl(jobIds)}`, - }); useEffect(() => { if (hasInfraMLReadCapabilities) { @@ -105,30 +97,24 @@ export const FlyoutHome = (props: Props) => { </EuiFlyoutHeader> <EuiFlyoutBody> - <EuiTabs> - <EuiTab isSelected={tab === 'jobs'} onClick={goToJobs}> - <FormattedMessage - defaultMessage="Jobs" - id="xpack.infra.ml.anomalyFlyout.jobsTabLabel" - /> - </EuiTab> - <EuiTab - disabled={jobIds.length === 0} - isSelected={tab === 'anomalies'} - {...anomaliesUrl} - > - <FormattedMessage - defaultMessage="Anomalies" - id="xpack.infra.ml.anomalyFlyout.anomaliesTabLabel" - /> - </EuiTab> - </EuiTabs> + <div> + <EuiText> + <p> + <FormattedMessage + defaultMessage="Anomaly detection is powered by machine learning. Machine learning jobs are available for the following resource types. Enable these jobs to begin detecting anomalies in your infrastructure metrics." + id="xpack.infra.ml.anomalyFlyout.create.description" + /> + </p> + </EuiText> + </div> + <EuiSpacer size="l" /> {hostJobSummaries.length > 0 && ( <> <JobsEnabledCallout hasHostJobs={hostJobSummaries.length > 0} hasK8sJobs={k8sJobSummaries.length > 0} + jobIds={jobIds} /> <EuiSpacer size="l" /> </> @@ -151,6 +137,7 @@ export const FlyoutHome = (props: Props) => { interface CalloutProps { hasHostJobs: boolean; hasK8sJobs: boolean; + jobIds: string[]; } const JobsEnabledCallout = (props: CalloutProps) => { let target = ''; @@ -175,8 +162,34 @@ const JobsEnabledCallout = (props: CalloutProps) => { pathname: '/jobs', }); + const anomaliesUrl = useLinkProps({ + app: 'ml', + pathname: `/explorer?_g=${createResultsUrl(props.jobIds)}`, + }); + return ( <> + <EuiFlexGroup gutterSize={'s'}> + <EuiFlexItem grow={false}> + <EuiButton {...manageJobsLinkProps} style={{ marginRight: 5 }}> + <FormattedMessage + defaultMessage="Manage jobs" + id="xpack.infra.ml.anomalyFlyout.manageJobs" + /> + </EuiButton> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiButton {...anomaliesUrl}> + <FormattedMessage + defaultMessage="View anomalies" + id="xpack.infra.ml.anomalyFlyout.anomaliesTabLabel" + /> + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + + <EuiSpacer size="l" /> + <EuiCallOut size="m" color="success" @@ -189,13 +202,6 @@ const JobsEnabledCallout = (props: CalloutProps) => { } iconType="check" /> - <EuiSpacer size="l" /> - <EuiButton {...manageJobsLinkProps}> - <FormattedMessage - defaultMessage="Manage Jobs" - id="xpack.infra.ml.anomalyFlyout.manageJobs" - /> - </EuiButton> </> ); }; @@ -211,30 +217,11 @@ interface CreateJobTab { const CreateJobTab = (props: CreateJobTab) => { return ( <> - <div> - <EuiText> - <h3> - <FormattedMessage - defaultMessage="Create ML Jobs" - id="xpack.infra.ml.anomalyFlyout.create.jobsTitle" - /> - </h3> - </EuiText> - <EuiText> - <p> - <FormattedMessage - defaultMessage="Machine Learning jobs are available for the following resource types. Enable these jobs to begin detecting anomalies in your infrastructure metrics" - id="xpack.infra.ml.anomalyFlyout.create.description" - /> - </p> - </EuiText> - </div> - - <EuiSpacer size="l" /> + {/* <EuiSpacer size="l" /> */} <EuiFlexGroup gutterSize={'m'}> <EuiFlexItem> <EuiCard - // isDisabled={props.hasSetupCapabilities} + isDisabled={!props.hasSetupCapabilities} icon={<EuiIcon type={'storage'} />} // title="Hosts" title={ @@ -245,7 +232,7 @@ const CreateJobTab = (props: CreateJobTab) => { } description={ <FormattedMessage - defaultMessage="Detect anomalies for CPU usage, memory usage, network traffic, and load." + defaultMessage="Detect anomalies for memory usage and network traffic." id="xpack.infra.ml.anomalyFlyout.create.hostDescription" /> } @@ -254,7 +241,7 @@ const CreateJobTab = (props: CreateJobTab) => { {props.hasHostJobs && ( <EuiButtonEmpty onClick={props.createHosts}> <FormattedMessage - defaultMessage="Recreate Jobs" + defaultMessage="Recreate jobs" id="xpack.infra.ml.anomalyFlyout.create.recreateButton" /> </EuiButtonEmpty> @@ -262,7 +249,7 @@ const CreateJobTab = (props: CreateJobTab) => { {!props.hasHostJobs && ( <EuiButton onClick={props.createHosts}> <FormattedMessage - defaultMessage="Create Jobs" + defaultMessage="Enable" id="xpack.infra.ml.anomalyFlyout.create.createButton" /> </EuiButton> @@ -273,7 +260,7 @@ const CreateJobTab = (props: CreateJobTab) => { </EuiFlexItem> <EuiFlexItem> <EuiCard - // isDisabled={props.hasSetupCapabilities} + isDisabled={!props.hasSetupCapabilities} icon={<EuiIcon type={'logoKubernetes'} />} title={ <FormattedMessage @@ -283,7 +270,7 @@ const CreateJobTab = (props: CreateJobTab) => { } description={ <FormattedMessage - defaultMessage="Detect anomalies for CPU usage, memory usage, network traffic, and load." + defaultMessage="Detect anomalies for memory usage and network traffic." id="xpack.infra.ml.anomalyFlyout.create.k8sDescription" /> } @@ -292,7 +279,7 @@ const CreateJobTab = (props: CreateJobTab) => { {props.hasK8sJobs && ( <EuiButtonEmpty onClick={props.createK8s}> <FormattedMessage - defaultMessage="Recreate Jobs" + defaultMessage="Recreate jobs" id="xpack.infra.ml.anomalyFlyout.create.recreateButton" /> </EuiButtonEmpty> @@ -300,7 +287,7 @@ const CreateJobTab = (props: CreateJobTab) => { {!props.hasK8sJobs && ( <EuiButton onClick={props.createK8s}> <FormattedMessage - defaultMessage="Create Jobs" + defaultMessage="Enable" id="xpack.infra.ml.anomalyFlyout.create.createButton" /> </EuiButton> diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx index 428c002da63838..c327d187f6bc20 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx @@ -20,6 +20,7 @@ import { useSourceViaHttp } from '../../../../../../containers/source/use_source import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module'; import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module'; import { FixedDatePicker } from '../../../../../../components/fixed_datepicker'; +import { DEFAULT_K8S_PARTITION_FIELD } from '../../../../../../containers/ml/modules/metrics_k8s/module_descriptor'; interface Props { jobType: 'hosts' | 'kubernetes'; @@ -107,7 +108,7 @@ export const JobSetupScreen = (props: Props) => { useEffect(() => { if (props.jobType === 'kubernetes') { - setPartitionField(['kubernetes.namespace']); + setPartitionField([DEFAULT_K8S_PARTITION_FIELD]); } }, [props.jobType]); From 6caf6d5080394418fba519166dde58addb5a0bf3 Mon Sep 17 00:00:00 2001 From: Dima Arnautov <dmitrii.arnautov@elastic.co> Date: Thu, 1 Oct 2020 17:54:56 +0200 Subject: [PATCH 037/128] [ML] Model management UI fixes and enhancements (#79072) * [ML] link to edit pipeline * [ML] view training data link * [ML] format stats and configs * [ML] refactor date_utils * [ML] fix types * [ML] change "View" icon and label * [ML] revert label change --- .../util/date_utils.test.ts | 0 .../application => common}/util/date_utils.ts | 11 ++- .../annotation_description_list/index.tsx | 12 +-- .../annotations_table/annotations_table.js | 14 +-- .../anomalies_table_columns.js | 2 +- .../anomalies_table/anomaly_details.js | 2 +- .../components/anomalies_table/links_menu.js | 2 +- .../components/data_grid/common.ts | 2 +- .../components/job_messages/job_messages.tsx | 7 +- .../model_snapshots/model_snapshots_table.tsx | 11 +-- .../revert_model_snapshot_flyout.tsx | 5 +- .../analytics_list/expanded_row.tsx | 2 +- .../models_management/expanded_row.tsx | 91 +++++++++++++++++-- .../models_management/models_list.tsx | 10 +- .../explorer_chart_distribution.js | 2 +- .../explorer_chart_single_metric.js | 2 +- .../explorer/explorer_swimlane.tsx | 2 +- .../reducers/explorer_reducer/reducer.ts | 2 +- .../forecasts_table/forecasts_table.js | 14 +-- .../components/job_details/format_values.js | 5 +- .../charts/event_rate_chart/overlay_range.tsx | 8 +- .../components/analytics_panel/table.tsx | 2 +- .../anomaly_detection_panel/table.tsx | 2 +- .../application/services/job_service.js | 2 +- .../forecasting_modal/forecasts_list.js | 2 +- .../timeseries_chart/timeseries_chart.js | 2 +- x-pack/plugins/ml/public/shared.ts | 2 +- 27 files changed, 138 insertions(+), 80 deletions(-) rename x-pack/plugins/ml/{public/application => common}/util/date_utils.test.ts (100%) rename x-pack/plugins/ml/{public/application => common}/util/date_utils.ts (78%) diff --git a/x-pack/plugins/ml/public/application/util/date_utils.test.ts b/x-pack/plugins/ml/common/util/date_utils.test.ts similarity index 100% rename from x-pack/plugins/ml/public/application/util/date_utils.test.ts rename to x-pack/plugins/ml/common/util/date_utils.test.ts diff --git a/x-pack/plugins/ml/public/application/util/date_utils.ts b/x-pack/plugins/ml/common/util/date_utils.ts similarity index 78% rename from x-pack/plugins/ml/public/application/util/date_utils.ts rename to x-pack/plugins/ml/common/util/date_utils.ts index 21adc0b4b9c66a..73ac68b2493f3c 100644 --- a/x-pack/plugins/ml/public/application/util/date_utils.ts +++ b/x-pack/plugins/ml/common/util/date_utils.ts @@ -6,10 +6,11 @@ // utility functions for handling dates -// @ts-ignore -import { formatDate } from '@elastic/eui/lib/services/format'; import dateMath from '@elastic/datemath'; -import { TimeRange } from '../../../../../../src/plugins/data/common'; +import { formatDate } from '@elastic/eui'; +import { TimeRange } from '../../../../../src/plugins/data/common'; +import { TIME_FORMAT } from '../constants/time_format'; + export function formatHumanReadableDate(ts: number) { return formatDate(ts, 'MMMM Do YYYY'); } @@ -28,3 +29,7 @@ export function validateTimeRange(time?: TimeRange): boolean { const momentDateTo = dateMath.parse(time.to); return !!(momentDateFrom && momentDateFrom.isValid() && momentDateTo && momentDateTo.isValid()); } + +export const timeFormatter = (value: number) => { + return formatDate(value, TIME_FORMAT); +}; diff --git a/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.tsx b/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.tsx index eee2f8dca244d7..156ad72ba9f9f2 100644 --- a/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.tsx +++ b/x-pack/plugins/ml/public/application/components/annotations/annotation_description_list/index.tsx @@ -15,7 +15,7 @@ import { EuiDescriptionList } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Annotation } from '../../../../../common/types/annotations'; -import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../../common/util/date_utils'; interface Props { annotation: Annotation; @@ -61,7 +61,7 @@ export const AnnotationDescriptionList = ({ annotation, detectorDescription }: P defaultMessage: 'Created by', } ), - description: annotation.create_username, + description: annotation.create_username ?? '', }); listItems.push({ title: i18n.translate( @@ -79,7 +79,7 @@ export const AnnotationDescriptionList = ({ annotation, detectorDescription }: P defaultMessage: 'Modified by', } ), - description: annotation.modified_username, + description: annotation.modified_username ?? '', }); } if (detectorDescription !== undefined) { @@ -94,19 +94,19 @@ export const AnnotationDescriptionList = ({ annotation, detectorDescription }: P if (annotation.partition_field_name !== undefined) { listItems.push({ title: annotation.partition_field_name, - description: annotation.partition_field_value, + description: annotation.partition_field_value ?? '', }); } if (annotation.over_field_name !== undefined) { listItems.push({ title: annotation.over_field_name, - description: annotation.over_field_value, + description: annotation.over_field_value ?? '', }); } if (annotation.by_field_name !== undefined) { listItems.push({ title: annotation.by_field_name, - description: annotation.by_field_value, + description: annotation.by_field_value ?? '', }); } diff --git a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js index 0527b8f6d9f609..7eb280c6247c2e 100644 --- a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js +++ b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js @@ -31,8 +31,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { RIGHT_ALIGNMENT } from '@elastic/eui/lib/services'; -import { formatDate } from '@elastic/eui/lib/services/format'; - import { addItemToRecentlyAccessed } from '../../../util/recently_accessed'; import { ml } from '../../../services/ml_api_service'; import { mlJobService } from '../../../services/job_service'; @@ -42,7 +40,6 @@ import { getLatestDataOrBucketTimestamp, isTimeSeriesViewJob, } from '../../../../../common/util/job_utils'; -import { TIME_FORMAT } from '../../../../../common/constants/time_format'; import { annotation$, @@ -56,6 +53,7 @@ import { import { withKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { ML_APP_URL_GENERATOR, ML_PAGES } from '../../../../../common/constants/ml_url_generator'; import { PLUGIN_ID } from '../../../../../common/constants/app'; +import { timeFormatter } from '../../../../../common/util/date_utils'; const CURRENT_SERIES = 'current_series'; /** @@ -377,10 +375,6 @@ class AnnotationsTableUI extends Component { ); } - function renderDate(date) { - return formatDate(date, TIME_FORMAT); - } - const columns = [ { field: 'annotation', @@ -397,7 +391,7 @@ class AnnotationsTableUI extends Component { defaultMessage: 'From', }), dataType: 'date', - render: renderDate, + render: timeFormatter, sortable: true, }, { @@ -406,7 +400,7 @@ class AnnotationsTableUI extends Component { defaultMessage: 'To', }), dataType: 'date', - render: renderDate, + render: timeFormatter, sortable: true, }, { @@ -415,7 +409,7 @@ class AnnotationsTableUI extends Component { defaultMessage: 'Last modified date', }), dataType: 'date', - render: renderDate, + render: timeFormatter, sortable: true, }, { diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js index 1f8c8633afa476..d2c4122bc1b57e 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomalies_table_columns.js @@ -16,7 +16,7 @@ import { formatHumanReadableDate, formatHumanReadableDateTime, formatHumanReadableDateTimeSeconds, -} from '../../util/date_utils'; +} from '../../../../common/util/date_utils'; import { DescriptionCell } from './description_cell'; import { DetectorCell } from './detector_cell'; diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js index cd3875f8cbd2a0..a2a3aea5988aa3 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/anomaly_details.js @@ -26,7 +26,7 @@ import { EuiTabbedContent, EuiText, } from '@elastic/eui'; -import { formatHumanReadableDateTimeSeconds } from '../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../common/util/date_utils'; import { EntityCell } from '../entity_cell'; import { diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js index d898734f34c931..079d56da60e5e2 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.js @@ -26,7 +26,7 @@ import { getFieldTypeFromMapping } from '../../services/mapping_service'; import { ml } from '../../services/ml_api_service'; import { mlJobService } from '../../services/job_service'; import { getUrlForRecord, openCustomUrlWindow } from '../../util/custom_url_utils'; -import { formatHumanReadableDateTimeSeconds } from '../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../common/util/date_utils'; import { getIndexPatternIdFromName } from '../../util/index_utils'; import { replaceStringTokens } from '../../util/string_utils'; import { ML_APP_URL_GENERATOR, ML_PAGES } from '../../../../common/constants/ml_url_generator'; diff --git a/x-pack/plugins/ml/public/application/components/data_grid/common.ts b/x-pack/plugins/ml/public/application/components/data_grid/common.ts index f252729cc20cd5..36b0573d609d87 100644 --- a/x-pack/plugins/ml/public/application/components/data_grid/common.ts +++ b/x-pack/plugins/ml/public/application/components/data_grid/common.ts @@ -37,7 +37,7 @@ import { OUTLIER_SCORE, TOP_CLASSES, } from '../../data_frame_analytics/common/constants'; -import { formatHumanReadableDateTimeSeconds } from '../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../common/util/date_utils'; import { getNestedProperty } from '../../util/object_utils'; import { mlFieldFormatService } from '../../services/field_format_service'; diff --git a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx index 798ceae0f0732f..f60cd61b25cd4b 100644 --- a/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx +++ b/x-pack/plugins/ml/public/application/components/job_messages/job_messages.tsx @@ -7,14 +7,13 @@ import React, { FC } from 'react'; import { EuiSpacer, EuiInMemoryTable, EuiButtonIcon, EuiToolTip } from '@elastic/eui'; -// @ts-ignore -import { formatDate } from '@elastic/eui/lib/services/format'; + import { i18n } from '@kbn/i18n'; import theme from '@elastic/eui/dist/eui_theme_light.json'; import { JobMessage } from '../../../../common/types/audit_message'; -import { TIME_FORMAT } from '../../../../common/constants/time_format'; import { JobIcon } from '../job_message_icon'; +import { timeFormatter } from '../../../../common/util/date_utils'; interface JobMessagesProps { messages: JobMessage[]; @@ -55,7 +54,7 @@ export const JobMessages: FC<JobMessagesProps> = ({ messages, loading, error, re name: i18n.translate('xpack.ml.jobMessages.timeLabel', { defaultMessage: 'Time', }), - render: (timestamp: number) => formatDate(timestamp, TIME_FORMAT), + render: timeFormatter, width: '120px', sortable: true, }, diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx index 64fdd97903b60f..5b175eb06a4a39 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/model_snapshots_table.tsx @@ -13,7 +13,6 @@ import { EuiInMemoryTable, EuiLoadingSpinner, EuiBasicTableColumn, - formatDate, } from '@elastic/eui'; import { checkPermission } from '../../capabilities/check_capabilities'; @@ -21,12 +20,12 @@ import { EditModelSnapshotFlyout } from './edit_model_snapshot_flyout'; import { RevertModelSnapshotFlyout } from './revert_model_snapshot_flyout'; import { ml } from '../../services/ml_api_service'; import { JOB_STATE, DATAFEED_STATE } from '../../../../common/constants/states'; -import { TIME_FORMAT } from '../../../../common/constants/time_format'; import { CloseJobConfirm } from './close_job_confirm'; import { ModelSnapshot, CombinedJobWithStats, } from '../../../../common/types/anomaly_detection_jobs'; +import { timeFormatter } from '../../../../common/util/date_utils'; interface Props { job: CombinedJobWithStats; @@ -138,7 +137,7 @@ export const ModelSnapshotTable: FC<Props> = ({ job, refreshJobList }) => { defaultMessage: 'Date created', }), dataType: 'date', - render: renderDate, + render: timeFormatter, sortable: true, }, { @@ -147,7 +146,7 @@ export const ModelSnapshotTable: FC<Props> = ({ job, refreshJobList }) => { defaultMessage: 'Latest timestamp', }), dataType: 'date', - render: renderDate, + render: timeFormatter, sortable: true, }, { @@ -246,10 +245,6 @@ export const ModelSnapshotTable: FC<Props> = ({ job, refreshJobList }) => { ); }; -function renderDate(date: number) { - return formatDate(date, TIME_FORMAT); -} - async function getCombinedJobState(jobId: string) { const jobs = await ml.jobs.jobs([jobId]); diff --git a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx index e37efe60f80184..62f5623f679646 100644 --- a/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/model_snapshots/revert_model_snapshot_flyout/revert_model_snapshot_flyout.tsx @@ -32,7 +32,6 @@ import { EuiHorizontalRule, EuiSuperSelect, EuiText, - formatDate, } from '@elastic/eui'; import { @@ -47,8 +46,8 @@ import { LineChartPoint } from '../../../jobs/new_job/common/chart_loader'; import { EventRateChart } from '../../../jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart'; import { Anomaly } from '../../../jobs/new_job/common/results_loader/results_loader'; import { parseInterval } from '../../../../../common/util/parse_interval'; -import { TIME_FORMAT } from '../../../../../common/constants/time_format'; import { CreateCalendar, CalendarEvent } from './create_calendar'; +import { timeFormatter } from '../../../../../common/util/date_utils'; interface Props { snapshot: ModelSnapshot; @@ -255,7 +254,7 @@ export const RevertModelSnapshotFlyout: FC<Props> = ({ snapshot, snapshots, job, <FormattedMessage id="xpack.ml.newJob.wizard.revertModelSnapshotFlyout.warningCallout.contents" defaultMessage="All anomaly detection results after {date} will be deleted." - values={{ date: formatDate(currentSnapshot.latest_record_time_stamp, TIME_FORMAT) }} + values={{ date: timeFormatter(currentSnapshot.latest_record_time_stamp) }} /> </EuiCallOut> diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx index 95204f9ba09fcd..a0bd437a667a21 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row.tsx @@ -11,7 +11,7 @@ import { EuiIcon, EuiLoadingSpinner, EuiTabbedContent } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { formatHumanReadableDateTimeSeconds } from '../../../../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../../../../common/util/date_utils'; import { DataFrameAnalyticsListRow } from './common'; import { ExpandedRowDetailsPane, SectionConfig, SectionItem } from './expanded_row_details_pane'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx index 7b9329fee783b5..803a2523a55e09 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/expanded_row.tsx @@ -20,16 +20,35 @@ import { EuiHorizontalRule, EuiFlexGroup, EuiTextColor, + EuiButtonEmpty, + EuiBadge, } from '@elastic/eui'; -// @ts-ignore -import { formatDate } from '@elastic/eui/lib/services/format'; +import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list'; import { ModelItemFull } from './models_list'; -import { TIME_FORMAT } from '../../../../../../../common/constants/time_format'; +import { useMlKibana } from '../../../../../contexts/kibana'; +import { timeFormatter } from '../../../../../../../common/util/date_utils'; interface ExpandedRowProps { item: ModelItemFull; } +const formatterDictionary: Record<string, (value: any) => JSX.Element | string | undefined> = { + tags: (tags: string[]) => { + if (tags.length === 0) return; + return ( + <div> + {tags.map((tag) => ( + <EuiBadge key={tag} color="hollow"> + {tag} + </EuiBadge> + ))} + </div> + ); + }, + create_time: timeFormatter, + timestamp: timeFormatter, +}; + export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => { const { inference_config: inferenceConfig, @@ -57,19 +76,45 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => { license_level, }; - function formatToListItems(items: Record<string, any>) { + function formatToListItems(items: Record<string, any>): EuiDescriptionListProps['listItems'] { return Object.entries(items) .map(([title, value]) => { - if (title.includes('timestamp')) { - value = formatDate(value, TIME_FORMAT); + if (title in formatterDictionary) { + return { + title, + description: formatterDictionary[title](value), + }; } - return { title, description: typeof value === 'object' ? JSON.stringify(value) : value }; + return { + title, + description: + typeof value === 'object' ? ( + <EuiCodeBlock + language="json" + fontSize="s" + paddingSize="s" + overflowHeight={300} + isCopyable={false} + > + {JSON.stringify(value, null, 2)} + </EuiCodeBlock> + ) : ( + value + ), + }; }) .filter(({ description }) => { return description !== undefined; }); } + const { + services: { + share, + application: { navigateToUrl }, + }, + } = useMlKibana(); + const tabs = [ { id: 'details', @@ -323,9 +368,35 @@ export const ExpandedRow: FC<ExpandedRowProps> = ({ item }) => { return ( <EuiFlexItem key={pipelineName}> <EuiPanel> - <EuiTitle size={'xs'}> - <h5>{pipelineName}</h5> - </EuiTitle> + <EuiFlexGroup alignItems="center" justifyContent="spaceBetween"> + <EuiFlexItem grow={false}> + <EuiTitle size={'xs'}> + <h5>{pipelineName}</h5> + </EuiTitle> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiButtonEmpty + onClick={async () => { + const ingestPipelinesAppUrlGenerator = share.urlGenerators.getUrlGenerator( + 'INGEST_PIPELINES_APP_URL_GENERATOR' + ); + await navigateToUrl( + await ingestPipelinesAppUrlGenerator.createUrl({ + page: 'pipeline_edit', + pipelineId: pipelineName, + absolute: true, + }) + ); + }} + > + <FormattedMessage + id="xpack.ml.inference.modelsList.expandedRow.editPipelineLabel" + defaultMessage="Edit" + /> + </EuiButtonEmpty> + </EuiFlexItem> + </EuiFlexGroup> + {description && <EuiText>{description}</EuiText>} <EuiSpacer size={'m'} /> <EuiTitle size={'xxs'}> diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx index dbc7a23f2258ba..d5a7ca6e96c064 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/models_management/models_list.tsx @@ -19,15 +19,13 @@ import { EuiBadge, SearchFilterConfig, } from '@elastic/eui'; -// @ts-ignore -import { formatDate } from '@elastic/eui/lib/services/format'; + import { EuiBasicTableColumn } from '@elastic/eui/src/components/basic_table/basic_table'; import { EuiTableSelectionType } from '@elastic/eui/src/components/basic_table/table_types'; import { Action } from '@elastic/eui/src/components/basic_table/action_types'; import { StatsBar, ModelsBarStats } from '../../../../../components/stats_bar'; import { useInferenceApiService } from '../../../../../services/ml_api_service/inference'; import { ModelsTableToConfigMapping } from './index'; -import { TIME_FORMAT } from '../../../../../../../common/constants/time_format'; import { DeleteModelsModal } from './delete_models_modal'; import { useMlKibana, useMlUrlGenerator, useNotifications } from '../../../../../contexts/kibana'; import { ExpandedRow } from './expanded_row'; @@ -46,6 +44,7 @@ import { useTableSettings } from '../analytics_list/use_table_settings'; import { filterAnalyticsModels, AnalyticsSearchBar } from '../analytics_search_bar'; import { ML_PAGES } from '../../../../../../../common/constants/ml_url_generator'; import { DataFrameAnalysisConfigType } from '../../../../../../../common/types/data_frame_analytics'; +import { timeFormatter } from '../../../../../../../common/util/date_utils'; type Stats = Omit<TrainedModelStat, 'model_id'>; @@ -277,7 +276,7 @@ export const ModelsList: FC = () => { description: i18n.translate('xpack.ml.inference.modelsList.viewTrainingDataActionLabel', { defaultMessage: 'View training data', }), - icon: 'list', + icon: 'visTable', type: 'icon', available: (item) => item.metadata?.analytics_config?.id, onClick: async (item) => { @@ -290,6 +289,7 @@ export const ModelsList: FC = () => { analysisType: getAnalysisType( item.metadata?.analytics_config.analysis ) as DataFrameAnalysisConfigType, + defaultIsTraining: true, }, }); @@ -375,7 +375,7 @@ export const ModelsList: FC = () => { defaultMessage: 'Created at', }), dataType: 'date', - render: (date: string) => formatDate(date, TIME_FORMAT), + render: timeFormatter, sortable: true, }, { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 00aca5d43be85f..994975912cd6ff 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -17,7 +17,7 @@ import d3 from 'd3'; import $ from 'jquery'; import moment from 'moment'; -import { formatHumanReadableDateTime } from '../../util/date_utils'; +import { formatHumanReadableDateTime } from '../../../../common/util/date_utils'; import { formatValue } from '../../formatters/format_value'; import { getSeverityColor, getSeverityWithLow } from '../../../../common/util/anomaly_utils'; import { diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js index 0a76211f2e330b..606d1c06904225 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js @@ -17,7 +17,7 @@ import $ from 'jquery'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; -import { formatHumanReadableDateTime } from '../../util/date_utils'; +import { formatHumanReadableDateTime } from '../../../../common/util/date_utils'; import { formatValue } from '../../formatters/format_value'; import { getSeverityColor, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx b/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx index 359dc11ca08d16..569709d648b3ca 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx +++ b/x-pack/plugins/ml/public/application/explorer/explorer_swimlane.tsx @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { Subject, Subscription } from 'rxjs'; import { TooltipValue } from '@elastic/charts'; import { htmlIdGenerator } from '@elastic/eui'; -import { formatHumanReadableDateTime } from '../util/date_utils'; +import { formatHumanReadableDateTime } from '../../../common/util/date_utils'; import { numTicksForDateFormat } from '../util/chart_utils'; import { getSeverityColor } from '../../../common/util/anomaly_utils'; import { mlEscape } from '../util/string_utils'; diff --git a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts index a38044a8b34254..c5fb0175c54e90 100644 --- a/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts +++ b/x-pack/plugins/ml/public/application/explorer/reducers/explorer_reducer/reducer.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { formatHumanReadableDateTime } from '../../../util/date_utils'; +import { formatHumanReadableDateTime } from '../../../../../common/util/date_utils'; import { getDefaultChartsData } from '../../explorer_charts/explorer_charts_container_service'; import { EXPLORER_ACTION, VIEW_BY_JOB_LABEL } from '../../explorer_constants'; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js index b32070fff73aad..44ebde634714ca 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/forecasts_table/forecasts_table.js @@ -16,10 +16,9 @@ import { EuiLink, EuiLoadingSpinner, } from '@elastic/eui'; -import { formatDate, formatNumber } from '@elastic/eui/lib/services/format'; +import { formatNumber } from '@elastic/eui/lib/services/format'; import { FORECAST_REQUEST_STATE } from '../../../../../../../common/constants/states'; -import { TIME_FORMAT } from '../../../../../../../common/constants/time_format'; import { addItemToRecentlyAccessed } from '../../../../../util/recently_accessed'; import { mlForecastService } from '../../../../../services/forecast_service'; import { i18n } from '@kbn/i18n'; @@ -34,6 +33,7 @@ import { ML_PAGES, } from '../../../../../../../common/constants/ml_url_generator'; import { PLUGIN_ID } from '../../../../../../../common/constants/app'; +import { timeFormatter } from '../../../../../../../common/util/date_utils'; const MAX_FORECASTS = 500; @@ -218,7 +218,7 @@ export class ForecastsTableUI extends Component { defaultMessage: 'Created', }), dataType: 'date', - render: (date) => formatDate(date, TIME_FORMAT), + render: timeFormatter, textOnly: true, sortable: true, scope: 'row', @@ -229,7 +229,7 @@ export class ForecastsTableUI extends Component { defaultMessage: 'From', }), dataType: 'date', - render: (date) => formatDate(date, TIME_FORMAT), + render: timeFormatter, textOnly: true, sortable: true, }, @@ -239,7 +239,7 @@ export class ForecastsTableUI extends Component { defaultMessage: 'To', }), dataType: 'date', - render: (date) => formatDate(date, TIME_FORMAT), + render: timeFormatter, textOnly: true, sortable: true, }, @@ -277,7 +277,7 @@ export class ForecastsTableUI extends Component { name: i18n.translate('xpack.ml.jobsList.jobDetails.forecastsTable.expiresLabel', { defaultMessage: 'Expires', }), - render: (date) => formatDate(date, TIME_FORMAT), + render: timeFormatter, textOnly: true, sortable: true, }, @@ -309,7 +309,7 @@ export class ForecastsTableUI extends Component { { defaultMessage: 'View forecast created at {createdDate}', values: { - createdDate: formatDate(forecast.forecast_create_timestamp, TIME_FORMAT), + createdDate: timeFormatter(forecast.forecast_create_timestamp), }, } ); diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js index 3fe4f0e5477a20..eff407a41fb0dd 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_details/format_values.js @@ -5,10 +5,9 @@ */ import numeral from '@elastic/numeral'; -import { formatDate } from '@elastic/eui/lib/services/format'; import { roundToDecimalPlace } from '../../../../formatters/round_to_decimal_place'; import { toLocaleString } from '../../../../util/string_utils'; -import { TIME_FORMAT } from '../../../../../../common/constants/time_format'; +import { timeFormatter } from '../../../../../../common/util/date_utils'; const DATA_FORMAT = '0.0 b'; @@ -29,7 +28,7 @@ export function formatValues([key, value]) { case 'latest_empty_bucket_timestamp': case 'latest_sparse_bucket_timestamp': case 'latest_bucket_timestamp': - value = formatDate(value, TIME_FORMAT); + value = timeFormatter(value); break; // data diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/overlay_range.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/overlay_range.tsx index a35c2d9c833a86..8ad359251b0298 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/overlay_range.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/overlay_range.tsx @@ -5,12 +5,10 @@ */ import React, { FC } from 'react'; -// @ts-ignore -import { formatDate } from '@elastic/eui/lib/services/format'; import { EuiIcon } from '@elastic/eui'; import { RectAnnotation, LineAnnotation, AnnotationDomainTypes } from '@elastic/charts'; import { LineChartPoint } from '../../../../common/chart_loader'; -import { TIME_FORMAT } from '../../../../../../../../common/constants/time_format'; +import { timeFormatter } from '../../../../../../../../common/util/date_utils'; interface Props { overlayKey: number; @@ -70,9 +68,7 @@ export const OverlayRange: FC<Props> = ({ <div style={{ textAlign: 'center' }}> <EuiIcon type="arrowUp" /> </div> - <div style={{ fontWeight: 'normal', color: '#343741' }}> - {formatDate(start, TIME_FORMAT)} - </div> + <div style={{ fontWeight: 'normal', color: '#343741' }}>{timeFormatter(start)}</div> </div> </> ) : undefined diff --git a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx index fc0645a0e9498d..4b469a0f5874d2 100644 --- a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/table.tsx @@ -23,7 +23,7 @@ import { getTaskStateBadge, progressColumn, } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/use_columns'; -import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../../common/util/date_utils'; import { ViewLink } from './actions'; diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx index 8515431d49b17c..b95aed01ebae81 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx @@ -20,7 +20,7 @@ import { EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../../common/util/date_utils'; import { ExplorerLink } from './actions'; import { getJobsFromGroup } from './utils'; import { GroupsDictionary, Group } from './anomaly_detection_panel'; diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index 939ad34e77a395..0971b47605135d 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -15,7 +15,7 @@ import { isWebUrl } from '../util/url_utils'; import { ML_DATA_PREVIEW_COUNT } from '../../../common/util/job_utils'; import { TIME_FORMAT } from '../../../common/constants/time_format'; import { parseInterval } from '../../../common/util/parse_interval'; -import { validateTimeRange } from '../util/date_utils'; +import { validateTimeRange } from '../../../common/util/date_utils'; let jobs = []; let datafeedIds = {}; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js index cbc2c324a8bc67..2378d8c835ce98 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/forecasting_modal/forecasts_list.js @@ -13,7 +13,7 @@ import React from 'react'; import { EuiButtonIcon, EuiIcon, EuiInMemoryTable, EuiText, EuiToolTip } from '@elastic/eui'; -import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../../common/util/date_utils'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js index 1d166b7be9bc16..4c87c3b374ff36 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/components/timeseries_chart/timeseries_chart.js @@ -33,7 +33,7 @@ import { showMultiBucketAnomalyMarker, showMultiBucketAnomalyTooltip, } from '../../../util/chart_utils'; -import { formatHumanReadableDateTimeSeconds } from '../../../util/date_utils'; +import { formatHumanReadableDateTimeSeconds } from '../../../../../common/util/date_utils'; import { getTimeBucketsFromCache } from '../../../util/time_buckets'; import { mlTableService } from '../../../services/table_service'; import { ContextChartMask } from '../context_chart_mask'; diff --git a/x-pack/plugins/ml/public/shared.ts b/x-pack/plugins/ml/public/shared.ts index ec884bfac5351c..62b8179c9d5b2b 100644 --- a/x-pack/plugins/ml/public/shared.ts +++ b/x-pack/plugins/ml/public/shared.ts @@ -20,4 +20,4 @@ export * from '../common/util/validators'; export * from './application/formatters/metric_change_description'; export * from './application/components/data_grid'; export * from './application/data_frame_analytics/common'; -export * from './application/util/date_utils'; +export * from '../common/util/date_utils'; From e248f32111626e0909d3fad4339f3b625bd71493 Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Thu, 1 Oct 2020 11:58:23 -0400 Subject: [PATCH 038/128] [Lens] Consistent Drag and Drop styles (#78674) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove wrapping div of DragDrop and pass props to child * Using EuiHighlight * Basic styles in for all DnD states * Fixing dimension button styles * Fix FieldButton to accept `…rest` props * A few other minor fixes * Fixed horizontal scroll of error message * Quick fix for invalid link --- .../public/field_button/field_button.tsx | 37 ++- x-pack/plugins/lens/public/_mixins.scss | 36 +++ .../__snapshots__/drag_drop.test.tsx.snap | 18 +- .../lens/public/drag_drop/drag_drop.scss | 55 +++- .../lens/public/drag_drop/drag_drop.test.tsx | 30 +- .../lens/public/drag_drop/drag_drop.tsx | 65 ++-- .../config_panel/config_panel.scss | 2 +- .../config_panel/config_panel.tsx | 6 +- .../config_panel/dimension_container.tsx | 2 +- .../config_panel/layer_panel.scss | 37 ++- .../editor_frame/config_panel/layer_panel.tsx | 302 ++++++++++-------- .../editor_frame/frame_layout.scss | 2 +- .../workspace_panel/workspace_panel.tsx | 12 +- .../workspace_panel_wrapper.scss | 27 +- .../indexpattern_datasource/datapanel.scss | 2 +- .../dimension_panel/dimension_panel.tsx | 22 +- .../indexpattern_datasource/field_item.scss | 20 +- .../indexpattern_datasource/field_item.tsx | 37 +-- .../definitions/shared_components/buckets.tsx | 10 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 21 files changed, 431 insertions(+), 293 deletions(-) diff --git a/src/plugins/kibana_react/public/field_button/field_button.tsx b/src/plugins/kibana_react/public/field_button/field_button.tsx index 97d1b327461205..4c0298a65ba5dd 100644 --- a/src/plugins/kibana_react/public/field_button/field_button.tsx +++ b/src/plugins/kibana_react/public/field_button/field_button.tsx @@ -19,7 +19,8 @@ import './field_button.scss'; import classNames from 'classnames'; -import React, { ReactNode, HTMLAttributes } from 'react'; +import React, { ReactNode, HTMLAttributes, ButtonHTMLAttributes } from 'react'; +import { CommonProps } from '@elastic/eui'; export interface FieldButtonProps extends HTMLAttributes<HTMLDivElement> { /** @@ -56,7 +57,14 @@ export interface FieldButtonProps extends HTMLAttributes<HTMLDivElement> { * The component will render a `<button>` when provided an `onClick` */ onClick?: () => void; + /** + * Applies to the inner `<button>` or `<div>` + */ dataTestSubj?: string; + /** + * Pass more button props to the `<button>` element + */ + buttonProps?: ButtonHTMLAttributes<HTMLButtonElement> & CommonProps; } const sizeToClassNameMap = { @@ -79,6 +87,8 @@ export function FieldButton({ isDraggable = false, onClick, dataTestSubj, + buttonProps, + ...rest }: FieldButtonProps) { const classes = classNames( 'kbnFieldButton', @@ -88,8 +98,18 @@ export function FieldButton({ className ); + const contentClasses = classNames('kbn-resetFocusState', 'kbnFieldButton__button'); + + const innerContent = ( + <> + {fieldIcon && <span className="kbnFieldButton__fieldIcon">{fieldIcon}</span>} + {fieldName && <span className="kbnFieldButton__name">{fieldName}</span>} + {fieldInfoIcon && <div className="kbnFieldButton__infoIcon">{fieldInfoIcon}</div>} + </> + ); + return ( - <div className={classes}> + <div className={classes} {...rest}> {onClick ? ( <button onClick={(e) => { @@ -99,17 +119,14 @@ export function FieldButton({ onClick(); }} data-test-subj={dataTestSubj} - className={'kbn-resetFocusState kbnFieldButton__button'} + className={contentClasses} + {...buttonProps} > - {fieldIcon && <span className="kbnFieldButton__fieldIcon">{fieldIcon}</span>} - {fieldName && <span className="kbnFieldButton__name">{fieldName}</span>} - {fieldInfoIcon && <div className="kbnFieldButton__infoIcon">{fieldInfoIcon}</div>} + {innerContent} </button> ) : ( - <div className={'kbn-resetFocusState kbnFieldButton__button'} data-test-subj={dataTestSubj}> - {fieldIcon && <span className="kbnFieldButton__fieldIcon">{fieldIcon}</span>} - {fieldName && <span className="kbnFieldButton__name">{fieldName}</span>} - {fieldInfoIcon && <div className="kbnFieldButton__infoIcon">{fieldInfoIcon}</div>} + <div className={contentClasses} data-test-subj={dataTestSubj}> + {innerContent} </div> )} diff --git a/x-pack/plugins/lens/public/_mixins.scss b/x-pack/plugins/lens/public/_mixins.scss index a3cf6caa5a429b..0db72d118cef14 100644 --- a/x-pack/plugins/lens/public/_mixins.scss +++ b/x-pack/plugins/lens/public/_mixins.scss @@ -11,3 +11,39 @@ transparentize(red, .9) 100% ); } + +// Static styles for a draggable item +@mixin lnsDraggable { + @include euiSlightShadow; + background: lightOrDarkTheme($euiColorEmptyShade, $euiColorLightestShade); + border: $euiBorderWidthThin dashed transparent; + cursor: grab; +} + +// Static styles for a drop area +@mixin lnsDroppable { + border: $euiBorderWidthThin dashed $euiBorderColor; +} + +// Hovering state for drag item and drop area +@mixin lnsDragDropHover { + &:hover { + border: $euiBorderWidthThin dashed $euiColorMediumShade; + } +} + +// Style for drop area when there's an item being dragged +@mixin lnsDroppableActive { + background-color: transparentize($euiColorVis0, .9); +} + +// Style for drop area while hovering with item +@mixin lnsDroppableActiveHover { + background-color: transparentize($euiColorVis0, .75); + border: $euiBorderWidthThin dashed $euiColorVis0; +} + +// Style for drop area that is not allowed for current item +@mixin lnsDroppableNotAllowed { + opacity: .5; +} diff --git a/x-pack/plugins/lens/public/drag_drop/__snapshots__/drag_drop.test.tsx.snap b/x-pack/plugins/lens/public/drag_drop/__snapshots__/drag_drop.test.tsx.snap index 3581151dd5f768..dc53f3a2bc2a77 100644 --- a/x-pack/plugins/lens/public/drag_drop/__snapshots__/drag_drop.test.tsx.snap +++ b/x-pack/plugins/lens/public/drag_drop/__snapshots__/drag_drop.test.tsx.snap @@ -1,17 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`DragDrop droppable is reflected in the className 1`] = ` -<div - class="lnsDragDrop lnsDragDrop-isDropTarget" +<button + class="lnsDragDrop lnsDragDrop-isDroppable lnsDragDrop-isDropTarget" data-test-subj="lnsDragDrop" > Hello! -</div> +</button> `; exports[`DragDrop items that have droppable=false get special styling when another item is dragged 1`] = ` -<div - className="lnsDragDrop lnsDragDrop-isNotDroppable" +<button + className="lnsDragDrop lnsDragDrop-isDroppable lnsDragDrop-isNotDroppable" data-test-subj="lnsDragDrop" onDragEnd={[Function]} onDragLeave={[Function]} @@ -20,15 +20,15 @@ exports[`DragDrop items that have droppable=false get special styling when anoth onDrop={[Function]} > Hello! -</div> +</button> `; exports[`DragDrop renders if nothing is being dragged 1`] = ` -<div - class="lnsDragDrop" +<button + class="lnsDragDrop lnsDragDrop-isDraggable" data-test-subj="lnsDragDrop" draggable="true" > Hello! -</div> +</button> `; diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.scss b/x-pack/plugins/lens/public/drag_drop/drag_drop.scss index c971540e165c11..410aaef9a51951 100644 --- a/x-pack/plugins/lens/public/drag_drop/drag_drop.scss +++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.scss @@ -1,13 +1,54 @@ -.lnsDragDrop-isNotDroppable { - opacity: .5; +@import '../variables'; +@import '../mixins'; + +.lnsDragDrop { + transition: background-color $euiAnimSpeedFast ease-in-out, border-color $euiAnimSpeedFast ease-in-out; +} + +// Draggable item +.lnsDragDrop-isDraggable { + @include lnsDraggable; + @include lnsDragDropHover; + + // Include a possible nested button like when using FieldButton + > .kbnFieldButton__button { + cursor: grab; + } + + &:focus { + @include euiFocusRing; + } +} + +// Draggable item when it is moving +.lnsDragDrop-isHidden { + opacity: 0; +} + +// Drop area +.lnsDragDrop-isDroppable { + @include lnsDroppable; } -// Fix specificity by chaining classes +// Drop area when there's an item being dragged +.lnsDragDrop-isDropTarget { + @include lnsDroppableActive; +} -.lnsDragDrop.lnsDragDrop-isDropTarget { - background-color: transparentize($euiColorSecondary, .9); +// Drop area while hovering with item +.lnsDragDrop-isActiveDropTarget { + @include lnsDroppableActiveHover; +} + +// Drop area that is not allowed for current item +.lnsDragDrop-isNotDroppable { + @include lnsDroppableNotAllowed; } -.lnsDragDrop.lnsDragDrop-isActiveDropTarget { - background-color: transparentize($euiColorSecondary, .75); +// Drop area will be replacing existing content +.lnsDragDrop-isReplacing { + &, + .lnsLayerPanel__triggerLink { + text-decoration: line-through; + } } diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx b/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx index 3240357c254eac..b1cc4c06c2165a 100644 --- a/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx +++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.test.tsx @@ -15,7 +15,7 @@ describe('DragDrop', () => { test('renders if nothing is being dragged', () => { const component = render( <DragDrop value="hello" draggable label="dragging"> - Hello! + <button>Hello!</button> </DragDrop> ); @@ -24,7 +24,11 @@ describe('DragDrop', () => { test('dragover calls preventDefault if droppable is true', () => { const preventDefault = jest.fn(); - const component = mount(<DragDrop droppable>Hello!</DragDrop>); + const component = mount( + <DragDrop droppable> + <button>Hello!</button> + </DragDrop> + ); component.find('[data-test-subj="lnsDragDrop"]').simulate('dragover', { preventDefault }); @@ -33,7 +37,11 @@ describe('DragDrop', () => { test('dragover does not call preventDefault if droppable is false', () => { const preventDefault = jest.fn(); - const component = mount(<DragDrop>Hello!</DragDrop>); + const component = mount( + <DragDrop> + <button>Hello!</button> + </DragDrop> + ); component.find('[data-test-subj="lnsDragDrop"]').simulate('dragover', { preventDefault }); @@ -51,7 +59,7 @@ describe('DragDrop', () => { const component = mount( <ChildDragDropProvider dragging={value} setDragging={setDragging}> <DragDrop value={value} draggable={true} label="drag label"> - Hello! + <button>Hello!</button> </DragDrop> </ChildDragDropProvider> ); @@ -74,7 +82,7 @@ describe('DragDrop', () => { const component = mount( <ChildDragDropProvider dragging="hola" setDragging={setDragging}> <DragDrop onDrop={onDrop} droppable={true} value={value}> - Hello! + <button>Hello!</button> </DragDrop> </ChildDragDropProvider> ); @@ -98,7 +106,7 @@ describe('DragDrop', () => { const component = mount( <ChildDragDropProvider dragging="hola" setDragging={setDragging}> <DragDrop onDrop={onDrop} droppable={false} value={{}}> - Hello! + <button>Hello!</button> </DragDrop> </ChildDragDropProvider> ); @@ -121,7 +129,7 @@ describe('DragDrop', () => { }} droppable > - Hello! + <button>Hello!</button> </DragDrop> ); @@ -132,10 +140,10 @@ describe('DragDrop', () => { const component = mount( <ChildDragDropProvider dragging={'ignored'} setDragging={() => {}}> <DragDrop value="ignored" draggable={true} label="a"> - Ignored + <button>Hello!</button> </DragDrop> <DragDrop onDrop={(x: unknown) => {}} droppable={false}> - Hello! + <button>Hello!</button> </DragDrop> </ChildDragDropProvider> ); @@ -154,14 +162,14 @@ describe('DragDrop', () => { }} > <DragDrop value="ignored" draggable={true} label="a"> - Ignored + <button>Hello!</button> </DragDrop> <DragDrop onDrop={(x: unknown) => {}} droppable getAdditionalClassesOnEnter={getAdditionalClasses} > - Hello! + <button>Hello!</button> </DragDrop> </ChildDragDropProvider> ); diff --git a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx index 6941974a63cd3f..b36415fee5b152 100644 --- a/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx +++ b/x-pack/plugins/lens/public/drag_drop/drag_drop.tsx @@ -41,9 +41,14 @@ interface BaseProps { value?: unknown; /** - * The React children. + * Optional comparison function to check whether a value is the dragged one */ - children: React.ReactNode; + isValueEqual?: (value1: unknown, value2: unknown) => boolean; + + /** + * The React element which will be passed the draggable handlers + */ + children: React.ReactElement; /** * Indicates whether or not the currently dragged item @@ -60,6 +65,18 @@ interface BaseProps { * The optional test subject associated with this DOM element. */ 'data-test-subj'?: string; + + /** + * Indicates to the user whether the currently dragged item + * will be moved or copied + */ + dragType?: 'copy' | 'move'; + + /** + * Indicates to the user whether the drop action will + * replace something that is existing or add a new one + */ + dropType?: 'add' | 'replace'; } /** @@ -98,12 +115,14 @@ type Props = DraggableProps | NonDraggableProps; export const DragDrop = (props: Props) => { const { dragging, setDragging } = useContext(DragContext); - const { value, draggable, droppable } = props; + const { value, draggable, droppable, isValueEqual } = props; return ( <DragDropInner {...props} dragging={droppable ? dragging : undefined} - isDragging={!!(draggable && value === dragging)} + isDragging={ + !!(draggable && ((isValueEqual && isValueEqual(value, dragging)) || value === dragging)) + } isNotDroppable={ // If the configuration has provided a droppable flag, but this particular item is not // droppable, then it should be less prominent. Ignores items that are both @@ -138,17 +157,25 @@ const DragDropInner = React.memo(function DragDropInner( setDragging, isDragging, isNotDroppable, + dragType = 'copy', + dropType = 'add', } = props; + const isMoveDragging = isDragging && dragType === 'move'; + const classes = classNames( 'lnsDragDrop', - className, { + 'lnsDragDrop-isDraggable': draggable, + 'lnsDragDrop-isDragging': isDragging, + 'lnsDragDrop-isHidden': isMoveDragging, + 'lnsDragDrop-isDroppable': !draggable, 'lnsDragDrop-isDropTarget': droppable, 'lnsDragDrop-isActiveDropTarget': droppable && state.isActive, - 'lnsDragDrop-isDragging': isDragging, - 'lnsDragDrop-isNotDroppable': isNotDroppable, + 'lnsDragDrop-isNotDroppable': !isMoveDragging && isNotDroppable, + 'lnsDragDrop-isReplacing': droppable && state.isActive && dropType === 'replace', }, + className, state.dragEnterClassNames ); @@ -210,18 +237,14 @@ const DragDropInner = React.memo(function DragDropInner( } }; - return ( - <div - data-test-subj={props['data-test-subj'] || 'lnsDragDrop'} - className={classes} - onDragOver={dragOver} - onDragLeave={dragLeave} - onDrop={drop} - draggable={draggable} - onDragEnd={dragEnd} - onDragStart={dragStart} - > - {children} - </div> - ); + return React.cloneElement(children, { + 'data-test-subj': props['data-test-subj'] || 'lnsDragDrop', + className: classNames(children.props.className, classes), + onDragOver: dragOver, + onDragLeave: dragLeave, + onDrop: drop, + draggable, + onDragEnd: dragEnd, + onDragStart: dragStart, + }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss index 1965b51f970345..a58b5c21e77242 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.scss @@ -3,5 +3,5 @@ // Remove EuiButton's default shadow to make button more subtle // sass-lint:disable-block no-important box-shadow: none !important; - border: 1px dashed currentColor; + border-color: $euiColorLightShade; } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx index ad16038f44911c..6b7e5ba8ea89d8 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.tsx @@ -121,6 +121,9 @@ function LayerPanels( <EuiFlexItem grow={true}> <EuiToolTip className="eui-fullWidth" + title={i18n.translate('xpack.lens.xyChart.addLayer', { + defaultMessage: 'Add a layer', + })} content={i18n.translate('xpack.lens.xyChart.addLayerTooltip', { defaultMessage: 'Use multiple layers to combine chart types or visualize different index patterns.', @@ -135,9 +138,6 @@ function LayerPanels( aria-label={i18n.translate('xpack.lens.xyChart.addLayerButton', { defaultMessage: 'Add layer', })} - title={i18n.translate('xpack.lens.xyChart.addLayerButton', { - defaultMessage: 'Add layer', - })} onClick={() => { dispatch({ type: 'UPDATE_STATE', diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx index a415eb44cf196a..19f4c0428260e1 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/dimension_container.tsx @@ -130,7 +130,7 @@ export function DimensionContainer({ return ( <> - <div className="lnsDimensionContainer__trigger">{trigger}</div> + {trigger} {flyout} </> ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss index b85c3e843613db..c77db2e65ce2d7 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.scss @@ -27,36 +27,41 @@ .lnsLayerPanel__dimension { @include euiFontSizeS; - background: lightOrDarkTheme($euiColorEmptyShade, $euiColorLightestShade); border-radius: $euiBorderRadius; display: flex; align-items: center; margin-top: $euiSizeXS; overflow: hidden; -} + width: 100%; + min-height: $euiSizeXXL; -.lnsLayerPanel__dimension-isHidden { - opacity: 0; -} + // NativeRenderer is messing this up + > div { + flex-grow: 1; + } -.lnsLayerPanel__dimension-isReplacing { - text-decoration: line-through; + &:focus, + &:focus-within { + @include euiFocusRing; + } } .lnsLayerPanel__triggerLink { - padding: $euiSizeS; width: 100%; - display: flex; - align-items: center; - min-height: $euiSizeXXL; -} + padding: $euiSizeS; + min-height: $euiSizeXXL - 2; -.lnsLayerPanel__anchor { - width: 100%; + &:focus { + background-color: transparent !important; // sass-lint:disable-line no-important + outline: none !important; // sass-lint:disable-line no-important + } } -.lnsLayerPanel__dndGrab { - padding: $euiSizeS; +.lnsLayerPanel__triggerLinkContent { + // Make EUI button content not centered + justify-content: flex-start; + padding: 0 !important; // sass-lint:disable-line no-important + color: $euiTextSubduedColor; } .lnsLayerPanel__styleEditor { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 46cd0292f2459a..ce2955da890d7c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -17,7 +17,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import classNames from 'classnames'; import { NativeRenderer } from '../../../native_renderer'; import { StateSetter, isDraggedOperation } from '../../../types'; import { DragContext, DragDrop, ChildDragDropProvider } from '../../../drag_drop'; @@ -33,6 +32,28 @@ const initialDimensionContainerState = { addingToGroupId: null, }; +function isConfiguration( + value: unknown +): value is { columnId: string; groupId: string; layerId: string } { + return ( + value && + typeof value === 'object' && + 'columnId' in value && + 'groupId' in value && + 'layerId' in value + ); +} + +function isSameConfiguration(config1: unknown, config2: unknown) { + return ( + isConfiguration(config1) && + isConfiguration(config2) && + config1.columnId === config2.columnId && + config1.groupId === config2.groupId && + config1.layerId === config2.layerId + ); +} + export function LayerPanel( props: Exclude<ConfigPanelWrapperProps, 'state' | 'setState'> & { layerId: string; @@ -203,25 +224,22 @@ export function LayerPanel( return ( <DragDrop key={accessor} - className={classNames('lnsLayerPanel__dimension', { - // eslint-disable-next-line @typescript-eslint/naming-convention - 'lnsLayerPanel__dimension-isHidden': - isDraggedOperation(dragDropContext.dragging) && - accessor === dragDropContext.dragging.columnId, - })} - getAdditionalClassesOnEnter={() => { - // If we are dragging another column, add an indication that the behavior will be a replacement' - if ( - isDraggedOperation(dragDropContext.dragging) && - group.groupId !== dragDropContext.dragging.groupId - ) { - return 'lnsLayerPanel__dimension-isReplacing'; - } - return ''; - }} + dragType={ + isDraggedOperation(dragDropContext.dragging) && + accessor === dragDropContext.dragging.columnId + ? 'move' + : 'copy' + } + dropType={ + isDraggedOperation(dragDropContext.dragging) && + group.groupId !== dragDropContext.dragging.groupId + ? 'replace' + : 'add' + } data-test-subj={group.dataTestSubj} draggable={!dimensionContainerState.isOpen} value={{ columnId: accessor, groupId: group.groupId, layerId }} + isValueEqual={isSameConfiguration} label={group.groupLabel} droppable={ Boolean(dragDropContext.dragging) && @@ -254,83 +272,84 @@ export function LayerPanel( } }} > - <DimensionContainer - dimensionContainerState={dimensionContainerState} - setDimensionContainerState={setDimensionContainerState} - groups={groups} - accessor={accessor} - groupId={group.groupId} - trigger={ - <NativeRenderer - render={props.datasourceMap[datasourceId].renderDimensionTrigger} - nativeProps={{ - ...layerDatasourceConfigProps, - columnId: accessor, - filterOperations: group.filterOperations, - suggestedPriority: group.suggestedPriority, - onClick: () => { - if (dimensionContainerState.isOpen) { - setDimensionContainerState(initialDimensionContainerState); - } else { - setDimensionContainerState({ - isOpen: true, - openId: accessor, - addingToGroupId: null, // not set for existing dimension - }); - } - }, - }} - /> - } - panel={ - <> - {datasourceDimensionEditor} - {visDimensionEditor} - </> - } - panelTitle={i18n.translate('xpack.lens.configure.configurePanelTitle', { - defaultMessage: '{groupLabel} configuration', - values: { - groupLabel: group.groupLabel, - }, - })} - /> + <div className="lnsLayerPanel__dimension"> + <DimensionContainer + dimensionContainerState={dimensionContainerState} + setDimensionContainerState={setDimensionContainerState} + groups={groups} + accessor={accessor} + groupId={group.groupId} + trigger={ + <NativeRenderer + render={props.datasourceMap[datasourceId].renderDimensionTrigger} + nativeProps={{ + ...layerDatasourceConfigProps, + columnId: accessor, + filterOperations: group.filterOperations, + suggestedPriority: group.suggestedPriority, + onClick: () => { + if (dimensionContainerState.isOpen) { + setDimensionContainerState(initialDimensionContainerState); + } else { + setDimensionContainerState({ + isOpen: true, + openId: accessor, + addingToGroupId: null, // not set for existing dimension + }); + } + }, + }} + /> + } + panel={ + <> + {datasourceDimensionEditor} + {visDimensionEditor} + </> + } + panelTitle={i18n.translate('xpack.lens.configure.configurePanelTitle', { + defaultMessage: '{groupLabel} configuration', + values: { + groupLabel: group.groupLabel, + }, + })} + /> - <EuiButtonIcon - data-test-subj="indexPattern-dimension-remove" - iconType="cross" - iconSize="s" - size="s" - color="danger" - aria-label={i18n.translate('xpack.lens.indexPattern.removeColumnLabel', { - defaultMessage: 'Remove configuration', - })} - title={i18n.translate('xpack.lens.indexPattern.removeColumnLabel', { - defaultMessage: 'Remove configuration', - })} - onClick={() => { - trackUiEvent('indexpattern_dimension_removed'); - props.updateAll( - datasourceId, - layerDatasource.removeColumn({ - layerId, - columnId: accessor, - prevState: layerDatasourceState, - }), - activeVisualization.removeDimension({ - layerId, - columnId: accessor, - prevState: props.visualizationState, - }) - ); - }} - /> + <EuiButtonIcon + data-test-subj="indexPattern-dimension-remove" + iconType="cross" + iconSize="s" + size="s" + color="danger" + aria-label={i18n.translate('xpack.lens.indexPattern.removeColumnLabel', { + defaultMessage: 'Remove configuration', + })} + title={i18n.translate('xpack.lens.indexPattern.removeColumnLabel', { + defaultMessage: 'Remove configuration', + })} + onClick={() => { + trackUiEvent('indexpattern_dimension_removed'); + props.updateAll( + datasourceId, + layerDatasource.removeColumn({ + layerId, + columnId: accessor, + prevState: layerDatasourceState, + }), + activeVisualization.removeDimension({ + layerId, + columnId: accessor, + prevState: props.visualizationState, + }) + ); + }} + /> + </div> </DragDrop> ); })} {group.supportsMoreColumns ? ( <DragDrop - className="lnsLayerPanel__dimension" data-test-subj={group.dataTestSubj} droppable={ Boolean(dragDropContext.dragging) && @@ -374,23 +393,23 @@ export function LayerPanel( } }} > - <DimensionContainer - dimensionContainerState={dimensionContainerState} - setDimensionContainerState={setDimensionContainerState} - groups={groups} - accessor={newId} - groupId={group.groupId} - trigger={ - <div className="lnsLayerPanel__triggerLink"> + <div className="lnsLayerPanel__dimension"> + <DimensionContainer + dimensionContainerState={dimensionContainerState} + setDimensionContainerState={setDimensionContainerState} + groups={groups} + accessor={newId} + groupId={group.groupId} + trigger={ <EuiButtonEmpty + className="lnsLayerPanel__triggerLink" + color="text" + size="xs" iconType="plusInCircleFilled" + contentProps={{ + className: 'lnsLayerPanel__triggerLinkContent', + }} data-test-subj="lns-empty-dimension" - aria-label={i18n.translate('xpack.lens.configure.addConfig', { - defaultMessage: 'Add a configuration', - })} - title={i18n.translate('xpack.lens.configure.addConfig', { - defaultMessage: 'Add a configuration', - })} onClick={() => { if (dimensionContainerState.isOpen) { setDimensionContainerState(initialDimensionContainerState); @@ -402,52 +421,51 @@ export function LayerPanel( }); } }} - size="xs" > <FormattedMessage id="xpack.lens.configure.emptyConfig" - defaultMessage="Drop a field here" + defaultMessage="Drop a field or click to add" /> </EuiButtonEmpty> - </div> - } - panelTitle={i18n.translate('xpack.lens.configure.configurePanelTitle', { - defaultMessage: '{groupLabel} configuration', - values: { - groupLabel: group.groupLabel, - }, - })} - panel={ - <NativeRenderer - render={props.datasourceMap[datasourceId].renderDimensionEditor} - nativeProps={{ - ...layerDatasourceConfigProps, - core: props.core, - columnId: newId, - filterOperations: group.filterOperations, - suggestedPriority: group.suggestedPriority, + } + panelTitle={i18n.translate('xpack.lens.configure.configurePanelTitle', { + defaultMessage: '{groupLabel} configuration', + values: { + groupLabel: group.groupLabel, + }, + })} + panel={ + <NativeRenderer + render={props.datasourceMap[datasourceId].renderDimensionEditor} + nativeProps={{ + ...layerDatasourceConfigProps, + core: props.core, + columnId: newId, + filterOperations: group.filterOperations, + suggestedPriority: group.suggestedPriority, - setState: (newState: unknown) => { - props.updateAll( - datasourceId, - newState, - activeVisualization.setDimension({ - layerId, - groupId: group.groupId, - columnId: newId, - prevState: props.visualizationState, - }) - ); - setDimensionContainerState({ - isOpen: true, - openId: newId, - addingToGroupId: null, // clear now that dimension exists - }); - }, - }} - /> - } - /> + setState: (newState: unknown) => { + props.updateAll( + datasourceId, + newState, + activeVisualization.setDimension({ + layerId, + groupId: group.groupId, + columnId: newId, + prevState: props.visualizationState, + }) + ); + setDimensionContainerState({ + isOpen: true, + openId: newId, + addingToGroupId: null, // clear now that dimension exists + }); + }, + }} + /> + } + /> + </div> </DragDrop> ) : null} </> diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss index bad0563f16f1fd..ac52190dc7b0d6 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/frame_layout.scss @@ -20,7 +20,7 @@ .lnsFrameLayout__pageBody { @include euiScrollBar; min-width: $lnsPanelMinWidth + $euiSizeXL; - overflow: hidden; + overflow: hidden auto; // Leave out bottom padding so the suggestions scrollbar stays flush to window edge // Leave out left padding so the left sidebar's focus states are visible outside of content bounds // This also means needing to add same amount of margin to page content and suggestion items diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index e56e55fdd5d6c4..2a5798ac6a70c0 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -221,7 +221,7 @@ export function InnerWorkspacePanel({ )} </strong> </h2> - <DropIllustration className="lnsWorkspacePanel__dropIllustration" /> + <DropIllustration aria-hidden={true} className="lnsWorkspacePanel__dropIllustration" /> {expression === null && ( <> <p> @@ -257,7 +257,7 @@ export function InnerWorkspacePanel({ if (localState.expressionBuildError) { return ( - <EuiFlexGroup direction="column" alignItems="center"> + <EuiFlexGroup style={{ maxWidth: '100%' }} direction="column" alignItems="center"> <EuiFlexItem> <EuiIcon type="alert" size="xl" color="danger" /> </EuiFlexItem> @@ -283,7 +283,7 @@ export function InnerWorkspacePanel({ onEvent={onEvent} renderError={(errorMessage?: string | null) => { return ( - <EuiFlexGroup direction="column" alignItems="center"> + <EuiFlexGroup style={{ maxWidth: '100%' }} direction="column" alignItems="center"> <EuiFlexItem> <EuiIcon type="alert" size="xl" color="danger" /> </EuiFlexItem> @@ -338,8 +338,10 @@ export function InnerWorkspacePanel({ droppable={Boolean(suggestionForDraggedField)} onDrop={onDrop} > - {renderVisualization()} - {Boolean(suggestionForDraggedField) && expression !== null && renderEmptyWorkspace()} + <div> + {renderVisualization()} + {Boolean(suggestionForDraggedField) && expression !== null && renderEmptyWorkspace()} + </div> </DragDrop> </WorkspacePanelWrapper> ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss index 7f7385f029ed4e..33b9b2fe1dbf0b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel_wrapper.scss @@ -1,3 +1,5 @@ +@import '../../../mixins'; + .lnsWorkspacePanelWrapper { @include euiScrollBar; overflow: hidden; @@ -7,6 +9,7 @@ display: flex; flex-direction: column; position: relative; // For positioning the dnd overlay + min-height: $euiSizeXXL * 10; .lnsWorkspacePanelWrapper__pageContentHeader { @include euiTitle('xs'); @@ -25,7 +28,7 @@ display: flex; align-items: stretch; justify-content: stretch; - overflow: hidden; + overflow: auto; > * { flex: 1 1 100%; @@ -41,6 +44,7 @@ // Disable the coloring of the DnD for this element as we'll // Color the whole panel instead background-color: transparent !important; // sass-lint:disable-line no-important + border: none !important; // sass-lint:disable-line no-important } .lnsExpressionRenderer { @@ -60,28 +64,25 @@ display: flex; justify-content: center; align-items: center; - transition: background-color $euiAnimSpeedNormal ease-in-out; + transition: background-color $euiAnimSpeedFast ease-in-out; .lnsDragDrop-isDropTarget & { - background-color: transparentize($euiColorSecondary, .9); + @include lnsDroppable; + @include lnsDroppableActive; p { - transition: filter $euiAnimSpeedNormal ease-in-out; + transition: filter $euiAnimSpeedFast ease-in-out; filter: blur(5px); } } .lnsDragDrop-isActiveDropTarget & { - background-color: transparentize($euiColorSecondary, .75); + @include lnsDroppableActiveHover; .lnsDropIllustration__hand { - animation: pulseArrowContinuous 1.5s ease-in-out 0s infinite normal forwards; + animation: lnsWorkspacePanel__illustrationPulseContinuous 1.5s ease-in-out 0s infinite normal forwards; } } - - &.lnsWorkspacePanel__emptyContent-onTop p { - display: none; - } } .lnsWorkspacePanelWrapper__toolbar { @@ -106,10 +107,10 @@ } .lnsDropIllustration__hand { - animation: pulseArrow 5s ease-in-out 0s infinite normal forwards; + animation: lnsWorkspacePanel__illustrationPulseArrow 5s ease-in-out 0s infinite normal forwards; } -@keyframes pulseArrow { +@keyframes lnsWorkspacePanel__illustrationPulseArrow { 0% { transform: translateY(0%); } 65% { transform: translateY(0%); } 72% { transform: translateY(10%); } @@ -118,7 +119,7 @@ 95% { transform: translateY(0); } } -@keyframes pulseArrowContinuous { +@keyframes lnsWorkspacePanel__illustrationPulseContinuous { 0% { transform: translateY(10%); } 25% { transform: translateY(15%); } 50% { transform: translateY(10%); } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss index 155b954e9cf170..df73789eadedfd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/datapanel.scss @@ -12,7 +12,7 @@ .lnsInnerIndexPatternDataPanel__fieldItems { // Quick fix for making sure the shadow and focus rings are visible outside the accordion bounds - padding: $euiSizeXS $euiSizeXS 0; + padding: $euiSizeXS; } .lnsInnerIndexPatternDataPanel__textField { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx index 6f0a9c2a86acd3..12b8d91c35ade9 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_panel.tsx @@ -250,6 +250,10 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens return null; } + const triggerLinkA11yText = i18n.translate('xpack.lens.configure.editConfig', { + defaultMessage: 'Click to edit configuration or drag to move', + }); + if (currentFieldIsInvalid) { return ( <EuiToolTip @@ -264,7 +268,7 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens })} </p> } - anchorClassName="lnsLayerPanel__anchor" + anchorClassName="eui-displayBlock" > <EuiLink color="danger" @@ -272,12 +276,8 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens className="lnsLayerPanel__triggerLink" onClick={props.onClick} data-test-subj="lns-dimensionTrigger" - aria-label={i18n.translate('xpack.lens.configure.editConfig', { - defaultMessage: 'Edit configuration', - })} - title={i18n.translate('xpack.lens.configure.editConfig', { - defaultMessage: 'Edit configuration', - })} + aria-label={triggerLinkA11yText} + title={triggerLinkA11yText} > <EuiFlexGroup gutterSize="s" alignItems="center" responsive={false}> <EuiFlexItem grow={false}> @@ -296,12 +296,8 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens className="lnsLayerPanel__triggerLink" onClick={props.onClick} data-test-subj="lns-dimensionTrigger" - aria-label={i18n.translate('xpack.lens.configure.editConfig', { - defaultMessage: 'Edit configuration', - })} - title={i18n.translate('xpack.lens.configure.editConfig', { - defaultMessage: 'Edit configuration', - })} + aria-label={triggerLinkA11yText} + title={triggerLinkA11yText} > {uniqueLabel} </EuiLink> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss index d74c332dd42e56..1b55d9623e2237 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.scss @@ -1,25 +1,25 @@ -.lnsFieldItem--missing { - .lnsFieldItem__info { - background: lightOrDarkTheme(transparentize($euiColorMediumShade, .9), $euiColorEmptyShade); - color: $euiColorDarkShade; - } -} - -.lnsFieldItem__info { +.lnsFieldItem { .lnsFieldItem__infoIcon { visibility: hidden; + opacity: 0; } - &:hover, - &:focus { + &:hover:not([class*='isActive']) { cursor: grab; .lnsFieldItem__infoIcon { visibility: visible; + opacity: 1; + transition: opacity $euiAnimSpeedFast ease-in-out 1s; } } } +.lnsFieldItem--missing { + background: lightOrDarkTheme(transparentize($euiColorMediumShade, .9), $euiColorEmptyShade); + color: $euiColorDarkShade; +} + .lnsFieldItem__topValue { margin-bottom: $euiSizeS; 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 7377d15bca6d73..2fbe23f9085f2c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/field_item.tsx @@ -33,6 +33,7 @@ import { } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { EuiHighlight } from '@elastic/eui'; import { Query, KBN_FIELD_TYPES, @@ -102,22 +103,6 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { isLoading: false, }); - const wrappableName = wrapOnDot(field.displayName)!; - const wrappableHighlight = wrapOnDot(highlight); - const highlightIndex = wrappableHighlight - ? wrappableName.toLowerCase().indexOf(wrappableHighlight.toLowerCase()) - : -1; - const wrappableHighlightableFieldName = - highlightIndex < 0 ? ( - wrappableName - ) : ( - <span> - <span>{wrappableName.substr(0, highlightIndex)}</span> - <strong>{wrappableName.substr(highlightIndex, wrappableHighlight.length)}</strong> - <span>{wrappableName.substr(highlightIndex + wrappableHighlight.length)}</span> - </span> - ); - function fetchData() { if (state.isLoading) { return; @@ -200,22 +185,20 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { ownFocus className="lnsFieldItem__popoverAnchor" display="block" + data-test-subj="lnsFieldListPanelField" container={document.querySelector<HTMLElement>('.application') || undefined} button={ <DragDrop label={field.displayName} value={value} - data-test-subj="lnsFieldListPanelField" + data-test-subj={`lnsFieldListPanelField-${field.name}`} draggable - className={`lnsFieldItem lnsFieldItem--${field.type} lnsFieldItem--${ - exists ? 'exists' : 'missing' - }`} > <FieldButton - className="lnsFieldItem__info" - isDraggable + className={`lnsFieldItem lnsFieldItem--${field.type} lnsFieldItem--${ + exists ? 'exists' : 'missing' + }`} isActive={infoIsOpen} - data-test-subj={`lnsFieldListPanelField-${field.name}`} onClick={togglePopover} aria-label={i18n.translate('xpack.lens.indexPattern.fieldStatsButtonAriaLabel', { defaultMessage: '{fieldName}: {fieldType}. Hit enter for a field preview.', @@ -225,7 +208,11 @@ export const InnerFieldItem = function InnerFieldItem(props: FieldItemProps) { }, })} fieldIcon={lensFieldIcon} - fieldName={wrappableHighlightableFieldName} + fieldName={ + <EuiHighlight search={wrapOnDot(highlight)}> + {wrapOnDot(field.displayName)} + </EuiHighlight> + } fieldInfoIcon={lensInfoIcon} /> </DragDrop> @@ -527,7 +514,7 @@ function FieldItemPopoverContents(props: State & FieldItemProps) { </EuiFlexItem> <EuiFlexItem grow={false} className="eui-textTruncate"> - <EuiText size="s" color="subdued"> + <EuiText size="xs" color="subdued"> {Math.round((otherCount / props.sampledValues!) * 100)}% </EuiText> </EuiFlexItem> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx index 47380f7865578c..50471ca84c0d83 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/buckets.tsx @@ -96,7 +96,13 @@ export const DraggableBucketContainer = ({ children: React.ReactNode; } & BucketContainerProps) => { return ( - <EuiDraggable spacing="m" index={idx} draggableId={id} disableInteractiveElementBlocking> + <EuiDraggable + style={{ marginBottom: 4 }} + spacing="none" + index={idx} + draggableId={id} + disableInteractiveElementBlocking + > {(provided) => <BucketContainer {...bucketContainerProps}>{children}</BucketContainer>} </EuiDraggable> ); @@ -134,7 +140,7 @@ export const DragDropBuckets = ({ }; return ( <EuiDragDropContext onDragEnd={handleDragEnd} onDragStart={onDragStart}> - <EuiDroppable droppableId={droppableId} spacing="s"> + <EuiDroppable droppableId={droppableId} spacing="none"> {children} </EuiDroppable> </EuiDragDropContext> diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index ba9d8e364bd173..34c339023171e1 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9464,7 +9464,6 @@ "xpack.lens.configPanel.color.tooltip.custom": "[自動]モードに戻すには、カスタム色をオフにしてください。", "xpack.lens.configPanel.color.tooltip.disabled": "レイヤーに「内訳条件」が含まれている場合は、個別の系列をカスタム色にできません。", "xpack.lens.configPanel.selectVisualization": "ビジュアライゼーションを選択してください", - "xpack.lens.configure.addConfig": "構成を追加", "xpack.lens.configure.editConfig": "構成の編集", "xpack.lens.configure.emptyConfig": "ここにフィールドをドロップ", "xpack.lens.dataPanelWrapper.switchDatasource": "データソースに切り替える", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 5d44e0c635bee0..f32b49fd4f2f00 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9470,7 +9470,6 @@ "xpack.lens.configPanel.color.tooltip.custom": "清除定制颜色以返回到“自动”模式。", "xpack.lens.configPanel.color.tooltip.disabled": "当图层包括“细分依据”,各个系列无法定制颜色。", "xpack.lens.configPanel.selectVisualization": "选择可视化", - "xpack.lens.configure.addConfig": "添加配置", "xpack.lens.configure.editConfig": "编辑配置", "xpack.lens.configure.emptyConfig": "将字段拖放到此处", "xpack.lens.dataPanelWrapper.switchDatasource": "切换到数据源", From 198c5d998816e940e4c8d92979504c86116bcd2d Mon Sep 17 00:00:00 2001 From: Marco Liberati <dej611@users.noreply.github.com> Date: Thu, 1 Oct 2020 18:02:37 +0200 Subject: [PATCH 039/128] [Lens] Fix embeddable title and description for reporting and dashboard tooltip (#78767) Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../lib/panel/panel_header/panel_header.tsx | 9 ++- .../public/embeddable/visualize_embeddable.ts | 2 +- .../__snapshots__/expression.test.tsx.snap | 4 +- .../datatable_visualization/expression.tsx | 10 ++- .../datatable_visualization/visualization.tsx | 4 +- .../editor_frame/expression_helpers.ts | 9 ++- .../editor_frame/state_helpers.ts | 4 ++ .../embeddable/embeddable.tsx | 6 ++ .../metric_visualization/expression.test.tsx | 69 +++++++++++++++++-- .../metric_visualization/expression.tsx | 24 +++++-- .../lens/public/metric_visualization/types.ts | 2 + .../visualization.test.ts | 8 ++- .../metric_visualization/visualization.tsx | 10 +-- .../public/pie_visualization/expression.tsx | 8 +++ .../pie_visualization/render_function.tsx | 7 +- .../public/pie_visualization/to_expression.ts | 13 ++-- .../lens/public/pie_visualization/types.ts | 2 + x-pack/plugins/lens/public/types.ts | 3 +- .../public/visualization_container.test.tsx | 7 +- .../lens/public/visualization_container.tsx | 11 ++- .../__snapshots__/to_expression.test.ts.snap | 6 ++ .../public/xy_visualization/expression.tsx | 15 +++- .../public/xy_visualization/to_expression.ts | 10 ++- .../lens/public/xy_visualization/types.ts | 2 + 24 files changed, 206 insertions(+), 39 deletions(-) diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx index 7dde84e510535e..dea483efb349f9 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx @@ -109,12 +109,11 @@ function renderTooltip(description: string) { ); } -const VISUALIZE_EMBEDDABLE_TYPE = 'visualization'; -type VisualizeEmbeddable = any; +type EmbeddableWithDescription = IEmbeddable & { getDescription: () => string }; -function getViewDescription(embeddable: IEmbeddable | VisualizeEmbeddable) { - if (embeddable.type === VISUALIZE_EMBEDDABLE_TYPE) { - const description = embeddable.getVisualizationDescription(); +function getViewDescription(embeddable: IEmbeddable | EmbeddableWithDescription) { + if ('getDescription' in embeddable) { + const description = embeddable.getDescription(); if (description) { return description; diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts index c091d396b49244..fe8a9adff40529 100644 --- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts +++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts @@ -167,7 +167,7 @@ export class VisualizeEmbeddable typeof inspectorAdapters === 'function' ? inspectorAdapters() : inspectorAdapters; } } - public getVisualizationDescription() { + public getDescription() { return this.vis.description; } diff --git a/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap index c0210c3915ce83..9c7bdc3397f9cb 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap +++ b/x-pack/plugins/lens/public/datatable_visualization/__snapshots__/expression.test.tsx.snap @@ -1,7 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`datatable_expression DatatableComponent it renders the title and value 1`] = ` -<VisualizationContainer> +<VisualizationContainer + reportTitle="My fanci metric chart" +> <EuiBasicTable className="lnsDataTable" columns={ diff --git a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx index 5ed693d2ead86c..af1773b4135998 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/expression.tsx @@ -33,6 +33,7 @@ export interface DatatableColumns { interface Args { title: string; + description?: string; columns: DatatableColumns & { type: 'lens_datatable_columns' }; } @@ -72,6 +73,10 @@ export const datatable: ExpressionFunctionDefinition< defaultMessage: 'Title', }), }, + description: { + types: ['string'], + help: '', + }, columns: { types: ['lens_datatable_columns'], help: '', @@ -207,7 +212,10 @@ export function DatatableComponent(props: DatatableRenderProps) { } return ( - <VisualizationContainer> + <VisualizationContainer + reportTitle={props.args.title} + reportDescription={props.args.description} + > <EuiBasicTable className="lnsDataTable" data-test-subj="lnsDataTable" diff --git a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx index 5b462f44b3dd53..1464ae6988a2df 100644 --- a/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/datatable_visualization/visualization.tsx @@ -194,7 +194,7 @@ export const datatableVisualization: Visualization<DatatableVisualizationState> }; }, - toExpression(state, datasourceLayers): Ast { + toExpression(state, datasourceLayers, { title, description } = {}): Ast { const layer = state.layers[0]; const datasource = datasourceLayers[layer.layerId]; const originalOrder = datasource.getTableSpec().map(({ columnId }) => columnId); @@ -211,6 +211,8 @@ export const datatableVisualization: Visualization<DatatableVisualizationState> type: 'function', function: 'lens_datatable', arguments: { + title: [title || ''], + description: [description || ''], columns: [ { type: 'expression', diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts index 952718e13c8cfd..e7568147dc568b 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/expression_helpers.ts @@ -73,7 +73,11 @@ export function buildExpression({ datasourceMap, datasourceStates, datasourceLayers, + title, + description, }: { + title?: string; + description?: string; visualization: Visualization | null; visualizationState: unknown; datasourceMap: Record<string, Datasource>; @@ -89,7 +93,10 @@ export function buildExpression({ if (visualization === null) { return null; } - const visualizationExpression = visualization.toExpression(visualizationState, datasourceLayers); + const visualizationExpression = visualization.toExpression(visualizationState, datasourceLayers, { + title, + description, + }); const completeExpression = prependDatasourceExpression( visualizationExpression, diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts index 6deb9ffd37a06f..1fe5224d0b1b4e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/state_helpers.ts @@ -61,6 +61,8 @@ export async function persistedStateToExpression( state: { visualization: visualizationState, datasourceStates: persistedDatasourceStates }, visualizationType, references, + title, + description, } = doc; if (!visualizationType) return null; const visualization = visualizations[visualizationType!]; @@ -78,6 +80,8 @@ export async function persistedStateToExpression( const datasourceLayers = createDatasourceLayers(datasources, datasourceStates); return buildExpression({ + title, + description, visualization, visualizationState, datasourceMap: datasources, diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 61a5d8cacdc4f3..16b19ca0af8497 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -295,6 +295,12 @@ export class Embeddable return this.deps.attributeService.getInputAsValueType(input); }; + // same API as Visualize + public getDescription() { + // mind that savedViz is loaded in async way here + return this.savedVis && this.savedVis.description; + } + destroy() { super.destroy(); if (this.domNode) { diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx index 0c92cdb2c31fcc..77a8ce64b21a28 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.test.tsx @@ -32,10 +32,21 @@ function sampleArgs() { accessor: 'a', layerId: 'l1', title: 'My fanci metric chart', + description: 'Fancy chart description', + metricTitle: 'My fanci metric chart', mode: 'full', }; - return { data, args }; + const noAttributesArgs: MetricConfig = { + accessor: 'a', + layerId: 'l1', + title: '', + description: '', + metricTitle: 'My fanci metric chart', + mode: 'full', + }; + + return { data, args, noAttributesArgs }; } describe('metric_expression', () => { @@ -53,7 +64,7 @@ describe('metric_expression', () => { }); describe('MetricChart component', () => { - test('it renders the title and value', () => { + test('it renders the all attributes when passed (title, description, metricTitle, value)', () => { const { data, args } = sampleArgs(); expect( @@ -61,6 +72,7 @@ describe('metric_expression', () => { ).toMatchInlineSnapshot(` <VisualizationContainer className="lnsMetricExpression__container" + reportDescription="Fancy chart description" reportTitle="My fanci metric chart" > <AutoScale> @@ -90,21 +102,66 @@ describe('metric_expression', () => { `); }); - test('it does not render title in reduced mode', () => { - const { data, args } = sampleArgs(); + test('it renders only chart content when title and description are empty strings', () => { + const { data, noAttributesArgs } = sampleArgs(); expect( shallow( <MetricChart data={data} - args={{ ...args, mode: 'reduced' }} + args={noAttributesArgs} formatFactory={(x) => x as IFieldFormat} /> ) ).toMatchInlineSnapshot(` <VisualizationContainer className="lnsMetricExpression__container" - reportTitle="My fanci metric chart" + reportDescription="" + reportTitle="" + > + <AutoScale> + <div + data-test-subj="lns_metric_value" + style={ + Object { + "fontSize": "60pt", + "fontWeight": 600, + } + } + > + 10110 + </div> + <div + data-test-subj="lns_metric_title" + style={ + Object { + "fontSize": "24pt", + } + } + > + My fanci metric chart + </div> + </AutoScale> + </VisualizationContainer> + `); + }); + + test('it does not render metricTitle in reduced mode', () => { + const { data, noAttributesArgs } = sampleArgs(); + + expect( + shallow( + <MetricChart + data={data} + args={{ ...noAttributesArgs, mode: 'reduced' }} + formatFactory={(x) => x as IFieldFormat} + /> + ) + ).toMatchInlineSnapshot(` + <VisualizationContainer + className="lnsMetricExpression__container" + reportDescription="" + reportTitle="" > <AutoScale> <div diff --git a/x-pack/plugins/lens/public/metric_visualization/expression.tsx b/x-pack/plugins/lens/public/metric_visualization/expression.tsx index 7eeef13240f721..29c9cc3e454c7a 100644 --- a/x-pack/plugins/lens/public/metric_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/expression.tsx @@ -43,6 +43,14 @@ export const metricChart: ExpressionFunctionDefinition< types: ['string'], help: 'The chart title.', }, + description: { + types: ['string'], + help: '', + }, + metricTitle: { + types: ['string'], + help: 'The title of the metric shown.', + }, accessor: { types: ['string'], help: 'The column whose value is being displayed', @@ -98,12 +106,16 @@ export function MetricChart({ args, formatFactory, }: MetricChartProps & { formatFactory: FormatFactory }) { - const { title, accessor, mode } = args; + const { metricTitle, title, description, accessor, mode } = args; let value = '-'; const firstTable = Object.values(data.tables)[0]; if (!accessor) { return ( - <VisualizationContainer reportTitle={title} className="lnsMetricExpression__container" /> + <VisualizationContainer + reportTitle={title} + reportDescription={description} + className="lnsMetricExpression__container" + /> ); } @@ -119,14 +131,18 @@ export function MetricChart({ } return ( - <VisualizationContainer reportTitle={title} className="lnsMetricExpression__container"> + <VisualizationContainer + reportTitle={title} + reportDescription={description} + className="lnsMetricExpression__container" + > <AutoScale> <div data-test-subj="lns_metric_value" style={{ fontSize: '60pt', fontWeight: 600 }}> {value} </div> {mode === 'full' && ( <div data-test-subj="lns_metric_title" style={{ fontSize: '24pt' }}> - {title} + {metricTitle} </div> )} </AutoScale> diff --git a/x-pack/plugins/lens/public/metric_visualization/types.ts b/x-pack/plugins/lens/public/metric_visualization/types.ts index 86a781716b3457..c4a3fd094abe61 100644 --- a/x-pack/plugins/lens/public/metric_visualization/types.ts +++ b/x-pack/plugins/lens/public/metric_visualization/types.ts @@ -11,5 +11,7 @@ export interface State { export interface MetricConfig extends State { title: string; + description: string; + metricTitle: string; mode: 'reduced' | 'full'; } diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts index aa3de93013e66b..80c7a174b32646 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.test.ts @@ -171,11 +171,17 @@ describe('metric_visualization', () => { "accessor": Array [ "a", ], + "description": Array [ + "", + ], + "metricTitle": Array [ + "shazm", + ], "mode": Array [ "full", ], "title": Array [ - "shazm", + "", ], }, "function": "lens_metric_chart", diff --git a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx index 72c07bed1acb22..77d189ce53d012 100644 --- a/x-pack/plugins/lens/public/metric_visualization/visualization.tsx +++ b/x-pack/plugins/lens/public/metric_visualization/visualization.tsx @@ -14,7 +14,7 @@ import { State } from './types'; const toExpression = ( state: State, datasourceLayers: Record<string, DatasourcePublicAPI>, - mode: 'reduced' | 'full' = 'full' + attributes?: { mode?: 'reduced' | 'full'; title?: string; description?: string } ): Ast | null => { if (!state.accessor) { return null; @@ -30,9 +30,11 @@ const toExpression = ( type: 'function', function: 'lens_metric_chart', arguments: { - title: [(operation && operation.label) || ''], + title: [attributes?.title || ''], + description: [attributes?.description || ''], + metricTitle: [(operation && operation.label) || ''], accessor: [state.accessor], - mode: [mode], + mode: [attributes?.mode || 'full'], }, }, ], @@ -104,7 +106,7 @@ export const metricVisualization: Visualization<State> = { toExpression, toPreviewExpression: (state, datasourceLayers) => - toExpression(state, datasourceLayers, 'reduced'), + toExpression(state, datasourceLayers, { mode: 'reduced' }), setDimension({ prevState, columnId }) { return { ...prevState, accessor: columnId }; diff --git a/x-pack/plugins/lens/public/pie_visualization/expression.tsx b/x-pack/plugins/lens/public/pie_visualization/expression.tsx index 89d93ab79233f7..d93145f29aa89a 100644 --- a/x-pack/plugins/lens/public/pie_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/expression.tsx @@ -37,6 +37,14 @@ export const pie: ExpressionFunctionDefinition< defaultMessage: 'Pie renderer', }), args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, groups: { types: ['string'], multi: true, diff --git a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx index d97ab146e000d7..8de810f9aa5d3f 100644 --- a/x-pack/plugins/lens/public/pie_visualization/render_function.tsx +++ b/x-pack/plugins/lens/public/pie_visualization/render_function.tsx @@ -228,7 +228,12 @@ export function PieComponent( ); } return ( - <VisualizationContainer className="lnsPieExpression__container" isReady={state.isReady}> + <VisualizationContainer + reportTitle={props.args.title} + reportDescription={props.args.description} + className="lnsPieExpression__container" + isReady={state.isReady} + > <Chart> <Settings // Legend is hidden in many scenarios diff --git a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts index f36b9efb930a93..d721a578a3788e 100644 --- a/x-pack/plugins/lens/public/pie_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/pie_visualization/to_expression.ts @@ -11,15 +11,16 @@ import { PieVisualizationState } from './types'; export function toExpression( state: PieVisualizationState, - datasourceLayers: Record<string, DatasourcePublicAPI> + datasourceLayers: Record<string, DatasourcePublicAPI>, + attributes: Partial<{ title: string; description: string }> = {} ) { - return expressionHelper(state, datasourceLayers, false); + return expressionHelper(state, datasourceLayers, { ...attributes, isPreview: false }); } function expressionHelper( state: PieVisualizationState, datasourceLayers: Record<string, DatasourcePublicAPI>, - isPreview: boolean + attributes: { isPreview: boolean; title?: string; description?: string } = { isPreview: false } ): Ast | null { const layer = state.layers[0]; const datasource = datasourceLayers[layer.layerId]; @@ -37,8 +38,10 @@ function expressionHelper( type: 'function', function: 'lens_pie', arguments: { + title: [attributes.title || ''], + description: [attributes.description || ''], shape: [state.shape], - hideLabels: [isPreview], + hideLabels: [attributes.isPreview], groups: operations.map((o) => o.columnId), metric: [layer.metric], numberDisplay: [layer.numberDisplay], @@ -57,5 +60,5 @@ export function toPreviewExpression( state: PieVisualizationState, datasourceLayers: Record<string, DatasourcePublicAPI> ) { - return expressionHelper(state, datasourceLayers, true); + return expressionHelper(state, datasourceLayers, { isPreview: true }); } diff --git a/x-pack/plugins/lens/public/pie_visualization/types.ts b/x-pack/plugins/lens/public/pie_visualization/types.ts index 74156bce2ea703..603c80aa00066e 100644 --- a/x-pack/plugins/lens/public/pie_visualization/types.ts +++ b/x-pack/plugins/lens/public/pie_visualization/types.ts @@ -28,6 +28,8 @@ export interface PieVisualizationState { } export type PieExpressionArgs = SharedLayerState & { + title?: string; + description?: string; shape: 'pie' | 'donut' | 'treemap'; hideLabels: boolean; }; diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index e97e0d612a2ee6..e5e8a645dd0e80 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -519,7 +519,8 @@ export interface Visualization<T = unknown> { toExpression: ( state: T, - datasourceLayers: Record<string, DatasourcePublicAPI> + datasourceLayers: Record<string, DatasourcePublicAPI>, + attributes?: Partial<{ title: string; description: string }> ) => Ast | string | null; /** * Expression to render a preview version of the chart in very constrained space. diff --git a/x-pack/plugins/lens/public/visualization_container.test.tsx b/x-pack/plugins/lens/public/visualization_container.test.tsx index 454399ec901215..7fc3541c63072b 100644 --- a/x-pack/plugins/lens/public/visualization_container.test.tsx +++ b/x-pack/plugins/lens/public/visualization_container.test.tsx @@ -43,13 +43,16 @@ describe('VisualizationContainer', () => { expect(reportingEl.prop('data-shared-item')).toBeTruthy(); }); - test('renders title for reporting, if provided', () => { + test('renders title and description for reporting, if provided', () => { const component = mount( - <VisualizationContainer reportTitle="shazam!">Hello!</VisualizationContainer> + <VisualizationContainer reportTitle="shazam!" reportDescription="Description"> + Hello! + </VisualizationContainer> ); const reportingEl = component.find('[data-shared-item]').first(); expect(reportingEl.prop('data-title')).toEqual('shazam!'); + expect(reportingEl.prop('data-description')).toEqual('Description'); }); test('renders style', () => { diff --git a/x-pack/plugins/lens/public/visualization_container.tsx b/x-pack/plugins/lens/public/visualization_container.tsx index 521d41b6f8d947..429dc63231cbee 100644 --- a/x-pack/plugins/lens/public/visualization_container.tsx +++ b/x-pack/plugins/lens/public/visualization_container.tsx @@ -12,6 +12,7 @@ import classNames from 'classnames'; interface Props extends React.HTMLAttributes<HTMLDivElement> { isReady?: boolean; reportTitle?: string; + reportDescription?: string; } /** @@ -21,16 +22,24 @@ interface Props extends React.HTMLAttributes<HTMLDivElement> { export function VisualizationContainer({ isReady = true, reportTitle, + reportDescription, children, className, ...rest }: Props) { + const attributes: Partial<{ 'data-title': string; 'data-description': string }> = {}; + if (reportTitle) { + attributes['data-title'] = reportTitle; + } + if (reportDescription) { + attributes['data-description'] = reportDescription; + } return ( <div data-shared-item data-render-complete={isReady} className={classNames(className, 'lnsVisualizationContainer')} - data-title={reportTitle} + {...attributes} {...rest} > {children} diff --git a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap index dd8c6377cacdc1..b35f915336eeed 100644 --- a/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap +++ b/x-pack/plugins/lens/public/xy_visualization/__snapshots__/to_expression.test.ts.snap @@ -27,6 +27,9 @@ Object { "type": "expression", }, ], + "description": Array [ + "", + ], "fittingFunction": Array [ "Carry", ], @@ -139,6 +142,9 @@ Object { "type": "expression", }, ], + "title": Array [ + "", + ], "xTitle": Array [ "", ], diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index 64e0a3670a9aae..f36525a9a0b146 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -85,6 +85,14 @@ export const xyChart: ExpressionFunctionDefinition< defaultMessage: 'An X/Y chart', }), args: { + title: { + types: ['string'], + help: 'The chart title.', + }, + description: { + types: ['string'], + help: '', + }, xTitle: { types: ['string'], help: i18n.translate('xpack.lens.xyChart.xTitle.help', { @@ -215,7 +223,12 @@ export function XYChartReportable(props: XYChartRenderProps) { }, [setState]); return ( - <VisualizationContainer className="lnsXyExpression__container" isReady={state.isReady}> + <VisualizationContainer + className="lnsXyExpression__container" + isReady={state.isReady} + reportTitle={props.args.title} + reportDescription={props.args.description} + > <MemoizedChart {...props} /> </VisualizationContainer> ); diff --git a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts index df8d571a1fdf85..5a3c8faa2d4762 100644 --- a/x-pack/plugins/lens/public/xy_visualization/to_expression.ts +++ b/x-pack/plugins/lens/public/xy_visualization/to_expression.ts @@ -15,7 +15,8 @@ interface ValidLayer extends LayerConfig { export const toExpression = ( state: State, - datasourceLayers: Record<string, DatasourcePublicAPI> + datasourceLayers: Record<string, DatasourcePublicAPI>, + attributes: Partial<{ title: string; description: string }> = {} ): Ast | null => { if (!state || !state.layers.length) { return null; @@ -31,7 +32,7 @@ export const toExpression = ( }); }); - return buildExpression(state, metadata, datasourceLayers); + return buildExpression(state, metadata, datasourceLayers, attributes); }; export function toPreviewExpression( @@ -81,7 +82,8 @@ export function getScaleType(metadata: OperationMetadata | null, defaultScale: S export const buildExpression = ( state: State, metadata: Record<string, Record<string, OperationMetadata | null>>, - datasourceLayers?: Record<string, DatasourcePublicAPI> + datasourceLayers?: Record<string, DatasourcePublicAPI>, + attributes: Partial<{ title: string; description: string }> = {} ): Ast | null => { const validLayers = state.layers.filter((layer): layer is ValidLayer => Boolean(layer.accessors.length) @@ -97,6 +99,8 @@ export const buildExpression = ( type: 'function', function: 'lens_xy_chart', arguments: { + title: [attributes?.title || ''], + description: [attributes?.description || ''], xTitle: [state.xTitle || ''], yTitle: [state.yTitle || ''], yRightTitle: [state.yRightTitle || ''], diff --git a/x-pack/plugins/lens/public/xy_visualization/types.ts b/x-pack/plugins/lens/public/xy_visualization/types.ts index 185fa20f169eec..cac982f852c7a4 100644 --- a/x-pack/plugins/lens/public/xy_visualization/types.ts +++ b/x-pack/plugins/lens/public/xy_visualization/types.ts @@ -383,6 +383,8 @@ export type LayerArgs = LayerConfig & { // Arguments to XY chart expression, with computed properties export interface XYArgs { + title?: string; + description?: string; xTitle: string; yTitle: string; yRightTitle: string; From a61f4d4cbfd08dfe2502cebd059464bf3df75707 Mon Sep 17 00:00:00 2001 From: Chris Roberson <chrisronline@gmail.com> Date: Thu, 1 Oct 2020 12:28:34 -0400 Subject: [PATCH 040/128] [Monitoring] Missing data alert (#78208) * WIP for alert * Surface alert most places * Fix up alert placement * Fix tests * Type fix * Update copy * Add alert presence to APM in the UI * Fetch data a little differently * We don't need moment * Add tests * PR feedback * Update copy * Fix up bug around grabbing old data * PR feedback * PR feedback * Fix tests --- x-pack/plugins/monitoring/common/constants.ts | 2 + x-pack/plugins/monitoring/common/types.ts | 6 +- .../monitoring/public/alerts/badge.tsx | 15 +- .../monitoring/public/alerts/callout.tsx | 11 +- .../public/alerts/filter_alert_states.ts | 23 + .../expression.tsx | 61 +++ .../missing_monitoring_data_alert/index.ts | 7 + .../missing_monitoring_data_alert.tsx | 28 + .../validation.tsx | 35 ++ .../monitoring/public/alerts/panel.tsx | 10 +- .../monitoring/public/alerts/status.tsx | 15 +- .../components/apm/instance/instance.js | 14 +- .../public/components/apm/instance/status.js | 3 +- .../components/apm/instances/instances.js | 32 +- .../public/components/apm/instances/status.js | 3 +- .../public/components/apm/overview/index.js | 4 +- .../public/components/beats/beat/beat.js | 18 +- .../components/beats/listing/listing.js | 29 +- .../components/beats/overview/overview.js | 3 +- .../public/components/beats/stats.js | 4 +- .../components/cluster/overview/apm_panel.js | 29 +- .../cluster/overview/beats_panel.js | 25 +- .../cluster/overview/elasticsearch_panel.js | 2 + .../components/cluster/overview/index.js | 54 +- .../cluster/overview/kibana_panel.js | 8 +- .../cluster/overview/logstash_panel.js | 3 +- .../components/elasticsearch/node/node.js | 15 +- .../components/elasticsearch/nodes/nodes.js | 10 +- .../components/kibana/instances/instances.js | 13 +- .../components/logstash/listing/listing.js | 13 +- x-pack/plugins/monitoring/public/plugin.ts | 2 + .../public/views/apm/instance/index.js | 18 +- .../public/views/apm/instances/index.js | 18 +- .../public/views/apm/overview/index.js | 24 +- .../public/views/beats/beat/index.js | 18 +- .../public/views/beats/listing/index.js | 18 +- .../public/views/beats/overview/index.js | 24 +- .../elasticsearch/node/advanced/index.js | 3 +- .../public/views/elasticsearch/node/index.js | 3 +- .../public/views/elasticsearch/nodes/index.js | 8 +- .../public/views/kibana/instance/index.js | 24 +- .../public/views/kibana/instances/index.js | 8 +- .../views/logstash/node/advanced/index.js | 28 +- .../public/views/logstash/node/index.js | 24 +- .../public/views/logstash/nodes/index.js | 8 +- .../server/alerts/alerts_factory.test.ts | 2 +- .../server/alerts/alerts_factory.ts | 3 + .../monitoring/server/alerts/base_alert.ts | 41 +- .../server/alerts/cpu_usage_alert.ts | 4 +- .../plugins/monitoring/server/alerts/index.ts | 1 + .../missing_monitoring_data_alert.test.ts | 459 ++++++++++++++++ .../alerts/missing_monitoring_data_alert.ts | 504 ++++++++++++++++++ .../monitoring/server/alerts/types.d.ts | 16 + .../server/lib/alerts/fetch_clusters.ts | 19 +- .../fetch_missing_monitoring_data.test.ts | 249 +++++++++ .../alerts/fetch_missing_monitoring_data.ts | 275 ++++++++++ .../get_listing_link_for_stack_product.ts | 28 + .../lib/alerts/get_stack_product_label.ts | 17 + .../get_type_label_for_stack_product.ts | 51 ++ 59 files changed, 2303 insertions(+), 89 deletions(-) create mode 100644 x-pack/plugins/monitoring/public/alerts/filter_alert_states.ts create mode 100644 x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.tsx create mode 100644 x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/index.ts create mode 100644 x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx create mode 100644 x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/validation.tsx create mode 100644 x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts create mode 100644 x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.test.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/get_listing_link_for_stack_product.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/get_stack_product_label.ts create mode 100644 x-pack/plugins/monitoring/server/lib/alerts/get_type_label_for_stack_product.ts diff --git a/x-pack/plugins/monitoring/common/constants.ts b/x-pack/plugins/monitoring/common/constants.ts index 6eb0d6e93d58a0..860f6439f3fdf5 100644 --- a/x-pack/plugins/monitoring/common/constants.ts +++ b/x-pack/plugins/monitoring/common/constants.ts @@ -236,6 +236,7 @@ export const ALERT_NODES_CHANGED = `${ALERT_PREFIX}alert_nodes_changed`; export const ALERT_ELASTICSEARCH_VERSION_MISMATCH = `${ALERT_PREFIX}alert_elasticsearch_version_mismatch`; export const ALERT_KIBANA_VERSION_MISMATCH = `${ALERT_PREFIX}alert_kibana_version_mismatch`; export const ALERT_LOGSTASH_VERSION_MISMATCH = `${ALERT_PREFIX}alert_logstash_version_mismatch`; +export const ALERT_MISSING_MONITORING_DATA = `${ALERT_PREFIX}alert_missing_monitoring_data`; /** * A listing of all alert types @@ -249,6 +250,7 @@ export const ALERTS = [ ALERT_ELASTICSEARCH_VERSION_MISMATCH, ALERT_KIBANA_VERSION_MISMATCH, ALERT_LOGSTASH_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, ]; /** diff --git a/x-pack/plugins/monitoring/common/types.ts b/x-pack/plugins/monitoring/common/types.ts index 4216a046fef9f3..825d2e454b3bba 100644 --- a/x-pack/plugins/monitoring/common/types.ts +++ b/x-pack/plugins/monitoring/common/types.ts @@ -31,10 +31,14 @@ export interface CommonAlertFilter { nodeUuid?: string; } -export interface CommonAlertCpuUsageFilter extends CommonAlertFilter { +export interface CommonAlertNodeUuidFilter extends CommonAlertFilter { nodeUuid: string; } +export interface CommonAlertStackProductFilter extends CommonAlertFilter { + stackProduct: string; +} + export interface CommonAlertParamDetail { label: string; type: AlertParamType; diff --git a/x-pack/plugins/monitoring/public/alerts/badge.tsx b/x-pack/plugins/monitoring/public/alerts/badge.tsx index 1d67eebb1705cc..cf75939b14efc3 100644 --- a/x-pack/plugins/monitoring/public/alerts/badge.tsx +++ b/x-pack/plugins/monitoring/public/alerts/badge.tsx @@ -18,7 +18,7 @@ import { CommonAlertStatus, CommonAlertState } from '../../common/types'; import { AlertSeverity } from '../../common/enums'; // @ts-ignore import { formatDateTimeLocal } from '../../common/formatting'; -import { AlertState } from '../../server/alerts/types'; +import { AlertMessage, AlertState } from '../../server/alerts/types'; import { AlertPanel } from './panel'; import { Legacy } from '../legacy_shims'; import { isInSetupMode } from '../lib/setup_mode'; @@ -39,9 +39,10 @@ interface AlertInPanel { interface Props { alerts: { [alertTypeId: string]: CommonAlertStatus }; stateFilter: (state: AlertState) => boolean; + nextStepsFilter: (nextStep: AlertMessage) => boolean; } export const AlertsBadge: React.FC<Props> = (props: Props) => { - const { stateFilter = () => true } = props; + const { stateFilter = () => true, nextStepsFilter = () => true } = props; const [showPopover, setShowPopover] = React.useState<AlertSeverity | boolean | null>(null); const inSetupMode = isInSetupMode(); const alerts = Object.values(props.alerts).filter(Boolean); @@ -80,7 +81,7 @@ export const AlertsBadge: React.FC<Props> = (props: Props) => { id: index + 1, title: alertStatus.alert.label, width: 400, - content: <AlertPanel alert={alertStatus} />, + content: <AlertPanel alert={alertStatus} nextStepsFilter={nextStepsFilter} />, }; }), ]; @@ -158,7 +159,13 @@ export const AlertsBadge: React.FC<Props> = (props: Props) => { id: index + 1, title: getDateFromState(alertStatus.alertState), width: 400, - content: <AlertPanel alert={alertStatus.alert} alertState={alertStatus.alertState} />, + content: ( + <AlertPanel + alert={alertStatus.alert} + alertState={alertStatus.alertState} + nextStepsFilter={nextStepsFilter} + /> + ), }; }), ]; diff --git a/x-pack/plugins/monitoring/public/alerts/callout.tsx b/x-pack/plugins/monitoring/public/alerts/callout.tsx index cad98dd1e6aec4..1ddd41c2684565 100644 --- a/x-pack/plugins/monitoring/public/alerts/callout.tsx +++ b/x-pack/plugins/monitoring/public/alerts/callout.tsx @@ -32,9 +32,10 @@ const TYPES = [ interface Props { alerts: { [alertTypeId: string]: CommonAlertStatus }; stateFilter: (state: AlertState) => boolean; + nextStepsFilter: (nextStep: AlertMessage) => boolean; } export const AlertsCallout: React.FC<Props> = (props: Props) => { - const { alerts, stateFilter = () => true } = props; + const { alerts, stateFilter = () => true, nextStepsFilter = () => true } = props; const callouts = TYPES.map((type) => { const list = []; @@ -56,11 +57,11 @@ export const AlertsCallout: React.FC<Props> = (props: Props) => { const nextStepsUi = state.ui.message.nextSteps && state.ui.message.nextSteps.length ? ( <ul> - {state.ui.message.nextSteps.map( - (step: AlertMessage, nextStepIndex: number) => ( + {state.ui.message.nextSteps + .filter(nextStepsFilter) + .map((step: AlertMessage, nextStepIndex: number) => ( <li key={nextStepIndex}>{replaceTokens(step)}</li> - ) - )} + ))} </ul> ) : null; diff --git a/x-pack/plugins/monitoring/public/alerts/filter_alert_states.ts b/x-pack/plugins/monitoring/public/alerts/filter_alert_states.ts new file mode 100644 index 00000000000000..63714a6921e3f1 --- /dev/null +++ b/x-pack/plugins/monitoring/public/alerts/filter_alert_states.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { CommonAlertState, CommonAlertStatus } from '../../common/types'; + +export function filterAlertStates( + alerts: { [type: string]: CommonAlertStatus }, + filter: (type: string, state: CommonAlertState) => boolean +) { + return Object.keys(alerts).reduce( + (accum: { [type: string]: CommonAlertStatus }, type: string) => { + accum[type] = { + ...alerts[type], + states: alerts[type].states.filter((state) => filter(type, state)), + }; + return accum; + }, + {} + ); +} diff --git a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.tsx b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.tsx new file mode 100644 index 00000000000000..7dc6155de529ee --- /dev/null +++ b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/expression.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { Fragment } from 'react'; +import { EuiForm, EuiSpacer } from '@elastic/eui'; +import { CommonAlertParamDetails } from '../../../common/types'; +import { AlertParamDuration } from '../flyout_expressions/alert_param_duration'; +import { AlertParamType } from '../../../common/enums'; +import { AlertParamPercentage } from '../flyout_expressions/alert_param_percentage'; + +export interface Props { + alertParams: { [property: string]: any }; + setAlertParams: (property: string, value: any) => void; + setAlertProperty: (property: string, value: any) => void; + errors: { [key: string]: string[] }; + paramDetails: CommonAlertParamDetails; +} + +export const Expression: React.FC<Props> = (props) => { + const { alertParams, paramDetails, setAlertParams, errors } = props; + + const alertParamsUi = Object.keys(alertParams).map((alertParamName) => { + const details = paramDetails[alertParamName]; + const value = alertParams[alertParamName]; + + switch (details.type) { + case AlertParamType.Duration: + return ( + <AlertParamDuration + key={alertParamName} + name={alertParamName} + duration={value} + label={details.label} + errors={errors[alertParamName]} + setAlertParams={setAlertParams} + /> + ); + case AlertParamType.Percentage: + return ( + <AlertParamPercentage + key={alertParamName} + name={alertParamName} + label={details.label} + percentage={value} + errors={errors[alertParamName]} + setAlertParams={setAlertParams} + /> + ); + } + }); + + return ( + <Fragment> + <EuiForm component="form">{alertParamsUi}</EuiForm> + <EuiSpacer /> + </Fragment> + ); +}; diff --git a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/index.ts b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/index.ts new file mode 100644 index 00000000000000..5169601c0e6e34 --- /dev/null +++ b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { createMissingMonitoringDataAlertType } from './missing_monitoring_data_alert'; diff --git a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx new file mode 100644 index 00000000000000..bcea98592adb29 --- /dev/null +++ b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/missing_monitoring_data_alert.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { AlertTypeModel } from '../../../../triggers_actions_ui/public/types'; +import { validate } from './validation'; +import { ALERT_MISSING_MONITORING_DATA } from '../../../common/constants'; +import { Expression } from './expression'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { MissingMonitoringDataAlert } from '../../../server/alerts'; + +export function createMissingMonitoringDataAlertType(): AlertTypeModel { + const alert = new MissingMonitoringDataAlert(); + return { + id: ALERT_MISSING_MONITORING_DATA, + name: alert.label, + iconClass: 'bell', + alertParamsExpression: (props: any) => ( + <Expression {...props} paramDetails={MissingMonitoringDataAlert.paramDetails} /> + ), + validate, + defaultActionMessage: '{{context.internalFullMessage}}', + requiresAppContext: true, + }; +} diff --git a/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/validation.tsx b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/validation.tsx new file mode 100644 index 00000000000000..fe84de9bd00ea3 --- /dev/null +++ b/x-pack/plugins/monitoring/public/alerts/missing_monitoring_data_alert/validation.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ValidationResult } from '../../../../triggers_actions_ui/public/types'; + +export function validate(opts: any): ValidationResult { + const validationResult = { errors: {} }; + + const errors: { [key: string]: string[] } = { + duration: [], + limit: [], + }; + if (!opts.duration) { + errors.duration.push( + i18n.translate('xpack.monitoring.alerts.missingData.validation.duration', { + defaultMessage: 'A valid duration is required.', + }) + ); + } + if (!opts.limit) { + errors.limit.push( + i18n.translate('xpack.monitoring.alerts.missingData.validation.limit', { + defaultMessage: 'A valid limit is required.', + }) + ); + } + + validationResult.errors = errors; + return validationResult; +} diff --git a/x-pack/plugins/monitoring/public/alerts/panel.tsx b/x-pack/plugins/monitoring/public/alerts/panel.tsx index 91604acf115fa1..ee605592e94081 100644 --- a/x-pack/plugins/monitoring/public/alerts/panel.tsx +++ b/x-pack/plugins/monitoring/public/alerts/panel.tsx @@ -30,11 +30,13 @@ import { BASE_ALERT_API_PATH } from '../../../alerts/common'; interface Props { alert: CommonAlertStatus; alertState?: CommonAlertState; + nextStepsFilter: (nextStep: AlertMessage) => boolean; } export const AlertPanel: React.FC<Props> = (props: Props) => { const { alert: { alert }, alertState, + nextStepsFilter = () => true, } = props; const [showFlyout, setShowFlyout] = React.useState(false); const [isEnabled, setIsEnabled] = React.useState(alert.rawAlert.enabled); @@ -198,9 +200,11 @@ export const AlertPanel: React.FC<Props> = (props: Props) => { const nextStepsUi = alertState.state.ui.message.nextSteps && alertState.state.ui.message.nextSteps.length ? ( <EuiListGroup> - {alertState.state.ui.message.nextSteps.map((step: AlertMessage, index: number) => ( - <EuiListGroupItem size="s" key={index} label={replaceTokens(step)} /> - ))} + {alertState.state.ui.message.nextSteps + .filter(nextStepsFilter) + .map((step: AlertMessage, index: number) => ( + <EuiListGroupItem size="s" key={index} label={replaceTokens(step)} /> + ))} </EuiListGroup> ) : null; diff --git a/x-pack/plugins/monitoring/public/alerts/status.tsx b/x-pack/plugins/monitoring/public/alerts/status.tsx index 0407ddfecf5e90..dba66df0e44741 100644 --- a/x-pack/plugins/monitoring/public/alerts/status.tsx +++ b/x-pack/plugins/monitoring/public/alerts/status.tsx @@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { CommonAlertStatus } from '../../common/types'; import { AlertSeverity } from '../../common/enums'; -import { AlertState } from '../../server/alerts/types'; +import { AlertMessage, AlertState } from '../../server/alerts/types'; import { AlertsBadge } from './badge'; import { isInSetupMode } from '../lib/setup_mode'; @@ -18,9 +18,16 @@ interface Props { showBadge: boolean; showOnlyCount: boolean; stateFilter: (state: AlertState) => boolean; + nextStepsFilter: (nextStep: AlertMessage) => boolean; } export const AlertsStatus: React.FC<Props> = (props: Props) => { - const { alerts, showBadge = false, showOnlyCount = false, stateFilter = () => true } = props; + const { + alerts, + showBadge = false, + showOnlyCount = false, + stateFilter = () => true, + nextStepsFilter = () => true, + } = props; const inSetupMode = isInSetupMode(); if (!alerts) { @@ -71,7 +78,9 @@ export const AlertsStatus: React.FC<Props> = (props: Props) => { } if (showBadge || inSetupMode) { - return <AlertsBadge alerts={alerts} stateFilter={stateFilter} />; + return ( + <AlertsBadge alerts={alerts} stateFilter={stateFilter} nextStepsFilter={nextStepsFilter} /> + ); } const severity = atLeastOneDanger ? AlertSeverity.Danger : AlertSeverity.Warning; diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js index eec24e741ac416..8934bbc41f5f6b 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instance/instance.js +++ b/x-pack/plugins/monitoring/public/components/apm/instance/instance.js @@ -18,8 +18,9 @@ import { } from '@elastic/eui'; import { Status } from './status'; import { FormattedMessage } from '@kbn/i18n/react'; +import { AlertsCallout } from '../../../alerts/callout'; -export function ApmServerInstance({ summary, metrics, ...props }) { +export function ApmServerInstance({ summary, metrics, alerts, ...props }) { const seriesToShow = [ metrics.apm_requests, metrics.apm_responses_valid, @@ -58,9 +59,18 @@ export function ApmServerInstance({ summary, metrics, ...props }) { </h1> </EuiScreenReaderOnly> <EuiPanel> - <Status stats={summary} /> + <Status stats={summary} alerts={alerts} /> </EuiPanel> <EuiSpacer size="m" /> + <AlertsCallout + alerts={alerts} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('APM servers')) { + return false; + } + return true; + }} + /> <EuiPageContent> <EuiFlexGroup wrap>{charts}</EuiFlexGroup> </EuiPageContent> diff --git a/x-pack/plugins/monitoring/public/components/apm/instance/status.js b/x-pack/plugins/monitoring/public/components/apm/instance/status.js index 9b78db54a2aa75..02a15d214ab9b3 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instance/status.js +++ b/x-pack/plugins/monitoring/public/components/apm/instance/status.js @@ -14,7 +14,7 @@ import { CALCULATE_DURATION_SINCE } from '../../../../common/constants'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -export function Status({ stats }) { +export function Status({ alerts, stats }) { const { name, output, version, uptime, timeOfLastEvent } = stats; const metrics = [ @@ -78,6 +78,7 @@ export function Status({ stats }) { return ( <SummaryStatus metrics={metrics} + alerts={alerts} IconComponent={IconComponent} data-test-subj="apmDetailStatus" /> diff --git a/x-pack/plugins/monitoring/public/components/apm/instances/instances.js b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js index e05ba1878caed7..4932fb9068fcc8 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instances/instances.js +++ b/x-pack/plugins/monitoring/public/components/apm/instances/instances.js @@ -28,8 +28,9 @@ import { SetupModeBadge } from '../../setup_mode/badge'; import { FormattedMessage } from '@kbn/i18n/react'; import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; import { SetupModeFeature } from '../../../../common/enums'; +import { AlertsStatus } from '../../../alerts/status'; -function getColumns(setupMode) { +function getColumns(alerts, setupMode) { return [ { name: i18n.translate('xpack.monitoring.apm.instances.nameTitle', { @@ -71,6 +72,29 @@ function getColumns(setupMode) { ); }, }, + { + name: i18n.translate('xpack.monitoring.beats.instances.alertsColumnTitle', { + defaultMessage: 'Alerts', + }), + field: 'alerts', + width: '175px', + sortable: true, + render: (_field, beat) => { + return ( + <AlertsStatus + showBadge={true} + alerts={alerts} + stateFilter={(state) => state.stackProductUuid === beat.uuid} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('APM servers')) { + return false; + } + return true; + }} + /> + ); + }, + }, { name: i18n.translate('xpack.monitoring.apm.instances.outputEnabledTitle', { defaultMessage: 'Output Enabled', @@ -127,7 +151,7 @@ function getColumns(setupMode) { ]; } -export function ApmServerInstances({ apms, setupMode }) { +export function ApmServerInstances({ apms, alerts, setupMode }) { const { pagination, sorting, onTableChange, data } = apms; let setupModeCallout = null; @@ -157,7 +181,7 @@ export function ApmServerInstances({ apms, setupMode }) { </h1> </EuiScreenReaderOnly> <EuiPanel> - <Status stats={data.stats} /> + <Status stats={data.stats} alerts={alerts} /> </EuiPanel> <EuiSpacer size="m" /> <EuiPageContent> @@ -165,7 +189,7 @@ export function ApmServerInstances({ apms, setupMode }) { <EuiMonitoringTable className="apmInstancesTable" rows={data.apms} - columns={getColumns(setupMode)} + columns={getColumns(alerts, setupMode)} sorting={sorting} pagination={pagination} setupMode={setupMode} diff --git a/x-pack/plugins/monitoring/public/components/apm/instances/status.js b/x-pack/plugins/monitoring/public/components/apm/instances/status.js index 818671626ec06d..c15e22029d9de4 100644 --- a/x-pack/plugins/monitoring/public/components/apm/instances/status.js +++ b/x-pack/plugins/monitoring/public/components/apm/instances/status.js @@ -14,7 +14,7 @@ import { CALCULATE_DURATION_SINCE } from '../../../../common/constants'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -export function Status({ stats }) { +export function Status({ alerts, stats }) { const { apms: { total }, totalEvents, @@ -68,6 +68,7 @@ export function Status({ stats }) { return ( <SummaryStatus metrics={metrics} + alerts={alerts} IconComponent={IconComponent} data-test-subj="apmDetailStatus" /> diff --git a/x-pack/plugins/monitoring/public/components/apm/overview/index.js b/x-pack/plugins/monitoring/public/components/apm/overview/index.js index 35efa6ac67ea89..b10592c2a49d29 100644 --- a/x-pack/plugins/monitoring/public/components/apm/overview/index.js +++ b/x-pack/plugins/monitoring/public/components/apm/overview/index.js @@ -19,7 +19,7 @@ import { import { Status } from '../instances/status'; import { FormattedMessage } from '@kbn/i18n/react'; -export function ApmOverview({ stats, metrics, ...props }) { +export function ApmOverview({ stats, metrics, alerts, ...props }) { const seriesToShow = [ metrics.apm_responses_valid, metrics.apm_responses_errors, @@ -54,7 +54,7 @@ export function ApmOverview({ stats, metrics, ...props }) { </h1> </EuiScreenReaderOnly> <EuiPanel> - <Status stats={stats} /> + <Status stats={stats} alerts={alerts} /> </EuiPanel> <EuiSpacer size="m" /> <EuiPageContent> diff --git a/x-pack/plugins/monitoring/public/components/beats/beat/beat.js b/x-pack/plugins/monitoring/public/components/beats/beat/beat.js index f489271659bfe1..470cdf588ca3d7 100644 --- a/x-pack/plugins/monitoring/public/components/beats/beat/beat.js +++ b/x-pack/plugins/monitoring/public/components/beats/beat/beat.js @@ -20,8 +20,9 @@ import { import { i18n } from '@kbn/i18n'; import { SummaryStatus } from '../../summary_status'; import { FormattedMessage } from '@kbn/i18n/react'; +import { AlertsCallout } from '../../../alerts/callout'; -export function Beat({ summary, metrics, ...props }) { +export function Beat({ summary, metrics, alerts, ...props }) { const metricsToShow = [ metrics.beat_event_rates, metrics.beat_fail_rates, @@ -134,13 +135,26 @@ export function Beat({ summary, metrics, ...props }) { <EuiPage> <EuiPageBody> <EuiPanel> - <SummaryStatus metrics={summarytStatsTop} data-test-subj="beatSummaryStatus01" /> + <SummaryStatus + metrics={summarytStatsTop} + alerts={alerts} + data-test-subj="beatSummaryStatus01" + /> </EuiPanel> <EuiSpacer size="m" /> <EuiPanel> <SummaryStatus metrics={summarytStatsBot} data-test-subj="beatSummaryStatus02" /> </EuiPanel> <EuiSpacer size="m" /> + <AlertsCallout + alerts={alerts} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Beat instances')) { + return false; + } + return true; + }} + /> <EuiPageContent> <EuiScreenReaderOnly> <h1> diff --git a/x-pack/plugins/monitoring/public/components/beats/listing/listing.js b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js index 60a35e00a4c636..dc65cd38aac533 100644 --- a/x-pack/plugins/monitoring/public/components/beats/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/beats/listing/listing.js @@ -26,10 +26,12 @@ import { SetupModeBadge } from '../../setup_mode/badge'; import { FormattedMessage } from '@kbn/i18n/react'; import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; import { SetupModeFeature } from '../../../../common/enums'; +import { AlertsStatus } from '../../../alerts/status'; export class Listing extends PureComponent { getColumns() { const setupMode = this.props.setupMode; + const alerts = this.props.alerts; return [ { @@ -72,6 +74,29 @@ export class Listing extends PureComponent { ); }, }, + { + name: i18n.translate('xpack.monitoring.beats.instances.alertsColumnTitle', { + defaultMessage: 'Alerts', + }), + field: 'alerts', + width: '175px', + sortable: true, + render: (_field, beat) => { + return ( + <AlertsStatus + showBadge={true} + alerts={alerts} + stateFilter={(state) => state.stackProductUuid === beat.uuid} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Beat instances')) { + return false; + } + return true; + }} + /> + ); + }, + }, { name: i18n.translate('xpack.monitoring.beats.instances.typeTitle', { defaultMessage: 'Type', @@ -122,7 +147,7 @@ export class Listing extends PureComponent { } render() { - const { stats, data, sorting, pagination, onTableChange, setupMode } = this.props; + const { stats, data, sorting, pagination, onTableChange, setupMode, alerts } = this.props; let setupModeCallOut = null; if (isSetupModeFeatureEnabled(SetupModeFeature.MetricbeatMigration)) { @@ -155,7 +180,7 @@ export class Listing extends PureComponent { </h1> </EuiScreenReaderOnly> <EuiPanel> - <Stats stats={stats} /> + <Stats stats={stats} alerts={alerts} /> </EuiPanel> <EuiSpacer size="m" /> <EuiPageContent> diff --git a/x-pack/plugins/monitoring/public/components/beats/overview/overview.js b/x-pack/plugins/monitoring/public/components/beats/overview/overview.js index 897f017f44f418..aa09da31504d3f 100644 --- a/x-pack/plugins/monitoring/public/components/beats/overview/overview.js +++ b/x-pack/plugins/monitoring/public/components/beats/overview/overview.js @@ -84,6 +84,7 @@ export function BeatsOverview({ latestVersions, stats, metrics, + alerts, ...props }) { const seriesToShow = [ @@ -113,7 +114,7 @@ export function BeatsOverview({ </h1> </EuiScreenReaderOnly> <EuiPanel> - <Stats stats={stats} /> + <Stats stats={stats} alerts={alerts} /> </EuiPanel> <EuiSpacer size="m" /> <EuiPanel>{renderLatestActive(latestActive, latestTypes, latestVersions)}</EuiPanel> diff --git a/x-pack/plugins/monitoring/public/components/beats/stats.js b/x-pack/plugins/monitoring/public/components/beats/stats.js index 89ec10bbaf1bb6..c1f06af14a4532 100644 --- a/x-pack/plugins/monitoring/public/components/beats/stats.js +++ b/x-pack/plugins/monitoring/public/components/beats/stats.js @@ -9,7 +9,7 @@ import { formatMetric } from '../../lib/format_number'; import { SummaryStatus } from '../summary_status'; import { i18n } from '@kbn/i18n'; -export function Stats({ stats }) { +export function Stats({ stats, alerts }) { const { total, types, @@ -51,5 +51,5 @@ export function Stats({ stats }) { 'data-test-subj': 'bytesSent', }); - return <SummaryStatus metrics={metrics} data-test-subj="beatsSummaryStatus" />; + return <SummaryStatus metrics={metrics} alerts={alerts} data-test-subj="beatsSummaryStatus" />; } diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js index 4bf07710393ea7..d0d5a36c3829b2 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/apm_panel.js @@ -24,14 +24,22 @@ import { EuiFlexGroup, } from '@elastic/eui'; import { formatTimestampToDuration } from '../../../../common'; -import { CALCULATE_DURATION_SINCE, APM_SYSTEM_ID } from '../../../../common/constants'; +import { + CALCULATE_DURATION_SINCE, + APM_SYSTEM_ID, + ALERT_MISSING_MONITORING_DATA, +} from '../../../../common/constants'; import { SetupModeTooltip } from '../../setup_mode/tooltip'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; import { SetupModeFeature } from '../../../../common/enums'; +import { shouldShowAlertBadge } from '../../../alerts/lib/should_show_alert_badge'; +import { AlertsBadge } from '../../../alerts/badge'; + +const SERVERS_PANEL_ALERTS = [ALERT_MISSING_MONITORING_DATA]; export function ApmPanel(props) { - const { setupMode } = props; + const { setupMode, alerts } = props; const apmsTotal = get(props, 'apms.total') || 0; // Do not show if we are not in setup mode if (apmsTotal === 0 && !setupMode.enabled) { @@ -50,6 +58,16 @@ export function ApmPanel(props) { /> ) : null; + let apmServersAlertStatus = null; + if (shouldShowAlertBadge(alerts, SERVERS_PANEL_ALERTS)) { + const alertsList = SERVERS_PANEL_ALERTS.map((alertType) => alerts[alertType]); + apmServersAlertStatus = ( + <EuiFlexItem grow={false}> + <AlertsBadge alerts={alertsList} /> + </EuiFlexItem> + ); + } + return ( <ClusterItemContainer {...props} @@ -140,7 +158,12 @@ export function ApmPanel(props) { </h3> </EuiTitle> </EuiFlexItem> - {setupModeMetricbeatMigrationTooltip} + <EuiFlexItem grow={false}> + <EuiFlexGroup gutterSize="s" alignItems="center"> + {setupModeMetricbeatMigrationTooltip} + {apmServersAlertStatus} + </EuiFlexGroup> + </EuiFlexItem> </EuiFlexGroup> <EuiHorizontalRule margin="m" /> <EuiDescriptionList type="column"> diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/beats_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/beats_panel.js index 3d9b455787a96d..628f57a0ffde3e 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/beats_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/beats_panel.js @@ -23,13 +23,17 @@ import { ClusterItemContainer, DisabledIfNoDataAndInSetupModeLink } from './help import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { SetupModeTooltip } from '../../setup_mode/tooltip'; -import { BEATS_SYSTEM_ID } from '../../../../common/constants'; +import { ALERT_MISSING_MONITORING_DATA, BEATS_SYSTEM_ID } from '../../../../common/constants'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; import { SetupModeFeature } from '../../../../common/enums'; +import { shouldShowAlertBadge } from '../../../alerts/lib/should_show_alert_badge'; +import { AlertsBadge } from '../../../alerts/badge'; + +const BEATS_PANEL_ALERTS = [ALERT_MISSING_MONITORING_DATA]; export function BeatsPanel(props) { - const { setupMode } = props; + const { setupMode, alerts } = props; const beatsTotal = get(props, 'beats.total') || 0; // Do not show if we are not in setup mode if (beatsTotal === 0 && !setupMode.enabled) { @@ -47,6 +51,16 @@ export function BeatsPanel(props) { /> ) : null; + let beatsAlertsStatus = null; + if (shouldShowAlertBadge(alerts, BEATS_PANEL_ALERTS)) { + const alertsList = BEATS_PANEL_ALERTS.map((alertType) => alerts[alertType]); + beatsAlertsStatus = ( + <EuiFlexItem grow={false}> + <AlertsBadge alerts={alertsList} /> + </EuiFlexItem> + ); + } + const beatTypes = props.beats.types.map((beat, index) => { return [ <EuiDescriptionListTitle @@ -145,7 +159,12 @@ export function BeatsPanel(props) { </h3> </EuiTitle> </EuiFlexItem> - {setupModeMetricbeatMigrationTooltip} + <EuiFlexItem grow={false}> + <EuiFlexGroup gutterSize="s" alignItems="center"> + {setupModeMetricbeatMigrationTooltip} + {beatsAlertsStatus} + </EuiFlexGroup> + </EuiFlexItem> </EuiFlexGroup> <EuiHorizontalRule margin="m" /> <EuiDescriptionList type="column">{beatTypes}</EuiDescriptionList> diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js index 61a24f31ca89ae..667f64458b8f97 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/elasticsearch_panel.js @@ -43,6 +43,7 @@ import { ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, } from '../../../../common/constants'; import { AlertsBadge } from '../../../alerts/badge'; import { shouldShowAlertBadge } from '../../../alerts/lib/should_show_alert_badge'; @@ -161,6 +162,7 @@ const NODES_PANEL_ALERTS = [ ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_ELASTICSEARCH_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, ]; export function ElasticsearchPanel(props) { diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/index.js b/x-pack/plugins/monitoring/public/components/cluster/overview/index.js index 66701c1dfd95a0..aebd1cee5f0be0 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/index.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/index.js @@ -12,7 +12,16 @@ import { BeatsPanel } from './beats_panel'; import { EuiPage, EuiPageBody, EuiScreenReaderOnly } from '@elastic/eui'; import { ApmPanel } from './apm_panel'; import { FormattedMessage } from '@kbn/i18n/react'; -import { STANDALONE_CLUSTER_CLUSTER_UUID } from '../../../../common/constants'; +import { + STANDALONE_CLUSTER_CLUSTER_UUID, + ALERT_MISSING_MONITORING_DATA, + ELASTICSEARCH_SYSTEM_ID, + KIBANA_SYSTEM_ID, + LOGSTASH_SYSTEM_ID, + BEATS_SYSTEM_ID, + APM_SYSTEM_ID, +} from '../../../../common/constants'; +import { filterAlertStates } from '../../../alerts/filter_alert_states'; export function Overview(props) { const isFromStandaloneCluster = props.cluster.cluster_uuid === STANDALONE_CLUSTER_CLUSTER_UUID; @@ -37,12 +46,22 @@ export function Overview(props) { license={props.cluster.license} setupMode={props.setupMode} showLicenseExpiration={props.showLicenseExpiration} - alerts={props.alerts} + alerts={filterAlertStates(props.alerts, (type, { state }) => { + if (type === ALERT_MISSING_MONITORING_DATA) { + return state.stackProduct === ELASTICSEARCH_SYSTEM_ID; + } + return true; + })} /> <KibanaPanel {...props.cluster.kibana} setupMode={props.setupMode} - alerts={props.alerts} + alerts={filterAlertStates(props.alerts, (type, { state }) => { + if (type === ALERT_MISSING_MONITORING_DATA) { + return state.stackProduct === KIBANA_SYSTEM_ID; + } + return true; + })} /> </Fragment> ) : null} @@ -50,12 +69,35 @@ export function Overview(props) { <LogstashPanel {...props.cluster.logstash} setupMode={props.setupMode} - alerts={props.alerts} + alerts={filterAlertStates(props.alerts, (type, { state }) => { + if (type === ALERT_MISSING_MONITORING_DATA) { + return state.stackProduct === LOGSTASH_SYSTEM_ID; + } + return true; + })} /> - <BeatsPanel {...props.cluster.beats} setupMode={props.setupMode} /> + <BeatsPanel + {...props.cluster.beats} + setupMode={props.setupMode} + alerts={filterAlertStates(props.alerts, (type, { state }) => { + if (type === ALERT_MISSING_MONITORING_DATA) { + return state.stackProduct === BEATS_SYSTEM_ID; + } + return true; + })} + /> - <ApmPanel {...props.cluster.apm} setupMode={props.setupMode} /> + <ApmPanel + {...props.cluster.apm} + setupMode={props.setupMode} + alerts={filterAlertStates(props.alerts, (type, { state }) => { + if (type === ALERT_MISSING_MONITORING_DATA) { + return state.stackProduct === APM_SYSTEM_ID; + } + return true; + })} + /> </EuiPageBody> </EuiPage> ); diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js index 7df0a3ca7138e6..1f20684bd97d72 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/kibana_panel.js @@ -28,14 +28,18 @@ import { import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; import { SetupModeTooltip } from '../../setup_mode/tooltip'; -import { KIBANA_SYSTEM_ID, ALERT_KIBANA_VERSION_MISMATCH } from '../../../../common/constants'; +import { + KIBANA_SYSTEM_ID, + ALERT_KIBANA_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, +} from '../../../../common/constants'; import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link'; import { AlertsBadge } from '../../../alerts/badge'; import { shouldShowAlertBadge } from '../../../alerts/lib/should_show_alert_badge'; import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; import { SetupModeFeature } from '../../../../common/enums'; -const INSTANCES_PANEL_ALERTS = [ALERT_KIBANA_VERSION_MISMATCH]; +const INSTANCES_PANEL_ALERTS = [ALERT_KIBANA_VERSION_MISMATCH, ALERT_MISSING_MONITORING_DATA]; export function KibanaPanel(props) { const setupMode = props.setupMode; diff --git a/x-pack/plugins/monitoring/public/components/cluster/overview/logstash_panel.js b/x-pack/plugins/monitoring/public/components/cluster/overview/logstash_panel.js index 2c61438dca17c3..7c0e04ab5d6158 100644 --- a/x-pack/plugins/monitoring/public/components/cluster/overview/logstash_panel.js +++ b/x-pack/plugins/monitoring/public/components/cluster/overview/logstash_panel.js @@ -15,6 +15,7 @@ import { LOGSTASH, LOGSTASH_SYSTEM_ID, ALERT_LOGSTASH_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, } from '../../../../common/constants'; import { @@ -40,7 +41,7 @@ import { shouldShowAlertBadge } from '../../../alerts/lib/should_show_alert_badg import { isSetupModeFeatureEnabled } from '../../../lib/setup_mode'; import { SetupModeFeature } from '../../../../common/enums'; -const NODES_PANEL_ALERTS = [ALERT_LOGSTASH_VERSION_MISMATCH]; +const NODES_PANEL_ALERTS = [ALERT_LOGSTASH_VERSION_MISMATCH, ALERT_MISSING_MONITORING_DATA]; export function LogstashPanel(props) { const { setupMode } = props; diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js index ac1a5212a8d269..47e30b71e03d01 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/node/node.js @@ -73,11 +73,22 @@ export const Node = ({ <NodeDetailStatus stats={nodeSummary} alerts={alerts} - alertsStateFilter={(state) => state.nodeId === nodeId} + alertsStateFilter={(state) => + state.nodeId === nodeId || state.stackProductUuid === nodeId + } /> </EuiPanel> <EuiSpacer size="m" /> - <AlertsCallout alerts={alerts} stateFilter={(state) => state.nodeId === nodeId} /> + <AlertsCallout + alerts={alerts} + stateFilter={(state) => state.nodeId === nodeId || state.stackProductUuid === nodeId} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Elasticsearch nodes')) { + return false; + } + return true; + }} + /> <EuiPageContent> <EuiFlexGrid columns={2} gutterSize="s"> {metricsToShow.map((metric, index) => ( diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js index f088f7c0d348af..41d3a579db5a23 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/nodes.js @@ -137,7 +137,15 @@ const getColumns = (showCgroupMetricsElasticsearch, setupMode, clusterUuid, aler <AlertsStatus showBadge={true} alerts={alerts} - stateFilter={(state) => state.nodeId === node.resolver} + stateFilter={(state) => + state.nodeId === node.resolver || state.stackProductUuid === node.resolver + } + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Elasticsearch nodes')) { + return false; + } + return true; + }} /> ); }, diff --git a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js index 10ad1634eda862..8095337dd37967 100644 --- a/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js +++ b/x-pack/plugins/monitoring/public/components/kibana/instances/instances.js @@ -91,7 +91,18 @@ const getColumns = (setupMode, alerts) => { width: '175px', sortable: true, render: () => { - return <AlertsStatus showBadge={true} alerts={alerts} />; + return ( + <AlertsStatus + showBadge={true} + alerts={alerts} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Kibana instances')) { + return false; + } + return true; + }} + /> + ); }, }, { diff --git a/x-pack/plugins/monitoring/public/components/logstash/listing/listing.js b/x-pack/plugins/monitoring/public/components/logstash/listing/listing.js index 4a1137079ebb41..a5db433bbfe0a8 100644 --- a/x-pack/plugins/monitoring/public/components/logstash/listing/listing.js +++ b/x-pack/plugins/monitoring/public/components/logstash/listing/listing.js @@ -84,7 +84,18 @@ export class Listing extends PureComponent { width: '175px', sortable: true, render: () => { - return <AlertsStatus showBadge={true} alerts={alerts} />; + return ( + <AlertsStatus + showBadge={true} + alerts={alerts} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Logstash nodes')) { + return false; + } + return true; + }} + /> + ); }, }, { diff --git a/x-pack/plugins/monitoring/public/plugin.ts b/x-pack/plugins/monitoring/public/plugin.ts index a9c26ca7103a2c..f4f66185346e80 100644 --- a/x-pack/plugins/monitoring/public/plugin.ts +++ b/x-pack/plugins/monitoring/public/plugin.ts @@ -23,6 +23,7 @@ import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { MonitoringStartPluginDependencies, MonitoringConfig } from './types'; import { TriggersAndActionsUIPublicPluginSetup } from '../../triggers_actions_ui/public'; import { createCpuUsageAlertType } from './alerts/cpu_usage_alert'; +import { createMissingMonitoringDataAlertType } from './alerts/missing_monitoring_data_alert'; import { createLegacyAlertTypes } from './alerts/legacy_alert'; import { createDiskUsageAlertType } from './alerts/disk_usage_alert'; @@ -72,6 +73,7 @@ export class MonitoringPlugin } plugins.triggers_actions_ui.alertTypeRegistry.register(createCpuUsageAlertType()); + plugins.triggers_actions_ui.alertTypeRegistry.register(createMissingMonitoringDataAlertType()); plugins.triggers_actions_ui.alertTypeRegistry.register(createDiskUsageAlertType()); const legacyAlertTypes = createLegacyAlertTypes(); for (const legacyAlertType of legacyAlertTypes) { diff --git a/x-pack/plugins/monitoring/public/views/apm/instance/index.js b/x-pack/plugins/monitoring/public/views/apm/instance/index.js index 752128782194e9..396d4651e0c5ed 100644 --- a/x-pack/plugins/monitoring/public/views/apm/instance/index.js +++ b/x-pack/plugins/monitoring/public/views/apm/instance/index.js @@ -18,7 +18,11 @@ 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'; +import { + CODE_PATH_APM, + ALERT_MISSING_MONITORING_DATA, + APM_SYSTEM_ID, +} from '../../../../common/constants'; uiRoutes.when('/apm/instances/:uuid', { template, @@ -50,6 +54,17 @@ uiRoutes.when('/apm/instances/:uuid', { reactNodeId: 'apmInstanceReact', $scope, $injector, + alerts: { + shouldFetch: true, + options: { + alertTypeIds: [ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: APM_SYSTEM_ID, + }, + ], + }, + }, }); $scope.$watch( @@ -69,6 +84,7 @@ uiRoutes.when('/apm/instances/:uuid', { summary={data.apmSummary || {}} metrics={data.metrics || {}} onBrush={this.onBrush} + alerts={this.alerts} zoomInfo={this.zoomInfo} /> ); diff --git a/x-pack/plugins/monitoring/public/views/apm/instances/index.js b/x-pack/plugins/monitoring/public/views/apm/instances/index.js index 1f5b089ea748e4..75f3ded89a595b 100644 --- a/x-pack/plugins/monitoring/public/views/apm/instances/index.js +++ b/x-pack/plugins/monitoring/public/views/apm/instances/index.js @@ -13,7 +13,11 @@ import template from './index.html'; import { ApmServerInstances } from '../../../components/apm/instances'; import { MonitoringViewBaseEuiTableController } from '../..'; import { SetupModeRenderer } from '../../../components/renderers'; -import { APM_SYSTEM_ID, CODE_PATH_APM } from '../../../../common/constants'; +import { + APM_SYSTEM_ID, + CODE_PATH_APM, + ALERT_MISSING_MONITORING_DATA, +} from '../../../../common/constants'; uiRoutes.when('/apm/instances', { template, @@ -47,6 +51,17 @@ uiRoutes.when('/apm/instances', { reactNodeId: 'apmInstancesReact', $scope, $injector, + alerts: { + shouldFetch: true, + options: { + alertTypeIds: [ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: APM_SYSTEM_ID, + }, + ], + }, + }, }); this.scope = $scope; @@ -67,6 +82,7 @@ uiRoutes.when('/apm/instances', { {flyoutComponent} <ApmServerInstances setupMode={setupMode} + alerts={this.alerts} apms={{ pagination, sorting, diff --git a/x-pack/plugins/monitoring/public/views/apm/overview/index.js b/x-pack/plugins/monitoring/public/views/apm/overview/index.js index 544fae39ee79d3..12821ec432c247 100644 --- a/x-pack/plugins/monitoring/public/views/apm/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/apm/overview/index.js @@ -12,7 +12,11 @@ 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'; +import { + CODE_PATH_APM, + ALERT_MISSING_MONITORING_DATA, + APM_SYSTEM_ID, +} from '../../../../common/constants'; uiRoutes.when('/apm', { template, @@ -42,13 +46,29 @@ uiRoutes.when('/apm', { reactNodeId: 'apmOverviewReact', $scope, $injector, + alerts: { + shouldFetch: true, + options: { + alertTypeIds: [ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: APM_SYSTEM_ID, + }, + ], + }, + }, }); $scope.$watch( () => this.data, (data) => { this.renderReact( - <ApmOverview {...data} onBrush={this.onBrush} zoomInfo={this.zoomInfo} /> + <ApmOverview + alerts={this.alerts} + {...data} + onBrush={this.onBrush} + zoomInfo={this.zoomInfo} + /> ); } ); diff --git a/x-pack/plugins/monitoring/public/views/beats/beat/index.js b/x-pack/plugins/monitoring/public/views/beats/beat/index.js index 6cffae2479128d..3e9e4e4b0373d9 100644 --- a/x-pack/plugins/monitoring/public/views/beats/beat/index.js +++ b/x-pack/plugins/monitoring/public/views/beats/beat/index.js @@ -11,7 +11,11 @@ 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 { + CODE_PATH_BEATS, + ALERT_MISSING_MONITORING_DATA, + BEATS_SYSTEM_ID, +} from '../../../../common/constants'; import { Beat } from '../../../components/beats/beat'; uiRoutes.when('/beats/beat/:beatUuid', { @@ -52,6 +56,17 @@ uiRoutes.when('/beats/beat/:beatUuid', { $scope, $injector, reactNodeId: 'monitoringBeatsInstanceApp', + alerts: { + shouldFetch: true, + options: { + alertTypeIds: [ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: BEATS_SYSTEM_ID, + }, + ], + }, + }, }); this.data = pageData; @@ -60,6 +75,7 @@ uiRoutes.when('/beats/beat/:beatUuid', { (data) => { this.renderReact( <Beat + alerts={this.alerts} summary={data.summary} metrics={data.metrics} onBrush={$scope.onBrush} diff --git a/x-pack/plugins/monitoring/public/views/beats/listing/index.js b/x-pack/plugins/monitoring/public/views/beats/listing/index.js index a1b2231901c54c..f8f0749d6d30e2 100644 --- a/x-pack/plugins/monitoring/public/views/beats/listing/index.js +++ b/x-pack/plugins/monitoring/public/views/beats/listing/index.js @@ -14,7 +14,11 @@ import template from './index.html'; import React, { Fragment } from 'react'; import { Listing } from '../../../components/beats/listing/listing'; import { SetupModeRenderer } from '../../../components/renderers'; -import { CODE_PATH_BEATS, BEATS_SYSTEM_ID } from '../../../../common/constants'; +import { + CODE_PATH_BEATS, + BEATS_SYSTEM_ID, + ALERT_MISSING_MONITORING_DATA, +} from '../../../../common/constants'; uiRoutes.when('/beats/beats', { template, @@ -46,6 +50,17 @@ uiRoutes.when('/beats/beats', { reactNodeId: 'monitoringBeatsInstancesApp', $scope, $injector, + alerts: { + shouldFetch: true, + options: { + alertTypeIds: [ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: BEATS_SYSTEM_ID, + }, + ], + }, + }, }); this.data = $route.current.locals.pageData; @@ -71,6 +86,7 @@ uiRoutes.when('/beats/beats', { <Listing stats={this.data.stats} data={this.data.listing} + alerts={this.alerts} setupMode={setupMode} sorting={this.sorting || sorting} pagination={this.pagination || pagination} diff --git a/x-pack/plugins/monitoring/public/views/beats/overview/index.js b/x-pack/plugins/monitoring/public/views/beats/overview/index.js index 6ecdc2bfadb7d4..ef80d7e77fe6ef 100644 --- a/x-pack/plugins/monitoring/public/views/beats/overview/index.js +++ b/x-pack/plugins/monitoring/public/views/beats/overview/index.js @@ -11,7 +11,11 @@ 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 { + CODE_PATH_BEATS, + ALERT_MISSING_MONITORING_DATA, + BEATS_SYSTEM_ID, +} from '../../../../common/constants'; import { BeatsOverview } from '../../../components/beats/overview'; uiRoutes.when('/beats', { @@ -44,6 +48,17 @@ uiRoutes.when('/beats', { $scope, $injector, reactNodeId: 'monitoringBeatsOverviewApp', + alerts: { + shouldFetch: true, + options: { + alertTypeIds: [ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: BEATS_SYSTEM_ID, + }, + ], + }, + }, }); this.data = $route.current.locals.pageData; @@ -51,7 +66,12 @@ uiRoutes.when('/beats', { () => this.data, (data) => { this.renderReact( - <BeatsOverview {...data} onBrush={$scope.onBrush} zoomInfo={$scope.zoomInfo} /> + <BeatsOverview + {...data} + alerts={this.alerts} + onBrush={$scope.onBrush} + zoomInfo={$scope.zoomInfo} + /> ); } ); 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 index 8c30e4a2c1b07e..ff7f29c58b2f6c 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/advanced/index.js @@ -20,6 +20,7 @@ import { MonitoringViewBaseController } from '../../../base_controller'; import { CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE, + ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, } from '../../../../../common/constants'; @@ -71,7 +72,7 @@ uiRoutes.when('/elasticsearch/nodes/:node/advanced', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE], + alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE, ALERT_MISSING_MONITORING_DATA], filters: [ { nodeUuid: nodeName, diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js index ed2603e6dfff3c..15b9b7b4c0e4a4 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/node/index.js @@ -21,6 +21,7 @@ import { MonitoringViewBaseController } from '../../base_controller'; import { CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE, + ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, } from '../../../../common/constants'; @@ -55,7 +56,7 @@ uiRoutes.when('/elasticsearch/nodes/:node', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE], + alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE, ALERT_MISSING_MONITORING_DATA], filters: [ { nodeUuid: nodeName, diff --git a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js index 66fcac43e4fc5c..ef807bf9b377d8 100644 --- a/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js +++ b/x-pack/plugins/monitoring/public/views/elasticsearch/nodes/index.js @@ -19,6 +19,7 @@ import { ELASTICSEARCH_SYSTEM_ID, CODE_PATH_ELASTICSEARCH, ALERT_CPU_USAGE, + ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, } from '../../../../common/constants'; @@ -87,7 +88,12 @@ uiRoutes.when('/elasticsearch/nodes', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE], + alertTypeIds: [ALERT_CPU_USAGE, ALERT_DISK_USAGE, ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: ELASTICSEARCH_SYSTEM_ID, + }, + ], }, }, }); diff --git a/x-pack/plugins/monitoring/public/views/kibana/instance/index.js b/x-pack/plugins/monitoring/public/views/kibana/instance/index.js index 20a1a517194156..29852501d1667b 100644 --- a/x-pack/plugins/monitoring/public/views/kibana/instance/index.js +++ b/x-pack/plugins/monitoring/public/views/kibana/instance/index.js @@ -27,7 +27,12 @@ import { import { MonitoringTimeseriesContainer } from '../../../components/chart'; import { DetailStatus } from '../../../components/kibana/detail_status'; import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_KIBANA, ALERT_KIBANA_VERSION_MISMATCH } from '../../../../common/constants'; +import { + CODE_PATH_KIBANA, + ALERT_KIBANA_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, + KIBANA_SYSTEM_ID, +} from '../../../../common/constants'; import { AlertsCallout } from '../../../alerts/callout'; function getPageData($injector) { @@ -76,7 +81,12 @@ uiRoutes.when('/kibana/instances/:uuid', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_KIBANA_VERSION_MISMATCH], + alertTypeIds: [ALERT_KIBANA_VERSION_MISMATCH, ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: KIBANA_SYSTEM_ID, + }, + ], }, }, }); @@ -104,7 +114,15 @@ uiRoutes.when('/kibana/instances/:uuid', { <DetailStatus stats={data.kibanaSummary} /> </EuiPanel> <EuiSpacer size="m" /> - <AlertsCallout alerts={this.alerts} /> + <AlertsCallout + alerts={this.alerts} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Kibana instances')) { + return false; + } + return true; + }} + /> <EuiPageContent> <EuiFlexGrid columns={2} gutterSize="s"> <EuiFlexItem grow={true}> diff --git a/x-pack/plugins/monitoring/public/views/kibana/instances/index.js b/x-pack/plugins/monitoring/public/views/kibana/instances/index.js index 765e112a23305a..fcb2ee53471a14 100644 --- a/x-pack/plugins/monitoring/public/views/kibana/instances/index.js +++ b/x-pack/plugins/monitoring/public/views/kibana/instances/index.js @@ -17,6 +17,7 @@ import { KIBANA_SYSTEM_ID, CODE_PATH_KIBANA, ALERT_KIBANA_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, } from '../../../../common/constants'; uiRoutes.when('/kibana/instances', { @@ -46,7 +47,12 @@ uiRoutes.when('/kibana/instances', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_KIBANA_VERSION_MISMATCH], + alertTypeIds: [ALERT_KIBANA_VERSION_MISMATCH, ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: KIBANA_SYSTEM_ID, + }, + ], }, }, }); 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 index 0fc3cc47502cd6..591db66b2698c1 100644 --- a/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/node/advanced/index.js @@ -26,7 +26,13 @@ import { EuiFlexItem, } from '@elastic/eui'; import { MonitoringTimeseriesContainer } from '../../../../components/chart'; -import { CODE_PATH_LOGSTASH } from '../../../../../common/constants'; +import { + CODE_PATH_LOGSTASH, + ALERT_LOGSTASH_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, + LOGSTASH_SYSTEM_ID, +} from '../../../../../common/constants'; +import { AlertsCallout } from '../../../../alerts/callout'; function getPageData($injector) { const $http = $injector.get('$http'); @@ -69,6 +75,17 @@ uiRoutes.when('/logstash/node/:uuid/advanced', { reactNodeId: 'monitoringLogstashNodeAdvancedApp', $scope, $injector, + alerts: { + shouldFetch: true, + options: { + alertTypeIds: [ALERT_LOGSTASH_VERSION_MISMATCH, ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: LOGSTASH_SYSTEM_ID, + }, + ], + }, + }, telemetryPageViewTitle: 'logstash_node_advanced', }); @@ -112,6 +129,15 @@ uiRoutes.when('/logstash/node/:uuid/advanced', { <DetailStatus stats={data.nodeSummary} /> </EuiPanel> <EuiSpacer size="m" /> + <AlertsCallout + alerts={this.alerts} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Logstash nodes')) { + return false; + } + return true; + }} + /> <EuiPageContent> <EuiFlexGrid columns={2} gutterSize="s"> {metricsToShow.map((metric, index) => ( diff --git a/x-pack/plugins/monitoring/public/views/logstash/node/index.js b/x-pack/plugins/monitoring/public/views/logstash/node/index.js index e2dee77133c72b..cccae6913052ab 100644 --- a/x-pack/plugins/monitoring/public/views/logstash/node/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/node/index.js @@ -26,7 +26,12 @@ import { } from '@elastic/eui'; import { MonitoringTimeseriesContainer } from '../../../components/chart'; import { MonitoringViewBaseController } from '../../base_controller'; -import { CODE_PATH_LOGSTASH, ALERT_LOGSTASH_VERSION_MISMATCH } from '../../../../common/constants'; +import { + CODE_PATH_LOGSTASH, + ALERT_LOGSTASH_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, + LOGSTASH_SYSTEM_ID, +} from '../../../../common/constants'; import { AlertsCallout } from '../../../alerts/callout'; function getPageData($injector) { @@ -73,7 +78,12 @@ uiRoutes.when('/logstash/node/:uuid', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_LOGSTASH_VERSION_MISMATCH], + alertTypeIds: [ALERT_LOGSTASH_VERSION_MISMATCH, ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: LOGSTASH_SYSTEM_ID, + }, + ], }, }, telemetryPageViewTitle: 'logstash_node', @@ -120,7 +130,15 @@ uiRoutes.when('/logstash/node/:uuid', { <DetailStatus stats={data.nodeSummary} /> </EuiPanel> <EuiSpacer size="m" /> - <AlertsCallout alerts={this.alerts} /> + <AlertsCallout + alerts={this.alerts} + nextStepsFilter={(nextStep) => { + if (nextStep.text.includes('Logstash nodes')) { + return false; + } + return true; + }} + /> <EuiPageContent> <EuiFlexGrid columns={2} gutterSize="s"> {metricsToShow.map((metric, index) => ( diff --git a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js index 6f2e47eb3f918e..20b2f68e2c67e2 100644 --- a/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js +++ b/x-pack/plugins/monitoring/public/views/logstash/nodes/index.js @@ -16,6 +16,7 @@ import { CODE_PATH_LOGSTASH, LOGSTASH_SYSTEM_ID, ALERT_LOGSTASH_VERSION_MISMATCH, + ALERT_MISSING_MONITORING_DATA, } from '../../../../common/constants'; uiRoutes.when('/logstash/nodes', { @@ -45,7 +46,12 @@ uiRoutes.when('/logstash/nodes', { alerts: { shouldFetch: true, options: { - alertTypeIds: [ALERT_LOGSTASH_VERSION_MISMATCH], + alertTypeIds: [ALERT_LOGSTASH_VERSION_MISMATCH, ALERT_MISSING_MONITORING_DATA], + filters: [ + { + stackProduct: LOGSTASH_SYSTEM_ID, + }, + ], }, }, }); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts index 60693eb42a30e9..ddc8dcafebd213 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.test.ts @@ -63,6 +63,6 @@ describe('AlertsFactory', () => { it('should get all', () => { const alerts = AlertsFactory.getAll(); - expect(alerts.length).toBe(8); + expect(alerts.length).toBe(9); }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts index 6b1c0d5fffe186..05a92cea5469b6 100644 --- a/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts +++ b/x-pack/plugins/monitoring/server/alerts/alerts_factory.ts @@ -6,6 +6,7 @@ import { CpuUsageAlert, + MissingMonitoringDataAlert, DiskUsageAlert, NodesChangedAlert, ClusterHealthAlert, @@ -19,6 +20,7 @@ import { ALERT_CLUSTER_HEALTH, ALERT_LICENSE_EXPIRATION, ALERT_CPU_USAGE, + ALERT_MISSING_MONITORING_DATA, ALERT_DISK_USAGE, ALERT_NODES_CHANGED, ALERT_LOGSTASH_VERSION_MISMATCH, @@ -31,6 +33,7 @@ export const BY_TYPE = { [ALERT_CLUSTER_HEALTH]: ClusterHealthAlert, [ALERT_LICENSE_EXPIRATION]: LicenseExpirationAlert, [ALERT_CPU_USAGE]: CpuUsageAlert, + [ALERT_MISSING_MONITORING_DATA]: MissingMonitoringDataAlert, [ALERT_DISK_USAGE]: DiskUsageAlert, [ALERT_NODES_CHANGED]: NodesChangedAlert, [ALERT_LOGSTASH_VERSION_MISMATCH]: LogstashVersionMismatchAlert, diff --git a/x-pack/plugins/monitoring/server/alerts/base_alert.ts b/x-pack/plugins/monitoring/server/alerts/base_alert.ts index aff84710d27add..61486626040f73 100644 --- a/x-pack/plugins/monitoring/server/alerts/base_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/base_alert.ts @@ -198,6 +198,15 @@ export class BaseAlert { const alertInstance: RawAlertInstance = states.alertInstances[instanceId]; if (alertInstance && this.filterAlertInstance(alertInstance, filters)) { accum[instanceId] = alertInstance; + if (alertInstance.state) { + accum[instanceId].state = { + alertStates: (alertInstance.state as AlertInstanceState).alertStates.filter( + (alertState: AlertState) => { + return this.filterAlertState(alertState, filters); + } + ), + }; + } } return accum; }, @@ -209,6 +218,10 @@ export class BaseAlert { return true; } + protected filterAlertState(alertState: AlertState, filters: CommonAlertFilter[]) { + return true; + } + protected async execute({ services, params, state }: AlertExecutorOptions): Promise<any> { const logger = this.getLogger(this.type); logger.debug( @@ -226,13 +239,7 @@ export class BaseAlert { return await mbSafeQuery(async () => _callCluster(endpoint, clientParams, options)); }; const availableCcs = this.config.ui.ccs.enabled ? await fetchAvailableCcs(callCluster) : []; - // Support CCS use cases by querying to find available remote clusters - // and then adding those to the index pattern we are searching against - let esIndexPattern = appendMetricbeatIndex(this.config, INDEX_PATTERN_ELASTICSEARCH); - if (availableCcs) { - esIndexPattern = getCcsIndexPattern(esIndexPattern, availableCcs); - } - const clusters = await fetchClusters(callCluster, esIndexPattern); + const clusters = await this.fetchClusters(callCluster, availableCcs, params); const uiSettings = (await this.getUiSettingsService()).asScopedToClient( services.savedObjectsClient ); @@ -241,6 +248,26 @@ export class BaseAlert { return await this.processData(data, clusters, services, logger, state); } + protected async fetchClusters( + callCluster: any, + availableCcs: string[] | undefined = undefined, + params: CommonAlertParams + ) { + let ccs; + if (!availableCcs) { + ccs = this.config.ui.ccs.enabled ? await fetchAvailableCcs(callCluster) : undefined; + } else { + ccs = availableCcs; + } + // Support CCS use cases by querying to find available remote clusters + // and then adding those to the index pattern we are searching against + let esIndexPattern = appendMetricbeatIndex(this.config, INDEX_PATTERN_ELASTICSEARCH); + if (ccs) { + esIndexPattern = getCcsIndexPattern(esIndexPattern, ccs); + } + return await fetchClusters(callCluster, esIndexPattern); + } + protected async fetchData( params: CommonAlertParams, callCluster: any, diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts index 4228354f52748e..ca9674c57216b7 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_alert.ts @@ -26,7 +26,7 @@ import { RawAlertInstance } from '../../../alerts/common'; import { parseDuration } from '../../../alerts/common/parse_duration'; import { CommonAlertFilter, - CommonAlertCpuUsageFilter, + CommonAlertNodeUuidFilter, CommonAlertParams, CommonAlertParamDetail, } from '../../common/types'; @@ -129,7 +129,7 @@ export class CpuUsageAlert extends BaseAlert { const alertInstanceState = (alertInstance.state as unknown) as AlertInstanceState; if (filters && filters.length) { for (const _filter of filters) { - const filter = _filter as CommonAlertCpuUsageFilter; + const filter = _filter as CommonAlertNodeUuidFilter; if (filter && filter.nodeUuid) { let nodeExistsInStates = false; for (const state of alertInstanceState.alertStates) { diff --git a/x-pack/plugins/monitoring/server/alerts/index.ts b/x-pack/plugins/monitoring/server/alerts/index.ts index 8fdac655144776..41f6daa38d1dca 100644 --- a/x-pack/plugins/monitoring/server/alerts/index.ts +++ b/x-pack/plugins/monitoring/server/alerts/index.ts @@ -6,6 +6,7 @@ export { BaseAlert } from './base_alert'; export { CpuUsageAlert } from './cpu_usage_alert'; +export { MissingMonitoringDataAlert } from './missing_monitoring_data_alert'; export { DiskUsageAlert } from './disk_usage_alert'; export { ClusterHealthAlert } from './cluster_health_alert'; export { LicenseExpirationAlert } from './license_expiration_alert'; diff --git a/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts new file mode 100644 index 00000000000000..4c06d9718c4559 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.test.ts @@ -0,0 +1,459 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { MissingMonitoringDataAlert } from './missing_monitoring_data_alert'; +import { ALERT_MISSING_MONITORING_DATA } from '../../common/constants'; +import { fetchMissingMonitoringData } from '../lib/alerts/fetch_missing_monitoring_data'; +import { fetchClusters } from '../lib/alerts/fetch_clusters'; + +const RealDate = Date; + +jest.mock('../lib/alerts/fetch_missing_monitoring_data', () => ({ + fetchMissingMonitoringData: jest.fn(), +})); +jest.mock('../lib/alerts/fetch_clusters', () => ({ + fetchClusters: jest.fn(), +})); + +describe('MissingMonitoringDataAlert', () => { + it('should have defaults', () => { + const alert = new MissingMonitoringDataAlert(); + expect(alert.type).toBe(ALERT_MISSING_MONITORING_DATA); + expect(alert.label).toBe('Missing monitoring data'); + expect(alert.defaultThrottle).toBe('1d'); + // @ts-ignore + expect(alert.defaultParams).toStrictEqual({ limit: '1d', duration: '5m' }); + // @ts-ignore + expect(alert.actionVariables).toStrictEqual([ + { name: 'stackProducts', description: 'The stack products missing monitoring data.' }, + { name: 'count', description: 'The number of stack products missing monitoring data.' }, + { + name: 'internalShortMessage', + description: 'The short internal message generated by Elastic.', + }, + { + name: 'internalFullMessage', + description: 'The full internal message generated by Elastic.', + }, + { name: 'state', description: 'The current state of the alert.' }, + { name: 'clusterName', description: 'The cluster to which the nodes belong.' }, + { name: 'action', description: 'The recommended action for this alert.' }, + { + name: 'actionPlain', + description: 'The recommended action for this alert, without any markdown.', + }, + ]); + }); + + describe('execute', () => { + function FakeDate() {} + FakeDate.prototype.valueOf = () => 1; + + const clusterUuid = 'abc123'; + const clusterName = 'testCluster'; + const stackProduct = 'elasticsearch'; + const stackProductUuid = 'esNode1'; + const stackProductName = 'esName1'; + const gapDuration = 3000001; + const missingData = [ + { + stackProduct, + stackProductUuid, + stackProductName, + clusterUuid, + gapDuration, + }, + { + stackProduct: 'kibana', + stackProductUuid: 'kibanaUuid1', + stackProductName: 'kibanaInstance1', + clusterUuid, + gapDuration: gapDuration + 10, + }, + ]; + const getUiSettingsService = () => ({ + asScopedToClient: jest.fn(), + }); + const getLogger = () => ({ + debug: jest.fn(), + }); + const monitoringCluster = null; + const config = { + ui: { + ccs: { enabled: true }, + container: { elasticsearch: { enabled: false } }, + metricbeat: { index: 'metricbeat-*' }, + }, + }; + const kibanaUrl = 'http://localhost:5601'; + + const replaceState = jest.fn(); + const scheduleActions = jest.fn(); + const getState = jest.fn(); + const executorOptions = { + services: { + callCluster: jest.fn(), + alertInstanceFactory: jest.fn().mockImplementation(() => { + return { + replaceState, + scheduleActions, + getState, + }; + }), + }, + state: {}, + }; + + beforeEach(() => { + // @ts-ignore + Date = FakeDate; + (fetchMissingMonitoringData as jest.Mock).mockImplementation(() => { + return missingData; + }); + (fetchClusters as jest.Mock).mockImplementation(() => { + return [{ clusterUuid, clusterName }]; + }); + }); + + afterEach(() => { + Date = RealDate; + replaceState.mockReset(); + scheduleActions.mockReset(); + getState.mockReset(); + }); + + it('should fire actions', async () => { + const alert = new MissingMonitoringDataAlert(); + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + false + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + // @ts-ignore + params: alert.defaultParams, + } as any); + const count = 2; + expect(replaceState).toHaveBeenCalledWith({ + alertStates: [ + { + ccs: undefined, + cluster: { clusterUuid, clusterName }, + gapDuration, + stackProduct, + stackProductName, + stackProductUuid, + ui: { + isFiring: true, + message: { + text: + 'For the past an hour, we have not detected any monitoring data from the Elasticsearch node: esName1, starting at #absolute', + nextSteps: [ + { + text: '#start_linkView all Elasticsearch nodes#end_link', + tokens: [ + { + startToken: '#start_link', + endToken: '#end_link', + type: 'link', + url: 'elasticsearch/nodes', + }, + ], + }, + { + text: 'Verify monitoring settings on the node', + }, + ], + tokens: [ + { + startToken: '#absolute', + type: 'time', + isAbsolute: true, + isRelative: false, + timestamp: 1, + }, + ], + }, + severity: 'danger', + resolvedMS: 0, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + { + ccs: undefined, + cluster: { clusterUuid, clusterName }, + gapDuration: gapDuration + 10, + stackProduct: 'kibana', + stackProductName: 'kibanaInstance1', + stackProductUuid: 'kibanaUuid1', + ui: { + isFiring: true, + message: { + text: + 'For the past an hour, we have not detected any monitoring data from the Kibana instance: kibanaInstance1, starting at #absolute', + nextSteps: [ + { + text: '#start_linkView all Kibana instances#end_link', + tokens: [ + { + startToken: '#start_link', + endToken: '#end_link', + type: 'link', + url: 'kibana/instances', + }, + ], + }, + { + text: 'Verify monitoring settings on the instance', + }, + ], + tokens: [ + { + startToken: '#absolute', + type: 'time', + isAbsolute: true, + isRelative: false, + timestamp: 1, + }, + ], + }, + severity: 'danger', + resolvedMS: 0, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + ], + }); + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `We have not detected any monitoring data for 2 stack product(s) in cluster: testCluster. [View what monitoring data we do have for these stack products.](http://localhost:5601/app/monitoring#overview?_g=(cluster_uuid:abc123))`, + internalShortMessage: `We have not detected any monitoring data for 2 stack product(s) in cluster: testCluster. Verify these stack products are up and running, then double check the monitoring settings.`, + action: `[View what monitoring data we do have for these stack products.](http://localhost:5601/app/monitoring#overview?_g=(cluster_uuid:abc123))`, + actionPlain: + 'Verify these stack products are up and running, then double check the monitoring settings.', + clusterName, + count, + stackProducts: 'Elasticsearch node: esName1, Kibana instance: kibanaInstance1', + state: 'firing', + }); + }); + + it('should not fire actions if under threshold', async () => { + (fetchMissingMonitoringData as jest.Mock).mockImplementation(() => { + return [ + { + ...missingData[0], + gapDuration: 1, + }, + ]; + }); + const alert = new MissingMonitoringDataAlert(); + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + false + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + // @ts-ignore + params: alert.defaultParams, + } as any); + expect(replaceState).toHaveBeenCalledWith({ + alertStates: [ + { + cluster: { + clusterUuid, + clusterName, + }, + gapDuration: 1, + stackProduct, + stackProductName, + stackProductUuid, + ui: { + isFiring: false, + lastCheckedMS: 0, + message: null, + resolvedMS: 0, + severity: 'danger', + triggeredMS: 0, + }, + }, + ], + }); + expect(scheduleActions).not.toHaveBeenCalled(); + }); + + it('should resolve with a resolved message', async () => { + (fetchMissingMonitoringData as jest.Mock).mockImplementation(() => { + return [ + { + ...missingData[0], + gapDuration: 1, + }, + ]; + }); + (getState as jest.Mock).mockImplementation(() => { + return { + alertStates: [ + { + cluster: { + clusterUuid, + clusterName, + }, + ccs: null, + gapDuration: 1, + stackProduct, + stackProductName, + stackProductUuid, + ui: { + isFiring: true, + message: null, + severity: 'danger', + resolvedMS: 0, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + ], + }; + }); + const alert = new MissingMonitoringDataAlert(); + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + false + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + // @ts-ignore + params: alert.defaultParams, + } as any); + const count = 1; + expect(replaceState).toHaveBeenCalledWith({ + alertStates: [ + { + cluster: { clusterUuid, clusterName }, + ccs: null, + gapDuration: 1, + stackProduct, + stackProductName, + stackProductUuid, + ui: { + isFiring: false, + message: { + text: + 'We are now seeing monitoring data for the Elasticsearch node: esName1, as of #resolved', + tokens: [ + { + startToken: '#resolved', + type: 'time', + isAbsolute: true, + isRelative: false, + timestamp: 1, + }, + ], + }, + severity: 'danger', + resolvedMS: 1, + triggeredMS: 1, + lastCheckedMS: 0, + }, + }, + ], + }); + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `We are now seeing monitoring data for 1 stack product(s) in cluster testCluster.`, + internalShortMessage: `We are now seeing monitoring data for 1 stack product(s) in cluster: testCluster.`, + clusterName, + count, + stackProducts: 'Elasticsearch node: esName1', + state: 'resolved', + }); + }); + + it('should handle ccs', async () => { + const ccs = 'testCluster'; + (fetchMissingMonitoringData as jest.Mock).mockImplementation(() => { + return [ + { + ...missingData[0], + ccs, + }, + ]; + }); + const alert = new MissingMonitoringDataAlert(); + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + false + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + // @ts-ignore + params: alert.defaultParams, + } as any); + const count = 1; + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `We have not detected any monitoring data for 1 stack product(s) in cluster: testCluster. [View what monitoring data we do have for these stack products.](http://localhost:5601/app/monitoring#overview?_g=(cluster_uuid:abc123,ccs:testCluster))`, + internalShortMessage: `We have not detected any monitoring data for 1 stack product(s) in cluster: testCluster. Verify these stack products are up and running, then double check the monitoring settings.`, + action: `[View what monitoring data we do have for these stack products.](http://localhost:5601/app/monitoring#overview?_g=(cluster_uuid:abc123,ccs:testCluster))`, + actionPlain: + 'Verify these stack products are up and running, then double check the monitoring settings.', + clusterName, + count, + stackProducts: 'Elasticsearch node: esName1', + state: 'firing', + }); + }); + + it('should fire with different messaging for cloud', async () => { + const alert = new MissingMonitoringDataAlert(); + alert.initializeAlertType( + getUiSettingsService as any, + monitoringCluster as any, + getLogger as any, + config as any, + kibanaUrl, + true + ); + const type = alert.getAlertType(); + await type.executor({ + ...executorOptions, + // @ts-ignore + params: alert.defaultParams, + } as any); + const count = 2; + expect(scheduleActions).toHaveBeenCalledWith('default', { + internalFullMessage: `We have not detected any monitoring data for 2 stack product(s) in cluster: testCluster. Verify these stack products are up and running, then double check the monitoring settings.`, + internalShortMessage: `We have not detected any monitoring data for 2 stack product(s) in cluster: testCluster. Verify these stack products are up and running, then double check the monitoring settings.`, + action: `[View what monitoring data we do have for these stack products.](http://localhost:5601/app/monitoring#overview?_g=(cluster_uuid:abc123))`, + actionPlain: + 'Verify these stack products are up and running, then double check the monitoring settings.', + clusterName, + count, + stackProducts: 'Elasticsearch node: esName1, Kibana instance: kibanaInstance1', + state: 'firing', + }); + }); + }); +}); diff --git a/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts new file mode 100644 index 00000000000000..6017314f332e68 --- /dev/null +++ b/x-pack/plugins/monitoring/server/alerts/missing_monitoring_data_alert.ts @@ -0,0 +1,504 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { IUiSettingsClient, Logger } from 'kibana/server'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import { BaseAlert } from './base_alert'; +import { + AlertData, + AlertCluster, + AlertState, + AlertMessage, + AlertMissingDataState, + AlertMissingData, + AlertMessageTimeToken, + AlertInstanceState, +} from './types'; +import { AlertInstance, AlertServices } from '../../../alerts/server'; +import { + INDEX_PATTERN, + ALERT_MISSING_MONITORING_DATA, + INDEX_PATTERN_ELASTICSEARCH, +} from '../../common/constants'; +import { getCcsIndexPattern } from '../lib/alerts/get_ccs_index_pattern'; +import { AlertMessageTokenType, AlertSeverity, AlertParamType } from '../../common/enums'; +import { RawAlertInstance } from '../../../alerts/common'; +import { parseDuration } from '../../../alerts/common/parse_duration'; +import { + CommonAlertFilter, + CommonAlertParams, + CommonAlertParamDetail, + CommonAlertStackProductFilter, + CommonAlertNodeUuidFilter, +} from '../../common/types'; +import { appendMetricbeatIndex } from '../lib/alerts/append_mb_index'; +import { fetchMissingMonitoringData } from '../lib/alerts/fetch_missing_monitoring_data'; +import { getTypeLabelForStackProduct } from '../lib/alerts/get_type_label_for_stack_product'; +import { getListingLinkForStackProduct } from '../lib/alerts/get_listing_link_for_stack_product'; +import { getStackProductLabel } from '../lib/alerts/get_stack_product_label'; +import { fetchClusters } from '../lib/alerts/fetch_clusters'; +import { fetchAvailableCcs } from '../lib/alerts/fetch_available_ccs'; +import { AlertingDefaults, createLink } from './alerts_common'; + +const RESOLVED = i18n.translate('xpack.monitoring.alerts.missingData.resolved', { + defaultMessage: 'resolved', +}); +const FIRING = i18n.translate('xpack.monitoring.alerts.missingData.firing', { + defaultMessage: 'firing', +}); + +const DEFAULT_DURATION = '5m'; +const DEFAULT_LIMIT = '1d'; + +// Go a bit farther back because we need to detect the difference between seeing the monitoring data versus just not looking far enough back +const LIMIT_BUFFER = 3 * 60 * 1000; + +interface MissingDataParams { + duration: string; + limit: string; +} + +export class MissingMonitoringDataAlert extends BaseAlert { + public static paramDetails = { + duration: { + label: i18n.translate('xpack.monitoring.alerts.missingData.paramDetails.duration.label', { + defaultMessage: `Notify if monitoring data is missing for`, + }), + type: AlertParamType.Duration, + } as CommonAlertParamDetail, + limit: { + label: i18n.translate('xpack.monitoring.alerts.missingData.paramDetails.limit.label', { + defaultMessage: `Look this far back in time for monitoring data`, + }), + type: AlertParamType.Duration, + } as CommonAlertParamDetail, + }; + + public type = ALERT_MISSING_MONITORING_DATA; + public label = i18n.translate('xpack.monitoring.alerts.missingData.label', { + defaultMessage: 'Missing monitoring data', + }); + + protected defaultParams: MissingDataParams = { + duration: DEFAULT_DURATION, + limit: DEFAULT_LIMIT, + }; + + protected actionVariables = [ + { + name: 'stackProducts', + description: i18n.translate( + 'xpack.monitoring.alerts.missingData.actionVariables.stackProducts', + { + defaultMessage: 'The stack products missing monitoring data.', + } + ), + }, + { + name: 'count', + description: i18n.translate('xpack.monitoring.alerts.missingData.actionVariables.count', { + defaultMessage: 'The number of stack products missing monitoring data.', + }), + }, + ...Object.values(AlertingDefaults.ALERT_TYPE.context), + ]; + + protected async fetchClusters( + callCluster: any, + availableCcs: string[] | undefined = undefined, + params: CommonAlertParams + ) { + const limit = parseDuration(((params as unknown) as MissingDataParams).limit); + let ccs; + if (!availableCcs) { + ccs = this.config.ui.ccs.enabled ? await fetchAvailableCcs(callCluster) : undefined; + } else { + ccs = availableCcs; + } + // Support CCS use cases by querying to find available remote clusters + // and then adding those to the index pattern we are searching against + let esIndexPattern = appendMetricbeatIndex(this.config, INDEX_PATTERN_ELASTICSEARCH); + if (ccs) { + esIndexPattern = getCcsIndexPattern(esIndexPattern, ccs); + } + return await fetchClusters(callCluster, esIndexPattern, { + timestamp: { + format: 'epoch_millis', + gte: limit - LIMIT_BUFFER, + }, + }); + } + + protected async fetchData( + params: CommonAlertParams, + callCluster: any, + clusters: AlertCluster[], + uiSettings: IUiSettingsClient, + availableCcs: string[] + ): Promise<AlertData[]> { + let indexPattern = appendMetricbeatIndex(this.config, INDEX_PATTERN); + if (availableCcs) { + indexPattern = getCcsIndexPattern(indexPattern, availableCcs); + } + const duration = parseDuration(((params as unknown) as MissingDataParams).duration); + const limit = parseDuration(((params as unknown) as MissingDataParams).limit); + const now = +new Date(); + const missingData = await fetchMissingMonitoringData( + callCluster, + clusters, + indexPattern, + this.config.ui.max_bucket_size, + now, + now - limit - LIMIT_BUFFER + ); + return missingData.map((missing) => { + return { + instanceKey: `${missing.clusterUuid}:${missing.stackProduct}:${missing.stackProductUuid}`, + clusterUuid: missing.clusterUuid, + shouldFire: missing.gapDuration > duration, + severity: AlertSeverity.Danger, + meta: { missing, limit }, + ccs: missing.ccs, + }; + }); + } + + protected filterAlertInstance(alertInstance: RawAlertInstance, filters: CommonAlertFilter[]) { + const alertInstanceState = (alertInstance.state as unknown) as AlertInstanceState; + if (filters && filters.length) { + for (const filter of filters) { + const stackProductFilter = filter as CommonAlertStackProductFilter; + if (stackProductFilter && stackProductFilter.stackProduct) { + let existsInState = false; + for (const state of alertInstanceState.alertStates) { + if ((state as AlertMissingDataState).stackProduct === stackProductFilter.stackProduct) { + existsInState = true; + break; + } + } + if (!existsInState) { + return false; + } + } + } + } + return true; + } + + protected filterAlertState(alertState: AlertState, filters: CommonAlertFilter[]) { + const state = alertState as AlertMissingDataState; + if (filters && filters.length) { + for (const filter of filters) { + const stackProductFilter = filter as CommonAlertStackProductFilter; + if (stackProductFilter && stackProductFilter.stackProduct) { + if (state.stackProduct !== stackProductFilter.stackProduct) { + return false; + } + } + + const nodeUuidFilter = filter as CommonAlertNodeUuidFilter; + if (nodeUuidFilter && nodeUuidFilter.nodeUuid) { + if (state.stackProductUuid !== nodeUuidFilter.nodeUuid) { + return false; + } + } + } + } + return true; + } + + protected getDefaultAlertState(cluster: AlertCluster, item: AlertData): AlertState { + const base = super.getDefaultAlertState(cluster, item); + return { + ...base, + ui: { + ...base.ui, + severity: AlertSeverity.Danger, + }, + }; + } + + protected getUiMessage(alertState: AlertState, item: AlertData): AlertMessage { + const { missing, limit } = item.meta as { missing: AlertMissingData; limit: number }; + if (!alertState.ui.isFiring) { + if (missing.gapDuration > limit) { + return { + text: i18n.translate('xpack.monitoring.alerts.missingData.ui.notQuiteResolvedMessage', { + defaultMessage: `We are still not seeing monitoring data for the {stackProduct} {type}: {stackProductName} and will stop trying. To change this, configure the alert to look farther back for data.`, + values: { + stackProduct: getStackProductLabel(missing.stackProduct), + type: getTypeLabelForStackProduct(missing.stackProduct, false), + stackProductName: missing.stackProductName, + }, + }), + }; + } + return { + text: i18n.translate('xpack.monitoring.alerts.missingData.ui.resolvedMessage', { + defaultMessage: `We are now seeing monitoring data for the {stackProduct} {type}: {stackProductName}, as of #resolved`, + values: { + stackProduct: getStackProductLabel(missing.stackProduct), + type: getTypeLabelForStackProduct(missing.stackProduct, false), + stackProductName: missing.stackProductName, + }, + }), + tokens: [ + { + startToken: '#resolved', + type: AlertMessageTokenType.Time, + isAbsolute: true, + isRelative: false, + timestamp: alertState.ui.resolvedMS, + } as AlertMessageTimeToken, + ], + }; + } + return { + text: i18n.translate('xpack.monitoring.alerts.missingData.ui.firingMessage', { + defaultMessage: `For the past {gapDuration}, we have not detected any monitoring data from the {stackProduct} {type}: {stackProductName}, starting at #absolute`, + values: { + gapDuration: moment.duration(missing.gapDuration, 'milliseconds').humanize(), + stackProduct: getStackProductLabel(missing.stackProduct), + type: getTypeLabelForStackProduct(missing.stackProduct, false), + stackProductName: missing.stackProductName, + }, + }), + nextSteps: [ + createLink( + i18n.translate('xpack.monitoring.alerts.missingData.ui.nextSteps.viewAll', { + defaultMessage: `#start_linkView all {stackProduct} {type}#end_link`, + values: { + type: getTypeLabelForStackProduct(missing.stackProduct), + stackProduct: getStackProductLabel(missing.stackProduct), + }, + }), + getListingLinkForStackProduct(missing.stackProduct), + AlertMessageTokenType.Link + ), + { + text: i18n.translate('xpack.monitoring.alerts.missingData.ui.nextSteps.verifySettings', { + defaultMessage: `Verify monitoring settings on the {type}`, + values: { + type: getTypeLabelForStackProduct(missing.stackProduct, false), + }, + }), + }, + ], + tokens: [ + { + startToken: '#absolute', + type: AlertMessageTokenType.Time, + isAbsolute: true, + isRelative: false, + timestamp: alertState.ui.triggeredMS, + } as AlertMessageTimeToken, + ], + }; + } + + protected executeActions( + instance: AlertInstance, + instanceState: AlertInstanceState, + item: AlertData | null, + cluster: AlertCluster + ) { + if (instanceState.alertStates.length === 0) { + return; + } + + const ccs = instanceState.alertStates.reduce((accum: string, state): string => { + if (state.ccs) { + return state.ccs; + } + return accum; + }, ''); + + const firingCount = instanceState.alertStates.filter((alertState) => alertState.ui.isFiring) + .length; + const firingStackProducts = instanceState.alertStates + .filter((_state) => (_state as AlertMissingDataState).ui.isFiring) + .map((_state) => { + const state = _state as AlertMissingDataState; + return `${getStackProductLabel(state.stackProduct)} ${getTypeLabelForStackProduct( + state.stackProduct, + false + )}: ${state.stackProductName}`; + }) + .join(', '); + if (firingCount > 0) { + const shortActionText = i18n.translate('xpack.monitoring.alerts.missingData.shortAction', { + defaultMessage: + 'Verify these stack products are up and running, then double check the monitoring settings.', + }); + const fullActionText = i18n.translate('xpack.monitoring.alerts.missingData.fullAction', { + defaultMessage: 'View what monitoring data we do have for these stack products.', + }); + const globalState = [`cluster_uuid:${cluster.clusterUuid}`]; + if (ccs) { + globalState.push(`ccs:${ccs}`); + } + const url = `${this.kibanaUrl}/app/monitoring#overview?_g=(${globalState.join(',')})`; + const action = `[${fullActionText}](${url})`; + const internalShortMessage = i18n.translate( + 'xpack.monitoring.alerts.missingData.firing.internalShortMessage', + { + defaultMessage: `We have not detected any monitoring data for {count} stack product(s) in cluster: {clusterName}. {shortActionText}`, + values: { + count: firingCount, + clusterName: cluster.clusterName, + shortActionText, + }, + } + ); + const internalFullMessage = i18n.translate( + 'xpack.monitoring.alerts.missingData.firing.internalFullMessage', + { + defaultMessage: `We have not detected any monitoring data for {count} stack product(s) in cluster: {clusterName}. {action}`, + values: { + count: firingCount, + clusterName: cluster.clusterName, + action, + }, + } + ); + instance.scheduleActions('default', { + internalShortMessage, + internalFullMessage: this.isCloud ? internalShortMessage : internalFullMessage, + state: FIRING, + stackProducts: firingStackProducts, + count: firingCount, + clusterName: cluster.clusterName, + action, + actionPlain: shortActionText, + }); + } else { + const resolvedCount = instanceState.alertStates.filter( + (alertState) => !alertState.ui.isFiring + ).length; + const resolvedStackProducts = instanceState.alertStates + .filter((_state) => !(_state as AlertMissingDataState).ui.isFiring) + .map((_state) => { + const state = _state as AlertMissingDataState; + return `${getStackProductLabel(state.stackProduct)} ${getTypeLabelForStackProduct( + state.stackProduct, + false + )}: ${state.stackProductName}`; + }) + .join(','); + if (resolvedCount > 0) { + instance.scheduleActions('default', { + internalShortMessage: i18n.translate( + 'xpack.monitoring.alerts.missingData.resolved.internalShortMessage', + { + defaultMessage: `We are now seeing monitoring data for {count} stack product(s) in cluster: {clusterName}.`, + values: { + count: resolvedCount, + clusterName: cluster.clusterName, + }, + } + ), + internalFullMessage: i18n.translate( + 'xpack.monitoring.alerts.missingData.resolved.internalFullMessage', + { + defaultMessage: `We are now seeing monitoring data for {count} stack product(s) in cluster {clusterName}.`, + values: { + count: resolvedCount, + clusterName: cluster.clusterName, + }, + } + ), + state: RESOLVED, + stackProducts: resolvedStackProducts, + count: resolvedCount, + clusterName: cluster.clusterName, + }); + } + } + } + + protected async processData( + data: AlertData[], + clusters: AlertCluster[], + services: AlertServices, + logger: Logger + ) { + for (const cluster of clusters) { + const stackProducts = data.filter((_item) => _item.clusterUuid === cluster.clusterUuid); + if (stackProducts.length === 0) { + continue; + } + + const firingInstances = stackProducts.reduce((list: string[], stackProduct) => { + const { missing } = stackProduct.meta as { missing: AlertMissingData; limit: number }; + if (stackProduct.shouldFire) { + list.push(`${missing.stackProduct}:${missing.stackProductUuid}`); + } + return list; + }, [] as string[]); + firingInstances.sort(); // It doesn't matter how we sort, but keep the order consistent + const instanceId = `${this.type}:${cluster.clusterUuid}:${firingInstances.join(',')}`; + const instance = services.alertInstanceFactory(instanceId); + const instanceState = (instance.getState() as unknown) as AlertInstanceState; + const alertInstanceState: AlertInstanceState = { + alertStates: instanceState?.alertStates || [], + }; + let shouldExecuteActions = false; + for (const stackProduct of stackProducts) { + const { missing } = stackProduct.meta as { missing: AlertMissingData; limit: number }; + let state: AlertMissingDataState; + const indexInState = alertInstanceState.alertStates.findIndex((alertState) => { + const _alertState = alertState as AlertMissingDataState; + return ( + _alertState.cluster.clusterUuid === cluster.clusterUuid && + _alertState.stackProduct === missing.stackProduct && + _alertState.stackProductUuid === missing.stackProductUuid + ); + }); + if (indexInState > -1) { + state = alertInstanceState.alertStates[indexInState] as AlertMissingDataState; + } else { + state = this.getDefaultAlertState(cluster, stackProduct) as AlertMissingDataState; + } + + state.stackProduct = missing.stackProduct; + state.stackProductUuid = missing.stackProductUuid; + state.stackProductName = missing.stackProductName; + state.gapDuration = missing.gapDuration; + + if (stackProduct.shouldFire) { + if (!state.ui.isFiring) { + state.ui.triggeredMS = new Date().valueOf(); + } + state.ui.isFiring = true; + state.ui.message = this.getUiMessage(state, stackProduct); + state.ui.severity = stackProduct.severity; + state.ui.resolvedMS = 0; + shouldExecuteActions = true; + } else if (!stackProduct.shouldFire && state.ui.isFiring) { + state.ui.isFiring = false; + state.ui.resolvedMS = new Date().valueOf(); + state.ui.message = this.getUiMessage(state, stackProduct); + shouldExecuteActions = true; + } + + if (indexInState === -1) { + alertInstanceState.alertStates.push(state); + } else { + alertInstanceState.alertStates = [ + ...alertInstanceState.alertStates.slice(0, indexInState), + state, + ...alertInstanceState.alertStates.slice(indexInState + 1), + ]; + } + } + + instance.replaceState(alertInstanceState); + if (shouldExecuteActions) { + this.executeActions(instance, alertInstanceState, null, cluster); + } + } + } +} diff --git a/x-pack/plugins/monitoring/server/alerts/types.d.ts b/x-pack/plugins/monitoring/server/alerts/types.d.ts index b685dcaed790f5..4b78bca9f47ca6 100644 --- a/x-pack/plugins/monitoring/server/alerts/types.d.ts +++ b/x-pack/plugins/monitoring/server/alerts/types.d.ts @@ -28,6 +28,13 @@ export interface AlertCpuUsageState extends AlertState { nodeName: string; } +export interface AlertMissingDataState extends AlertState { + stackProduct: string; + stackProductUuid: string; + stackProductName: string; + gapDuration: number; +} + export interface AlertDiskUsageState extends AlertState { diskUsage: number; nodeId: string; @@ -93,6 +100,15 @@ export interface AlertDiskUsageNodeStats { ccs?: string; } +export interface AlertMissingData { + stackProduct: string; + stackProductUuid: string; + stackProductName: string; + clusterUuid: string; + gapDuration: number; + ccs?: string; +} + export interface AlertData { instanceKey: string; clusterUuid: string; diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts index 48ad31d20a3951..d474338bce9229 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_clusters.ts @@ -6,7 +6,18 @@ import { get } from 'lodash'; import { AlertCluster } from '../../alerts/types'; -export async function fetchClusters(callCluster: any, index: string): Promise<AlertCluster[]> { +interface RangeFilter { + [field: string]: { + format?: string; + gte: string | number; + }; +} + +export async function fetchClusters( + callCluster: any, + index: string, + rangeFilter: RangeFilter = { timestamp: { gte: 'now-2m' } } +): Promise<AlertCluster[]> { const params = { index, filterPath: [ @@ -25,11 +36,7 @@ export async function fetchClusters(callCluster: any, index: string): Promise<Al }, }, { - range: { - timestamp: { - gte: 'now-2m', - }, - }, + range: rangeFilter, }, ], }, diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.test.ts new file mode 100644 index 00000000000000..b09f5a88dba9c0 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.test.ts @@ -0,0 +1,249 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { fetchMissingMonitoringData } from './fetch_missing_monitoring_data'; + +function getResponse( + index: string, + products: Array<{ + uuid: string; + timestamp: number; + nameSource: any; + }> +) { + return { + buckets: products.map((product) => { + return { + key: product.uuid, + most_recent: { + value: product.timestamp, + }, + document: { + hits: { + hits: [ + { + _index: index, + _source: product.nameSource, + }, + ], + }, + }, + }; + }), + }; +} + +describe('fetchMissingMonitoringData', () => { + let callCluster = jest.fn(); + const index = '.monitoring-*'; + const startMs = 100; + const size = 10; + + it('fetch as expected', async () => { + const now = 10; + const clusters = [ + { + clusterUuid: 'clusterUuid1', + clusterName: 'clusterName1', + }, + ]; + callCluster = jest.fn().mockImplementation((...args) => { + return { + aggregations: { + clusters: { + buckets: clusters.map((cluster) => ({ + key: cluster.clusterUuid, + es_uuids: getResponse('.monitoring-es-*', [ + { + uuid: 'nodeUuid1', + nameSource: { + source_node: { + name: 'nodeName1', + }, + }, + timestamp: 9, + }, + { + uuid: 'nodeUuid2', + nameSource: { + source_node: { + name: 'nodeName2', + }, + }, + timestamp: 2, + }, + ]), + kibana_uuids: getResponse('.monitoring-kibana-*', [ + { + uuid: 'kibanaUuid1', + nameSource: { + kibana_stats: { + kibana: { + name: 'kibanaName1', + }, + }, + }, + timestamp: 4, + }, + ]), + logstash_uuids: getResponse('.monitoring-logstash-*', [ + { + uuid: 'logstashUuid1', + nameSource: { + logstash_stats: { + logstash: { + host: 'logstashName1', + }, + }, + }, + timestamp: 2, + }, + ]), + beats: { + beats_uuids: getResponse('.monitoring-beats-*', [ + { + uuid: 'beatUuid1', + nameSource: { + beats_stats: { + beat: { + name: 'beatName1', + }, + }, + }, + timestamp: 0, + }, + ]), + }, + apms: { + apm_uuids: getResponse('.monitoring-beats-*', [ + { + uuid: 'apmUuid1', + nameSource: { + beats_stats: { + beat: { + name: 'apmName1', + type: 'apm-server', + }, + }, + }, + timestamp: 1, + }, + ]), + }, + })), + }, + }, + }; + }); + const result = await fetchMissingMonitoringData( + callCluster, + clusters, + index, + size, + now, + startMs + ); + expect(result).toEqual([ + { + stackProduct: 'elasticsearch', + stackProductUuid: 'nodeUuid1', + stackProductName: 'nodeName1', + clusterUuid: 'clusterUuid1', + gapDuration: 1, + ccs: null, + }, + { + stackProduct: 'elasticsearch', + stackProductUuid: 'nodeUuid2', + stackProductName: 'nodeName2', + clusterUuid: 'clusterUuid1', + gapDuration: 8, + ccs: null, + }, + { + stackProduct: 'kibana', + stackProductUuid: 'kibanaUuid1', + stackProductName: 'kibanaName1', + clusterUuid: 'clusterUuid1', + gapDuration: 6, + ccs: null, + }, + { + stackProduct: 'logstash', + stackProductUuid: 'logstashUuid1', + stackProductName: 'logstashName1', + clusterUuid: 'clusterUuid1', + gapDuration: 8, + ccs: null, + }, + { + stackProduct: 'beats', + stackProductUuid: 'beatUuid1', + stackProductName: 'beatName1', + clusterUuid: 'clusterUuid1', + gapDuration: 10, + ccs: null, + }, + { + stackProduct: 'apm', + stackProductUuid: 'apmUuid1', + stackProductName: 'apmName1', + clusterUuid: 'clusterUuid1', + gapDuration: 9, + ccs: null, + }, + ]); + }); + + it('should handle ccs', async () => { + const now = 10; + const clusters = [ + { + clusterUuid: 'clusterUuid1', + clusterName: 'clusterName1', + }, + ]; + callCluster = jest.fn().mockImplementation((...args) => { + return { + aggregations: { + clusters: { + buckets: clusters.map((cluster) => ({ + key: cluster.clusterUuid, + es_uuids: getResponse('Monitoring:.monitoring-es-*', [ + { + uuid: 'nodeUuid1', + nameSource: { + source_node: { + name: 'nodeName1', + }, + }, + timestamp: 9, + }, + ]), + })), + }, + }, + }; + }); + const result = await fetchMissingMonitoringData( + callCluster, + clusters, + index, + size, + now, + startMs + ); + expect(result).toEqual([ + { + stackProduct: 'elasticsearch', + stackProductUuid: 'nodeUuid1', + stackProductName: 'nodeName1', + clusterUuid: 'clusterUuid1', + gapDuration: 1, + ccs: 'Monitoring', + }, + ]); + }); +}); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts new file mode 100644 index 00000000000000..91fc05137a8c18 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_missing_monitoring_data.ts @@ -0,0 +1,275 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { get } from 'lodash'; +import { AlertCluster, AlertMissingData } from '../../alerts/types'; +import { + KIBANA_SYSTEM_ID, + BEATS_SYSTEM_ID, + APM_SYSTEM_ID, + LOGSTASH_SYSTEM_ID, + ELASTICSEARCH_SYSTEM_ID, +} from '../../../common/constants'; + +interface ClusterBucketESResponse { + key: string; + kibana_uuids?: UuidResponse; + logstash_uuids?: UuidResponse; + es_uuids?: UuidResponse; + beats?: { + beats_uuids: UuidResponse; + }; + apms?: { + apm_uuids: UuidResponse; + }; +} + +interface UuidResponse { + buckets: UuidBucketESResponse[]; +} + +interface UuidBucketESResponse { + key: string; + most_recent: { + value: number; + }; + document: { + hits: { + hits: TopHitESResponse[]; + }; + }; +} + +interface TopHitESResponse { + _index: string; + _source: { + source_node?: { + name: string; + }; + kibana_stats?: { + kibana: { + name: string; + }; + }; + logstash_stats?: { + logstash: { + host: string; + }; + }; + beats_stats?: { + beat: { + name: string; + type: string; + }; + }; + }; +} + +function getStackProductFromIndex(index: string, beatType: string) { + if (index.includes('-kibana-')) { + return KIBANA_SYSTEM_ID; + } + if (index.includes('-beats-')) { + if (beatType === 'apm-server') { + return APM_SYSTEM_ID; + } + return BEATS_SYSTEM_ID; + } + if (index.includes('-logstash-')) { + return LOGSTASH_SYSTEM_ID; + } + if (index.includes('-es-')) { + return ELASTICSEARCH_SYSTEM_ID; + } + return ''; +} + +export async function fetchMissingMonitoringData( + callCluster: any, + clusters: AlertCluster[], + index: string, + size: number, + nowInMs: number, + startMs: number +): Promise<AlertMissingData[]> { + const endMs = nowInMs; + + const nameFields = [ + 'source_node.name', + 'kibana_stats.kibana.name', + 'logstash_stats.logstash.host', + 'beats_stats.beat.name', + 'beat_stats.beat.type', + ]; + const subAggs = { + most_recent: { + max: { + field: 'timestamp', + }, + }, + document: { + top_hits: { + size: 1, + sort: [ + { + timestamp: { + order: 'desc', + }, + }, + ], + _source: { + includes: ['_index', ...nameFields], + }, + }, + }, + }; + + const params = { + index, + filterPath: ['aggregations.clusters.buckets'], + body: { + size: 0, + query: { + bool: { + filter: [ + { + terms: { + cluster_uuid: clusters.map((cluster) => cluster.clusterUuid), + }, + }, + { + range: { + timestamp: { + format: 'epoch_millis', + gte: startMs, + lte: endMs, + }, + }, + }, + ], + }, + }, + aggs: { + clusters: { + terms: { + field: 'cluster_uuid', + size, + }, + aggs: { + es_uuids: { + terms: { + field: 'node_stats.node_id', + size, + }, + aggs: subAggs, + }, + kibana_uuids: { + terms: { + field: 'kibana_stats.kibana.uuid', + size, + }, + aggs: subAggs, + }, + beats: { + filter: { + bool: { + must_not: { + term: { + 'beats_stats.beat.type': 'apm-server', + }, + }, + }, + }, + aggs: { + beats_uuids: { + terms: { + field: 'beats_stats.beat.uuid', + size, + }, + aggs: subAggs, + }, + }, + }, + apms: { + filter: { + bool: { + must: { + term: { + 'beats_stats.beat.type': 'apm-server', + }, + }, + }, + }, + aggs: { + apm_uuids: { + terms: { + field: 'beats_stats.beat.uuid', + size, + }, + aggs: subAggs, + }, + }, + }, + logstash_uuids: { + terms: { + field: 'logstash_stats.logstash.uuid', + size, + }, + aggs: subAggs, + }, + }, + }, + }, + }, + }; + + const response = await callCluster('search', params); + const clusterBuckets = get( + response, + 'aggregations.clusters.buckets', + [] + ) as ClusterBucketESResponse[]; + const uniqueList: { [id: string]: AlertMissingData } = {}; + for (const clusterBucket of clusterBuckets) { + const clusterUuid = clusterBucket.key; + + const uuidBuckets = [ + ...(clusterBucket.es_uuids?.buckets || []), + ...(clusterBucket.kibana_uuids?.buckets || []), + ...(clusterBucket.logstash_uuids?.buckets || []), + ...(clusterBucket.beats?.beats_uuids.buckets || []), + ...(clusterBucket.apms?.apm_uuids.buckets || []), + ]; + + for (const uuidBucket of uuidBuckets) { + const stackProductUuid = uuidBucket.key; + const indexName = get(uuidBucket, `document.hits.hits[0]._index`); + const stackProduct = getStackProductFromIndex( + indexName, + get(uuidBucket, `document.hits.hits[0]._source.beats_stats.beat.type`) + ); + const differenceInMs = nowInMs - uuidBucket.most_recent.value; + let stackProductName = stackProductUuid; + for (const nameField of nameFields) { + stackProductName = get(uuidBucket, `document.hits.hits[0]._source.${nameField}`); + if (stackProductName) { + break; + } + } + + uniqueList[`${clusterUuid}${stackProduct}${stackProductUuid}`] = { + stackProduct, + stackProductUuid, + stackProductName, + clusterUuid, + gapDuration: differenceInMs, + ccs: indexName.includes(':') ? indexName.split(':')[0] : null, + }; + } + } + + const missingData = Object.values(uniqueList); + return missingData; +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/get_listing_link_for_stack_product.ts b/x-pack/plugins/monitoring/server/lib/alerts/get_listing_link_for_stack_product.ts new file mode 100644 index 00000000000000..1936ac1bc6183a --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/get_listing_link_for_stack_product.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { + BEATS_SYSTEM_ID, + ELASTICSEARCH_SYSTEM_ID, + KIBANA_SYSTEM_ID, + LOGSTASH_SYSTEM_ID, + APM_SYSTEM_ID, +} from '../../../common/constants'; + +export function getListingLinkForStackProduct(stackProduct: string) { + switch (stackProduct) { + case ELASTICSEARCH_SYSTEM_ID: + return 'elasticsearch/nodes'; + case LOGSTASH_SYSTEM_ID: + return 'logstash/nodes'; + case KIBANA_SYSTEM_ID: + return 'kibana/instances'; + case BEATS_SYSTEM_ID: + return 'beats/beats'; + case APM_SYSTEM_ID: + return 'apm/instances'; + } + return ''; +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/get_stack_product_label.ts b/x-pack/plugins/monitoring/server/lib/alerts/get_stack_product_label.ts new file mode 100644 index 00000000000000..9dafd775bac144 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/get_stack_product_label.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { capitalize } from 'lodash'; +import { APM_SYSTEM_ID, BEATS_SYSTEM_ID } from '../../../common/constants'; + +export function getStackProductLabel(stackProduct: string) { + switch (stackProduct) { + case APM_SYSTEM_ID: + return 'APM'; + case BEATS_SYSTEM_ID: + return 'Beat'; + } + return capitalize(stackProduct); +} diff --git a/x-pack/plugins/monitoring/server/lib/alerts/get_type_label_for_stack_product.ts b/x-pack/plugins/monitoring/server/lib/alerts/get_type_label_for_stack_product.ts new file mode 100644 index 00000000000000..74801de10438f5 --- /dev/null +++ b/x-pack/plugins/monitoring/server/lib/alerts/get_type_label_for_stack_product.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { i18n } from '@kbn/i18n'; +import { + BEATS_SYSTEM_ID, + ELASTICSEARCH_SYSTEM_ID, + KIBANA_SYSTEM_ID, + LOGSTASH_SYSTEM_ID, + APM_SYSTEM_ID, +} from '../../../common/constants'; + +const NODES = i18n.translate('xpack.monitoring.alerts.typeLabel.nodes', { + defaultMessage: 'nodes', +}); + +const INSTANCES = i18n.translate('xpack.monitoring.alerts.typeLabel.instances', { + defaultMessage: 'instances', +}); + +const SERVERS = i18n.translate('xpack.monitoring.alerts.typeLabel.servers', { + defaultMessage: 'servers', +}); + +const NODE = i18n.translate('xpack.monitoring.alerts.typeLabel.node', { + defaultMessage: 'node', +}); + +const INSTANCE = i18n.translate('xpack.monitoring.alerts.typeLabel.instance', { + defaultMessage: 'instance', +}); + +const SERVER = i18n.translate('xpack.monitoring.alerts.typeLabel.server', { + defaultMessage: 'server', +}); + +export function getTypeLabelForStackProduct(stackProduct: string, plural: boolean = true) { + switch (stackProduct) { + case ELASTICSEARCH_SYSTEM_ID: + case LOGSTASH_SYSTEM_ID: + return plural ? NODES : NODE; + case KIBANA_SYSTEM_ID: + case BEATS_SYSTEM_ID: + return plural ? INSTANCES : INSTANCE; + case APM_SYSTEM_ID: + return plural ? SERVERS : SERVER; + } + return 'n/a'; +} From 7836998e76e64cdc15bb3233f2a113222837a80d Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper <Zacqary@users.noreply.github.com> Date: Thu, 1 Oct 2020 11:31:39 -0500 Subject: [PATCH 041/128] [Metrics UI] Display No Data context.values as [NO DATA] (#78038) Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../inventory_metric_threshold_executor.ts | 6 ++++-- .../metric_threshold_executor.ts | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) 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 99904f15b46061..b56ede19743939 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 @@ -148,8 +148,10 @@ export const FIRED_ACTIONS = { const formatMetric = (metric: SnapshotMetricType, value: number) => { const metricFormatter = get(METRIC_FORMATTERS, metric, METRIC_FORMATTERS.count); - if (value == null) { - return ''; + if (isNaN(value)) { + return i18n.translate('xpack.infra.metrics.alerting.inventory.noDataFormattedValue', { + defaultMessage: '[NO DATA]', + }); } const formatter = createFormatter(metricFormatter.formatter, metricFormatter.template); return formatter(value); 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 c85685b4cdca8e..4dec552c5bd6c6 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 @@ -131,11 +131,24 @@ const formatAlertResult = <AlertResult>( } & AlertResult ) => { const { metric, currentValue, threshold } = alertResult; - if (!metric.endsWith('.pct')) return alertResult; + const noDataValue = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.noDataFormattedValue', + { + defaultMessage: '[NO DATA]', + } + ); + if (!metric.endsWith('.pct')) + return { + ...alertResult, + currentValue: currentValue ?? noDataValue, + }; const formatter = createFormatter('percent'); return { ...alertResult, - currentValue: formatter(currentValue), + currentValue: + currentValue !== null && typeof currentValue !== 'undefined' + ? formatter(currentValue) + : noDataValue, threshold: Array.isArray(threshold) ? threshold.map((v: number) => formatter(v)) : threshold, }; }; From 963fe0c1d1ceaea53666e039dbe0863dde2c5c12 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet <nicolas.chaulet@elastic.co> Date: Thu, 1 Oct 2020 13:09:02 -0400 Subject: [PATCH 042/128] [Ingest Manager] Ensure we trigger agent policy updated event when we bump revision. (#78836) --- .../server/services/agent_policy.ts | 18 +++++++-- .../apis/settings/update.ts | 40 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/services/agent_policy.ts b/x-pack/plugins/ingest_manager/server/services/agent_policy.ts index 29821a530098c2..12ea8ab92f6c4f 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_policy.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_policy.ts @@ -33,7 +33,7 @@ const SAVED_OBJECT_TYPE = AGENT_POLICY_SAVED_OBJECT_TYPE; class AgentPolicyService { private triggerAgentPolicyUpdatedEvent = async ( soClient: SavedObjectsClientContract, - action: string, + action: 'created' | 'updated' | 'deleted', agentPolicyId: string ) => { return agentPolicyUpdateEventHandler(soClient, action, agentPolicyId); @@ -258,7 +258,11 @@ class AgentPolicyService { id: string, options?: { user?: AuthenticatedUser } ): Promise<AgentPolicy> { - return this._update(soClient, id, {}, options?.user); + const res = await this._update(soClient, id, {}, options?.user); + + await this.triggerAgentPolicyUpdatedEvent(soClient, 'updated', id); + + return res; } public async bumpAllAgentPolicies( soClient: SavedObjectsClientContract, @@ -277,7 +281,15 @@ class AgentPolicyService { }; return policy; }); - return soClient.bulkUpdate<AgentPolicySOAttributes>(bumpedPolicies); + const res = await soClient.bulkUpdate<AgentPolicySOAttributes>(bumpedPolicies); + + await Promise.all( + currentPolicies.saved_objects.map((policy) => + this.triggerAgentPolicyUpdatedEvent(soClient, 'updated', policy.id) + ) + ); + + return res; } public async assignPackagePolicies( diff --git a/x-pack/test/ingest_manager_api_integration/apis/settings/update.ts b/x-pack/test/ingest_manager_api_integration/apis/settings/update.ts index 86292b535db2d4..4340cd4832307b 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/settings/update.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/settings/update.ts @@ -5,16 +5,20 @@ */ import expect from '@kbn/expect'; +import { Client } from 'elasticsearch'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; import { skipIfNoDockerRegistry } from '../../helpers'; +import { setupIngest } from '../fleet/agents/services'; export default function (providerContext: FtrProviderContext) { const { getService } = providerContext; const supertest = getService('supertest'); const kibanaServer = getService('kibanaServer'); + const esClient: Client = getService('legacyEs'); describe('Settings - update', async function () { skipIfNoDockerRegistry(providerContext); + setupIngest(providerContext); it("should bump all agent policy's revision", async function () { const { body: testPolicy1PostRes } = await supertest @@ -49,5 +53,41 @@ export default function (providerContext: FtrProviderContext) { expect(getTestPolicy1Res.attributes.revision).equal(2); expect(getTestPolicy2Res.attributes.revision).equal(2); }); + + it('should create agent actions', async function () { + const { body: testPolicyRes } = await supertest + .post(`/api/ingest_manager/agent_policies`) + .set('kbn-xsrf', 'xxxx') + .send({ + name: 'test', + description: '', + namespace: 'default', + }); + + await supertest + .put(`/api/ingest_manager/settings`) + .set('kbn-xsrf', 'xxxx') + .send({ kibana_urls: ['http://localhost:1232/abc', 'http://localhost:1232/abc'] }); + + const res = await esClient.search({ + index: '.kibana', + body: { + query: { + bool: { + must: [ + { + terms: { + type: ['fleet-agent-actions'], + }, + }, + { match: { 'fleet-agent-actions.policy_id': testPolicyRes.item.id } }, + ], + }, + }, + }, + }); + + expect(res.hits.hits.length).equal(2); + }); }); } From 574205dc72b63a3c30ff159684012d8e3191ef2d Mon Sep 17 00:00:00 2001 From: Tiago Costa <tiagoffcc@hotmail.com> Date: Thu, 1 Oct 2020 18:14:59 +0100 Subject: [PATCH 043/128] chore(NA): remove non existing plugin paths from case api integration tests (#79127) * chore(NA): remove non existing plugin paths from case api integration tests config * chore(NA): remove unused import --- x-pack/test/case_api_integration/common/config.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/x-pack/test/case_api_integration/common/config.ts b/x-pack/test/case_api_integration/common/config.ts index 5d34f8b04981a0..72d1bc4ec9a37a 100644 --- a/x-pack/test/case_api_integration/common/config.ts +++ b/x-pack/test/case_api_integration/common/config.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import path from 'path'; - import { CA_CERT_PATH } from '@kbn/dev-utils'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; @@ -78,8 +76,6 @@ export function createTestConfig(name: string, options: CreateTestConfigOptions) `--xpack.actions.enabledActionTypes=${JSON.stringify(enabledActionTypes)}`, '--xpack.eventLog.logEntries=true', ...disabledPlugins.map((key) => `--xpack.${key}.enabled=false`), - `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'alerts')}`, - `--plugin-path=${path.join(__dirname, 'fixtures', 'plugins', 'actions')}`, ...(ssl ? [ `--elasticsearch.hosts=${servers.elasticsearch.protocol}://${servers.elasticsearch.hostname}:${servers.elasticsearch.port}`, From fd7dd41617a7464e6b0e61a83800c05ccf6a5189 Mon Sep 17 00:00:00 2001 From: Quynh Nguyen <43350163+qn895@users.noreply.github.com> Date: Thu, 1 Oct 2020 12:41:12 -0500 Subject: [PATCH 044/128] [ML] Update transform cloning to include description and new fields (#78364) --- .../public/app/common/request.test.ts | 33 +++++++++++++ .../transform/public/app/common/request.ts | 22 ++++++--- .../step_details/step_details_form.tsx | 18 +++++++ .../step_details/step_details_summary.tsx | 2 + .../test/functional/apps/transform/cloning.ts | 15 +++++- .../functional/services/transform/wizard.ts | 47 +++++++++++++++++++ 6 files changed, 129 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/transform/public/app/common/request.test.ts b/x-pack/plugins/transform/public/app/common/request.test.ts index 913ea8964eaf0e..46ace2c3315a5a 100644 --- a/x-pack/plugins/transform/public/app/common/request.test.ts +++ b/x-pack/plugins/transform/public/app/common/request.test.ts @@ -17,6 +17,7 @@ import { defaultQuery, getPreviewTransformRequestBody, getCreateTransformRequestBody, + getCreateTransformSettingsRequestBody, getPivotQuery, isDefaultQuery, isMatchAllQuery, @@ -159,6 +160,7 @@ describe('Transform: Common', () => { transformDescription: 'the-transform-description', transformFrequency: '1m', transformSettingsMaxPageSearchSize: 100, + transformSettingsDocsPerSecond: 400, destinationIndex: 'the-destination-index', touched: true, valid: true, @@ -180,6 +182,7 @@ describe('Transform: Common', () => { }, settings: { max_page_search_size: 100, + docs_per_second: 400, }, source: { index: ['the-index-pattern-title'], @@ -187,4 +190,34 @@ describe('Transform: Common', () => { }, }); }); + + test('getCreateTransformSettingsRequestBody() with multiple settings', () => { + const transformDetailsState: Partial<StepDetailsExposedState> = { + transformSettingsDocsPerSecond: 400, + transformSettingsMaxPageSearchSize: 100, + }; + + const request = getCreateTransformSettingsRequestBody(transformDetailsState); + + expect(request).toEqual({ + settings: { + docs_per_second: 400, + max_page_search_size: 100, + }, + }); + }); + + test('getCreateTransformSettingsRequestBody() with one setting', () => { + const transformDetailsState: Partial<StepDetailsExposedState> = { + transformSettingsDocsPerSecond: 400, + }; + + const request = getCreateTransformSettingsRequestBody(transformDetailsState); + + expect(request).toEqual({ + settings: { + docs_per_second: 400, + }, + }); + }); }); diff --git a/x-pack/plugins/transform/public/app/common/request.ts b/x-pack/plugins/transform/public/app/common/request.ts index 45160d125309db..8ee235baf7c5aa 100644 --- a/x-pack/plugins/transform/public/app/common/request.ts +++ b/x-pack/plugins/transform/public/app/common/request.ts @@ -130,6 +130,20 @@ export function getPreviewTransformRequestBody( return request; } +export const getCreateTransformSettingsRequestBody = ( + transformDetailsState: Partial<StepDetailsExposedState> +): { settings?: PutTransformsRequestSchema['settings'] } => { + const settings: PutTransformsRequestSchema['settings'] = { + ...(transformDetailsState.transformSettingsMaxPageSearchSize + ? { max_page_search_size: transformDetailsState.transformSettingsMaxPageSearchSize } + : {}), + ...(transformDetailsState.transformSettingsDocsPerSecond + ? { docs_per_second: transformDetailsState.transformSettingsDocsPerSecond } + : {}), + }; + return Object.keys(settings).length > 0 ? { settings } : {}; +}; + export const getCreateTransformRequestBody = ( indexPatternTitle: IndexPattern['title'], pivotState: StepDefineExposedState, @@ -164,13 +178,7 @@ export const getCreateTransformRequestBody = ( } : {}), // conditionally add additional settings - ...(transformDetailsState.transformSettingsMaxPageSearchSize - ? { - settings: { - max_page_search_size: transformDetailsState.transformSettingsMaxPageSearchSize, - }, - } - : {}), + ...getCreateTransformSettingsRequestBody(transformDetailsState), }); export function isHttpFetchError(error: any): error is HttpFetchError { 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 00ab516f625fe9..9b43879512e4db 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 @@ -63,6 +63,7 @@ export interface StepDetailsExposedState { transformDescription: string; transformFrequency: string; transformSettingsMaxPageSearchSize: number; + transformSettingsDocsPerSecond?: number; valid: boolean; indexPatternTimeField?: string | undefined; } @@ -100,6 +101,20 @@ export function applyTransformConfigToDetailsState( state.continuousModeDelay = time?.delay ?? defaultContinuousModeDelay; state.isContinuousModeEnabled = true; } + if (transformConfig.description !== undefined) { + state.transformDescription = transformConfig.description; + } + if (transformConfig.frequency !== undefined) { + state.transformFrequency = transformConfig.frequency; + } + if (transformConfig.settings) { + if (typeof transformConfig.settings?.max_page_search_size === 'number') { + state.transformSettingsMaxPageSearchSize = transformConfig.settings.max_page_search_size; + } + if (typeof transformConfig.settings?.docs_per_second === 'number') { + state.transformSettingsDocsPerSecond = transformConfig.settings.docs_per_second; + } + } } return state; } @@ -275,6 +290,8 @@ export const StepDetailsForm: FC<Props> = React.memo( const [transformSettingsMaxPageSearchSize, setTransformSettingsMaxPageSearchSize] = useState( defaults.transformSettingsMaxPageSearchSize ); + const [transformSettingsDocsPerSecond] = useState(defaults.transformSettingsDocsPerSecond); + const isTransformSettingsMaxPageSearchSizeValid = transformSettingsMaxPageSearchSizeValidator( transformSettingsMaxPageSearchSize ); @@ -301,6 +318,7 @@ export const StepDetailsForm: FC<Props> = React.memo( transformDescription, transformFrequency, transformSettingsMaxPageSearchSize, + transformSettingsDocsPerSecond, destinationIndex, touched: true, valid, diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx index 45cd8aa465522c..f5444eaf6640af 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx @@ -98,6 +98,7 @@ export const StepDetailsSummary: FC<StepDetailsExposedState> = React.memo((props paddingSize="s" > <EuiFormRow + data-test-subj={'transformWizardAdvancedSettingsFrequencyLabel'} label={i18n.translate('xpack.transform.stepDetailsSummary.frequencyLabel', { defaultMessage: 'Frequency', })} @@ -105,6 +106,7 @@ export const StepDetailsSummary: FC<StepDetailsExposedState> = React.memo((props <span>{transformFrequency}</span> </EuiFormRow> <EuiFormRow + data-test-subj={'transformWizardAdvancedSettingsMaxPageSearchSizeLabel'} label={i18n.translate('xpack.transform.stepDetailsSummary.maxPageSearchSizeLabel', { defaultMessage: 'Maximum page search size', })} diff --git a/x-pack/test/functional/apps/transform/cloning.ts b/x-pack/test/functional/apps/transform/cloning.ts index a147b56d56251f..421eab656f6063 100644 --- a/x-pack/test/functional/apps/transform/cloning.ts +++ b/x-pack/test/functional/apps/transform/cloning.ts @@ -18,6 +18,10 @@ function getTransformConfig(): TransformPivotConfig { }, description: 'ecommerce batch transform with avg(products.base_price) grouped by terms(category.keyword)', + frequency: '3s', + settings: { + max_page_search_size: 250, + }, dest: { index: `user-ec_2_${date}` }, }; } @@ -155,7 +159,7 @@ export default function ({ getService }: FtrProviderContext) { await transform.testExecution.logTestStep('should input the transform description'); await transform.wizard.assertTransformDescriptionInputExists(); - await transform.wizard.assertTransformDescriptionValue(''); + await transform.wizard.assertTransformDescriptionValue(transformConfig.description!); await transform.wizard.setTransformDescription(testData.transformDescription); await transform.testExecution.logTestStep('should input the destination index'); @@ -173,6 +177,15 @@ export default function ({ getService }: FtrProviderContext) { await transform.wizard.assertContinuousModeSwitchExists(); await transform.wizard.assertContinuousModeSwitchCheckState(false); + await transform.testExecution.logTestStep( + 'should display the advanced settings and show pre-filled configuration' + ); + await transform.wizard.openTransformAdvancedSettingsAccordion(); + await transform.wizard.assertTransformFrequencyValue(transformConfig.frequency!); + await transform.wizard.assertTransformMaxPageSearchSizeValue( + transformConfig.settings!.max_page_search_size! + ); + await transform.testExecution.logTestStep('should load the create step'); await transform.wizard.advanceToCreateStep(); diff --git a/x-pack/test/functional/services/transform/wizard.ts b/x-pack/test/functional/services/transform/wizard.ts index 9cfdbadac8a3be..b05f1ff26199ed 100644 --- a/x-pack/test/functional/services/transform/wizard.ts +++ b/x-pack/test/functional/services/transform/wizard.ts @@ -522,6 +522,53 @@ export function TransformWizardProvider({ getService }: FtrProviderContext) { ); }, + async assertTransformAdvancedSettingsAccordionExists() { + await testSubjects.existOrFail('transformWizardAccordionAdvancedSettings'); + }, + + // for now we expect this to be used only for opening the accordion + async openTransformAdvancedSettingsAccordion() { + await this.assertTransformAdvancedSettingsAccordionExists(); + await testSubjects.click('transformWizardAccordionAdvancedSettings'); + await this.assertTransformFrequencyInputExists(); + await this.assertTransformMaxPageSearchSizeInputExists(); + }, + + async assertTransformFrequencyInputExists() { + await testSubjects.existOrFail('transformFrequencyInput'); + expect(await testSubjects.isDisplayed('transformFrequencyInput')).to.eql( + true, + `Expected 'Frequency' input to be displayed` + ); + }, + + async assertTransformFrequencyValue(expectedValue: string) { + const actualValue = await testSubjects.getAttribute('transformFrequencyInput', 'value'); + expect(actualValue).to.eql( + expectedValue, + `Transform frequency input text should be '${expectedValue}' (got '${actualValue}')` + ); + }, + + async assertTransformMaxPageSearchSizeInputExists() { + await testSubjects.existOrFail('transformMaxPageSearchSizeInput'); + expect(await testSubjects.isDisplayed('transformMaxPageSearchSizeInput')).to.eql( + true, + `Expected 'Maximum page search size' input to be displayed` + ); + }, + + async assertTransformMaxPageSearchSizeValue(expectedValue: number) { + const actualValue = await testSubjects.getAttribute( + 'transformMaxPageSearchSizeInput', + 'value' + ); + expect(actualValue).to.eql( + expectedValue, + `Transform maximum page search size input text should be '${expectedValue}' (got '${actualValue}')` + ); + }, + async assertCreateAndStartButtonExists() { await testSubjects.existOrFail('transformWizardCreateAndStartButton'); expect(await testSubjects.isDisplayed('transformWizardCreateAndStartButton')).to.eql( From 085f8a17ff2a6f83e42f867c509d573fbadb968b Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski <jon@budzenski.me> Date: Thu, 1 Oct 2020 12:42:37 -0500 Subject: [PATCH 045/128] Revert "[Metrics UI] Add ability to override datafeeds and job config for partition field (#78875)" This reverts commit ee7672aaf074dc4ebaf0ffb88d95d5f1bf9e1d18. --- .../containers/ml/infra_ml_module_types.ts | 4 +- .../containers/ml/infra_ml_setup_state.ts | 289 ++++++++++++++++++ .../metrics_hosts/module_descriptor.ts | 135 +++----- .../modules/metrics_k8s/module_descriptor.ts | 143 +++------ .../anomoly_detection_flyout.tsx | 4 +- .../ml/anomaly_detection/flyout_home.tsx | 113 ++++--- .../ml/anomaly_detection/job_setup_screen.tsx | 3 +- 7 files changed, 444 insertions(+), 247 deletions(-) create mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts index e36f38add641aa..a9f2671de82598 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts @@ -33,11 +33,11 @@ export interface ModuleDescriptor<JobType extends string> { partitionField?: string ) => Promise<SetupMlModuleResponsePayload>; cleanUpModule: (spaceId: string, sourceId: string) => Promise<DeleteJobsResponsePayload>; - validateSetupIndices?: ( + validateSetupIndices: ( indices: string[], timestampField: string ) => Promise<ValidationIndicesResponsePayload>; - validateSetupDatasets?: ( + validateSetupDatasets: ( indices: string[], timestampField: string, startTime: number, diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts new file mode 100644 index 00000000000000..0dfe3b301f2400 --- /dev/null +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts @@ -0,0 +1,289 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { isEqual } from 'lodash'; +import { useCallback, useEffect, useMemo, useState } from 'react'; +import { usePrevious } from 'react-use'; +import { + combineDatasetFilters, + DatasetFilter, + filterDatasetFilter, + isExampleDataIndex, +} from '../../../common/infra_ml'; +import { + AvailableIndex, + ValidationIndicesError, + ValidationUIError, +} from '../../components/logging/log_analysis_setup/initial_configuration_step'; +import { useTrackedPromise } from '../../utils/use_tracked_promise'; +import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; + +type SetupHandler = ( + indices: string[], + startTime: number | undefined, + endTime: number | undefined, + datasetFilter: DatasetFilter +) => void; + +interface AnalysisSetupStateArguments<JobType extends string> { + cleanUpAndSetUpModule: SetupHandler; + moduleDescriptor: ModuleDescriptor<JobType>; + setUpModule: SetupHandler; + sourceConfiguration: ModuleSourceConfiguration; +} + +const fourWeeksInMs = 86400000 * 7 * 4; + +export const useAnalysisSetupState = <JobType extends string>({ + cleanUpAndSetUpModule, + moduleDescriptor: { validateSetupDatasets, validateSetupIndices }, + setUpModule, + sourceConfiguration, +}: AnalysisSetupStateArguments<JobType>) => { + const [startTime, setStartTime] = useState<number | undefined>(Date.now() - fourWeeksInMs); + const [endTime, setEndTime] = useState<number | undefined>(undefined); + + const isTimeRangeValid = useMemo( + () => (startTime != null && endTime != null ? startTime < endTime : true), + [endTime, startTime] + ); + + const [validatedIndices, setValidatedIndices] = useState<AvailableIndex[]>( + sourceConfiguration.indices.map((indexName) => ({ + name: indexName, + validity: 'unknown' as const, + })) + ); + + const updateIndicesWithValidationErrors = useCallback( + (validationErrors: ValidationIndicesError[]) => + setValidatedIndices((availableIndices) => + availableIndices.map((previousAvailableIndex) => { + const indexValiationErrors = validationErrors.filter( + ({ index }) => index === previousAvailableIndex.name + ); + + if (indexValiationErrors.length > 0) { + return { + validity: 'invalid', + name: previousAvailableIndex.name, + errors: indexValiationErrors, + }; + } else if (previousAvailableIndex.validity === 'valid') { + return { + ...previousAvailableIndex, + validity: 'valid', + errors: [], + }; + } else { + return { + validity: 'valid', + name: previousAvailableIndex.name, + isSelected: !isExampleDataIndex(previousAvailableIndex.name), + availableDatasets: [], + datasetFilter: { + type: 'includeAll' as const, + }, + }; + } + }) + ), + [] + ); + + const updateIndicesWithAvailableDatasets = useCallback( + (availableDatasets: Array<{ indexName: string; datasets: string[] }>) => + setValidatedIndices((availableIndices) => + availableIndices.map((previousAvailableIndex) => { + if (previousAvailableIndex.validity !== 'valid') { + return previousAvailableIndex; + } + + const availableDatasetsForIndex = availableDatasets.filter( + ({ indexName }) => indexName === previousAvailableIndex.name + ); + const newAvailableDatasets = availableDatasetsForIndex.flatMap( + ({ datasets }) => datasets + ); + + // filter out datasets that have disappeared if this index' datasets were updated + const newDatasetFilter: DatasetFilter = + availableDatasetsForIndex.length > 0 + ? filterDatasetFilter(previousAvailableIndex.datasetFilter, (dataset) => + newAvailableDatasets.includes(dataset) + ) + : previousAvailableIndex.datasetFilter; + + return { + ...previousAvailableIndex, + availableDatasets: newAvailableDatasets, + datasetFilter: newDatasetFilter, + }; + }) + ), + [] + ); + + const validIndexNames = useMemo( + () => validatedIndices.filter((index) => index.validity === 'valid').map((index) => index.name), + [validatedIndices] + ); + + const selectedIndexNames = useMemo( + () => + validatedIndices + .filter((index) => index.validity === 'valid' && index.isSelected) + .map((i) => i.name), + [validatedIndices] + ); + + const datasetFilter = useMemo( + () => + validatedIndices + .flatMap((validatedIndex) => + validatedIndex.validity === 'valid' + ? validatedIndex.datasetFilter + : { type: 'includeAll' as const } + ) + .reduce(combineDatasetFilters, { type: 'includeAll' as const }), + [validatedIndices] + ); + + const [validateIndicesRequest, validateIndices] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + return await validateSetupIndices( + sourceConfiguration.indices, + sourceConfiguration.timestampField + ); + }, + onResolve: ({ data: { errors } }) => { + updateIndicesWithValidationErrors(errors); + }, + onReject: () => { + setValidatedIndices([]); + }, + }, + [sourceConfiguration.indices, sourceConfiguration.timestampField] + ); + + const [validateDatasetsRequest, validateDatasets] = useTrackedPromise( + { + cancelPreviousOn: 'resolution', + createPromise: async () => { + if (validIndexNames.length === 0) { + return { data: { datasets: [] } }; + } + + return await validateSetupDatasets( + validIndexNames, + sourceConfiguration.timestampField, + startTime ?? 0, + endTime ?? Date.now() + ); + }, + onResolve: ({ data: { datasets } }) => { + updateIndicesWithAvailableDatasets(datasets); + }, + }, + [validIndexNames, sourceConfiguration.timestampField, startTime, endTime] + ); + + const setUp = useCallback(() => { + return setUpModule(selectedIndexNames, startTime, endTime, datasetFilter); + }, [setUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); + + const cleanUpAndSetUp = useCallback(() => { + return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime, datasetFilter); + }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); + + const isValidating = useMemo( + () => validateIndicesRequest.state === 'pending' || validateDatasetsRequest.state === 'pending', + [validateDatasetsRequest.state, validateIndicesRequest.state] + ); + + const validationErrors = useMemo<ValidationUIError[]>(() => { + if (isValidating) { + return []; + } + + return [ + // validate request status + ...(validateIndicesRequest.state === 'rejected' || + validateDatasetsRequest.state === 'rejected' + ? [{ error: 'NETWORK_ERROR' as const }] + : []), + // validation request results + ...validatedIndices.reduce<ValidationUIError[]>((errors, index) => { + return index.validity === 'invalid' && selectedIndexNames.includes(index.name) + ? [...errors, ...index.errors] + : errors; + }, []), + // index count + ...(selectedIndexNames.length === 0 ? [{ error: 'TOO_FEW_SELECTED_INDICES' as const }] : []), + // time range + ...(!isTimeRangeValid ? [{ error: 'INVALID_TIME_RANGE' as const }] : []), + ]; + }, [ + isValidating, + validateIndicesRequest.state, + validateDatasetsRequest.state, + validatedIndices, + selectedIndexNames, + isTimeRangeValid, + ]); + + const prevStartTime = usePrevious(startTime); + const prevEndTime = usePrevious(endTime); + const prevValidIndexNames = usePrevious(validIndexNames); + + useEffect(() => { + if (!isTimeRangeValid) { + return; + } + + validateIndices(); + }, [isTimeRangeValid, validateIndices]); + + useEffect(() => { + if (!isTimeRangeValid) { + return; + } + + if ( + startTime !== prevStartTime || + endTime !== prevEndTime || + !isEqual(validIndexNames, prevValidIndexNames) + ) { + validateDatasets(); + } + }, [ + endTime, + isTimeRangeValid, + prevEndTime, + prevStartTime, + prevValidIndexNames, + startTime, + validIndexNames, + validateDatasets, + ]); + + return { + cleanUpAndSetUp, + datasetFilter, + endTime, + isValidating, + selectedIndexNames, + setEndTime, + setStartTime, + setUp, + startTime, + validatedIndices, + setValidatedIndices, + validationErrors, + }; +}; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts index 711ee76d42a64f..cec87fb1144e33 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts @@ -10,27 +10,17 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; +import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; +import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; import { metricsHostsJobTypes, getJobId, MetricsHostsJobType, DatasetFilter, bucketSpan, + partitionField, } from '../../../../../common/infra_ml'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_memory_usage.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_memory_usage.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_in.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_in.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_out.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_out.json'; -type JobType = 'hosts_memory_usage' | 'hosts_network_in' | 'hosts_network_out'; const moduleId = 'metrics_ui_hosts'; const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { defaultMessage: 'Metrics anomanly detection', @@ -64,68 +54,23 @@ const setUpModule = async ( end: number | undefined, datasetFilter: DatasetFilter, { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - partitionField?: string + pField?: string ) => { const indexNamePattern = indices.join(','); - const jobIds: JobType[] = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; - - const jobOverrides = jobIds.map((id) => { - const { job: defaultJobConfig } = getDefaultJobConfigs(id); - - // eslint-disable-next-line @typescript-eslint/naming-convention - const analysis_config = { - ...defaultJobConfig.analysis_config, - }; - - if (partitionField) { - analysis_config.detectors[0].partition_field_name = partitionField; - if (analysis_config.influencers.indexOf(partitionField) === -1) { - analysis_config.influencers.push(partitionField); - } - } - - return { - job_id: id, - data_description: { - time_field: timestampField, - }, - analysis_config, - custom_settings: { - metrics_source_config: { - indexPattern: indexNamePattern, - timestampField, - bucketSpan, - }, - }, - }; - }); - - const datafeedOverrides = jobIds.map((id) => { - const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id); - - if (!partitionField || id === 'hosts_memory_usage') { - // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field - return defaultDatafeedConfig; - } - - // If we have a partition field, we need to change the aggregation to do a terms agg at the top level - const aggregations = { - [partitionField]: { - terms: { - field: partitionField, - }, - aggregations: { - ...defaultDatafeedConfig.aggregations, - }, + const jobIds = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; + const jobOverrides = jobIds.map((id) => ({ + job_id: id, + data_description: { + time_field: timestampField, + }, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, }, - }; - - return { - ...defaultDatafeedConfig, - job_id: id, - aggregations, - }; - }); + }, + })); return callSetupMlModuleAPI( moduleId, @@ -135,34 +80,36 @@ const setUpModule = async ( sourceId, indexNamePattern, jobOverrides, - datafeedOverrides + [] ); }; -const getDefaultJobConfigs = (jobId: JobType) => { - switch (jobId) { - case 'hosts_memory_usage': - return { - datafeed: MemoryDatafeed, - job: MemoryJob, - }; - case 'hosts_network_in': - return { - datafeed: NetworkInDatafeed, - job: NetworkInJob, - }; - case 'hosts_network_out': - return { - datafeed: NetworkOutDatafeed, - job: NetworkOutJob, - }; - } -}; - const cleanUpModule = async (spaceId: string, sourceId: string) => { return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); }; +const validateSetupIndices = async (indices: string[], timestampField: string) => { + return await callValidateIndicesAPI(indices, [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + ]); +}; + +const validateSetupDatasets = async ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number +) => { + return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +}; + export const metricHostsModule: ModuleDescriptor<MetricsHostsJobType> = { moduleId, moduleName, @@ -174,4 +121,6 @@ export const metricHostsModule: ModuleDescriptor<MetricsHostsJobType> = { getModuleDefinition, setUpModule, cleanUpModule, + validateSetupDatasets, + validateSetupIndices, }; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts index 41c6df92fb379c..cbcff1c307af6e 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts @@ -10,28 +10,17 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; +import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; +import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; import { metricsK8SJobTypes, getJobId, MetricK8sJobType, DatasetFilter, bucketSpan, + partitionField, } from '../../../../../common/infra_ml'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_memory_usage.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_memory_usage.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_in.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_in.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_out.json'; -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_out.json'; -type JobType = 'k8s_memory_usage' | 'k8s_network_in' | 'k8s_network_out'; -export const DEFAULT_K8S_PARTITION_FIELD = 'kubernetes.namespace'; const moduleId = 'metrics_ui_k8s'; const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { defaultMessage: 'Metrics anomanly detection', @@ -65,72 +54,26 @@ const setUpModule = async ( end: number | undefined, datasetFilter: DatasetFilter, { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - partitionField?: string + pField?: string ) => { const indexNamePattern = indices.join(','); - const jobIds: JobType[] = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; - const jobOverrides = jobIds.map((id) => { - const { job: defaultJobConfig } = getDefaultJobConfigs(id); - - // eslint-disable-next-line @typescript-eslint/naming-convention - const analysis_config = { - ...defaultJobConfig.analysis_config, - }; - - if (partitionField) { - analysis_config.detectors[0].partition_field_name = partitionField; - if (analysis_config.influencers.indexOf(partitionField) === -1) { - analysis_config.influencers.push(partitionField); - } - } - - return { - job_id: id, - data_description: { - time_field: timestampField, - }, - analysis_config, - custom_settings: { - metrics_source_config: { - indexPattern: indexNamePattern, - timestampField, - bucketSpan, - }, - }, - }; - }); - - const datafeedOverrides = jobIds.map((id) => { - const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id); - - if (!partitionField || id === 'k8s_memory_usage') { - // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field - return defaultDatafeedConfig; - } - - // Because the ML K8s jobs ship with a default partition field of {kubernetes.namespace}, ignore that agg and wrap it in our own agg. - const innerAggregation = - defaultDatafeedConfig.aggregations[DEFAULT_K8S_PARTITION_FIELD].aggregations; - - // If we have a partition field, we need to change the aggregation to do a terms agg to partition the data at the top level - const aggregations = { - [partitionField]: { - terms: { - field: partitionField, - size: 25, // 25 is arbitratry and only used to keep the number of buckets to a managable level in the event that the user choose a high cardinality partition field. - }, - aggregations: { - ...innerAggregation, - }, + const jobIds = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; + const jobOverrides = jobIds.map((id) => ({ + job_id: id, + analysis_config: { + bucket_span: `${bucketSpan}ms`, + }, + data_description: { + time_field: timestampField, + }, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, }, - }; - - return { - ...defaultDatafeedConfig, - job_id: id, - aggregations, - }; - }); + }, + })); return callSetupMlModuleAPI( moduleId, @@ -140,34 +83,36 @@ const setUpModule = async ( sourceId, indexNamePattern, jobOverrides, - datafeedOverrides + [] ); }; -const getDefaultJobConfigs = (jobId: JobType) => { - switch (jobId) { - case 'k8s_memory_usage': - return { - datafeed: MemoryDatafeed, - job: MemoryJob, - }; - case 'k8s_network_in': - return { - datafeed: NetworkInDatafeed, - job: NetworkInJob, - }; - case 'k8s_network_out': - return { - datafeed: NetworkOutDatafeed, - job: NetworkOutJob, - }; - } -}; - const cleanUpModule = async (spaceId: string, sourceId: string) => { return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); }; +const validateSetupIndices = async (indices: string[], timestampField: string) => { + return await callValidateIndicesAPI(indices, [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + ]); +}; + +const validateSetupDatasets = async ( + indices: string[], + timestampField: string, + startTime: number, + endTime: number +) => { + return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +}; + export const metricHostsModule: ModuleDescriptor<MetricK8sJobType> = { moduleId, moduleName, @@ -179,4 +124,6 @@ export const metricHostsModule: ModuleDescriptor<MetricK8sJobType> = { getModuleDefinition, setUpModule, cleanUpModule, + validateSetupDatasets, + validateSetupIndices, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx index b5d224910e819d..b063713fa2c971 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx @@ -50,10 +50,10 @@ export const AnomalyDetectionFlyout = () => { return ( <> - <EuiButtonEmpty iconSide={'left'} iconType={'inspect'} onClick={openFlyout}> + <EuiButtonEmpty iconSide={'right'} onClick={openFlyout}> <FormattedMessage id="xpack.infra.ml.anomalyDetectionButton" - defaultMessage="Anomaly detection" + defaultMessage="Anomaly Detection" /> </EuiButtonEmpty> {showFlyout && ( diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx index 5b520084ebb744..801dff9c4a17a5 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx @@ -5,7 +5,7 @@ */ import React, { useState, useCallback, useEffect } from 'react'; -import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiSpacer } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiText, EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -30,7 +30,7 @@ interface Props { } export const FlyoutHome = (props: Props) => { - const [tab] = useState<'jobs' | 'anomalies'>('jobs'); + const [tab, setTab] = useState<'jobs' | 'anomalies'>('jobs'); const { goToSetup } = props; const { fetchJobStatus: fetchHostJobStatus, @@ -56,10 +56,18 @@ export const FlyoutHome = (props: Props) => { goToSetup('kubernetes'); }, [goToSetup]); + const goToJobs = useCallback(() => { + setTab('jobs'); + }, []); + const jobIds = [ ...(k8sJobSummaries || []).map((k) => k.id), ...(hostJobSummaries || []).map((h) => h.id), ]; + const anomaliesUrl = useLinkProps({ + app: 'ml', + pathname: `/explorer?_g=${createResultsUrl(jobIds)}`, + }); useEffect(() => { if (hasInfraMLReadCapabilities) { @@ -97,24 +105,30 @@ export const FlyoutHome = (props: Props) => { </EuiFlyoutHeader> <EuiFlyoutBody> - <div> - <EuiText> - <p> - <FormattedMessage - defaultMessage="Anomaly detection is powered by machine learning. Machine learning jobs are available for the following resource types. Enable these jobs to begin detecting anomalies in your infrastructure metrics." - id="xpack.infra.ml.anomalyFlyout.create.description" - /> - </p> - </EuiText> - </div> - + <EuiTabs> + <EuiTab isSelected={tab === 'jobs'} onClick={goToJobs}> + <FormattedMessage + defaultMessage="Jobs" + id="xpack.infra.ml.anomalyFlyout.jobsTabLabel" + /> + </EuiTab> + <EuiTab + disabled={jobIds.length === 0} + isSelected={tab === 'anomalies'} + {...anomaliesUrl} + > + <FormattedMessage + defaultMessage="Anomalies" + id="xpack.infra.ml.anomalyFlyout.anomaliesTabLabel" + /> + </EuiTab> + </EuiTabs> <EuiSpacer size="l" /> {hostJobSummaries.length > 0 && ( <> <JobsEnabledCallout hasHostJobs={hostJobSummaries.length > 0} hasK8sJobs={k8sJobSummaries.length > 0} - jobIds={jobIds} /> <EuiSpacer size="l" /> </> @@ -137,7 +151,6 @@ export const FlyoutHome = (props: Props) => { interface CalloutProps { hasHostJobs: boolean; hasK8sJobs: boolean; - jobIds: string[]; } const JobsEnabledCallout = (props: CalloutProps) => { let target = ''; @@ -162,34 +175,8 @@ const JobsEnabledCallout = (props: CalloutProps) => { pathname: '/jobs', }); - const anomaliesUrl = useLinkProps({ - app: 'ml', - pathname: `/explorer?_g=${createResultsUrl(props.jobIds)}`, - }); - return ( <> - <EuiFlexGroup gutterSize={'s'}> - <EuiFlexItem grow={false}> - <EuiButton {...manageJobsLinkProps} style={{ marginRight: 5 }}> - <FormattedMessage - defaultMessage="Manage jobs" - id="xpack.infra.ml.anomalyFlyout.manageJobs" - /> - </EuiButton> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiButton {...anomaliesUrl}> - <FormattedMessage - defaultMessage="View anomalies" - id="xpack.infra.ml.anomalyFlyout.anomaliesTabLabel" - /> - </EuiButton> - </EuiFlexItem> - </EuiFlexGroup> - - <EuiSpacer size="l" /> - <EuiCallOut size="m" color="success" @@ -202,6 +189,13 @@ const JobsEnabledCallout = (props: CalloutProps) => { } iconType="check" /> + <EuiSpacer size="l" /> + <EuiButton {...manageJobsLinkProps}> + <FormattedMessage + defaultMessage="Manage Jobs" + id="xpack.infra.ml.anomalyFlyout.manageJobs" + /> + </EuiButton> </> ); }; @@ -217,11 +211,30 @@ interface CreateJobTab { const CreateJobTab = (props: CreateJobTab) => { return ( <> - {/* <EuiSpacer size="l" /> */} + <div> + <EuiText> + <h3> + <FormattedMessage + defaultMessage="Create ML Jobs" + id="xpack.infra.ml.anomalyFlyout.create.jobsTitle" + /> + </h3> + </EuiText> + <EuiText> + <p> + <FormattedMessage + defaultMessage="Machine Learning jobs are available for the following resource types. Enable these jobs to begin detecting anomalies in your infrastructure metrics" + id="xpack.infra.ml.anomalyFlyout.create.description" + /> + </p> + </EuiText> + </div> + + <EuiSpacer size="l" /> <EuiFlexGroup gutterSize={'m'}> <EuiFlexItem> <EuiCard - isDisabled={!props.hasSetupCapabilities} + // isDisabled={props.hasSetupCapabilities} icon={<EuiIcon type={'storage'} />} // title="Hosts" title={ @@ -232,7 +245,7 @@ const CreateJobTab = (props: CreateJobTab) => { } description={ <FormattedMessage - defaultMessage="Detect anomalies for memory usage and network traffic." + defaultMessage="Detect anomalies for CPU usage, memory usage, network traffic, and load." id="xpack.infra.ml.anomalyFlyout.create.hostDescription" /> } @@ -241,7 +254,7 @@ const CreateJobTab = (props: CreateJobTab) => { {props.hasHostJobs && ( <EuiButtonEmpty onClick={props.createHosts}> <FormattedMessage - defaultMessage="Recreate jobs" + defaultMessage="Recreate Jobs" id="xpack.infra.ml.anomalyFlyout.create.recreateButton" /> </EuiButtonEmpty> @@ -249,7 +262,7 @@ const CreateJobTab = (props: CreateJobTab) => { {!props.hasHostJobs && ( <EuiButton onClick={props.createHosts}> <FormattedMessage - defaultMessage="Enable" + defaultMessage="Create Jobs" id="xpack.infra.ml.anomalyFlyout.create.createButton" /> </EuiButton> @@ -260,7 +273,7 @@ const CreateJobTab = (props: CreateJobTab) => { </EuiFlexItem> <EuiFlexItem> <EuiCard - isDisabled={!props.hasSetupCapabilities} + // isDisabled={props.hasSetupCapabilities} icon={<EuiIcon type={'logoKubernetes'} />} title={ <FormattedMessage @@ -270,7 +283,7 @@ const CreateJobTab = (props: CreateJobTab) => { } description={ <FormattedMessage - defaultMessage="Detect anomalies for memory usage and network traffic." + defaultMessage="Detect anomalies for CPU usage, memory usage, network traffic, and load." id="xpack.infra.ml.anomalyFlyout.create.k8sDescription" /> } @@ -279,7 +292,7 @@ const CreateJobTab = (props: CreateJobTab) => { {props.hasK8sJobs && ( <EuiButtonEmpty onClick={props.createK8s}> <FormattedMessage - defaultMessage="Recreate jobs" + defaultMessage="Recreate Jobs" id="xpack.infra.ml.anomalyFlyout.create.recreateButton" /> </EuiButtonEmpty> @@ -287,7 +300,7 @@ const CreateJobTab = (props: CreateJobTab) => { {!props.hasK8sJobs && ( <EuiButton onClick={props.createK8s}> <FormattedMessage - defaultMessage="Enable" + defaultMessage="Create Jobs" id="xpack.infra.ml.anomalyFlyout.create.createButton" /> </EuiButton> diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx index c327d187f6bc20..428c002da63838 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx @@ -20,7 +20,6 @@ import { useSourceViaHttp } from '../../../../../../containers/source/use_source import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module'; import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module'; import { FixedDatePicker } from '../../../../../../components/fixed_datepicker'; -import { DEFAULT_K8S_PARTITION_FIELD } from '../../../../../../containers/ml/modules/metrics_k8s/module_descriptor'; interface Props { jobType: 'hosts' | 'kubernetes'; @@ -108,7 +107,7 @@ export const JobSetupScreen = (props: Props) => { useEffect(() => { if (props.jobType === 'kubernetes') { - setPartitionField([DEFAULT_K8S_PARTITION_FIELD]); + setPartitionField(['kubernetes.namespace']); } }, [props.jobType]); From 6d8f74a128973eb6e29a85921086f1975eb958d5 Mon Sep 17 00:00:00 2001 From: Jen Huang <its.jenetic@gmail.com> Date: Thu, 1 Oct 2020 10:57:19 -0700 Subject: [PATCH 046/128] [Ingest Manager] Match package spec `dataset`->`data_stream` and `config_templates`->`policy_templates` renaming (#78699) * Match elastic/package-spec#24 `datasets`->`data_streams` property renaming * Match elastic/package-spec#24 `datasets.name`->`data_streams.dataset` property renaming * Match elastic/package-spec#24 `/dataset`->`/data_stream` directory renaming * Match elastic/package-spec#50 `config_templates`->`policy_templates` property renaming * Update API integration test fixtures (test packages) * Temporarily skip API integration tests * Temporarily skip more API integration tests * Pin to custom docker image, unskip test suites, clean up broken icon paths in test package manifests * Skip the only (yay!) failing test suite * Revert "Skip the only (yay!) failing test suite" This reverts commit 3db32e2528f16e3a659939d4d2168b26e5f931d3. * Re-skip tests and revert docker image Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../common/openapi/spec_oas3.json | 6 +- .../common/services/limited_package.ts | 2 +- .../package_to_package_policy.test.ts | 48 ++++++------ .../services/package_to_package_policy.ts | 16 ++-- .../ingest_manager/common/types/models/epm.ts | 14 ++-- x-pack/plugins/ingest_manager/dev_docs/epm.md | 4 +- .../dev_docs/indexing_strategy.md | 30 ++++---- .../services/validate_package_policy.test..ts | 24 +++--- .../services/validate_package_policy.ts | 20 ++--- .../step_configure_package.tsx | 16 ++-- .../ingest_manager/types/index.ts | 2 +- .../services/epm/elasticsearch/index.test.ts | 10 +-- .../services/epm/elasticsearch/index.ts | 8 +- .../ingest_pipeline/ingest_pipelines.test.ts | 16 ++-- .../elasticsearch/ingest_pipeline/install.ts | 44 +++++------ .../epm/elasticsearch/template/install.ts | 71 +++++++++--------- .../epm/elasticsearch/template/template.ts | 24 +++--- .../elasticsearch/transform/transform.test.ts | 18 ++--- .../epm/kibana/index_pattern/install.ts | 30 ++++---- .../services/epm/packages/assets.test.ts | 16 ++-- .../server/services/epm/packages/assets.ts | 6 +- .../server/services/epm/packages/install.ts | 4 +- .../services/epm/registry/index.test.ts | 4 +- .../server/services/epm/registry/index.ts | 8 +- .../server/services/package_policy.test.ts | 20 ++--- .../server/services/package_policy.ts | 14 ++-- .../ingest_manager/server/types/index.tsx | 2 +- .../apache_0.1.4.tar.gz | Bin 581243 -> 579555 bytes .../elasticsearch/ilm_policy/all_assets.json | 0 .../elasticsearch/ingest_pipeline/default.yml | 0 .../ingest_pipeline/pipeline1.yml | 0 .../ingest_pipeline/pipeline2.yml | 0 .../test_logs/fields/ecs.yml | 0 .../test_logs/fields/fields.yml | 0 .../0.1.0/data_stream/test_logs}/manifest.yml | 0 .../test_metrics/fields/ecs.yml | 0 .../test_metrics/fields/fields.yml | 0 .../test_metrics/manifest.yml | 0 .../elasticsearch/ilm_policy/all_assets.json | 0 .../elasticsearch/ingest_pipeline/default.yml | 0 .../ingest_pipeline/pipeline1.yml | 0 .../test_logs/fields/ecs.yml | 0 .../test_logs/fields/fields.yml | 0 .../test_logs/manifest.yml | 0 .../test_logs2/fields/ecs.yml | 0 .../test_logs2/fields/fields.yml | 0 .../test_logs2/manifest.yml | 0 .../test_metrics/fields/ecs.yml | 0 .../test_metrics/fields/fields.yml | 0 .../test_metrics/manifest.yml | 0 .../elasticsearch/ilm_policy/all_assets.json | 0 .../elasticsearch/ingest_pipeline/default.yml | 0 .../test_logs/fields/ecs.yml | 0 .../test_logs/fields/fields.yml | 0 .../0.1.0/data_stream}/test_logs/manifest.yml | 0 .../test_metrics/fields/ecs.yml | 0 .../test_metrics/fields/fields.yml | 0 .../test_metrics/manifest.yml | 0 .../datastreams/0.1.0/manifest.yml | 7 +- .../elasticsearch/ilm_policy/all_assets.json | 0 .../elasticsearch/ingest_pipeline/default.yml | 0 .../test_logs/fields/ecs.yml | 0 .../test_logs/fields/fields.yml | 0 .../test_logs/manifest.yml | 0 .../test_metrics/fields/ecs.yml | 0 .../test_metrics/fields/fields.yml | 0 .../test_metrics/manifest.yml | 0 .../datastreams/0.2.0/manifest.yml | 5 -- .../test/fields/fields.yml | 0 .../data_stream}/test/manifest.yml | 0 .../multiple_versions/0.1.0/manifest.yml | 9 +-- .../test/fields/fields.yml | 0 .../data_stream}/test/manifest.yml | 0 .../multiple_versions/0.2.0/manifest.yml | 9 +-- .../test/fields/fields.yml | 0 .../0.3.0/data_stream}/test/manifest.yml | 0 .../multiple_versions/0.3.0/manifest.yml | 7 +- .../test/fields/fields.yml | 0 .../0.1.0/data_stream/test}/manifest.yml | 2 +- .../test/fields/fields.yml | 0 .../test/manifest.yml | 0 .../apis/index.js | 2 +- .../apps/endpoint/index.ts | 2 +- .../apis/index.ts | 2 +- 84 files changed, 254 insertions(+), 268 deletions(-) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_logs/elasticsearch/ilm_policy/all_assets.json (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_logs/elasticsearch/ingest_pipeline/default.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_logs/elasticsearch/ingest_pipeline/pipeline2.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_logs/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_logs/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/{multiple_versions/0.1.0/dataset/test => all_assets/0.1.0/data_stream/test_logs}/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_metrics/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_metrics/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/{dataset => data_stream}/test_metrics/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs/elasticsearch/ilm_policy/all_assets.json (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs/elasticsearch/ingest_pipeline/default.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs2/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs2/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_logs2/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_metrics/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_metrics/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/{dataset => data_stream}/test_metrics/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/{dataset => data_stream}/test_logs/elasticsearch/ilm_policy/all_assets.json (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/{dataset => data_stream}/test_logs/elasticsearch/ingest_pipeline/default.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/{dataset => data_stream}/test_logs/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/{dataset => data_stream}/test_logs/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/{all_assets/0.1.0/dataset => datastreams/0.1.0/data_stream}/test_logs/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/{dataset => data_stream}/test_metrics/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/{dataset => data_stream}/test_metrics/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/{dataset => data_stream}/test_metrics/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_logs/elasticsearch/ilm_policy/all_assets.json (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_logs/elasticsearch/ingest_pipeline/default.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_logs/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_logs/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_logs/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_metrics/fields/ecs.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_metrics/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/{dataset => data_stream}/test_metrics/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/{dataset => data_stream}/test/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/{0.2.0/dataset => 0.1.0/data_stream}/test/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/{dataset => data_stream}/test/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/{0.3.0/dataset => 0.2.0/data_stream}/test/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/{dataset => data_stream}/test/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/{overrides/0.1.0/dataset => multiple_versions/0.3.0/data_stream}/test/manifest.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/{dataset => data_stream}/test/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/{datastreams/0.1.0/dataset/test_logs => overrides/0.1.0/data_stream/test}/manifest.yml (77%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/{dataset => data_stream}/test/fields/fields.yml (100%) rename x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/{dataset => data_stream}/test/manifest.yml (100%) diff --git a/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json index 28a88aa2be6055..a780ae55997938 100644 --- a/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json +++ b/x-pack/plugins/ingest_manager/common/openapi/spec_oas3.json @@ -1543,7 +1543,7 @@ } }, "format_version": "1.0.0", - "datasets": [ + "data_streams": [ { "title": "CoreDNS logs", "name": "log", @@ -1764,7 +1764,7 @@ ] } }, - "datasets": [ + "data_streams": [ { "id": "endpoint", "title": "Endpoint Events", @@ -3961,7 +3961,7 @@ "format_version": { "type": "string" }, - "datasets": { + "data_streams": { "type": "array", "items": { "type": "object", diff --git a/x-pack/plugins/ingest_manager/common/services/limited_package.ts b/x-pack/plugins/ingest_manager/common/services/limited_package.ts index 21d1dbd1556b7c..8d2a251ae015ee 100644 --- a/x-pack/plugins/ingest_manager/common/services/limited_package.ts +++ b/x-pack/plugins/ingest_manager/common/services/limited_package.ts @@ -7,7 +7,7 @@ import { PackageInfo, AgentPolicy, PackagePolicy } from '../types'; // Assume packages only ever include 1 config template for now export const isPackageLimited = (packageInfo: PackageInfo): boolean => { - return packageInfo.config_templates?.[0]?.multiple === false; + return packageInfo.policy_templates?.[0]?.multiple === false; }; export const doesAgentPolicyAlreadyIncludePackage = ( diff --git a/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.test.ts b/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.test.ts index 6c3559d7cc5a03..a62fcddd16e0f0 100644 --- a/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.test.ts +++ b/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.test.ts @@ -34,14 +34,14 @@ describe('Ingest Manager - packageToPackagePolicy', () => { describe('packageToPackagePolicyInputs', () => { it('returns empty array for packages with no config templates', () => { expect(packageToPackagePolicyInputs(mockPackage)).toEqual([]); - expect(packageToPackagePolicyInputs({ ...mockPackage, config_templates: [] })).toEqual([]); + expect(packageToPackagePolicyInputs({ ...mockPackage, policy_templates: [] })).toEqual([]); }); it('returns empty array for packages with a config template but no inputs', () => { expect( packageToPackagePolicyInputs(({ ...mockPackage, - config_templates: [{ inputs: [] }], + policy_templates: [{ inputs: [] }], } as unknown) as PackageInfo) ).toEqual([]); }); @@ -50,13 +50,13 @@ describe('Ingest Manager - packageToPackagePolicy', () => { expect( packageToPackagePolicyInputs(({ ...mockPackage, - config_templates: [{ inputs: [{ type: 'foo' }] }], + policy_templates: [{ inputs: [{ type: 'foo' }] }], } as unknown) as PackageInfo) ).toEqual([{ type: 'foo', enabled: true, streams: [] }]); expect( packageToPackagePolicyInputs(({ ...mockPackage, - config_templates: [{ inputs: [{ type: 'foo' }, { type: 'bar' }] }], + policy_templates: [{ inputs: [{ type: 'foo' }, { type: 'bar' }] }], } as unknown) as PackageInfo) ).toEqual([ { type: 'foo', enabled: true, streams: [] }, @@ -68,12 +68,12 @@ describe('Ingest Manager - packageToPackagePolicy', () => { expect( packageToPackagePolicyInputs(({ ...mockPackage, - datasets: [ - { type: 'logs', name: 'foo', streams: [{ input: 'foo' }] }, - { type: 'logs', name: 'bar', streams: [{ input: 'bar' }] }, - { type: 'logs', name: 'bar2', streams: [{ input: 'bar' }] }, + data_streams: [ + { type: 'logs', dataset: 'foo', streams: [{ input: 'foo' }] }, + { type: 'logs', dataset: 'bar', streams: [{ input: 'bar' }] }, + { type: 'logs', dataset: 'bar2', streams: [{ input: 'bar' }] }, ], - config_templates: [ + policy_templates: [ { inputs: [{ type: 'foo' }, { type: 'bar' }], }, @@ -102,15 +102,15 @@ describe('Ingest Manager - packageToPackagePolicy', () => { expect( packageToPackagePolicyInputs(({ ...mockPackage, - datasets: [ + data_streams: [ { type: 'logs', - name: 'foo', + dataset: 'foo', streams: [{ input: 'foo', vars: [{ default: 'foo-var-value', name: 'var-name' }] }], }, { type: 'logs', - name: 'bar', + dataset: 'bar', streams: [ { input: 'bar', @@ -120,7 +120,7 @@ describe('Ingest Manager - packageToPackagePolicy', () => { }, { type: 'logs', - name: 'bar2', + dataset: 'bar2', streams: [ { input: 'bar', @@ -129,7 +129,7 @@ describe('Ingest Manager - packageToPackagePolicy', () => { ], }, ], - config_templates: [ + policy_templates: [ { inputs: [{ type: 'foo' }, { type: 'bar' }], }, @@ -173,15 +173,15 @@ describe('Ingest Manager - packageToPackagePolicy', () => { expect( packageToPackagePolicyInputs(({ ...mockPackage, - datasets: [ + data_streams: [ { type: 'logs', - name: 'foo', + dataset: 'foo', streams: [{ input: 'foo', vars: [{ default: 'foo-var-value', name: 'var-name' }] }], }, { type: 'logs', - name: 'bar', + dataset: 'bar', streams: [ { input: 'bar', @@ -191,7 +191,7 @@ describe('Ingest Manager - packageToPackagePolicy', () => { }, { type: 'logs', - name: 'bar2', + dataset: 'bar2', streams: [ { input: 'bar', @@ -201,7 +201,7 @@ describe('Ingest Manager - packageToPackagePolicy', () => { }, { type: 'logs', - name: 'disabled', + dataset: 'disabled', streams: [ { input: 'with-disabled-streams', @@ -212,7 +212,7 @@ describe('Ingest Manager - packageToPackagePolicy', () => { }, { type: 'logs', - name: 'disabled2', + dataset: 'disabled2', streams: [ { input: 'with-disabled-streams', @@ -221,7 +221,7 @@ describe('Ingest Manager - packageToPackagePolicy', () => { ], }, ], - config_templates: [ + policy_templates: [ { inputs: [ { @@ -372,13 +372,13 @@ describe('Ingest Manager - packageToPackagePolicy', () => { }); }); it('returns package policy with inputs', () => { - const mockPackageWithConfigTemplates = ({ + const mockPackageWithPolicyTemplates = ({ ...mockPackage, - config_templates: [{ inputs: [{ type: 'foo' }] }], + policy_templates: [{ inputs: [{ type: 'foo' }] }], } as unknown) as PackageInfo; expect( - packageToPackagePolicy(mockPackageWithConfigTemplates, '1', '2', 'default', 'pkgPolicy-1') + packageToPackagePolicy(mockPackageWithPolicyTemplates, '1', '2', 'default', 'pkgPolicy-1') ).toEqual({ policy_id: '1', namespace: 'default', diff --git a/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.ts b/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.ts index eab2e8ac2d745a..822747916ebc5c 100644 --- a/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.ts +++ b/x-pack/plugins/ingest_manager/common/services/package_to_package_policy.ts @@ -5,7 +5,7 @@ */ import { PackageInfo, - RegistryConfigTemplate, + RegistryPolicyTemplate, RegistryVarsEntry, RegistryStream, PackagePolicy, @@ -22,14 +22,14 @@ const getStreamsForInputType = ( ): Array<RegistryStream & { data_stream: { type: string; dataset: string } }> => { const streams: Array<RegistryStream & { data_stream: { type: string; dataset: string } }> = []; - (packageInfo.datasets || []).forEach((dataset) => { - (dataset.streams || []).forEach((stream) => { + (packageInfo.data_streams || []).forEach((dataStream) => { + (dataStream.streams || []).forEach((stream) => { if (stream.input === inputType) { streams.push({ ...stream, data_stream: { - type: dataset.type, - dataset: dataset.name, + type: dataStream.type, + dataset: dataStream.dataset, }, }); } @@ -46,9 +46,9 @@ export const packageToPackagePolicyInputs = (packageInfo: PackageInfo): PackageP const inputs: PackagePolicy['inputs'] = []; // Assume package will only ever ship one package policy template for now - const packagePolicyTemplate: RegistryConfigTemplate | null = - packageInfo.config_templates && packageInfo.config_templates[0] - ? packageInfo.config_templates[0] + const packagePolicyTemplate: RegistryPolicyTemplate | null = + packageInfo.policy_templates && packageInfo.policy_templates[0] + ? packageInfo.policy_templates[0] : null; // Create package policy input property diff --git a/x-pack/plugins/ingest_manager/common/types/models/epm.ts b/x-pack/plugins/ingest_manager/common/types/models/epm.ts index 8bc5d9f7210b25..d2d1f22dda3a06 100644 --- a/x-pack/plugins/ingest_manager/common/types/models/epm.ts +++ b/x-pack/plugins/ingest_manager/common/types/models/epm.ts @@ -67,8 +67,8 @@ export interface RegistryPackage { assets?: string[]; internal?: boolean; format_version: string; - datasets?: Dataset[]; - config_templates?: RegistryConfigTemplate[]; + data_streams?: RegistryDataStream[]; + policy_templates?: RegistryPolicyTemplate[]; download: string; path: string; } @@ -80,7 +80,7 @@ interface RegistryImage { size?: string; type?: string; } -export interface RegistryConfigTemplate { +export interface RegistryPolicyTemplate { name: string; title: string; description: string; @@ -127,8 +127,8 @@ export type RegistrySearchResult = Pick< | 'internal' | 'download' | 'path' - | 'datasets' - | 'config_templates' + | 'data_streams' + | 'policy_templates' >; export type ScreenshotItem = RegistryImage; @@ -174,9 +174,9 @@ export type ElasticsearchAssetTypeToParts = Record< ElasticsearchAssetParts[] >; -export interface Dataset { +export interface RegistryDataStream { type: string; - name: string; + dataset: string; title: string; release: string; streams?: RegistryStream[]; diff --git a/x-pack/plugins/ingest_manager/dev_docs/epm.md b/x-pack/plugins/ingest_manager/dev_docs/epm.md index 20209d09e6cc21..a066b6deb3bc84 100644 --- a/x-pack/plugins/ingest_manager/dev_docs/epm.md +++ b/x-pack/plugins/ingest_manager/dev_docs/epm.md @@ -26,5 +26,5 @@ When a package is installed or upgraded, certain Kibana and Elasticsearch assets ### Generation - Index templates are generated from `YAML` files contained in the package. -- There is one index template per dataset. -- For the generation of an index template, all `yml` files contained in the package subdirectory `dataset/DATASET_NAME/fields/` are used. +- There is one index template per data stream. +- For the generation of an index template, all `yml` files contained in the package subdirectory `data_stream/DATASET_NAME/fields/` are used. diff --git a/x-pack/plugins/ingest_manager/dev_docs/indexing_strategy.md b/x-pack/plugins/ingest_manager/dev_docs/indexing_strategy.md index fd7edcb7fcca0b..42a0bbc2188699 100644 --- a/x-pack/plugins/ingest_manager/dev_docs/indexing_strategy.md +++ b/x-pack/plugins/ingest_manager/dev_docs/indexing_strategy.md @@ -6,48 +6,48 @@ Overall documentation of Ingest Management is now maintained in the `elastic/sta Ingest Management enforces an indexing strategy to allow the system to automatically detect indices and run queries on it. In short the indexing strategy looks as following: ``` -{dataset.type}-{dataset.name}-{dataset.namespace} +{data_stream.type}-{data_stream.dataset}-{data_stream.namespace} ``` -The `{dataset.type}` can be `logs` or `metrics`. The `{dataset.namespace}` is the part where the user can use free form. The only two requirement are that it has only characters allowed in an Elasticsearch index name and does NOT contain a `-`. The `dataset` is defined by the data that is indexed. The same requirements as for the namespace apply. It is expected that the fields for type, namespace and dataset are part of each event and are constant keywords. If there is a dataset or a namespace with a `-` inside, it is recommended to replace it either by a `.` or a `_`. +The `{data_stream.type}` can be `logs` or `metrics`. The `{data_stream.namespace}` is the part where the user can use free form. The only two requirement are that it has only characters allowed in an Elasticsearch index name and does NOT contain a `-`. The `data_stream` is defined by the data that is indexed. The same requirements as for the namespace apply. It is expected that the fields for type, dataset, and namespace are part of each event and are constant keywords. If there is a dataset or a namespace with a `-` inside, it is recommended to replace it either by a `.` or a `_`. -Note: More `{dataset.type}`s might be added in the future like `traces`. +Note: More `{data_stream.type}`s might be added in the future like `traces`. This indexing strategy has a few advantages: -* Each index contains only the fields which are relevant for the dataset. This leads to more dense indices and better field completion. -* ILM policies can be applied per namespace per dataset. -* Rollups can be specified per namespace per dataset. -* Having the namespace user configurable makes setting security permissions possible. -* Having a global metrics and logs template, allows to create new indices on demand which still follow the convention. This is common in the case of k8s as an example. -* Constant keywords allow to narrow down the indices we need to access for querying very efficiently. This is especially relevant in environments which a large number of indices or with indices on slower nodes. +- Each index contains only the fields which are relevant for the datta stream. This leads to more dense indices and better field completion. +- ILM policies can be applied per namespace per data stream. +- Rollups can be specified per namespace per data stream. +- Having the namespace user configurable makes setting security permissions possible. +- Having a global metrics and logs template, allows to create new indices on demand which still follow the convention. This is common in the case of k8s as an example. +- Constant keywords allow to narrow down the indices we need to access for querying very efficiently. This is especially relevant in environments which a large number of indices or with indices on slower nodes. Overall it creates smaller indices in size, makes querying more efficient and allows users to define their own naming parts in namespace and still benefiting from all features that can be built on top of the indexing startegy. ## Ingest Pipeline -The ingest pipelines for a specific dataset will have the following naming scheme: +The ingest pipelines for a specific data stream will have the following naming scheme: ``` -{dataset.type}-{dataset.name}-{package.version} +{data_stream.type}-{data_stream.dataset}-{package.version} ``` -As an example, the ingest pipeline for the Nginx access logs is called `logs-nginx.access-3.4.1`. The same ingest pipeline is used for all namespaces. It is possible that a dataset has multiple ingest pipelines in which case a suffix is added to the name. +As an example, the ingest pipeline for the Nginx access logs is called `logs-nginx.access-3.4.1`. The same ingest pipeline is used for all namespaces. It is possible that a data stream has multiple ingest pipelines in which case a suffix is added to the name. The version is included in each pipeline to allow upgrades. The pipeline itself is listed in the index template and is automatically applied at ingest time. ## Templates & ILM Policies -To make the above strategy possible, alias templates are required. For each type there is a basic alias template with a default ILM policy. These default templates apply to all indices which follow the indexing strategy and do not have a more specific dataset alias template. +To make the above strategy possible, alias templates are required. For each type there is a basic alias template with a default ILM policy. These default templates apply to all indices which follow the indexing strategy and do not have a more specific data stream alias template. The `metrics` and `logs` alias template contain all the basic fields from ECS. Each type template contains an ILM policy. Modifying this default ILM policy will affect all data covered by the default templates. -The templates for a dataset are called as following: +The templates for a data stream are called as following: ``` -{dataset.type}-{dataset.name} +{data_stream.type}-{data_stream.dataset} ``` The pattern used inside the index template is `{type}-{dataset}-*` to match all namespaces. diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test..ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test..ts index aae750cb674991..d621db615f2bd6 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test..ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.test..ts @@ -7,7 +7,7 @@ import { PackageInfo, InstallationStatus, NewPackagePolicy, - RegistryConfigTemplate, + RegistryPolicyTemplate, } from '../../../../types'; import { validatePackagePolicy, validationHasErrors } from './validate_package_policy'; @@ -32,9 +32,9 @@ describe('Ingest Manager - validatePackagePolicy()', () => { }, }, status: InstallationStatus.notInstalled, - datasets: [ + data_streams: [ { - name: 'foo', + dataset: 'foo', streams: [ { input: 'foo', @@ -44,7 +44,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { ], }, { - name: 'bar', + dataset: 'bar', streams: [ { input: 'bar', @@ -59,7 +59,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { ], }, { - name: 'bar2', + dataset: 'bar2', streams: [ { input: 'bar', @@ -69,7 +69,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { ], }, { - name: 'disabled', + dataset: 'disabled', streams: [ { input: 'with-disabled-streams', @@ -80,7 +80,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { ], }, { - name: 'disabled2', + dataset: 'disabled2', streams: [ { input: 'with-disabled-streams', @@ -90,7 +90,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { ], }, ], - config_templates: [ + policy_templates: [ { name: 'pkgPolicy1', title: 'Package policy 1', @@ -465,7 +465,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { expect( validatePackagePolicy(validPackagePolicy, { ...mockPackage, - config_templates: undefined, + policy_templates: undefined, }) ).toEqual({ name: null, @@ -476,7 +476,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { expect( validatePackagePolicy(validPackagePolicy, { ...mockPackage, - config_templates: [], + policy_templates: [], }) ).toEqual({ name: null, @@ -490,7 +490,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { expect( validatePackagePolicy(validPackagePolicy, { ...mockPackage, - config_templates: [{} as RegistryConfigTemplate], + policy_templates: [{} as RegistryPolicyTemplate], }) ).toEqual({ name: null, @@ -501,7 +501,7 @@ describe('Ingest Manager - validatePackagePolicy()', () => { expect( validatePackagePolicy(validPackagePolicy, { ...mockPackage, - config_templates: [({ inputs: [] } as unknown) as RegistryConfigTemplate], + policy_templates: [({ inputs: [] } as unknown) as RegistryPolicyTemplate], }) ).toEqual({ name: null, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts index 03060c5dcb20e0..04cd21884e8f28 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/services/validate_package_policy.ts @@ -65,11 +65,11 @@ export const validatePackagePolicy = ( } if ( - !packageInfo.config_templates || - packageInfo.config_templates.length === 0 || - !packageInfo.config_templates[0] || - !packageInfo.config_templates[0].inputs || - packageInfo.config_templates[0].inputs.length === 0 + !packageInfo.policy_templates || + packageInfo.policy_templates.length === 0 || + !packageInfo.policy_templates[0] || + !packageInfo.policy_templates[0].inputs || + packageInfo.policy_templates[0].inputs.length === 0 ) { validationResults.inputs = null; return validationResults; @@ -78,16 +78,16 @@ export const validatePackagePolicy = ( const registryInputsByType: Record< string, RegistryInput - > = packageInfo.config_templates[0].inputs.reduce((inputs, registryInput) => { + > = packageInfo.policy_templates[0].inputs.reduce((inputs, registryInput) => { inputs[registryInput.type] = registryInput; return inputs; }, {} as Record<string, RegistryInput>); const registryStreamsByDataset: Record<string, RegistryStream[]> = ( - packageInfo.datasets || [] - ).reduce((datasets, registryDataset) => { - datasets[registryDataset.name] = registryDataset.streams || []; - return datasets; + packageInfo.data_streams || [] + ).reduce((dataStreams, registryDataStream) => { + dataStreams[registryDataStream.dataset] = registryDataStream.streams || []; + return dataStreams; }, {} as Record<string, RegistryStream[]>); // Validate each package policy input with either its own config fields or streams diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_configure_package.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_configure_package.tsx index b77153daee2fc5..d3d5e60c34e584 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_configure_package.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_policy/create_package_policy_page/step_configure_package.tsx @@ -17,13 +17,13 @@ const findStreamsForInputType = ( ): Array<RegistryStream & { data_stream: { dataset: string } }> => { const streams: Array<RegistryStream & { data_stream: { dataset: string } }> = []; - (packageInfo.datasets || []).forEach((dataset) => { - (dataset.streams || []).forEach((stream) => { + (packageInfo.data_streams || []).forEach((dataStream) => { + (dataStream.streams || []).forEach((stream) => { if (stream.input === inputType) { streams.push({ ...stream, data_stream: { - dataset: dataset.name, + dataset: dataStream.dataset, }, }); } @@ -53,14 +53,14 @@ export const StepConfigurePackagePolicy: React.FunctionComponent<{ // Configure inputs (and their streams) // Assume packages only export one config template for now const renderConfigureInputs = () => - packageInfo.config_templates && - packageInfo.config_templates[0] && - packageInfo.config_templates[0].inputs && - packageInfo.config_templates[0].inputs.length ? ( + packageInfo.policy_templates && + packageInfo.policy_templates[0] && + packageInfo.policy_templates[0].inputs && + packageInfo.policy_templates[0].inputs.length ? ( <> <EuiHorizontalRule margin="m" /> <EuiFlexGroup direction="column" gutterSize="none"> - {packageInfo.config_templates[0].inputs.map((packageInput) => { + {packageInfo.policy_templates[0].inputs.map((packageInput) => { const packagePolicyInput = packagePolicy.inputs.find( (input) => input.type === packageInput.type ); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts index 71a44089b8bf77..e825448f359d63 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/types/index.ts @@ -89,7 +89,7 @@ export { RegistryVarsEntry, RegistryInput, RegistryStream, - RegistryConfigTemplate, + RegistryPolicyTemplate, PackageList, PackageListItem, PackagesGroupedByStatus, diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.test.ts index bdd8883ea29c24..78aa17da5030c6 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.test.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Dataset } from '../../../types'; -import { getDatasetAssetBaseName } from './index'; +import { RegistryDataStream } from '../../../types'; +import { getRegistryDataStreamAssetBaseName } from './index'; test('getBaseName', () => { - const dataset: Dataset = { - name: 'nginx.access', + const dataStream: RegistryDataStream = { + dataset: 'nginx.access', title: 'Nginx Acess Logs', release: 'beta', type: 'logs', @@ -17,6 +17,6 @@ test('getBaseName', () => { package: 'nginx', path: 'access', }; - const name = getDatasetAssetBaseName(dataset); + const name = getRegistryDataStreamAssetBaseName(dataStream); expect(name).toStrictEqual('logs-nginx.access'); }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.ts index 0cb09ba054bf10..17cd28cc8a0811 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/index.ts @@ -4,12 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Dataset } from '../../../types'; +import { RegistryDataStream } from '../../../types'; /** * Creates the base name for Elasticsearch assets in the form of - * {type}-{id} + * {type}-{dataset} */ -export function getDatasetAssetBaseName(dataset: Dataset): string { - return `${dataset.type}-${dataset.name}`; +export function getRegistryDataStreamAssetBaseName(dataStream: RegistryDataStream): string { + return `${dataStream.type}-${dataStream.dataset}`; } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/ingest_pipelines.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/ingest_pipelines.test.ts index 36a19c512a8b48..378dd271779b4e 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/ingest_pipelines.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/ingest_pipelines.test.ts @@ -7,7 +7,7 @@ import { readFileSync } from 'fs'; import path from 'path'; import { rewriteIngestPipeline, getPipelineNameForInstallation } from './install'; -import { Dataset } from '../../../../types'; +import { RegistryDataStream } from '../../../../types'; test('a json-format pipeline with pipeline references is correctly rewritten', () => { const inputStandard = readFileSync( @@ -106,8 +106,8 @@ test('a yml-format pipeline with no pipeline references stays unchanged', () => }); test('getPipelineNameForInstallation gets correct name', () => { - const dataset: Dataset = { - name: 'coredns.log', + const dataStream: RegistryDataStream = { + dataset: 'coredns.log', title: 'CoreDNS logs', release: 'ga', type: 'logs', @@ -118,19 +118,19 @@ test('getPipelineNameForInstallation gets correct name', () => { const packageVersion = '1.0.1'; const pipelineRefName = 'pipeline-json'; const pipelineEntryNameForInstallation = getPipelineNameForInstallation({ - pipelineName: dataset.ingest_pipeline, - dataset, + pipelineName: dataStream.ingest_pipeline, + dataStream, packageVersion, }); const pipelineRefNameForInstallation = getPipelineNameForInstallation({ pipelineName: pipelineRefName, - dataset, + dataStream, packageVersion, }); expect(pipelineEntryNameForInstallation).toBe( - `${dataset.type}-${dataset.name}-${packageVersion}` + `${dataStream.type}-${dataStream.dataset}-${packageVersion}` ); expect(pipelineRefNameForInstallation).toBe( - `${dataset.type}-${dataset.name}-${packageVersion}-${pipelineRefName}` + `${dataStream.type}-${dataStream.dataset}-${packageVersion}-${pipelineRefName}` ); }); diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/install.ts index 878c6ea8f28047..6088bcb71f8789 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/ingest_pipeline/install.ts @@ -7,7 +7,7 @@ import { SavedObjectsClientContract } from 'src/core/server'; import { EsAssetReference, - Dataset, + RegistryDataStream, ElasticsearchAssetType, RegistryPackage, } from '../../../../types'; @@ -30,17 +30,19 @@ export const installPipelines = async ( // unlike other ES assets, pipeline names are versioned so after a template is updated // it can be created pointing to the new template, without removing the old one and effecting data // so do not remove the currently installed pipelines here - const datasets = registryPackage.datasets; - if (!datasets?.length) return []; + const dataStreams = registryPackage.data_streams; + if (!dataStreams?.length) return []; const pipelinePaths = paths.filter((path) => isPipeline(path)); // get and save pipeline refs before installing pipelines - const pipelineRefs = datasets.reduce<EsAssetReference[]>((acc, dataset) => { - const filteredPaths = pipelinePaths.filter((path) => isDatasetPipeline(path, dataset.path)); + const pipelineRefs = dataStreams.reduce<EsAssetReference[]>((acc, dataStream) => { + const filteredPaths = pipelinePaths.filter((path) => + isDataStreamPipeline(path, dataStream.path) + ); const pipelineObjectRefs = filteredPaths.map((path) => { const { name } = getNameAndExtension(path); const nameForInstallation = getPipelineNameForInstallation({ pipelineName: name, - dataset, + dataStream, packageVersion: registryPackage.version, }); return { id: nameForInstallation, type: ElasticsearchAssetType.ingestPipeline }; @@ -49,11 +51,11 @@ export const installPipelines = async ( return acc; }, []); await saveInstalledEsRefs(savedObjectsClient, registryPackage.name, pipelineRefs); - const pipelines = datasets.reduce<Array<Promise<EsAssetReference[]>>>((acc, dataset) => { - if (dataset.ingest_pipeline) { + const pipelines = dataStreams.reduce<Array<Promise<EsAssetReference[]>>>((acc, dataStream) => { + if (dataStream.ingest_pipeline) { acc.push( - installPipelinesForDataset({ - dataset, + installPipelinesForDataStream({ + dataStream, callCluster, paths: pipelinePaths, pkgVersion: registryPackage.version, @@ -86,18 +88,18 @@ export function rewriteIngestPipeline( return pipeline; } -export async function installPipelinesForDataset({ +export async function installPipelinesForDataStream({ callCluster, pkgVersion, paths, - dataset, + dataStream, }: { callCluster: CallESAsCurrentUser; pkgVersion: string; paths: string[]; - dataset: Dataset; + dataStream: RegistryDataStream; }): Promise<EsAssetReference[]> { - const pipelinePaths = paths.filter((path) => isDatasetPipeline(path, dataset.path)); + const pipelinePaths = paths.filter((path) => isDataStreamPipeline(path, dataStream.path)); let pipelines: any[] = []; const substitutions: RewriteSubstitution[] = []; @@ -105,7 +107,7 @@ export async function installPipelinesForDataset({ const { name, extension } = getNameAndExtension(path); const nameForInstallation = getPipelineNameForInstallation({ pipelineName: name, - dataset, + dataStream, packageVersion: pkgVersion, }); const content = Registry.getAsset(path).toString('utf-8'); @@ -175,13 +177,13 @@ async function installPipeline({ const isDirectory = ({ path }: Registry.ArchiveEntry) => path.endsWith('/'); -const isDatasetPipeline = (path: string, datasetName: string) => { +const isDataStreamPipeline = (path: string, dataStreamDataset: string) => { const pathParts = Registry.pathParts(path); return ( !isDirectory({ path }) && pathParts.type === ElasticsearchAssetType.ingestPipeline && pathParts.dataset !== undefined && - datasetName === pathParts.dataset + dataStreamDataset === pathParts.dataset ); }; const isPipeline = (path: string) => { @@ -206,15 +208,15 @@ const getNameAndExtension = ( export const getPipelineNameForInstallation = ({ pipelineName, - dataset, + dataStream, packageVersion, }: { pipelineName: string; - dataset: Dataset; + dataStream: RegistryDataStream; packageVersion: string; }): string => { - const isPipelineEntry = pipelineName === dataset.ingest_pipeline; + const isPipelineEntry = pipelineName === dataStream.ingest_pipeline; const suffix = isPipelineEntry ? '' : `-${pipelineName}`; // if this is the pipeline entry, don't add a suffix - return `${dataset.type}-${dataset.name}-${packageVersion}${suffix}`; + return `${dataStream.type}-${dataStream.dataset}-${packageVersion}${suffix}`; }; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts index f4e8c3bfd99d3f..8f80feb268910e 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/install.ts @@ -7,7 +7,7 @@ import Boom from 'boom'; import { SavedObjectsClientContract } from 'src/core/server'; import { - Dataset, + RegistryDataStream, RegistryPackage, ElasticsearchAssetType, TemplateRef, @@ -38,29 +38,32 @@ export const installTemplates = async ( registryPackage.name, ElasticsearchAssetType.indexTemplate ); - // build templates per dataset from yml files - const datasets = registryPackage.datasets; - if (!datasets) return []; + // build templates per data stream from yml files + const dataStreams = registryPackage.data_streams; + if (!dataStreams) return []; // get template refs to save - const installedTemplateRefs = datasets.map((dataset) => ({ - id: generateTemplateName(dataset), + const installedTemplateRefs = dataStreams.map((dataStream) => ({ + id: generateTemplateName(dataStream), type: ElasticsearchAssetType.indexTemplate, })); // add package installation's references to index templates await saveInstalledEsRefs(savedObjectsClient, registryPackage.name, installedTemplateRefs); - if (datasets) { - const installTemplatePromises = datasets.reduce<Array<Promise<TemplateRef>>>((acc, dataset) => { - acc.push( - installTemplateForDataset({ - pkg: registryPackage, - callCluster, - dataset, - }) - ); - return acc; - }, []); + if (dataStreams) { + const installTemplatePromises = dataStreams.reduce<Array<Promise<TemplateRef>>>( + (acc, dataStream) => { + acc.push( + installTemplateForDataStream({ + pkg: registryPackage, + callCluster, + dataStream, + }) + ); + return acc; + }, + [] + ); const res = await Promise.all(installTemplatePromises); const installedTemplates = res.flat(); @@ -158,25 +161,25 @@ const isComponentTemplate = (path: string) => { }; /** - * installTemplatesForDataset installs one template for each dataset + * installTemplateForDataStream installs one template for each data stream * - * The template is currently loaded with the pkgey-package-dataset + * The template is currently loaded with the pkgkey-package-data_stream */ -export async function installTemplateForDataset({ +export async function installTemplateForDataStream({ pkg, callCluster, - dataset, + dataStream, }: { pkg: RegistryPackage; callCluster: CallESAsCurrentUser; - dataset: Dataset; + dataStream: RegistryDataStream; }): Promise<TemplateRef> { - const fields = await loadFieldsFromYaml(pkg, dataset.path); + const fields = await loadFieldsFromYaml(pkg, dataStream.path); return installTemplate({ callCluster, fields, - dataset, + dataStream, packageVersion: pkg.version, packageName: pkg.name, }); @@ -237,7 +240,7 @@ function buildComponentTemplates(registryElasticsearch: RegistryElasticsearch | return { settingsTemplate, mappingsTemplate }; } -async function installDatasetComponentTemplates( +async function installDataStreamComponentTemplates( templateName: string, registryElasticsearch: RegistryElasticsearch | undefined, callCluster: CallESAsCurrentUser @@ -277,35 +280,35 @@ async function installDatasetComponentTemplates( export async function installTemplate({ callCluster, fields, - dataset, + dataStream, packageVersion, packageName, }: { callCluster: CallESAsCurrentUser; fields: Field[]; - dataset: Dataset; + dataStream: RegistryDataStream; packageVersion: string; packageName: string; }): Promise<TemplateRef> { const mappings = generateMappings(processFields(fields)); - const templateName = generateTemplateName(dataset); + const templateName = generateTemplateName(dataStream); let pipelineName; - if (dataset.ingest_pipeline) { + if (dataStream.ingest_pipeline) { pipelineName = getPipelineNameForInstallation({ - pipelineName: dataset.ingest_pipeline, - dataset, + pipelineName: dataStream.ingest_pipeline, + dataStream, packageVersion, }); } - const composedOfTemplates = await installDatasetComponentTemplates( + const composedOfTemplates = await installDataStreamComponentTemplates( templateName, - dataset.elasticsearch, + dataStream.elasticsearch, callCluster ); const template = getTemplate({ - type: dataset.type, + type: dataStream.type, templateName, mappings, pipelineName, diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts index 71e49acf1766fc..00c2e873ba1295 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts @@ -6,13 +6,13 @@ import { Field, Fields } from '../../fields/field'; import { - Dataset, + RegistryDataStream, CallESAsCurrentUser, TemplateRef, IndexTemplate, IndexTemplateMappings, } from '../../../../types'; -import { getDatasetAssetBaseName } from '../index'; +import { getRegistryDataStreamAssetBaseName } from '../index'; interface Properties { [key: string]: any; @@ -222,22 +222,24 @@ function getDefaultProperties(field: Field): Properties { /** * Generates the template name out of the given information */ -export function generateTemplateName(dataset: Dataset): string { - return getDatasetAssetBaseName(dataset); +export function generateTemplateName(dataStream: RegistryDataStream): string { + return getRegistryDataStreamAssetBaseName(dataStream); } /** - * Returns a map of the dataset path fields to elasticsearch index pattern. - * @param datasets an array of Dataset objects + * Returns a map of the data stream path fields to elasticsearch index pattern. + * @param dataStreams an array of RegistryDataStream objects */ -export function generateESIndexPatterns(datasets: Dataset[] | undefined): Record<string, string> { - if (!datasets) { +export function generateESIndexPatterns( + dataStreams: RegistryDataStream[] | undefined +): Record<string, string> { + if (!dataStreams) { return {}; } const patterns: Record<string, string> = {}; - for (const dataset of datasets) { - patterns[dataset.path] = generateTemplateName(dataset) + '-*'; + for (const dataStream of dataStreams) { + patterns[dataStream.path] = generateTemplateName(dataStream) + '-*'; } return patterns; } @@ -389,7 +391,7 @@ const updateExistingIndex = async ({ }) => { const { settings, mappings } = indexTemplate.template; - // for now, remove from object so as not to update stream or dataset properties of the index until type and name + // for now, remove from object so as not to update stream or data stream properties of the index until type and name // are added in https://github.com/elastic/kibana/issues/66551. namespace value we will continue // to skip updating and assume the value in the index mapping is correct delete mappings.properties.stream; diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts index 7cb507d15679ed..768c6af1d89154 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/transform/transform.test.ts @@ -114,10 +114,10 @@ describe('test transform install', () => { ({ name: 'endpoint', version: '0.16.0-dev.0', - datasets: [ + data_streams: [ { type: 'metrics', - name: 'endpoint.metadata', + dataset: 'endpoint.metadata', title: 'Endpoint Metadata', release: 'experimental', package: 'endpoint', @@ -131,7 +131,7 @@ describe('test transform install', () => { }, { type: 'metrics', - name: 'endpoint.metadata_current', + dataset: 'endpoint.metadata_current', title: 'Endpoint Metadata Current', release: 'experimental', package: 'endpoint', @@ -146,7 +146,7 @@ describe('test transform install', () => { ], } as unknown) as RegistryPackage, [ - 'endpoint-0.16.0-dev.0/dataset/policy/elasticsearch/ingest_pipeline/default.json', + 'endpoint-0.16.0-dev.0/data_stream/policy/elasticsearch/ingest_pipeline/default.json', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata/default.json', 'endpoint-0.16.0-dev.0/elasticsearch/transform/metadata_current/default.json', ], @@ -302,10 +302,10 @@ describe('test transform install', () => { ({ name: 'endpoint', version: '0.16.0-dev.0', - datasets: [ + data_streams: [ { type: 'metrics', - name: 'endpoint.metadata_current', + dataset: 'endpoint.metadata_current', title: 'Endpoint Metadata', release: 'experimental', package: 'endpoint', @@ -404,10 +404,10 @@ describe('test transform install', () => { ({ name: 'endpoint', version: '0.16.0-dev.0', - datasets: [ + data_streams: [ { type: 'metrics', - name: 'endpoint.metadata', + dataset: 'endpoint.metadata', title: 'Endpoint Metadata', release: 'experimental', package: 'endpoint', @@ -421,7 +421,7 @@ describe('test transform install', () => { }, { type: 'metrics', - name: 'endpoint.metadata_current', + dataset: 'endpoint.metadata_current', title: 'Endpoint Metadata Current', release: 'experimental', package: 'endpoint', diff --git a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts index 7fe3713e186ee0..bde542412f1237 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/kibana/index_pattern/install.ts @@ -122,8 +122,8 @@ export async function installIndexPatterns( return; } - // get all dataset fields from all installed packages - const fields = await getAllDatasetFieldsByType(installedPackagesInfo, indexPatternType); + // get all data stream fields from all installed packages + const fields = await getAllDataStreamFieldsByType(installedPackagesInfo, indexPatternType); const kibanaIndexPattern = createIndexPattern(indexPatternType, fields); // create or overwrite the index pattern @@ -135,23 +135,27 @@ export async function installIndexPatterns( } // loops through all given packages and returns an array -// of all fields from all datasets matching datasetType -export const getAllDatasetFieldsByType = async ( +// of all fields from all data streams matching data stream type +export const getAllDataStreamFieldsByType = async ( packages: RegistryPackage[], - datasetType: IndexPatternType + dataStreamType: IndexPatternType ): Promise<Fields> => { - const datasetsPromises = packages.reduce<Array<Promise<Field[]>>>((acc, pkg) => { - if (pkg.datasets) { - // filter out datasets by datasetType - const matchingDatasets = pkg.datasets.filter((dataset) => dataset.type === datasetType); - matchingDatasets.forEach((dataset) => acc.push(loadFieldsFromYaml(pkg, dataset.path))); + const dataStreamsPromises = packages.reduce<Array<Promise<Field[]>>>((acc, pkg) => { + if (pkg.data_streams) { + // filter out data streams by data stream type + const matchingDataStreams = pkg.data_streams.filter( + (dataStream) => dataStream.type === dataStreamType + ); + matchingDataStreams.forEach((dataStream) => + acc.push(loadFieldsFromYaml(pkg, dataStream.path)) + ); } return acc; }, []); - // get all the datasets for each installed package into one array - const allDatasetFields: Fields[] = await Promise.all(datasetsPromises); - return allDatasetFields.flat(); + // get all the data stream fields for each installed package into one array + const allDataStreamFields: Fields[] = await Promise.all(dataStreamsPromises); + return allDataStreamFields.flat(); }; // creates or updates index pattern diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts index 6d5ca036aeb130..78b42b03be8318 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.test.ts @@ -11,8 +11,8 @@ const tests = [ { package: { assets: [ - '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', - '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', + '/package/coredns/1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', + '/package/coredns/1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-json.json', ], path: '/package/coredns/1.0.1', }, @@ -21,15 +21,15 @@ const tests = [ return true; }, expected: [ - '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', - '/package/coredns/1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', + '/package/coredns/1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', + '/package/coredns/1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-json.json', ], }, { package: { assets: [ - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', + '/package/coredns-1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', + '/package/coredns-1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-json.json', ], path: '/package/coredns/1.0.1', }, @@ -43,8 +43,8 @@ const tests = [ { package: { assets: [ - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', - '/package/coredns-1.0.1/dataset/log/elasticsearch/ingest-pipeline/pipeline-json.json', + '/package/coredns-1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-plaintext.json', + '/package/coredns-1.0.1/data_stream/log/elasticsearch/ingest-pipeline/pipeline-json.json', ], }, // Filter which does not exist diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts index 19a023eb2ad4c5..a8abc12917781d 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/assets.ts @@ -9,9 +9,9 @@ import * as Registry from '../registry'; import { ensureCachedArchiveInfo } from '../registry'; // paths from RegistryPackage are routes to the assets on EPR -// e.g. `/package/nginx/1.2.0/dataset/access/fields/fields.yml` +// e.g. `/package/nginx/1.2.0/data_stream/access/fields/fields.yml` // paths for ArchiveEntry are routes to the assets in the archive -// e.g. `nginx-1.2.0/dataset/access/fields/fields.yml` +// e.g. `nginx-1.2.0/data_stream/access/fields/fields.yml` // RegistryPackage paths have a `/package/` prefix compared to ArchiveEntry paths // and different package and version structure const EPR_PATH_PREFIX = '/package'; @@ -37,7 +37,7 @@ export function getAssets( // if dataset, filter for them if (datasetName) { - const comparePath = `${packageInfo.path}/dataset/${datasetName}/`; + const comparePath = `${packageInfo.path}/data_stream/${datasetName}/`; if (!path.includes(comparePath)) { continue; } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts index d501b05d96c1cc..d7262ebb66b2e8 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/install.ts @@ -259,7 +259,7 @@ export async function installPackage({ const removable = !isRequiredPackage(pkgName); const { internal = false } = registryPackageInfo; - const toSaveESIndexPatterns = generateESIndexPatterns(registryPackageInfo.datasets); + const toSaveESIndexPatterns = generateESIndexPatterns(registryPackageInfo.data_streams); // add the package installation to the saved object. // if some installation already exists, just update install info @@ -304,7 +304,7 @@ export async function installPackage({ // currently only the base package has an ILM policy // at some point ILM policies can be installed/modified - // per dataset and we should then save them + // per data stream and we should then save them await installILMPolicy(paths, callCluster); // installs versionized pipelines without removing currently installed ones diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.test.ts index b40638eefbae2f..2fd9175549026e 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.test.ts @@ -41,11 +41,11 @@ const testPaths = [ }, }, { - path: 'coredns-1.0.1/dataset/stats/fields/coredns.stats.yml', + path: 'coredns-1.0.1/data_stream/stats/fields/coredns.stats.yml', assetParts: { dataset: 'stats', file: 'coredns.stats.yml', - path: 'coredns-1.0.1/dataset/stats/fields/coredns.stats.yml', + path: 'coredns-1.0.1/data_stream/stats/fields/coredns.stats.yml', pkgkey: 'coredns-1.0.1', service: '', type: 'fields', diff --git a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts index 96f75306413905..22f1b670b2cc4f 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/registry/index.ts @@ -158,12 +158,12 @@ export function pathParts(path: string): AssetParts { let [pkgkey, service, type, file] = path.split('/'); - // if it's a dataset - if (service === 'dataset') { + // if it's a data stream + if (service === 'data_stream') { // save the dataset name dataset = type; - // drop the `dataset/dataset-name` portion & re-parse - [pkgkey, service, type, file] = path.replace(`dataset/${dataset}/`, '').split('/'); + // drop the `data_stream/dataset-name` portion & re-parse + [pkgkey, service, type, file] = path.replace(`data_stream/${dataset}/`, '').split('/'); } // This is to cover for the fields.yml files inside the "fields" directory diff --git a/x-pack/plugins/ingest_manager/server/services/package_policy.test.ts b/x-pack/plugins/ingest_manager/server/services/package_policy.test.ts index 0d89c529576327..6064e5bae06348 100644 --- a/x-pack/plugins/ingest_manager/server/services/package_policy.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/package_policy.test.ts @@ -45,14 +45,14 @@ describe('Package policy service', () => { it('should work with config variables from the stream', async () => { const inputs = await packagePolicyService.assignPackageStream( ({ - datasets: [ + data_streams: [ { type: 'logs', - name: 'package.dataset1', + dataset: 'package.dataset1', streams: [{ input: 'log', template_path: 'some_template_path.yml' }], }, ], - config_templates: [ + policy_templates: [ { inputs: [{ type: 'log' }], }, @@ -64,7 +64,7 @@ describe('Package policy service', () => { enabled: true, streams: [ { - id: 'dataset01', + id: 'datastream01', data_stream: { dataset: 'package.dataset1', type: 'logs' }, enabled: true, vars: { @@ -84,7 +84,7 @@ describe('Package policy service', () => { enabled: true, streams: [ { - id: 'dataset01', + id: 'datastream01', data_stream: { dataset: 'package.dataset1', type: 'logs' }, enabled: true, vars: { @@ -106,14 +106,14 @@ describe('Package policy service', () => { it('should work with config variables at the input level', async () => { const inputs = await packagePolicyService.assignPackageStream( ({ - datasets: [ + data_streams: [ { - name: 'package.dataset1', + dataset: 'package.dataset1', type: 'logs', streams: [{ input: 'log', template_path: 'some_template_path.yml' }], }, ], - config_templates: [ + policy_templates: [ { inputs: [{ type: 'log' }], }, @@ -130,7 +130,7 @@ describe('Package policy service', () => { }, streams: [ { - id: 'dataset01', + id: 'datastream01', data_stream: { dataset: 'package.dataset1', type: 'logs' }, enabled: true, }, @@ -150,7 +150,7 @@ describe('Package policy service', () => { }, streams: [ { - id: 'dataset01', + id: 'datastream01', data_stream: { dataset: 'package.dataset1', type: 'logs' }, enabled: true, compiled_stream: { diff --git a/x-pack/plugins/ingest_manager/server/services/package_policy.ts b/x-pack/plugins/ingest_manager/server/services/package_policy.ts index 3a02544250ff0a..d91f6e8580fc3f 100644 --- a/x-pack/plugins/ingest_manager/server/services/package_policy.ts +++ b/x-pack/plugins/ingest_manager/server/services/package_policy.ts @@ -375,19 +375,19 @@ async function _assignPackageStreamToStream( return { ...stream, compiled_stream: undefined }; } const datasetPath = getDataset(stream.data_stream.dataset); - const packageDatasets = pkgInfo.datasets; - if (!packageDatasets) { - throw new Error('Stream template not found, no datasets'); + const packageDataStreams = pkgInfo.data_streams; + if (!packageDataStreams) { + throw new Error('Stream template not found, no data streams'); } - const packageDataset = packageDatasets.find( - (pkgDataset) => pkgDataset.name === stream.data_stream.dataset + const packageDataStream = packageDataStreams.find( + (pkgDataStream) => pkgDataStream.dataset === stream.data_stream.dataset ); - if (!packageDataset) { + if (!packageDataStream) { throw new Error(`Stream template not found, unable to find dataset ${datasetPath}`); } - const streamFromPkg = (packageDataset.streams || []).find( + const streamFromPkg = (packageDataStream.streams || []).find( (pkgStream) => pkgStream.input === input.type ); if (!streamFromPkg) { diff --git a/x-pack/plugins/ingest_manager/server/types/index.tsx b/x-pack/plugins/ingest_manager/server/types/index.tsx index b43d6355c479aa..fc5ba1af196adb 100644 --- a/x-pack/plugins/ingest_manager/server/types/index.tsx +++ b/x-pack/plugins/ingest_manager/server/types/index.tsx @@ -45,7 +45,7 @@ export { InstallationStatus, PackageInfo, RegistryVarsEntry, - Dataset, + RegistryDataStream, RegistryElasticsearch, AssetReference, EsAssetReference, diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz b/x-pack/test/ingest_manager_api_integration/apis/fixtures/direct_upload_packages/apache_0.1.4.tar.gz index cc983f6ac6d1aa53ac1b4a5b38b020a072a2326a..9cc4009d35c31da9e555cc98a0235f55efcf565e 100644 GIT binary patch delta 461888 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¨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%0Acucd;*@(zH6@Ggf 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ν6$7bzm2snIHKH6N~@Z(e@G$i!`CnBb0R+f=pBI9 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 N-*nG%1;Ym}1^@|_VI%+m delta 463579 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 (){+O1OWLm%-H7#Z$^0-#vaFJ_N2-j=VfQJr)@$ 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�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�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%0A7B! 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 STmT3F0RR6Xxx!HZyb1vQQ#9WI diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/pipeline2.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/pipeline2.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/pipeline2.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/pipeline2.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/dataset/test/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/dataset/test/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_logs/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_metrics/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/data_stream/test_metrics/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/elasticsearch/ingest_pipeline/pipeline1.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs2/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs2/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs2/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs2/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs2/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_logs2/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_logs2/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_metrics/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_metrics/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_metrics/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_metrics/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_metrics/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/dataset/test_metrics/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.2.0/data_stream/test_metrics/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/all_assets/0.1.0/dataset/test_logs/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_logs/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_metrics/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/data_stream/test_metrics/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml index 0ab43760b7ee87..28979cca0771bc 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/manifest.yml @@ -1,5 +1,5 @@ format_version: 1.0.0 -name: datastreams +name: datastreams title: datastream test description: This is a test package for testing that datastreams rollover when mappings are incompatible version: 0.1.0 @@ -13,8 +13,3 @@ requirement: versions: '>7.7.0' kibana: versions: '>7.7.0' - -icons: - - src: '/img/logo_overrides_64_color.svg' - size: '16x16' - type: 'image/svg+xml' diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ilm_policy/all_assets.json rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/elasticsearch/ilm_policy/all_assets.json diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/elasticsearch/ingest_pipeline/default.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/elasticsearch/ingest_pipeline/default.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_logs/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_logs/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/ecs.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_metrics/fields/ecs.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/ecs.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_metrics/fields/ecs.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_metrics/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_metrics/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_metrics/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/dataset/test_metrics/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/data_stream/test_metrics/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml index 1aa1410bd0aefc..b389a86702fa83 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.2.0/manifest.yml @@ -13,8 +13,3 @@ requirement: versions: '>7.7.0' kibana: versions: '>7.7.0' - -icons: - - src: '/img/logo_overrides_64_color.svg' - size: '16x16' - type: 'image/svg+xml' diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/dataset/test/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/data_stream/test/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/dataset/test/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/data_stream/test/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/dataset/test/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/data_stream/test/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/dataset/test/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/data_stream/test/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/manifest.yml index 32c626b1157390..f4b6eccbda9577 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/manifest.yml +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.1.0/manifest.yml @@ -1,6 +1,6 @@ format_version: 1.0.0 -name: multiple_versions -title: Package install/update test +name: multiple_versions +title: Package install/update test description: This is a test package for installing or updating a package version: 0.1.0 categories: [] @@ -13,8 +13,3 @@ requirement: versions: '>7.7.0' kibana: versions: '>7.7.0' - -icons: - - src: '/img/logo_overrides_64_color.svg' - size: '16x16' - type: 'image/svg+xml' diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/dataset/test/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/data_stream/test/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/dataset/test/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/data_stream/test/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/dataset/test/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/data_stream/test/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/dataset/test/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/data_stream/test/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/manifest.yml index 773903a69e7f7c..be7c93484d9875 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/manifest.yml +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.2.0/manifest.yml @@ -1,6 +1,6 @@ format_version: 1.0.0 -name: multiple_versions -title: Package install/update test +name: multiple_versions +title: Package install/update test description: This is a test package for installing or updating a packagee version: 0.2.0 categories: [] @@ -13,8 +13,3 @@ requirement: versions: '>7.7.0' kibana: versions: '>7.7.0' - -icons: - - src: '/img/logo_overrides_64_color.svg' - size: '16x16' - type: 'image/svg+xml' diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/dataset/test/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/data_stream/test/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/dataset/test/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/data_stream/test/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/dataset/test/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/data_stream/test/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/dataset/test/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/data_stream/test/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/manifest.yml index 49c85994d2c2cd..630788b00fca70 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/manifest.yml +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/multiple_versions/0.3.0/manifest.yml @@ -1,6 +1,6 @@ format_version: 1.0.0 name: multiple_versions -title: Package install/update test +title: Package install/update test description: This is a test package for installing or updating a package version: 0.3.0 categories: [] @@ -13,8 +13,3 @@ requirement: versions: '>7.7.0' kibana: versions: '>7.7.0' - -icons: - - src: '/img/logo_overrides_64_color.svg' - size: '16x16' - type: 'image/svg+xml' diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/dataset/test/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/data_stream/test/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/dataset/test/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/data_stream/test/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/data_stream/test/manifest.yml similarity index 77% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/data_stream/test/manifest.yml index 8cd522e2845bbc..9ac3c68a0be9ec 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/datastreams/0.1.0/dataset/test_logs/manifest.yml +++ b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/overrides/0.1.0/data_stream/test/manifest.yml @@ -6,4 +6,4 @@ elasticsearch: index_template.mappings: dynamic: false index_template.settings: - index.lifecycle.name: reference \ No newline at end of file + index.lifecycle.name: reference diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/dataset/test/fields/fields.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/data_stream/test/fields/fields.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/dataset/test/fields/fields.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/data_stream/test/fields/fields.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/dataset/test/manifest.yml b/x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/data_stream/test/manifest.yml similarity index 100% rename from x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/dataset/test/manifest.yml rename to x-pack/test/ingest_manager_api_integration/apis/fixtures/test_packages/prerelease/0.1.0-dev.0+abc/data_stream/test/manifest.yml diff --git a/x-pack/test/ingest_manager_api_integration/apis/index.js b/x-pack/test/ingest_manager_api_integration/apis/index.js index 7c1ebef337baa2..ec509e1485a9f8 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/index.js +++ b/x-pack/test/ingest_manager_api_integration/apis/index.js @@ -5,7 +5,7 @@ */ export default function ({ loadTestFile }) { - describe('Ingest Manager Endpoints', function () { + describe.skip('Ingest Manager Endpoints', function () { this.tags('ciGroup7'); // Ingest Manager setup loadTestFile(require.resolve('./setup')); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index 654aa18fba523c..c3862d130202d6 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -13,7 +13,7 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe('endpoint', function () { + describe.skip('endpoint', function () { this.tags('ciGroup7'); const ingestManager = getService('ingestManager'); const log = getService('log'); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts index 3d344c1b3b51b6..6c5764faed6316 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts @@ -10,7 +10,7 @@ import { getRegistryUrl as getRegistryUrlFromIngest } from '../../../plugins/ing export default function endpointAPIIntegrationTests(providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe('Endpoint plugin', function () { + describe.skip('Endpoint plugin', function () { const ingestManager = getService('ingestManager'); this.tags('ciGroup7'); From b3c37cf94cc461cf04511b6acb3880439f8fd858 Mon Sep 17 00:00:00 2001 From: Jen Huang <its.jenetic@gmail.com> Date: Thu, 1 Oct 2020 11:09:59 -0700 Subject: [PATCH 047/128] [Fleet] Fix agent policy change action migration (#79046) * Fix agent policy change action migration for encrypted `data` property * Parse & re-stringify `config`->`policy` data --- .../plugins/ingest_manager/server/plugin.ts | 2 +- .../server/saved_objects/index.ts | 14 +++-- .../saved_objects/migrations/to_v7_10_0.ts | 52 ++++++++++++++----- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/plugin.ts b/x-pack/plugins/ingest_manager/server/plugin.ts index f0f7bca29c99e8..6237b6d9ba3574 100644 --- a/x-pack/plugins/ingest_manager/server/plugin.ts +++ b/x-pack/plugins/ingest_manager/server/plugin.ts @@ -172,7 +172,7 @@ export class IngestManagerPlugin this.encryptedSavedObjectsSetup = deps.encryptedSavedObjects; this.cloud = deps.cloud; - registerSavedObjects(core.savedObjects); + registerSavedObjects(core.savedObjects, deps.encryptedSavedObjects); registerEncryptedSavedObjects(deps.encryptedSavedObjects); // Register feature diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts index b3a8c7390176f2..95433f896b9a35 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects/index.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/index.ts @@ -33,7 +33,9 @@ import { * Please update typings in `/common/types` as well as * schemas in `/server/types` if mappings are updated. */ -const savedObjectTypes: { [key: string]: SavedObjectsType } = { +const getSavedObjectTypes = ( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +): { [key: string]: SavedObjectsType } => ({ [GLOBAL_SETTINGS_SAVED_OBJECT_TYPE]: { name: GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, hidden: false, @@ -111,7 +113,7 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { }, }, migrations: { - '7.10.0': migrateAgentActionToV7100, + '7.10.0': migrateAgentActionToV7100(encryptedSavedObjects), }, }, [AGENT_EVENT_SAVED_OBJECT_TYPE]: { @@ -304,9 +306,13 @@ const savedObjectTypes: { [key: string]: SavedObjectsType } = { }, }, }, -}; +}); -export function registerSavedObjects(savedObjects: SavedObjectsServiceSetup) { +export function registerSavedObjects( + savedObjects: SavedObjectsServiceSetup, + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +) { + const savedObjectTypes = getSavedObjectTypes(encryptedSavedObjects); Object.values(savedObjectTypes).forEach((type) => { savedObjects.registerType(type); }); diff --git a/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts b/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts index 53af5ae42e410f..2a49c135074e52 100644 --- a/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts +++ b/x-pack/plugins/ingest_manager/server/saved_objects/migrations/to_v7_10_0.ts @@ -4,7 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectMigrationFn } from 'kibana/server'; +import { SavedObjectMigrationFn, SavedObjectUnsanitizedDoc } from 'kibana/server'; +import { EncryptedSavedObjectsPluginSetup } from '../../../../encrypted_saved_objects/server'; import { Agent, AgentEvent, @@ -94,17 +95,42 @@ export const migrateSettingsToV7100: SavedObjectMigrationFn< return settingsDoc; }; -export const migrateAgentActionToV7100: SavedObjectMigrationFn<AgentAction, AgentAction> = ( - agentActionDoc -) => { - // @ts-expect-error - if (agentActionDoc.attributes.type === 'CONFIG_CHANGE') { - agentActionDoc.attributes.type = 'POLICY_CHANGE'; - if (agentActionDoc.attributes.data?.config) { - agentActionDoc.attributes.data.policy = agentActionDoc.attributes.data.config; - delete agentActionDoc.attributes.data.config; +export const migrateAgentActionToV7100 = ( + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup +): SavedObjectMigrationFn<AgentAction, AgentAction> => { + return encryptedSavedObjects.createMigration( + (agentActionDoc): agentActionDoc is SavedObjectUnsanitizedDoc<AgentAction> => { + // @ts-expect-error + return agentActionDoc.attributes.type === 'CONFIG_CHANGE'; + }, + (agentActionDoc) => { + let agentActionData; + try { + agentActionData = agentActionDoc.attributes.data + ? JSON.parse(agentActionDoc.attributes.data) + : undefined; + } catch (e) { + // Silently swallow JSON parsing error + } + if (agentActionData && agentActionData.config) { + const { + attributes: { data, ...restOfAttributes }, + } = agentActionDoc; + const { config, ...restOfData } = agentActionData; + return { + ...agentActionDoc, + attributes: { + ...restOfAttributes, + type: 'POLICY_CHANGE', + data: JSON.stringify({ + ...restOfData, + policy: config, + }), + }, + }; + } else { + return agentActionDoc; + } } - } - - return agentActionDoc; + ); }; From bad6eab79280359d3773eb522950b88e632b9497 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger <walter@elastic.co> Date: Thu, 1 Oct 2020 20:27:22 +0200 Subject: [PATCH 048/128] [ML] DF Analytics: Collapsable sections on results pages (#76641) - Fixes cell color coding based on influence score for outlier detection results page data grid. (Part of #77046) - Introduces expandable sections (<ExpandableSection />). In contrast to plain accordions, the main idea of this component is that it should also provide some sort of useful summary when collapsed instead of just being an expandable title. For example, the "Analysis" section is collapsed by default, but still offers information like analysis type, source and destination index. This concept should allow us to keep the analytics results pages usable with more content (additional results, evaluations, visualizations) being added over time. - The "Analysis" section is a reuse of the expandable row from the analytics jobs list. Some design adjustments have been made to make it usable in both places. --- .../color_range_legend/color_range_legend.tsx | 18 +- .../data_frame_analytics/common/fields.ts | 2 + .../expandable_section.scss | 3 + .../expandable_section/expandable_section.tsx | 94 +++++++ .../components/expandable_section/index.ts | 7 + .../outlier_exploration.tsx | 247 ++++++++++++++---- .../outlier_exploration/use_outlier_data.ts | 24 +- .../pages/analytics_exploration/page.tsx | 28 +- .../expanded_row_details_pane.scss | 8 + .../expanded_row_details_pane.tsx | 39 ++- .../expanded_row_messages_pane.scss | 9 + .../expanded_row_messages_pane.tsx | 16 +- .../translations/translations/ja-JP.json | 4 - .../translations/translations/zh-CN.json | 4 - .../ml/data_frame_analytics_results.ts | 2 +- 15 files changed, 390 insertions(+), 115 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.scss create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/index.ts create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.scss create mode 100644 x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss diff --git a/x-pack/plugins/ml/public/application/components/color_range_legend/color_range_legend.tsx b/x-pack/plugins/ml/public/application/components/color_range_legend/color_range_legend.tsx index 25af3f0ec2f7f1..a3b68b85aa9fab 100644 --- a/x-pack/plugins/ml/public/application/components/color_range_legend/color_range_legend.tsx +++ b/x-pack/plugins/ml/public/application/components/color_range_legend/color_range_legend.tsx @@ -7,7 +7,7 @@ import React, { useEffect, useRef, FC } from 'react'; import d3 from 'd3'; -import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { EuiText } from '@elastic/eui'; const COLOR_RANGE_RESOLUTION = 10; @@ -134,15 +134,11 @@ export const ColorRangeLegend: FC<ColorRangeLegendProps> = ({ } return ( - <EuiFlexGroup gutterSize="s"> - <EuiFlexItem grow={false}> - <EuiText size="xs"> - <strong>{title}</strong> - </EuiText> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <svg ref={d3Container} /> - </EuiFlexItem> - </EuiFlexGroup> + <> + <EuiText size="xs" color="subdued"> + <p>{title}</p> + </EuiText> + <svg ref={d3Container} /> + </> ); }; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts index f9c9bf26a9d16e..e4581f0a87bddc 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/common/fields.ts @@ -48,6 +48,8 @@ export const EXTENDED_NUMERICAL_TYPES = new Set([ // eslint-disable-next-line @typescript-eslint/naming-convention export const ML__ID_COPY = 'ml__id_copy'; +// eslint-disable-next-line @typescript-eslint/naming-convention +export const ML__INCREMENTAL_ID = 'ml__incremental_id'; export const isKeywordAndTextType = (fieldName: string): boolean => { const { fields } = newJobCapsService; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.scss new file mode 100644 index 00000000000000..e296744b2737d4 --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.scss @@ -0,0 +1,3 @@ +.mlExpandableSection { + padding: 0 $euiSizeS $euiSizeS $euiSizeS; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx new file mode 100644 index 00000000000000..97fb8fd29e5a7b --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/expandable_section.tsx @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import './expandable_section.scss'; + +import React, { useState, FC, ReactNode } from 'react'; + +import { + EuiBadge, + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiLoadingContent, + EuiPanel, + EuiText, +} from '@elastic/eui'; + +interface HeaderItem { + // id is used as the React key and to construct a data-test-subj + id: string; + label?: ReactNode; + value: ReactNode; +} + +const isHeaderItems = (arg: any): arg is HeaderItem[] => { + return Array.isArray(arg); +}; + +export interface ExpandableSectionProps { + content: ReactNode; + headerItems?: HeaderItem[] | 'loading'; + isExpanded?: boolean; + dataTestId: string; + title: ReactNode; +} + +export const ExpandableSection: FC<ExpandableSectionProps> = ({ + headerItems, + // For now we don't have a need for complete external control + // and just want to pass in a default value. If we wanted + // full external control we'd also need to add a onToggleExpanded() + // callback. + isExpanded: isExpandedDefault = true, + content, + dataTestId, + title, +}) => { + const [isExpanded, setIsExpanded] = useState(isExpandedDefault); + const toggleExpanded = () => { + setIsExpanded(!isExpanded); + }; + + return ( + <EuiPanel paddingSize="none" data-test-subj={`mlDFExpandableSection-${dataTestId}`}> + <div className="mlExpandableSection"> + <EuiButtonEmpty + onClick={toggleExpanded} + iconType={isExpanded ? 'arrowUp' : 'arrowDown'} + size="l" + iconSide="right" + flush="left" + > + {title} + </EuiButtonEmpty> + {headerItems === 'loading' && <EuiLoadingContent lines={1} />} + {isHeaderItems(headerItems) && ( + <EuiFlexGroup> + {headerItems.map(({ label, value, id }) => ( + <EuiFlexItem + grow={false} + key={id} + data-test-subj={`mlDFExpandableSectionItem-${dataTestId}-${id}`} + > + {label !== undefined && value !== undefined && ( + <> + <EuiText size="xs" color="subdued"> + <p>{label}</p> + </EuiText> + <EuiBadge>{value}</EuiBadge> + </> + )} + {label === undefined && value} + </EuiFlexItem> + ))} + </EuiFlexGroup> + )} + </div> + {isExpanded && content} + </EuiPanel> + ); +}; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/index.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/index.ts new file mode 100644 index 00000000000000..ad7ce84902e87a --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/expandable_section/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { ExpandableSection, ExpandableSectionProps } from './expandable_section'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx index e165ee54acab88..7d7f5efcae3215 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/outlier_exploration.tsx @@ -4,11 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState, FC } from 'react'; +import React, { useEffect, useState, FC } from 'react'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiPanel, EuiSpacer } from '@elastic/eui'; +import { + EuiDataGridColumn, + EuiHorizontalRule, + EuiLoadingSpinner, + EuiSpacer, + EuiText, +} from '@elastic/eui'; + +import type { DataFrameAnalysisConfigType } from '../../../../../../../common/types/data_frame_analytics'; import { useColorRange, @@ -19,18 +28,100 @@ import { ColorRangeLegend } from '../../../../../components/color_range_legend'; import { DataGrid } from '../../../../../components/data_grid'; import { SavedSearchQuery } from '../../../../../contexts/ml'; import { getToastNotifications } from '../../../../../util/dependency_cache'; +import { ml } from '../../../../../services/ml_api_service'; -import { defaultSearchQuery, useResultsViewConfig } from '../../../../common'; +import { getAnalysisType, defaultSearchQuery, useResultsViewConfig } from '../../../../common'; -import { getTaskStateBadge } from '../../../analytics_management/components/analytics_list/use_columns'; +import { isGetDataFrameAnalyticsStatsResponseOk } from '../../../analytics_management/services/analytics_service/get_analytics'; + +import { + DataFrameAnalyticsListRow, + DATA_FRAME_MODE, +} from '../../../analytics_management/components/analytics_list/common'; +import { ExpandedRow } from '../../../analytics_management/components/analytics_list/expanded_row'; +import { ExpandableSection, ExpandableSectionProps } from '../expandable_section'; import { ExplorationQueryBar } from '../exploration_query_bar'; -import { ExplorationTitle } from '../exploration_title'; import { IndexPatternPrompt } from '../index_pattern_prompt'; import { getFeatureCount } from './common'; import { useOutlierData } from './use_outlier_data'; +const getAnalyticsSectionHeaderItems = ( + expandedRowItem: DataFrameAnalyticsListRow | undefined +): ExpandableSectionProps['headerItems'] => { + return expandedRowItem !== undefined + ? [ + { + id: 'analysisTypeLabel', + label: ( + <FormattedMessage + id="xpack.ml.dataframe.analytics.exploration.analysisTypeLabel" + defaultMessage="Type" + /> + ), + value: expandedRowItem.job_type, + }, + { + id: 'analysisSourceIndexLabel', + label: ( + <FormattedMessage + id="xpack.ml.dataframe.analytics.exploration.analysisSourceIndexLabel" + defaultMessage="Source index" + /> + ), + value: expandedRowItem.config.source.index, + }, + { + id: 'analysisDestinationIndexLabel', + label: ( + <FormattedMessage + id="xpack.ml.dataframe.analytics.exploration.analysisDestinationIndexLabel" + defaultMessage="Destination index" + /> + ), + value: expandedRowItem.config.dest.index, + }, + ] + : 'loading'; +}; + +const getResultsSectionHeaderItems = ( + columnsWithCharts: EuiDataGridColumn[], + tableItems: Array<Record<string, any>>, + rowCount: number, + colorRange: ReturnType<typeof useColorRange> +): ExpandableSectionProps['headerItems'] => { + return columnsWithCharts.length > 0 && tableItems.length > 0 + ? [ + { + id: 'explorationTableTotalDocs', + label: ( + <FormattedMessage + id="xpack.ml.dataframe.analytics.exploration.explorationTableTotalDocsLabel" + defaultMessage="Total docs" + /> + ), + value: rowCount, + }, + { + id: 'colorRangeLegend', + value: ( + <ColorRangeLegend + colorRange={colorRange} + title={i18n.translate( + 'xpack.ml.dataframe.analytics.exploration.colorRangeLegendTitle', + { + defaultMessage: 'Feature influence score', + } + )} + /> + ), + }, + ] + : 'loading'; +}; + export type TableItem = Record<string, any>; interface ExplorationProps { @@ -38,12 +129,7 @@ interface ExplorationProps { } export const OutlierExploration: FC<ExplorationProps> = React.memo(({ jobId }) => { - const explorationTitle = i18n.translate('xpack.ml.dataframe.analytics.exploration.jobIdTitle', { - defaultMessage: 'Outlier detection job ID {jobId}', - values: { jobId }, - }); - - const { indexPattern, jobConfig, jobStatus, needsDestIndexPattern } = useResultsViewConfig(jobId); + const { indexPattern, jobConfig, needsDestIndexPattern } = useResultsViewConfig(jobId); const [searchQuery, setSearchQuery] = useState<SavedSearchQuery>(defaultSearchQuery); const outlierData = useOutlierData(indexPattern, jobConfig, searchQuery); @@ -55,47 +141,74 @@ export const OutlierExploration: FC<ExplorationProps> = React.memo(({ jobId }) = jobConfig !== undefined ? getFeatureCount(jobConfig.dest.results_field, tableItems) : 1 ); - return ( - <EuiPanel data-test-subj="mlDFAnalyticsOutlierExplorationTablePanel"> + const [expandedRowItem, setExpandedRowItem] = useState<DataFrameAnalyticsListRow | undefined>(); + + const fetchStats = async () => { + const analyticsConfigs = await ml.dataFrameAnalytics.getDataFrameAnalytics(jobId); + const analyticsStats = await ml.dataFrameAnalytics.getDataFrameAnalyticsStats(jobId); + + const config = analyticsConfigs.data_frame_analytics[0]; + const stats = isGetDataFrameAnalyticsStatsResponseOk(analyticsStats) + ? analyticsStats.data_frame_analytics[0] + : undefined; + + if (stats === undefined) { + return; + } + + const newExpandedRowItem: DataFrameAnalyticsListRow = { + checkpointing: {}, + config, + id: config.id, + job_type: getAnalysisType(config.analysis) as DataFrameAnalysisConfigType, + mode: DATA_FRAME_MODE.BATCH, + state: stats.state, + stats, + }; + + setExpandedRowItem(newExpandedRowItem); + }; + + useEffect(() => { + fetchStats(); + }, [jobConfig?.id]); + + // Analytics section header items and content + const analyticsSectionHeaderItems = getAnalyticsSectionHeaderItems(expandedRowItem); + const analyticsSectionContent = ( + <> + <EuiHorizontalRule size="full" margin="none" /> + {expandedRowItem === undefined && ( + <EuiText textAlign="center"> + <EuiSpacer size="l" /> + <EuiLoadingSpinner size="l" /> + <EuiSpacer size="l" /> + </EuiText> + )} + {(columnsWithCharts.length > 0 || searchQuery !== defaultSearchQuery) && + indexPattern !== undefined && + jobConfig !== undefined && + columnsWithCharts.length > 0 && + tableItems.length > 0 && + expandedRowItem !== undefined && <ExpandedRow item={expandedRowItem} />} + </> + ); + + // Results section header items and content + const resultsSectionHeaderItems = getResultsSectionHeaderItems( + columnsWithCharts, + tableItems, + outlierData.rowCount, + colorRange + ); + const resultsSectionContent = ( + <> {jobConfig !== undefined && needsDestIndexPattern && ( <IndexPatternPrompt destIndex={jobConfig.dest.index} /> )} - <EuiFlexGroup - alignItems="center" - justifyContent="spaceBetween" - responsive={false} - gutterSize="s" - > - <EuiFlexItem grow={false}> - <ExplorationTitle title={explorationTitle} /> - </EuiFlexItem> - {jobStatus !== undefined && ( - <EuiFlexItem grow={false}> - <span>{getTaskStateBadge(jobStatus)}</span> - </EuiFlexItem> - )} - </EuiFlexGroup> - <EuiHorizontalRule margin="xs" /> {(columnsWithCharts.length > 0 || searchQuery !== defaultSearchQuery) && indexPattern !== undefined && ( <> - <EuiFlexGroup justifyContent="spaceBetween"> - <EuiFlexItem> - <ExplorationQueryBar indexPattern={indexPattern} setSearchQuery={setSearchQuery} /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - <EuiSpacer size="s" /> - <ColorRangeLegend - colorRange={colorRange} - title={i18n.translate( - 'xpack.ml.dataframe.analytics.exploration.colorRangeLegendTitle', - { - defaultMessage: 'Feature influence score', - } - )} - /> - </EuiFlexItem> - </EuiFlexGroup> <EuiSpacer size="s" /> {columnsWithCharts.length > 0 && tableItems.length > 0 && ( <DataGrid @@ -106,6 +219,46 @@ export const OutlierExploration: FC<ExplorationProps> = React.memo(({ jobId }) = )} </> )} - </EuiPanel> + </> + ); + + return ( + <> + {(columnsWithCharts.length > 0 || searchQuery !== defaultSearchQuery) && + indexPattern !== undefined && ( + <> + <ExplorationQueryBar indexPattern={indexPattern} setSearchQuery={setSearchQuery} /> + <EuiSpacer size="m" /> + </> + )} + + <ExpandableSection + dataTestId="analysis" + content={analyticsSectionContent} + headerItems={analyticsSectionHeaderItems} + isExpanded={false} + title={ + <FormattedMessage + id="xpack.ml.dataframe.analytics.exploration.analysisSectionTitle" + defaultMessage="Analysis" + /> + } + /> + + <EuiSpacer size="m" /> + + <ExpandableSection + dataTestId="results" + content={resultsSectionContent} + headerItems={resultsSectionHeaderItems} + title={ + <FormattedMessage + id="xpack.ml.dataframe.analytics.exploration.explorationTableTitle" + defaultMessage="Results" + /> + } + /> + <EuiSpacer size="m" /> + </> ); }); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts index 151e5ea4e6feb2..eded8e82a79199 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/outlier_exploration/use_outlier_data.ts @@ -31,10 +31,19 @@ import { getToastNotifications } from '../../../../../util/dependency_cache'; import { getIndexData, getIndexFields, DataFrameAnalyticsConfig } from '../../../../common'; import { FEATURE_INFLUENCE } from '../../../../common/constants'; import { DEFAULT_RESULTS_FIELD } from '../../../../../../../common/constants/data_frame_analytics'; -import { sortExplorationResultsFields, ML__ID_COPY } from '../../../../common/fields'; +import { + sortExplorationResultsFields, + ML__ID_COPY, + ML__INCREMENTAL_ID, +} from '../../../../common/fields'; import { getFeatureCount, getOutlierScoreFieldName } from './common'; +interface FeatureInfluence { + feature_name: string; + influence: number; +} + export const useOutlierData = ( indexPattern: IndexPattern | undefined, jobConfig: DataFrameAnalyticsConfig | undefined, @@ -61,7 +70,7 @@ export const useOutlierData = ( // reduce default selected rows from 20 to 8 for performance reasons. 8, // by default, hide feature-influence columns and the doc id copy - (d) => !d.includes(`.${FEATURE_INFLUENCE}.`) && d !== ML__ID_COPY + (d) => !d.includes(`.${FEATURE_INFLUENCE}.`) && d !== ML__ID_COPY && d !== ML__INCREMENTAL_ID ); useEffect(() => { @@ -138,9 +147,16 @@ export const useOutlierData = ( // column with feature values get color coded by its corresponding influencer value if ( fullItem[resultsField] !== undefined && - fullItem[resultsField][`${FEATURE_INFLUENCE}.${columnId}`] !== undefined + fullItem[resultsField][FEATURE_INFLUENCE] !== undefined && + fullItem[resultsField][FEATURE_INFLUENCE].find( + (d: FeatureInfluence) => d.feature_name === columnId + ) !== undefined ) { - backgroundColor = colorRange(fullItem[resultsField][`${FEATURE_INFLUENCE}.${columnId}`]); + backgroundColor = colorRange( + fullItem[resultsField][FEATURE_INFLUENCE].find( + (d: FeatureInfluence) => d.feature_name === columnId + ).influence + ); } // column with influencer values get color coded by its own value diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx index 4620bbd969fab2..d2767a9612e3bb 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/page.tsx @@ -6,11 +6,7 @@ import React, { Fragment, FC } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; - import { - EuiBetaBadge, EuiPage, EuiPageBody, EuiPageContentBody, @@ -41,32 +37,12 @@ export const Page: FC<{ <EuiPageContentHeader> <EuiPageContentHeaderSection> <EuiTitle> - <h1> - <FormattedMessage - id="xpack.ml.dataframe.analytics.exploration.title" - defaultMessage="Analytics exploration" - /> - <span> </span> - <EuiBetaBadge - label={i18n.translate( - 'xpack.ml.dataframe.analytics.exploration.experimentalBadgeLabel', - { - defaultMessage: 'Experimental', - } - )} - tooltipContent={i18n.translate( - 'xpack.ml.dataframe.analytics.exploration.experimentalBadgeTooltipContent', - { - defaultMessage: `Data frame analytics are an experimental feature. We'd love to hear your feedback.`, - } - )} - /> - </h1> + <h1>{jobId}</h1> </EuiTitle> </EuiPageContentHeaderSection> </EuiPageContentHeader> <EuiPageContentBody style={{ maxWidth: 'calc(100% - 0px)' }}> - <EuiSpacer size="l" /> + <EuiSpacer size="m" /> {analysisType === ANALYSIS_CONFIG_TYPE.OUTLIER_DETECTION && ( <OutlierExploration jobId={jobId} /> )} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.scss new file mode 100644 index 00000000000000..efc9296350232a --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.scss @@ -0,0 +1,8 @@ +.mlExpandedRowDetails { + padding: 0 $euiSizeS $euiSizeS $euiSizeS; +} + +/* Hide the basic table's header */ +.mlExpandedRowDetailsSection thead { + display: none; +} diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.tsx index 71ca2b6f60492f..41722f7559de2e 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_details_pane.tsx @@ -4,16 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ +import './expanded_row_details_pane.scss'; + import React, { Fragment, FC, ReactElement } from 'react'; -import { - EuiDescriptionList, - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiTitle, - EuiSpacer, -} from '@elastic/eui'; +import { EuiBasicTable, EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer } from '@elastic/eui'; export interface SectionItem { title: string; @@ -34,13 +29,33 @@ export const Section: FC<SectionProps> = ({ section }) => { return null; } + const columns = [ + { + field: 'title', + name: '', + render: (v: SectionItem['title']) => <strong>{v}</strong>, + }, + { + field: 'description', + name: '', + render: (v: SectionItem['description']) => <>{v}</>, + }, + ]; + return ( - <EuiPanel> + <> <EuiTitle size="xs"> <span>{section.title}</span> </EuiTitle> - <EuiDescriptionList compressed type="column" listItems={section.items} /> - </EuiPanel> + <EuiBasicTable<SectionItem> + compressed + items={section.items} + columns={columns} + tableCaption={section.title} + tableLayout="auto" + className="mlExpandedRowDetailsSection" + /> + </> ); }; @@ -50,7 +65,7 @@ interface ExpandedRowDetailsPaneProps { export const ExpandedRowDetailsPane: FC<ExpandedRowDetailsPaneProps> = ({ sections }) => { return ( - <EuiFlexGroup> + <EuiFlexGroup className="mlExpandedRowDetails"> <EuiFlexItem style={{ width: '50%' }}> {sections .filter((s) => s.position === 'left') diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss new file mode 100644 index 00000000000000..5a4d1b3190402d --- /dev/null +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.scss @@ -0,0 +1,9 @@ +.mlExpandedRowJobMessages { + padding: 0 $euiSizeS $euiSizeS $euiSizeS; +} + +/* override ML legacy class "job-messages-table" */ +.mlExpandedRowJobMessages .euiTable, .mlExpandedRowJobMessages .euiTableRowCell { + background-color: transparent !important; +} + diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx index 942e335526d68d..91925b7f0afe1a 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/analytics_list/expanded_row_messages_pane.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import './expanded_row_messages_pane.scss'; + import React, { FC, useState, useEffect, useCallback } from 'react'; import { i18n } from '@kbn/i18n'; import { ml } from '../../../../../services/ml_api_service'; @@ -43,11 +45,13 @@ export const ExpandedRowMessagesPane: FC<Props> = ({ analyticsId }) => { useRefreshAnalyticsList({ onRefresh: getMessages }); return ( - <JobMessages - messages={messages} - loading={isLoading} - error={errorMessage} - refreshMessage={getMessages} - /> + <div className="mlExpandedRowJobMessages"> + <JobMessages + messages={messages} + loading={isLoading} + error={errorMessage} + refreshMessage={getMessages} + /> + </div> ); }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 34c339023171e1..bd9a66b48f6332 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10754,10 +10754,6 @@ "xpack.ml.dataframe.analytics.errorCallout.queryParsingErrorBody": "クエリ構文が無効であり、結果を返しませんでした。クエリ構文を確認し、再試行してください。", "xpack.ml.dataframe.analytics.errorCallout.queryParsingErrorTitle": "クエリをパースできません。", "xpack.ml.dataframe.analytics.exploration.colorRangeLegendTitle": "機能影響スコア", - "xpack.ml.dataframe.analytics.exploration.experimentalBadgeLabel": "実験的", - "xpack.ml.dataframe.analytics.exploration.experimentalBadgeTooltipContent": "データフレーム分析は実験段階の機能です。フィードバックをお待ちしています。", - "xpack.ml.dataframe.analytics.exploration.jobIdTitle": "外れ値検出ジョブID {jobId}", - "xpack.ml.dataframe.analytics.exploration.title": "分析の探索", "xpack.ml.dataframe.analytics.explorationResults.documentsShownHelpText": "予測があるドキュメントを示す", "xpack.ml.dataframe.analytics.explorationResults.fieldSelection": "{docFieldsCount, number}件中 showing {selectedFieldsLength, number}件の{docFieldsCount, plural, one {フィールド} other {フィールド}}", "xpack.ml.dataframe.analytics.explorationResults.firstDocumentsShownHelpText": "予測がある最初の{searchSize}のドキュメントを示す", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index f32b49fd4f2f00..3ce92782340058 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10760,10 +10760,6 @@ "xpack.ml.dataframe.analytics.errorCallout.queryParsingErrorBody": "查询语法无效,未返回任何结果。请检查查询语法并重试。", "xpack.ml.dataframe.analytics.errorCallout.queryParsingErrorTitle": "无法解析查询。", "xpack.ml.dataframe.analytics.exploration.colorRangeLegendTitle": "功能影响分数", - "xpack.ml.dataframe.analytics.exploration.experimentalBadgeLabel": "实验性", - "xpack.ml.dataframe.analytics.exploration.experimentalBadgeTooltipContent": "数据帧分析为实验功能。我们很乐意听取您的反馈意见。", - "xpack.ml.dataframe.analytics.exploration.jobIdTitle": "离群值检测作业 ID {jobId}", - "xpack.ml.dataframe.analytics.exploration.title": "分析浏览", "xpack.ml.dataframe.analytics.explorationResults.documentsShownHelpText": "正在显示有相关预测存在的文档", "xpack.ml.dataframe.analytics.explorationResults.fieldSelection": "已选择 {docFieldsCount, number} 个{docFieldsCount, plural, one {字段} other {字段}}中的 {selectedFieldsLength, number} 个", "xpack.ml.dataframe.analytics.explorationResults.firstDocumentsShownHelpText": "正在显示有相关预测存在的前 {searchSize} 个文档", 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 b6a6ff8eb6c637..8a72badebd923a 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 @@ -36,7 +36,7 @@ export function MachineLearningDataFrameAnalyticsResultsProvider({ }, async assertOutlierTablePanelExists() { - await testSubjects.existOrFail('mlDFAnalyticsOutlierExplorationTablePanel'); + await testSubjects.existOrFail('mlDFExpandableSection-results'); }, async assertResultsTableExists() { From 46084cbbe851c0128c3250bb6d9c00ee4a765eb8 Mon Sep 17 00:00:00 2001 From: nnamdifrankie <56440728+nnamdifrankie@users.noreply.github.com> Date: Thu, 1 Oct 2020 14:50:40 -0400 Subject: [PATCH 049/128] [Ingest]: add more test for transform index (#79154) --- .../apis/epm/install_remove_assets.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts index d33d0445d1cd6e..5170867d7b5457 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/install_remove_assets.ts @@ -91,6 +91,14 @@ export default function (providerContext: FtrProviderContext) { }); expect(res.statusCode).equal(200); }); + it('should have created the index for the transform', async function () { + // the index is defined in the transform file + const res = await es.transport.request({ + method: 'GET', + path: `/logs-all_assets.test_log_current_default`, + }); + expect(res.statusCode).equal(200); + }); it('should have installed the kibana assets', async function () { const resIndexPatternLogs = await kibanaServer.savedObjects.get({ type: 'index-pattern', @@ -260,6 +268,19 @@ export default function (providerContext: FtrProviderContext) { ); expect(res.statusCode).equal(404); }); + it('should have deleted the index for the transform', async function () { + // the index is defined in the transform file + const res = await es.transport.request( + { + method: 'GET', + path: `/logs-all_assets.test_log_current_default`, + }, + { + ignore: [404], + } + ); + expect(res.statusCode).equal(404); + }); it('should have uninstalled the kibana assets', async function () { let resDashboard; try { From e08f6a38f61023496b7389659d768ca0685663fc Mon Sep 17 00:00:00 2001 From: Devon Thomson <devon.thomson@hotmail.com> Date: Thu, 1 Oct 2020 14:52:28 -0400 Subject: [PATCH 050/128] Always Show Embeddable Panel Header in Edit Mode (#79152) * Always show header in edit mode --- .../embeddable/public/lib/panel/panel_header/panel_header.tsx | 2 +- test/functional/apps/dashboard/dashboard_options.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx index dea483efb349f9..c538b98949a43e 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx @@ -136,7 +136,7 @@ export function PanelHeader({ }: PanelHeaderProps) { const viewDescription = getViewDescription(embeddable); const showTitle = !hidePanelTitle && (!isViewMode || title || viewDescription !== ''); - const showPanelBar = badges.length > 0 || notifications.length > 0 || showTitle; + const showPanelBar = !isViewMode || badges.length > 0 || notifications.length > 0 || showTitle; const classes = classNames('embPanel__header', { // eslint-disable-next-line @typescript-eslint/naming-convention 'embPanel__header--floater': !showPanelBar, diff --git a/test/functional/apps/dashboard/dashboard_options.js b/test/functional/apps/dashboard/dashboard_options.js index d48e46e58f6d00..4e7c3f4cdc79bc 100644 --- a/test/functional/apps/dashboard/dashboard_options.js +++ b/test/functional/apps/dashboard/dashboard_options.js @@ -44,7 +44,7 @@ export default function ({ getService, getPageObjects }) { await PageObjects.dashboard.checkHideTitle(); await retry.try(async () => { const titles = await PageObjects.dashboard.getPanelTitles(); - expect(titles[0]).to.eql(undefined); + expect(titles[0]).to.eql(''); }); }); From a8e3c8ac78acb3bf1ee0f76d353fae2d8bd0ade1 Mon Sep 17 00:00:00 2001 From: Lukas Olson <olson.lukas@gmail.com> Date: Thu, 1 Oct 2020 11:56:50 -0700 Subject: [PATCH 051/128] [Search] Fix timeout upgrade link (#79045) --- .../data/public/search/errors/timeout_error.test.tsx | 6 +++--- src/plugins/data/public/search/errors/timeout_error.tsx | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plugins/data/public/search/errors/timeout_error.test.tsx b/src/plugins/data/public/search/errors/timeout_error.test.tsx index 87b491b976ebc1..ad3384c389fbf2 100644 --- a/src/plugins/data/public/search/errors/timeout_error.test.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.test.tsx @@ -37,9 +37,9 @@ describe('SearchTimeoutError', () => { expect(component.find('EuiButton').length).toBe(1); component.find('EuiButton').simulate('click'); - expect(startMock.application.navigateToApp).toHaveBeenCalledWith('management', { - path: '/kibana/indexPatterns', - }); + expect(startMock.application.navigateToUrl).toHaveBeenCalledWith( + 'https://www.elastic.co/subscriptions' + ); }); it('Should create contact admin message', () => { diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/errors/timeout_error.tsx index 56aecb42f5326e..a9ff0c3b38ae6d 100644 --- a/src/plugins/data/public/search/errors/timeout_error.tsx +++ b/src/plugins/data/public/search/errors/timeout_error.tsx @@ -78,9 +78,7 @@ export class SearchTimeoutError extends KbnError { private onClick(application: ApplicationStart) { switch (this.mode) { case TimeoutErrorMode.UPGRADE: - application.navigateToApp('management', { - path: `/kibana/indexPatterns`, - }); + application.navigateToUrl('https://www.elastic.co/subscriptions'); break; case TimeoutErrorMode.CHANGE: application.navigateToApp('management', { From 12d01250249801d232bb01234c3044ef2fd6e642 Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Thu, 1 Oct 2020 12:23:45 -0700 Subject: [PATCH 052/128] [babel] remove unused/unneeded babel plugins (#79173) Co-authored-by: spalger <spalger@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- packages/kbn-babel-preset/node_preset.js | 18 ------------------ packages/kbn-babel-preset/package.json | 4 +--- packages/kbn-babel-preset/webpack_preset.js | 19 ------------------- yarn.lock | 20 ++------------------ 4 files changed, 3 insertions(+), 58 deletions(-) diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index ee06e2588b0222..45afe5d5ebc32b 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -18,23 +18,6 @@ */ module.exports = (_, options = {}) => { - const overrides = []; - if (!process.env.ALLOW_PERFORMANCE_HOOKS_IN_TASK_MANAGER) { - overrides.push({ - test: [/x-pack[\/\\]legacy[\/\\]plugins[\/\\]task_manager/], - plugins: [ - [ - require.resolve('babel-plugin-filter-imports'), - { - imports: { - perf_hooks: ['performance'], - }, - }, - ], - ], - }); - } - return { presets: [ [ @@ -74,6 +57,5 @@ module.exports = (_, options = {}) => { }, ], ], - overrides, }; }; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index d73294b4cf8739..d6d1a78dd4a23a 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -1,7 +1,7 @@ { "name": "@kbn/babel-preset", - "private": true, "version": "1.0.0", + "private": true, "license": "Apache-2.0", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.10.4", @@ -13,10 +13,8 @@ "@babel/preset-react": "^7.10.4", "@babel/preset-typescript": "^7.10.4", "babel-plugin-add-module-exports": "^1.0.2", - "babel-plugin-filter-imports": "^3.0.0", "babel-plugin-styled-components": "^1.10.7", "babel-plugin-transform-define": "^1.3.1", - "babel-plugin-transform-imports": "^2.0.0", "react-is": "^16.8.0", "styled-components": "^5.1.0" } diff --git a/packages/kbn-babel-preset/webpack_preset.js b/packages/kbn-babel-preset/webpack_preset.js index 97462a579e3c46..a43d607edb17c2 100644 --- a/packages/kbn-babel-preset/webpack_preset.js +++ b/packages/kbn-babel-preset/webpack_preset.js @@ -40,24 +40,5 @@ module.exports = () => { }, ], ], - // NOTE: we can enable this by default for everything as soon as we only have one instance - // of lodash across the entire project. For now we are just enabling it for siem - // as they are extensively using the lodash v4 - overrides: [ - { - test: [/x-pack[\/\\]legacy[\/\\]plugins[\/\\]siem[\/\\]public/], - plugins: [ - [ - require.resolve('babel-plugin-transform-imports'), - { - 'lodash/?(((\\w*)?/?)*)': { - transform: 'lodash/${1}/${member}', - preventFullImport: false, - }, - }, - ], - ], - }, - ], }; }; diff --git a/yarn.lock b/yarn.lock index 608207400ec714..876f938cfce88a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1098,7 +1098,7 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.9.5": +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.5": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== @@ -7367,14 +7367,6 @@ babel-plugin-extract-import-names@1.6.16: dependencies: "@babel/helper-plugin-utils" "7.10.4" -babel-plugin-filter-imports@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-filter-imports/-/babel-plugin-filter-imports-3.0.0.tgz#a849683837ad29960da17492fb32789ab6b09a11" - integrity sha512-p/chjzVTgCxUqyLM0q/pfWVZS7IJTwGQMwNg0LOvuQpKiTftQgZDtkGB8XvETnUw19rRcL7bJCTopSwibTN2tA== - dependencies: - "@babel/types" "^7.4.0" - lodash "^4.17.11" - babel-plugin-istanbul@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" @@ -7528,14 +7520,6 @@ babel-plugin-transform-define@^1.3.1: lodash "^4.17.11" traverse "0.6.6" -babel-plugin-transform-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-imports/-/babel-plugin-transform-imports-2.0.0.tgz#9e5f49f751a9d34ba8f4bb988c7e48ed2419c6b6" - integrity sha512-65ewumYJ85QiXdcB/jmiU0y0jg6eL6CdnDqQAqQ8JMOKh1E52VPG3NJzbVKWcgovUR5GBH8IWpCXQ7I8Q3wjgw== - dependencies: - "@babel/types" "^7.4" - is-valid-path "^0.1.1" - babel-plugin-transform-inline-consecutive-adds@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz#323d47a3ea63a83a7ac3c811ae8e6941faf2b0d1" @@ -17695,7 +17679,7 @@ is-valid-glob@^1.0.0: resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= -is-valid-path@0.1.1, is-valid-path@^0.1.1: +is-valid-path@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-valid-path/-/is-valid-path-0.1.1.tgz#110f9ff74c37f663e1ec7915eb451f2db93ac9df" integrity sha1-EQ+f90w39mPh7HkV60UfLbk6yd8= From d0f8e5cbea83c9e8f1bd003e904922795c3346ca Mon Sep 17 00:00:00 2001 From: Caroline Horn <549577+cchaos@users.noreply.github.com> Date: Thu, 1 Oct 2020 15:49:14 -0400 Subject: [PATCH 053/128] Fix z-index of KQL Suggestions dropdown (#79184) Fix from #4084 --- src/plugins/data/public/ui/typeahead/suggestions_component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/data/public/ui/typeahead/suggestions_component.tsx b/src/plugins/data/public/ui/typeahead/suggestions_component.tsx index 054c6318b9772c..50ed9e9542d369 100644 --- a/src/plugins/data/public/ui/typeahead/suggestions_component.tsx +++ b/src/plugins/data/public/ui/typeahead/suggestions_component.tsx @@ -154,7 +154,7 @@ export class SuggestionsComponent extends Component<Props> { const StyledSuggestionsListDiv = styled.div` ${(props: { queryBarRect: DOMRect; verticalListPosition: string }) => ` position: absolute; - z-index: 999; + z-index: 4001; left: ${props.queryBarRect.left}px; width: ${props.queryBarRect.width}px; ${props.verticalListPosition}`} From 0a7462dc4acb79bc28873b4ce82a510aa624397c Mon Sep 17 00:00:00 2001 From: Lee Drengenberg <lee.drengenberg@elastic.co> Date: Thu, 1 Oct 2020 15:35:36 -0500 Subject: [PATCH 054/128] move apps lower in tree, add metricbeat dashboard screenshot test (#79001) --- .../services/common/failure_debugging.ts | 2 +- .../functional/services/common/screenshots.ts | 2 +- test/functional/services/common/snapshots.ts | 2 +- .../{test/functional => }/apps/ccs/ccs.js | 0 .../{test/functional => }/apps/ccs/index.js | 0 .../functional => }/apps/filebeat/filebeat.js | 0 .../functional => }/apps/filebeat/index.js | 0 .../apps/heartbeat/_heartbeat.js | 0 .../functional => }/apps/heartbeat/index.js | 0 .../apps/management/_index_pattern_create.js | 0 .../functional => }/apps/management/index.js | 0 .../apps/metricbeat/_metricbeat.js | 0 .../apps/metricbeat/_metricbeat_dashboard.js | 59 +++++++++++++++++++ .../functional => }/apps/metricbeat/index.js | 1 + .../apps/monitoring/_monitoring.js | 0 .../apps/monitoring/_monitoring_metricbeat.js | 0 .../functional => }/apps/monitoring/index.js | 0 .../apps/packetbeat/_packetbeat.js | 0 .../functional => }/apps/packetbeat/index.js | 0 .../functional => }/apps/reporting/index.js | 0 .../apps/reporting/reporting_watcher.js | 0 .../apps/reporting/reporting_watcher_png.js | 0 .../functional => }/apps/reporting/util.js | 0 .../apps/sample_data/e_commerce.js | 0 .../functional => }/apps/sample_data/index.js | 0 .../apps/telemetry/_telemetry.js | 0 .../functional => }/apps/telemetry/index.js | 0 .../apps/winlogbeat/_winlogbeat.js | 0 .../functional => }/apps/winlogbeat/index.js | 0 ...onfig.stack_functional_integration_base.js | 17 ++++-- .../configs/tests_list.js | 6 +- 31 files changed, 81 insertions(+), 8 deletions(-) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/ccs/ccs.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/ccs/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/filebeat/filebeat.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/filebeat/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/heartbeat/_heartbeat.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/heartbeat/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/management/_index_pattern_create.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/management/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/metricbeat/_metricbeat.js (100%) create mode 100644 x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js rename x-pack/test/stack_functional_integration/{test/functional => }/apps/metricbeat/index.js (86%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/monitoring/_monitoring.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/monitoring/_monitoring_metricbeat.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/monitoring/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/packetbeat/_packetbeat.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/packetbeat/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/reporting/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/reporting/reporting_watcher.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/reporting/reporting_watcher_png.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/reporting/util.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/sample_data/e_commerce.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/sample_data/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/telemetry/_telemetry.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/telemetry/index.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/winlogbeat/_winlogbeat.js (100%) rename x-pack/test/stack_functional_integration/{test/functional => }/apps/winlogbeat/index.js (100%) diff --git a/test/functional/services/common/failure_debugging.ts b/test/functional/services/common/failure_debugging.ts index aa67c455e0100a..8b0e095b71ff87 100644 --- a/test/functional/services/common/failure_debugging.ts +++ b/test/functional/services/common/failure_debugging.ts @@ -38,7 +38,7 @@ export async function FailureDebuggingProvider({ getService }: FtrProviderContex const log = getService('log'); const browser = getService('browser'); - if (process.env.CI !== 'true') { + if (process.env.CI !== 'true' && !process.env.stack_functional_integration) { await del(config.get('failureDebugging.htmlDirectory')); } diff --git a/test/functional/services/common/screenshots.ts b/test/functional/services/common/screenshots.ts index daa55240f3eb77..5bce0d4cf6c874 100644 --- a/test/functional/services/common/screenshots.ts +++ b/test/functional/services/common/screenshots.ts @@ -40,7 +40,7 @@ export async function ScreenshotsProvider({ getService }: FtrProviderContext) { const FAILURE_DIRECTORY = resolve(config.get('screenshots.directory'), 'failure'); const BASELINE_DIRECTORY = resolve(config.get('screenshots.directory'), 'baseline'); - if (process.env.CI !== 'true') { + if (process.env.CI !== 'true' && !process.env.stack_functional_integration) { await del([SESSION_DIRECTORY, FAILURE_DIRECTORY]); } diff --git a/test/functional/services/common/snapshots.ts b/test/functional/services/common/snapshots.ts index 2e0b360e594e53..03eadff82e31f2 100644 --- a/test/functional/services/common/snapshots.ts +++ b/test/functional/services/common/snapshots.ts @@ -35,7 +35,7 @@ export async function SnapshotsProvider({ getService }: FtrProviderContext) { const SESSION_DIRECTORY = resolve(config.get('snapshots.directory'), 'session'); const BASELINE_DIRECTORY = resolve(config.get('snapshots.directory'), 'baseline'); - if (process.env.CI !== 'true') { + if (process.env.CI !== 'true' && !process.env.stack_functional_integration) { await del([SESSION_DIRECTORY]); } diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/ccs/ccs.js b/x-pack/test/stack_functional_integration/apps/ccs/ccs.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/ccs/ccs.js rename to x-pack/test/stack_functional_integration/apps/ccs/ccs.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/ccs/index.js b/x-pack/test/stack_functional_integration/apps/ccs/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/ccs/index.js rename to x-pack/test/stack_functional_integration/apps/ccs/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/filebeat.js b/x-pack/test/stack_functional_integration/apps/filebeat/filebeat.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/filebeat/filebeat.js rename to x-pack/test/stack_functional_integration/apps/filebeat/filebeat.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/filebeat/index.js b/x-pack/test/stack_functional_integration/apps/filebeat/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/filebeat/index.js rename to x-pack/test/stack_functional_integration/apps/filebeat/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/_heartbeat.js b/x-pack/test/stack_functional_integration/apps/heartbeat/_heartbeat.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/_heartbeat.js rename to x-pack/test/stack_functional_integration/apps/heartbeat/_heartbeat.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/index.js b/x-pack/test/stack_functional_integration/apps/heartbeat/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/heartbeat/index.js rename to x-pack/test/stack_functional_integration/apps/heartbeat/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/management/_index_pattern_create.js b/x-pack/test/stack_functional_integration/apps/management/_index_pattern_create.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/management/_index_pattern_create.js rename to x-pack/test/stack_functional_integration/apps/management/_index_pattern_create.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/management/index.js b/x-pack/test/stack_functional_integration/apps/management/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/management/index.js rename to x-pack/test/stack_functional_integration/apps/management/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/_metricbeat.js b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/_metricbeat.js rename to x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat.js diff --git a/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js new file mode 100644 index 00000000000000..42f707fb778547 --- /dev/null +++ b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { REPO_ROOT } from '@kbn/dev-utils'; + +export default function ({ getService, getPageObjects, updateBaselines }) { + const screenshot = getService('screenshots'); + const browser = getService('browser'); + const esArchiver = getService('esArchiver'); + const PageObjects = getPageObjects(['common', 'dashboard', 'timePicker']); + + describe('check metricbeat Dashboard', function () { + before(async function () { + await esArchiver.load(`${REPO_ROOT}/../integration-test/test/es_archives/metricbeat`); + + // this navigateToActualURL takes the place of navigating to the dashboard landing page, + // filtering on the dashboard name, selecting it, setting the timepicker, and going to full screen + await PageObjects.common.navigateToActualUrl( + 'dashboard', + 'view/Metricbeat-system-overview-ecs?_g=(filters:!(),refreshInterval:(pause:!t,value:0),' + + 'time:(from:%272020-09-29T19:02:37.902Z%27,to:%272020-09-29T19:06:43.218Z%27))&_a=' + + '(description:%27Overview%20of%20system%20metrics%27,filters:!(),fullScreenMode:!t,' + + 'options:(darkTheme:!f),query:(language:kuery,query:%27%27),timeRestore:!f,' + + 'title:%27%5BMetricbeat%20System%5D%20Overview%20ECS%27,viewMode:view)', + { + ensureCurrentUrl: false, + shouldLoginIfPrompted: true, + } + ); + // await PageObjects.common.navigateToApp('dashboard', { insertTimestamp: false }); + // await PageObjects.dashboard.loadSavedDashboard('[Metricbeat System] Overview ECS'); + // await PageObjects.timePicker.setAbsoluteRange( + // 'Sep 29, 2020 @ 14:02:37.902', + // 'Sep 29, 2020 @ 14:06:43.218' + // ); + // await PageObjects.dashboard.clickFullScreenMode(); + + await PageObjects.common.sleep(2000); + await PageObjects.dashboard.waitForRenderComplete(); + await browser.setScreenshotSize(1000, 1000); + }); + + it('[Metricbeat System] Overview ECS should match snapshot', async function () { + try { + const percentDifference = await screenshot.compareAgainstBaseline( + 'metricbeat_dashboard', + updateBaselines + ); + expect(percentDifference).to.be.lessThan(0.01); + } finally { + await PageObjects.dashboard.clickExitFullScreenLogoButton(); + } + }); + }); +} diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/index.js b/x-pack/test/stack_functional_integration/apps/metricbeat/index.js similarity index 86% rename from x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/index.js rename to x-pack/test/stack_functional_integration/apps/metricbeat/index.js index d45d6c835a315c..148762c6a8b776 100644 --- a/x-pack/test/stack_functional_integration/test/functional/apps/metricbeat/index.js +++ b/x-pack/test/stack_functional_integration/apps/metricbeat/index.js @@ -7,5 +7,6 @@ export default function ({ loadTestFile }) { describe('metricbeat app', function () { loadTestFile(require.resolve('./_metricbeat')); + loadTestFile(require.resolve('./_metricbeat_dashboard')); }); } diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/_monitoring.js b/x-pack/test/stack_functional_integration/apps/monitoring/_monitoring.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/monitoring/_monitoring.js rename to x-pack/test/stack_functional_integration/apps/monitoring/_monitoring.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/_monitoring_metricbeat.js b/x-pack/test/stack_functional_integration/apps/monitoring/_monitoring_metricbeat.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/monitoring/_monitoring_metricbeat.js rename to x-pack/test/stack_functional_integration/apps/monitoring/_monitoring_metricbeat.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/monitoring/index.js b/x-pack/test/stack_functional_integration/apps/monitoring/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/monitoring/index.js rename to x-pack/test/stack_functional_integration/apps/monitoring/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/_packetbeat.js b/x-pack/test/stack_functional_integration/apps/packetbeat/_packetbeat.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/_packetbeat.js rename to x-pack/test/stack_functional_integration/apps/packetbeat/_packetbeat.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/index.js b/x-pack/test/stack_functional_integration/apps/packetbeat/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/packetbeat/index.js rename to x-pack/test/stack_functional_integration/apps/packetbeat/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/index.js b/x-pack/test/stack_functional_integration/apps/reporting/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/reporting/index.js rename to x-pack/test/stack_functional_integration/apps/reporting/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher.js b/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher.js rename to x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher_png.js b/x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher_png.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/reporting/reporting_watcher_png.js rename to x-pack/test/stack_functional_integration/apps/reporting/reporting_watcher_png.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/reporting/util.js b/x-pack/test/stack_functional_integration/apps/reporting/util.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/reporting/util.js rename to x-pack/test/stack_functional_integration/apps/reporting/util.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/e_commerce.js b/x-pack/test/stack_functional_integration/apps/sample_data/e_commerce.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/sample_data/e_commerce.js rename to x-pack/test/stack_functional_integration/apps/sample_data/e_commerce.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/sample_data/index.js b/x-pack/test/stack_functional_integration/apps/sample_data/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/sample_data/index.js rename to x-pack/test/stack_functional_integration/apps/sample_data/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/_telemetry.js b/x-pack/test/stack_functional_integration/apps/telemetry/_telemetry.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/telemetry/_telemetry.js rename to x-pack/test/stack_functional_integration/apps/telemetry/_telemetry.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/telemetry/index.js b/x-pack/test/stack_functional_integration/apps/telemetry/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/telemetry/index.js rename to x-pack/test/stack_functional_integration/apps/telemetry/index.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/_winlogbeat.js b/x-pack/test/stack_functional_integration/apps/winlogbeat/_winlogbeat.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/_winlogbeat.js rename to x-pack/test/stack_functional_integration/apps/winlogbeat/_winlogbeat.js diff --git a/x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/index.js b/x-pack/test/stack_functional_integration/apps/winlogbeat/index.js similarity index 100% rename from x-pack/test/stack_functional_integration/test/functional/apps/winlogbeat/index.js rename to x-pack/test/stack_functional_integration/apps/winlogbeat/index.js diff --git a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js index 96d338a04b01b9..a838b129242a15 100644 --- a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js +++ b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js @@ -6,12 +6,12 @@ import { resolve } from 'path'; import buildState from './build_state'; -import { ToolingLog } from '@kbn/dev-utils'; +import { ToolingLog, REPO_ROOT } from '@kbn/dev-utils'; import chalk from 'chalk'; import { esTestConfig, kbnTestConfig } from '@kbn/test'; const reportName = 'Stack Functional Integration Tests'; -const testsFolder = '../test/functional/apps'; +const testsFolder = '../apps'; const log = new ToolingLog({ level: 'info', writeTo: process.stdout, @@ -19,13 +19,14 @@ const log = new ToolingLog({ log.info(`WORKSPACE in config file ${process.env.WORKSPACE}`); const stateFilePath = process.env.WORKSPACE ? `${process.env.WORKSPACE}/qa/envvars.sh` - : '../../../../../integration-test/qa/envvars.sh'; + : `${REPO_ROOT}/../integration-test/qa/envvars.sh`; const prepend = (testFile) => require.resolve(`${testsFolder}/${testFile}`); export default async ({ readConfigFile }) => { const defaultConfigs = await readConfigFile(require.resolve('../../functional/config')); const { tests, ...provisionedConfigs } = buildState(resolve(__dirname, stateFilePath)); + process.env.stack_functional_integration = true; const servers = { kibana: kbnTestConfig.getUrlParts(), @@ -43,6 +44,14 @@ export default async ({ readConfigFile }) => { // If we need to do things like disable animations, we can do it in configure_start_kibana.sh, in the provisioner...which lives in the integration-test private repo uiSettings: {}, security: { disableTestUser: true }, + // choose where screenshots should be saved + screenshots: { + directory: resolve(`${REPO_ROOT}/../integration-test`, 'test/screenshots'), + }, + // choose where esArchiver should load archives from + esArchiver: { + directory: resolve(`${REPO_ROOT}/../integration-test`, 'test/es_archives'), + }, }; return settings; }; @@ -55,7 +64,7 @@ function truncate(testPath) { return dropKibanaPath(testPath); } function highLight(testPath) { - const dropTestsPath = splitRight(/^.+test[\\/]functional[\\/]apps[\\/](.*)[\\/]/gm); + const dropTestsPath = splitRight(/^.+apps[\\/](.*)[\\/]/gm); const cleaned = dropTestsPath(testPath); const colored = chalk.greenBright.bold(cleaned); return testPath.replace(cleaned, colored); diff --git a/x-pack/test/stack_functional_integration/configs/tests_list.js b/x-pack/test/stack_functional_integration/configs/tests_list.js index 0d91a078b73fdf..44b622a8bc9c58 100644 --- a/x-pack/test/stack_functional_integration/configs/tests_list.js +++ b/x-pack/test/stack_functional_integration/configs/tests_list.js @@ -20,7 +20,11 @@ export default (envObj) => { } if (envObj.BEATS.includes('metricbeat')) { - xs.push('metricbeat'); + xs.push('metricbeat/_metricbeat'); + if (envObj.XPACK === 'YES') { + // the esArchive and dashboard png are specific to the default distribution (with XPACK) + xs.push('metricbeat/_metricbeat_dashboard'); + } } if (envObj.BEATS.includes('filebeat')) { xs.push('filebeat'); From 3078908093a32f4538e436ba227c650f745c5d33 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Thu, 1 Oct 2020 21:46:13 +0100 Subject: [PATCH 055/128] [Security Solution] Searchstrategy integration (#78147) * init tests * add integration test for topNflow search strategy * add integration tests * add more tests * more tests * fix types * fix types * fix integration test * fix types * rm grapgql * fix type * fixup * fix test error * fix integration test * skip failing test * fix integration * skip failing test * skip failing test * fix integration tests for kpi network * fix integration test * fix integration test * fix import * remove additional data Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../factory/hosts/details/helpers.ts | 9 +- .../hosts/last_first_seen/__mocks__/index.ts | 3 - .../factory/hosts/last_first_seen/index.ts | 1 - .../apis/security_solution/authentications.ts | 107 +++--- .../apis/security_solution/host_details.ts | 237 ++++++++++++ .../apis/security_solution/hosts.ts | 212 +++++------ .../apis/security_solution/index.js | 26 +- .../security_solution/kpi_host_details.ts | 185 ---------- .../apis/security_solution/kpi_hosts.ts | 265 ++++++++------ .../apis/security_solution/kpi_network.ts | 345 ++++++++++++------ .../apis/security_solution/network_details.ts | 80 ++-- .../apis/security_solution/network_dns.ts | 143 ++++---- .../security_solution/network_top_n_flow.ts | 301 ++++++++------- .../apis/security_solution/overview_host.ts | 55 +-- .../security_solution/overview_network.ts | 131 ++++--- .../apis/security_solution/sources.ts | 4 +- .../apis/security_solution/timeline.ts | 8 +- .../security_solution/timeline_details.ts | 223 +++++------ .../apis/security_solution/tls.ts | 243 ++++++------ .../security_solution/uncommon_processes.ts | 81 ++-- .../apis/security_solution/users.ts | 78 ++-- .../es_archives/filebeat/default/data.json.gz | Bin 711714 -> 711718 bytes .../packetbeat/default/data.json.gz | Bin 78494 -> 78491 bytes 23 files changed, 1438 insertions(+), 1299 deletions(-) create mode 100644 x-pack/test/api_integration/apis/security_solution/host_details.ts delete mode 100644 x-pack/test/api_integration/apis/security_solution/kpi_host_details.ts diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts index ed705e7f6ad56d..644278963742d3 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/details/helpers.ts @@ -7,6 +7,7 @@ import { set } from '@elastic/safer-lodash-set/fp'; import { get, has, head } from 'lodash/fp'; import { hostFieldsMap } from '../../../../../../common/ecs/ecs_fields'; import { HostItem } from '../../../../../../common/search_strategy/security_solution/hosts'; +import { toArray } from '../../../../helpers/to_array'; import { HostAggEsItem, HostBuckets, HostValue } from '../../../../../lib/hosts/types'; @@ -36,7 +37,10 @@ export const formatHostItem = (bucket: HostAggEsItem): HostItem => HOST_FIELDS.reduce<HostItem>((flattenedFields, fieldName) => { const fieldValue = getHostFieldValue(fieldName, bucket); if (fieldValue != null) { - return set(fieldName, fieldValue, flattenedFields); + if (fieldName === '_id') { + return set('_id', fieldValue, flattenedFields); + } + return set(fieldName, toArray(fieldValue), flattenedFields); } return flattenedFields; }, {}); @@ -72,6 +76,9 @@ const getHostFieldValue = (fieldName: string, bucket: HostAggEsItem): string | s case 'host.os.version': return get('os.hits.hits[0]._source.host.os.version', bucket) || null; } + } else if (aggField === '_id') { + const hostName = get(`host_name`, bucket); + return hostName ? getFirstItem(hostName) : null; } return null; }; 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 224dcd1f8de24e..00427863c5f4b0 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 @@ -57,9 +57,6 @@ export const formattedSearchStrategyResponse = { dsl: [ '{\n "allowNoIndices": true,\n "index": [\n "apm-*-transaction*",\n "auditbeat-*",\n "endgame-*",\n "filebeat-*",\n "logs-*",\n "packetbeat-*",\n "winlogbeat-*"\n ],\n "ignoreUnavailable": true,\n "body": {\n "docvalue_fields": [],\n "aggregations": {\n "firstSeen": {\n "min": {\n "field": "@timestamp"\n }\n },\n "lastSeen": {\n "max": {\n "field": "@timestamp"\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n {\n "term": {\n "host.name": "siem-kibana"\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": false\n }\n}', ], - response: [ - '{\n "isPartial": false,\n "isRunning": false,\n "rawResponse": {\n "took": 230,\n "timed_out": false,\n "_shards": {\n "total": 21,\n "successful": 21,\n "skipped": 0,\n "failed": 0\n },\n "hits": {\n "total": -1,\n "max_score": 0,\n "hits": []\n },\n "aggregations": {\n "lastSeen": {\n "value": 1599554931759,\n "value_as_string": "2020-09-08T08:48:51.759Z"\n },\n "firstSeen": {\n "value": 1591611722000,\n "value_as_string": "2020-06-08T10:22:02.000Z"\n }\n }\n },\n "total": 21,\n "loaded": 21\n}', - ], }, firstSeen: '2020-06-08T10:22:02.000Z', lastSeen: '2020-09-08T08:48:51.759Z', diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/index.ts index 56895583c2ae9b..ee97436d2653f5 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/hosts/last_first_seen/index.ts @@ -28,7 +28,6 @@ export const firstLastSeenHost: SecuritySolutionFactory<HostsQueries.firstLastSe const aggregations: HostAggEsItem = get('aggregations', response.rawResponse) || {}; const inspect = { dsl: [inspectStringifyObject(buildFirstLastSeenHostQuery(options))], - response: [inspectStringifyObject(response)], }; return { diff --git a/x-pack/test/api_integration/apis/security_solution/authentications.ts b/x-pack/test/api_integration/apis/security_solution/authentications.ts index d36f9aeaa8804f..c0a3570c9d8e2c 100644 --- a/x-pack/test/api_integration/apis/security_solution/authentications.ts +++ b/x-pack/test/api_integration/apis/security_solution/authentications.ts @@ -5,11 +5,8 @@ */ import expect from '@kbn/expect'; +import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; -// @ts-expect-error -import { authenticationsQuery } from '../../../../plugins/security_solution/public/hosts/containers/authentications/index.gql_query'; -// @ts-expect-error -import { GetAuthenticationsQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; const FROM = '2000-01-01T00:00:00.000Z'; @@ -22,70 +19,66 @@ const EDGE_LENGTH = 1; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); describe('authentications', () => { before(() => esArchiver.load('auditbeat/hosts')); after(() => esArchiver.unload('auditbeat/hosts')); - it('Make sure that we get Authentication data', () => { - return client - .query<GetAuthenticationsQuery.Query>({ - query: authenticationsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 1, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get Authentication data', async () => { + const { body: authentications } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.authentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const authentications = resp.data.source.Authentications; - expect(authentications.edges.length).to.be(EDGE_LENGTH); - expect(authentications.totalCount).to.be(TOTAL_COUNT); - expect(authentications.pageInfo.fakeTotalCount).to.equal(3); - }); + .expect(200); + + expect(authentications.edges.length).to.be(EDGE_LENGTH); + expect(authentications.totalCount).to.be(TOTAL_COUNT); + expect(authentications.pageInfo.fakeTotalCount).to.equal(3); }); - it('Make sure that pagination is working in Authentications query', () => { - return client - .query<GetAuthenticationsQuery.Query>({ - query: authenticationsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - pagination: { - activePage: 2, - cursorStart: 1, - fakePossibleCount: 5, - querySize: 2, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that pagination is working in Authentications query', async () => { + const { body: authentications } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.authentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + pagination: { + activePage: 2, + cursorStart: 1, + fakePossibleCount: 5, + querySize: 2, + }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const authentications = resp.data.source.Authentications; - expect(authentications.edges.length).to.be(EDGE_LENGTH); - expect(authentications.totalCount).to.be(TOTAL_COUNT); - expect(authentications.edges[0]!.node.lastSuccess!.host!.name).to.eql([HOST_NAME]); - }); + .expect(200); + + expect(authentications.edges.length).to.be(EDGE_LENGTH); + expect(authentications.totalCount).to.be(TOTAL_COUNT); + expect(authentications.edges[0]!.node.lastSuccess!.host!.name).to.eql([HOST_NAME]); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/host_details.ts b/x-pack/test/api_integration/apis/security_solution/host_details.ts new file mode 100644 index 00000000000000..9fe9df1fae5067 --- /dev/null +++ b/x-pack/test/api_integration/apis/security_solution/host_details.ts @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; +import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; + +export default function ({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const supertest = getService('supertest'); + + describe('Host Details', () => { + describe('With filebeat', () => { + before(() => esArchiver.load('filebeat/default')); + after(() => esArchiver.unload('filebeat/default')); + + const FROM = '2000-01-01T00:00:00.000Z'; + const TO = '3000-01-01T00:00:00.000Z'; + const expectedResult = { + isPartial: false, + isRunning: false, + rawResponse: { + took: 12, + timed_out: false, + _shards: { + total: 1, + successful: 1, + skipped: 0, + failed: 0, + }, + hits: { + total: 6157, + max_score: null, + hits: [], + }, + aggregations: { + cloud_instance_id: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + host_mac: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + host_ip: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '151.205.0.17', + doc_count: 1, + timestamp: { + value: 1549766627000, + value_as_string: '2019-02-10T02:43:47.000Z', + }, + }, + ], + }, + cloud_machine_type: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + cloud_region: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + host_os_version: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '9 (stretch)', + doc_count: 6157, + timestamp: { + value: 1549767613001, + value_as_string: '2019-02-10T03:00:13.001Z', + }, + }, + ], + }, + host_architecture: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'armv7l', + doc_count: 6157, + timestamp: { + value: 1549767613001, + value_as_string: '2019-02-10T03:00:13.001Z', + }, + }, + ], + }, + cloud_provider: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [], + }, + host_os_platform: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'raspbian', + doc_count: 6157, + timestamp: { + value: 1549767613001, + value_as_string: '2019-02-10T03:00:13.001Z', + }, + }, + ], + }, + host_os_name: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'Raspbian GNU/Linux', + doc_count: 6157, + timestamp: { + value: 1549767613001, + value_as_string: '2019-02-10T03:00:13.001Z', + }, + }, + ], + }, + host_os_family: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: '', + doc_count: 6157, + timestamp: { + value: 1549767613001, + value_as_string: '2019-02-10T03:00:13.001Z', + }, + }, + ], + }, + host_name: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'raspberrypi', + doc_count: 6157, + timestamp: { + value: 1549767613001, + value_as_string: '2019-02-10T03:00:13.001Z', + }, + }, + ], + }, + host_id: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [ + { + key: 'b19a781f683541a7a25ee345133aa399', + doc_count: 6157, + timestamp: { + value: 1549767613001, + value_as_string: '2019-02-10T03:00:13.001Z', + }, + }, + ], + }, + }, + }, + total: 1, + loaded: 1, + inspect: { + dsl: [ + '{\n "allowNoIndices": true,\n "index": [\n "filebeat-*"\n ],\n "ignoreUnavailable": true,\n "body": {\n "aggregations": {\n "host_architecture": {\n "terms": {\n "field": "host.architecture",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_id": {\n "terms": {\n "field": "host.id",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_ip": {\n "terms": {\n "field": "host.ip",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_mac": {\n "terms": {\n "field": "host.mac",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_name": {\n "terms": {\n "field": "host.name",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_family": {\n "terms": {\n "field": "host.os.family",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_name": {\n "terms": {\n "field": "host.os.name",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_platform": {\n "terms": {\n "field": "host.os.platform",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "host_os_version": {\n "terms": {\n "field": "host.os.version",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_instance_id": {\n "terms": {\n "field": "cloud.instance.id",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_machine_type": {\n "terms": {\n "field": "cloud.machine.type",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_provider": {\n "terms": {\n "field": "cloud.provider",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n },\n "cloud_region": {\n "terms": {\n "field": "cloud.region",\n "size": 10,\n "order": {\n "timestamp": "desc"\n }\n },\n "aggs": {\n "timestamp": {\n "max": {\n "field": "@timestamp"\n }\n }\n }\n }\n },\n "query": {\n "bool": {\n "filter": [\n {\n "term": {\n "host.name": "raspberrypi"\n }\n },\n {\n "range": {\n "@timestamp": {\n "format": "strict_date_optional_time",\n "gte": "2000-01-01T00:00:00.000Z",\n "lte": "3000-01-01T00:00:00.000Z"\n }\n }\n }\n ]\n }\n },\n "size": 0,\n "track_total_hits": false\n }\n}', + ], + }, + hostDetails: { + _id: 'raspberrypi', + host: { + architecture: ['armv7l'], + id: ['b19a781f683541a7a25ee345133aa399'], + ip: ['151.205.0.17'], + mac: [], + name: ['raspberrypi'], + os: { + family: [''], + name: ['Raspbian GNU/Linux'], + platform: ['raspbian'], + version: ['9 (stretch)'], + }, + }, + cloud: { + instance: { + id: [], + }, + machine: { + type: [], + }, + provider: [], + region: [], + }, + }, + }; + + it('Make sure that we get HostDetails data', async () => { + const { + body: { hostDetails }, + } = await supertest + .post('/internal/search/securitySolutionSearchStrategy') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.details, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + hostName: 'raspberrypi', + inspect: false, + }) + .expect(200); + expect(hostDetails).to.eql(expectedResult.hostDetails); + }); + }); + }); +} diff --git a/x-pack/test/api_integration/apis/security_solution/hosts.ts b/x-pack/test/api_integration/apis/security_solution/hosts.ts index 621718013db7f2..d48a021b02db39 100644 --- a/x-pack/test/api_integration/apis/security_solution/hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/hosts.ts @@ -5,17 +5,12 @@ */ import expect from '@kbn/expect'; - import { + HostsQueries, Direction, - GetHostOverviewQuery, - GetHostFirstLastSeenQuery, - GetHostsTableQuery, HostsFields, -} from '../../../../plugins/security_solution/public/graphql/types'; -import { HostOverviewQuery } from '../../../../plugins/security_solution/public/hosts/containers/hosts/details/host_overview.gql_query'; -import { HostFirstLastSeenGqlQuery } from '../../../../plugins/security_solution/public/hosts/containers/hosts/first_last_seen/first_last_seen.gql_query'; -import { HostsTableQuery } from '../../../../plugins/security_solution/public/hosts/containers/hosts/hosts_table.gql_query'; +} from '../../../../plugins/security_solution/common/search_strategy'; + import { FtrProviderContext } from '../../ftr_provider_context'; const FROM = '2000-01-01T00:00:00.000Z'; @@ -29,85 +24,77 @@ const CURSOR_ID = '2ab45fc1c41e4c84bbd02202a7e5761f'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); describe('hosts', () => { before(() => esArchiver.load('auditbeat/hosts')); after(() => esArchiver.unload('auditbeat/hosts')); - it('Make sure that we get Hosts Table data', () => { - return client - .query<GetHostsTableQuery.Query>({ - query: HostsTableQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - sort: { - field: HostsFields.lastSeen, - direction: Direction.asc, - }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 3, - querySize: 1, - }, - inspect: false, + it('Make sure that we get Hosts Table data', async () => { + const { body: hosts } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.hosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + sort: { + field: HostsFields.lastSeen, + direction: Direction.asc, }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 3, + querySize: 1, + }, + inspect: false, }) - .then((resp) => { - const hosts = resp.data.source.Hosts; - expect(hosts.edges.length).to.be(EDGE_LENGTH); - expect(hosts.totalCount).to.be(TOTAL_COUNT); - expect(hosts.pageInfo.fakeTotalCount).to.equal(3); - }); + .expect(200); + expect(hosts.edges.length).to.be(EDGE_LENGTH); + expect(hosts.totalCount).to.be(TOTAL_COUNT); + expect(hosts.pageInfo.fakeTotalCount).to.equal(3); }); - it('Make sure that pagination is working in Hosts Table query', () => { - return client - .query<GetHostsTableQuery.Query>({ - query: HostsTableQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - sort: { - field: HostsFields.lastSeen, - direction: Direction.asc, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - pagination: { - activePage: 2, - cursorStart: 1, - fakePossibleCount: 5, - querySize: 2, - }, - inspect: false, + it('Make sure that pagination is working in Hosts Table query', async () => { + const { body: hosts } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.hosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + sort: { + field: HostsFields.lastSeen, + direction: Direction.asc, + }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + pagination: { + activePage: 2, + cursorStart: 1, + fakePossibleCount: 5, + querySize: 2, }, + inspect: false, }) - .then((resp) => { - const hosts = resp.data.source.Hosts; - - expect(hosts.edges.length).to.be(EDGE_LENGTH); - expect(hosts.totalCount).to.be(TOTAL_COUNT); - expect(hosts.edges[0]!.node.host!.os!.name).to.eql([HOST_NAME]); - }); + .expect(200); + expect(hosts.edges.length).to.be(EDGE_LENGTH); + expect(hosts.totalCount).to.be(TOTAL_COUNT); + expect(hosts.edges[0]!.node.host!.os!.name).to.eql([HOST_NAME]); }); - it('Make sure that we get Host Overview data', () => { - const expectedHost: Omit<GetHostOverviewQuery.HostOverview, 'inspect'> = { + it('Make sure that we get Host details data', async () => { + const expectedHostDetails = { _id: 'zeek-sensor-san-francisco', - endpoint: null, host: { architecture: ['x86_64'], id: [CURSOR_ID], @@ -119,68 +106,59 @@ export default function ({ getService }: FtrProviderContext) { name: [HOST_NAME], platform: ['ubuntu'], version: ['18.04.2 LTS (Bionic Beaver)'], - __typename: 'OsEcsFields', }, - type: null, - __typename: 'HostEcsFields', }, cloud: { instance: { id: ['132972452'], - __typename: 'CloudInstance', }, machine: { type: [], - __typename: 'CloudMachine', }, provider: ['digitalocean'], region: ['sfo2'], - __typename: 'CloudFields', }, - __typename: 'HostItem', }; - - return client - .query<GetHostOverviewQuery.Query>({ - query: HostOverviewQuery, - variables: { - sourceId: 'default', - hostName: 'zeek-sensor-san-francisco', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + const { + body: { hostDetails }, + } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.details, + hostName: 'zeek-sensor-san-francisco', + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const hosts = resp.data.source.HostOverview; - expect(hosts).to.eql(expectedHost); - }); + .expect(200); + + expect(hostDetails).to.eql(expectedHostDetails); }); - it('Make sure that we get Last First Seen for a Host', () => { - return client - .query<GetHostFirstLastSeenQuery.Query>({ - query: HostFirstLastSeenGqlQuery, - variables: { - sourceId: 'default', - hostName: 'zeek-sensor-san-francisco', - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - }, + it('Make sure that we get Last First Seen for a Host', async () => { + const { body: firstLastSeenHost } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.firstLastSeen, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + hostName: 'zeek-sensor-san-francisco', }) - .then((resp) => { - const firstLastSeenHost = resp.data.source.HostFirstLastSeen; - expect(firstLastSeenHost).to.eql({ - __typename: 'FirstLastSeenHost', - firstSeen: '2019-02-19T19:36:23.561Z', - lastSeen: '2019-02-19T20:42:33.561Z', - }); - }); + .expect(200); + const expected = { + firstSeen: '2019-02-19T19:36:23.561Z', + lastSeen: '2019-02-19T20:42:33.561Z', + }; + + expect(firstLastSeenHost.firstSeen).to.eql(expected.firstSeen); + expect(firstLastSeenHost.lastSeen).to.eql(expected.lastSeen); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/index.js b/x-pack/test/api_integration/apis/security_solution/index.js index 3d24af44138007..b28ddf7efd575b 100644 --- a/x-pack/test/api_integration/apis/security_solution/index.js +++ b/x-pack/test/api_integration/apis/security_solution/index.js @@ -5,24 +5,26 @@ */ export default function ({ loadTestFile }) { - describe('Siem GraphQL Endpoints', () => { - // loadTestFile(require.resolve('./authentications')); + describe('SecuritySolution Endpoints', () => { + loadTestFile(require.resolve('./authentications')); loadTestFile(require.resolve('./hosts')); - // loadTestFile(require.resolve('./kpi_network')); - // loadTestFile(require.resolve('./kpi_hosts')); - // loadTestFile(require.resolve('./network_dns')); - // loadTestFile(require.resolve('./network_top_n_flow')); - // loadTestFile(require.resolve('./overview_host')); + loadTestFile(require.resolve('./host_details')); + loadTestFile(require.resolve('./kpi_network')); + loadTestFile(require.resolve('./kpi_hosts')); + loadTestFile(require.resolve('./network_details')); + loadTestFile(require.resolve('./network_dns')); + loadTestFile(require.resolve('./network_top_n_flow')); + loadTestFile(require.resolve('./overview_host')); + loadTestFile(require.resolve('./overview_network')); loadTestFile(require.resolve('./saved_objects/notes')); loadTestFile(require.resolve('./saved_objects/pinned_events')); loadTestFile(require.resolve('./saved_objects/timeline')); loadTestFile(require.resolve('./sources')); - // loadTestFile(require.resolve('./overview_network')); // loadTestFile(require.resolve('./timeline')); - // loadTestFile(require.resolve('./timeline_details')); - // loadTestFile(require.resolve('./uncommon_processes')); - // loadTestFile(require.resolve('./users')); - // loadTestFile(require.resolve('./tls')); + loadTestFile(require.resolve('./timeline_details')); + loadTestFile(require.resolve('./uncommon_processes')); + loadTestFile(require.resolve('./users')); + loadTestFile(require.resolve('./tls')); loadTestFile(require.resolve('./feature_controls')); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_host_details.ts b/x-pack/test/api_integration/apis/security_solution/kpi_host_details.ts deleted file mode 100644 index 27e4e02ee7d089..00000000000000 --- a/x-pack/test/api_integration/apis/security_solution/kpi_host_details.ts +++ /dev/null @@ -1,185 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -// @ts-expect-error -import { kpiHostDetailsQuery } from '../../../../plugins/security_solution/public/hosts/containers/kpi_host_details/index.gql_query'; -// @ts-expect-error -import { GetKpiHostDetailsQuery } from '../../../../plugins/security_solution/public/graphql/types'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export default function ({ getService }: FtrProviderContext) { - const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); - describe('Kpi Host Details', () => { - describe('With filebeat', () => { - before(() => esArchiver.load('filebeat/default')); - after(() => esArchiver.unload('filebeat/default')); - - const FROM = '2000-01-01T00:00:00.000Z'; - const TO = '3000-01-01T00:00:00.000Z'; - const expectedResult = { - __typename: 'KpiHostDetailsData', - authSuccess: 0, - authSuccessHistogram: null, - authFailure: 0, - authFailureHistogram: null, - uniqueSourceIps: 121, - uniqueSourceIpsHistogram: [ - { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 52, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 31, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 88, - __typename: 'KpiHostHistogramData', - }, - ], - uniqueDestinationIps: 154, - uniqueDestinationIpsHistogram: [ - { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 61, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 45, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 114, - __typename: 'KpiHostHistogramData', - }, - ], - }; - - it('Make sure that we get KpiHostDetails data', () => { - return client - .query<GetKpiHostDetailsQuery.Query>({ - query: kpiHostDetailsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - hostName: 'zeek-sensor-san-francisco', - docValueFields: [], - inspect: false, - }, - }) - .then((resp) => { - const kpiHosts = resp.data.source.KpiHostDetails; - expect(kpiHosts!).to.eql(expectedResult); - }); - }); - }); - - describe('With auditbeat', () => { - before(() => esArchiver.load('auditbeat/default')); - after(() => esArchiver.unload('auditbeat/default')); - - const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf(); - const TO = new Date('3000-01-01T00:00:00.000Z').valueOf(); - const expectedResult = { - __typename: 'KpiHostDetailsData', - authSuccess: 0, - authSuccessHistogram: null, - authFailure: 0, - authFailureHistogram: null, - uniqueSourceIps: 121, - uniqueSourceIpsHistogram: [ - { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 52, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 31, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 88, - __typename: 'KpiHostHistogramData', - }, - ], - uniqueDestinationIps: 154, - uniqueDestinationIpsHistogram: [ - { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 61, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 45, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 114, - __typename: 'KpiHostHistogramData', - }, - ], - }; - it('Make sure that we get KpiHostDetails data', () => { - return client - .query<GetKpiHostDetailsQuery.Query>({ - query: kpiHostDetailsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - hostName: 'zeek-sensor-san-francisco', - inspect: false, - }, - }) - .then((resp) => { - const kpiHosts = resp.data.source.KpiHostDetails; - expect(kpiHosts!).to.eql(expectedResult); - }); - }); - }); - }); -} diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts index 64109bd4d93218..b141087c4e3ba8 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_hosts.ts @@ -5,15 +5,13 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { kpiHostsQuery } from '../../../../plugins/security_solution/public/hosts/containers/kpi_hosts/index.gql_query'; -// @ts-expect-error -import { GetKpiHostsQuery } from '../../../../plugins/security_solution/public/graphql/types'; +import { HostsKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); + describe('Kpi Hosts', () => { describe('With filebeat', () => { before(() => esArchiver.load('filebeat/default')); @@ -22,28 +20,23 @@ export default function ({ getService }: FtrProviderContext) { const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; const expectedResult = { - __typename: 'KpiHostsData', hosts: 1, hostsHistogram: [ { x: new Date('2019-02-09T16:00:00.000Z').valueOf(), y: 1, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-09T19:00:00.000Z').valueOf(), y: 0, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-09T22:00:00.000Z').valueOf(), y: 1, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-10T01:00:00.000Z').valueOf(), y: 1, - __typename: 'KpiHostHistogramData', }, ], authSuccess: 0, @@ -55,22 +48,18 @@ export default function ({ getService }: FtrProviderContext) { { x: new Date('2019-02-09T16:00:00.000Z').valueOf(), y: 52, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-09T19:00:00.000Z').valueOf(), y: 0, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-09T22:00:00.000Z').valueOf(), y: 31, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-10T01:00:00.000Z').valueOf(), y: 88, - __typename: 'KpiHostHistogramData', }, ], uniqueDestinationIps: 154, @@ -78,46 +67,87 @@ export default function ({ getService }: FtrProviderContext) { { x: new Date('2019-02-09T16:00:00.000Z').valueOf(), y: 61, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-09T19:00:00.000Z').valueOf(), y: 0, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-09T22:00:00.000Z').valueOf(), y: 45, - __typename: 'KpiHostHistogramData', }, { x: new Date('2019-02-10T01:00:00.000Z').valueOf(), y: 114, - __typename: 'KpiHostHistogramData', }, ], }; - it('Make sure that we get KpiHosts data', () => { - return client - .query<GetKpiHostsQuery.Query>({ - query: kpiHostsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get KpiHosts data', async () => { + const { body: kpiHosts } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/hostsKpiHostsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + }); + + it('Make sure that we get KpiAuthentications data', async () => { + const { body } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/hostsKpiAuthenticationsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure!).to.eql(expectedResult.authSuccess); + expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + }); + + it('Make sure that we get KpiUniqueIps data', async () => { + const { body } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/hostsKpiUniqueIpsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const kpiHosts = resp.data.source.KpiHosts; - expect(kpiHosts!).to.eql(expectedResult); - }); + .expect(200); + expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram!).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); @@ -128,101 +158,108 @@ export default function ({ getService }: FtrProviderContext) { const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; const expectedResult = { - __typename: 'KpiHostsData', - hosts: 1, + hosts: 6, hostsHistogram: [ { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 1, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 1, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 1, - __typename: 'KpiHostHistogramData', - }, - ], - authSuccess: 0, - authSuccessHistogram: null, - authFailure: 0, - authFailureHistogram: null, - uniqueSourceIps: 121, - uniqueSourceIpsHistogram: [ - { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 52, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiHostHistogramData', + x: new Date('2018-11-27T00:00:00.000Z').valueOf(), + y: 6, }, { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 31, - __typename: 'KpiHostHistogramData', + x: new Date('2018-11-27T00:30:00.000Z').valueOf(), + y: 6, }, { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 88, - __typename: 'KpiHostHistogramData', + x: new Date('2018-11-27T01:00:00.000Z').valueOf(), + y: 6, }, - ], - uniqueDestinationIps: 154, - uniqueDestinationIpsHistogram: [ { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 61, - __typename: 'KpiHostHistogramData', + x: new Date('2018-11-27T01:30:00.000Z').valueOf(), + y: 6, }, { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiHostHistogramData', + x: new Date('2018-11-27T02:00:00.000Z').valueOf(), + y: 6, }, { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 45, - __typename: 'KpiHostHistogramData', - }, - { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 114, - __typename: 'KpiHostHistogramData', + x: new Date('2018-11-27T02:30:00.000Z').valueOf(), + y: 6, }, ], + authSuccess: null, + authSuccessHistogram: null, + authFailure: 0, + authFailureHistogram: null, + uniqueSourceIps: null, + uniqueSourceIpsHistogram: null, + uniqueDestinationIps: null, + uniqueDestinationIpsHistogram: null, }; - it('Make sure that we get KpiHosts data', () => { - return client - .query<GetKpiHostsQuery.Query>({ - query: kpiHostsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + + it('Make sure that we get KpiHosts data', async () => { + const { body: kpiHosts } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/hostsKpiHostsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsKpiQueries.kpiHosts, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiHosts.hostsHistogram!).to.eql(expectedResult.hostsHistogram); + expect(kpiHosts.hosts!).to.eql(expectedResult.hosts); + }); + + it('Make sure that we get KpiAuthentications data', async () => { + const { body } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/hostsKpiAuthenticationsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsKpiQueries.kpiAuthentications, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + expect(body.authenticationsSuccess!).to.eql(expectedResult.authSuccess); + expect(body.authenticationsSuccessHistogram!).to.eql(expectedResult.authSuccessHistogram); + expect(body.authenticationsFailure!).to.eql(expectedResult.authSuccess); + expect(body.authenticationsFailureHistogram!).to.eql(expectedResult.authFailureHistogram); + }); + + it('Make sure that we get KpiUniqueIps data', async () => { + const { body } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/hostsKpiUniqueIpsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsKpiQueries.kpiUniqueIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const kpiHosts = resp.data.source.KpiHosts; - expect(kpiHosts!).to.eql(expectedResult); - }); + .expect(200); + expect(body.uniqueDestinationIps!).to.eql(expectedResult.uniqueDestinationIps); + expect(body.uniqueDestinationIpsHistogram!).to.eql( + expectedResult.uniqueDestinationIpsHistogram + ); + expect(body.uniqueSourceIps!).to.eql(expectedResult.uniqueSourceIps); + expect(body.uniqueSourceIpsHistogram!).to.eql(expectedResult.uniqueSourceIpsHistogram); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts index 14b061d678898f..641e6658d28ccf 100644 --- a/x-pack/test/api_integration/apis/security_solution/kpi_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/kpi_network.ts @@ -5,15 +5,13 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { kpiNetworkQuery } from '../../../../plugins/security_solution/public/network/containers/kpi_network/index.gql_query'; -// @ts-expect-error -import { GetKpiNetworkQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { NetworkKpiQueries } from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); + describe('Kpi Network', () => { describe('With filebeat', () => { before(() => esArchiver.load('filebeat/default')); @@ -22,51 +20,42 @@ export default function ({ getService }: FtrProviderContext) { const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; const expectedResult = { - __typename: 'KpiNetworkData', - networkEvents: 6158, + networkEvents: 6157, uniqueFlowId: 712, uniqueSourcePrivateIps: 8, uniqueSourcePrivateIpsHistogram: [ { x: new Date('2019-02-09T16:00:00.000Z').valueOf(), y: 8, - __typename: 'KpiNetworkHistogramData', }, { x: new Date('2019-02-09T19:00:00.000Z').valueOf(), y: 0, - __typename: 'KpiNetworkHistogramData', }, { x: new Date('2019-02-09T22:00:00.000Z').valueOf(), y: 8, - __typename: 'KpiNetworkHistogramData', }, { x: new Date('2019-02-10T01:00:00.000Z').valueOf(), y: 7, - __typename: 'KpiNetworkHistogramData', }, ], uniqueDestinationPrivateIps: 9, uniqueDestinationPrivateIpsHistogram: [ { - __typename: 'KpiNetworkHistogramData', x: new Date('2019-02-09T16:00:00.000Z').valueOf(), y: 8, }, { - __typename: 'KpiNetworkHistogramData', x: new Date('2019-02-09T19:00:00.000Z').valueOf(), y: 0, }, { - __typename: 'KpiNetworkHistogramData', x: new Date('2019-02-09T22:00:00.000Z').valueOf(), y: 8, }, { - __typename: 'KpiNetworkHistogramData', x: new Date('2019-02-10T01:00:00.000Z').valueOf(), y: 8, }, @@ -75,26 +64,133 @@ export default function ({ getService }: FtrProviderContext) { tlsHandshakes: 62, }; - it('Make sure that we get KpiNetwork data', () => { - return client - .query<GetKpiNetworkQuery.Query>({ - query: kpiNetworkQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get KpiNetwork uniqueFlows data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiUniqueFlowsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.uniqueFlows, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); + }); + + it('Make sure that we get KpiNetwork networkEvents data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiNetworkEventsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.networkEvents, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const kpiNetwork = resp.data.source.KpiNetwork; - expect(kpiNetwork).to.eql(expectedResult); - }); + .expect(200); + + expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); + }); + + it('Make sure that we get KpiNetwork DNS data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiDnsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.dns, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); + }); + + it('Make sure that we get KpiNetwork networkEvents data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiNetworkEventsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.networkEvents, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); + }); + + it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiTlsHandshakesQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.tlsHandshakes, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); + }); + + it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiUniquePrivateIpsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.uniquePrivateIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( + expectedResult.uniqueDestinationPrivateIps + ); + expect(kpiNetwork.uniqueDestinationPrivateIpsHistogram).to.eql( + expectedResult.uniqueDestinationPrivateIpsHistogram + ); + expect(kpiNetwork.uniqueSourcePrivateIps).to.eql(expectedResult.uniqueSourcePrivateIps); + expect(kpiNetwork.uniqueSourcePrivateIpsHistogram).to.eql( + expectedResult.uniqueSourcePrivateIpsHistogram + ); }); }); @@ -105,78 +201,123 @@ export default function ({ getService }: FtrProviderContext) { const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; const expectedResult = { - __typename: 'KpiNetworkData', - networkEvents: 6158, - uniqueFlowId: 712, - uniqueSourcePrivateIps: 8, - uniqueSourcePrivateIpsHistogram: [ - { - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 8, - __typename: 'KpiNetworkHistogramData', - }, - { - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - __typename: 'KpiNetworkHistogramData', - }, - { - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 8, - __typename: 'KpiNetworkHistogramData', - }, - { - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 7, - __typename: 'KpiNetworkHistogramData', - }, - ], - uniqueDestinationPrivateIps: 9, - uniqueDestinationPrivateIpsHistogram: [ - { - __typename: 'KpiNetworkHistogramData', - x: new Date('2019-02-09T16:00:00.000Z').valueOf(), - y: 8, - }, - { - __typename: 'KpiNetworkHistogramData', - x: new Date('2019-02-09T19:00:00.000Z').valueOf(), - y: 0, - }, - { - __typename: 'KpiNetworkHistogramData', - x: new Date('2019-02-09T22:00:00.000Z').valueOf(), - y: 8, - }, - { - __typename: 'KpiNetworkHistogramData', - x: new Date('2019-02-10T01:00:00.000Z').valueOf(), - y: 8, - }, - ], - dnsQueries: 169, - tlsHandshakes: 62, + networkEvents: 665, + uniqueFlowId: 124, + uniqueSourcePrivateIps: null, + uniqueSourcePrivateIpsHistogram: null, + uniqueDestinationPrivateIps: null, + uniqueDestinationPrivateIpsHistogram: null, + dnsQueries: 0, + tlsHandshakes: 1, }; - it('Make sure that we get KpiNetwork data', () => { - return client - .query<GetKpiNetworkQuery.Query>({ - query: kpiNetworkQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + + it('Make sure that we get KpiNetwork uniqueFlows data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiUniqueFlowsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.uniqueFlows, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['packetbeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.uniqueFlowId).to.eql(expectedResult.uniqueFlowId); + }); + + it('Make sure that we get KpiNetwork DNS data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiDnsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.dns, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['packetbeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.dnsQueries).to.eql(expectedResult.dnsQueries); + }); + + it('Make sure that we get KpiNetwork networkEvents data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiNetworkEventsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.networkEvents, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + defaultIndex: ['packetbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const kpiNetwork = resp.data.source.KpiNetwork; - expect(kpiNetwork).to.eql(expectedResult); - }); + .expect(200); + + expect(kpiNetwork.networkEvents).to.eql(expectedResult.networkEvents); + }); + + it('Make sure that we get KpiNetwork tlsHandshakes data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiTlsHandshakesQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.tlsHandshakes, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['packetbeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.tlsHandshakes).to.eql(expectedResult.tlsHandshakes); + }); + + it('Make sure that we get KpiNetwork uniquePrivateIps data', async () => { + const { body: kpiNetwork } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/networkKpiUniquePrivateIpsQuery') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkKpiQueries.uniquePrivateIps, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['filebeat-*'], + docValueFields: [], + inspect: false, + }) + .expect(200); + + expect(kpiNetwork.uniqueDestinationPrivateIps).to.eql( + expectedResult.uniqueDestinationPrivateIps + ); + expect(kpiNetwork.uniqueDestinationPrivateIpsHistogram).to.eql( + expectedResult.uniqueDestinationPrivateIpsHistogram + ); + expect(kpiNetwork.uniqueSourcePrivateIps).to.eql(expectedResult.uniqueSourcePrivateIps); + expect(kpiNetwork.uniqueSourcePrivateIpsHistogram).to.eql( + expectedResult.uniqueSourcePrivateIpsHistogram + ); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_details.ts b/x-pack/test/api_integration/apis/security_solution/network_details.ts index 7b851e875454de..2b602760be3426 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_details.ts @@ -5,41 +5,37 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { ipOverviewQuery } from '../../../../plugins/security_solution/public/network/containers/details/index.gql_query'; -// @ts-expect-error -import { GetIpOverviewQuery } from '../../../../plugins/security_solution/public/graphql/types'; +import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; + import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); - describe('IP Overview', () => { + const supertest = getService('supertest'); + describe('Network details', () => { describe('With filebeat', () => { before(() => esArchiver.load('filebeat/default')); after(() => esArchiver.unload('filebeat/default')); - it('Make sure that we get KpiNetwork data', () => { - return client - .query<GetIpOverviewQuery.Query>({ - query: ipOverviewQuery, - variables: { - sourceId: 'default', - ip: '151.205.0.17', - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, - }, + it('Make sure that we get Network details data', async () => { + const { body } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + ip: '151.205.0.17', + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + factoryQueryType: NetworkQueries.details, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const ipOverview = resp.data.source.IpOverview; - expect(ipOverview!.source!.geo!.continent_name).to.be('North America'); - expect(ipOverview!.source!.geo!.location!.lat!).to.be(37.751); - expect(ipOverview!.host.os!.platform!).to.be('raspbian'); - expect(ipOverview!.destination!.geo!.continent_name).to.be('North America'); - expect(ipOverview!.destination!.geo!.location!.lat!).to.be(37.751); - expect(ipOverview!.host.os!.platform!).to.be('raspbian'); - }); + .expect(200); + + expect(body.networkDetails!.source!.geo!.continent_name).to.be('North America'); + expect(body.networkDetails!.source!.geo!.location!.lat!).to.be(37.751); + expect(body.networkDetails!.host.os!.platform!).to.be('raspbian'); + expect(body.networkDetails!.destination!.geo!.continent_name).to.be('North America'); + expect(body.networkDetails!.destination!.geo!.location!.lat!).to.be(37.751); + expect(body.networkDetails!.host.os!.platform!).to.be('raspbian'); }); }); @@ -47,24 +43,22 @@ export default function ({ getService }: FtrProviderContext) { before(() => esArchiver.load('packetbeat/default')); after(() => esArchiver.unload('packetbeat/default')); - it('Make sure that we get KpiNetwork data', () => { - return client - .query<GetIpOverviewQuery.Query>({ - query: ipOverviewQuery, - variables: { - sourceId: 'default', - ip: '185.53.91.88', - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, - }, + it('Make sure that we get Network details data', async () => { + const { body } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + ip: '185.53.91.88', + defaultIndex: ['packetbeat-*'], + factoryQueryType: NetworkQueries.details, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const ipOverview = resp.data.source.IpOverview; - expect(ipOverview!.host.id!).to.be('2ce8b1e7d69e4a1d9c6bcddc473da9d9'); - expect(ipOverview!.host.name!).to.be('zeek-sensor-amsterdam'); - expect(ipOverview!.host.os!.platform!).to.be('ubuntu'); - }); + .expect(200); + + expect(body.networkDetails!.host.id!).to.be('2ce8b1e7d69e4a1d9c6bcddc473da9d9'); + expect(body.networkDetails!.host.name!).to.be('zeek-sensor-amsterdam'); + expect(body.networkDetails!.host.os!.platform!).to.be('ubuntu'); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_dns.ts b/x-pack/test/api_integration/apis/security_solution/network_dns.ts index b53e2cc72853a2..806e0e60a69b20 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_dns.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_dns.ts @@ -5,20 +5,19 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { networkDnsQuery } from '../../../../plugins/security_solution/public/network/containers/network_dns/index.gql_query'; import { + NetworkQueries, + NetworkDnsEdges, Direction, - // @ts-expect-error - GetNetworkDnsQuery, - // @ts-expect-error NetworkDnsFields, -} from '../../../../plugins/security_solution/public/graphql/types'; +} from '../../../../plugins/security_solution/common/search_strategy'; + import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); + describe('Network DNS', () => { describe('With packetbeat', () => { before(() => esArchiver.load('packetbeat/dns')); @@ -27,79 +26,75 @@ export default function ({ getService }: FtrProviderContext) { const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; - it('Make sure that we get Dns data and sorting by uniqueDomains ascending', () => { - return client - .query<GetNetworkDnsQuery.Query>({ - query: networkDnsQuery, - variables: { - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, - isPtrIncluded: false, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.asc }, - sourceId: 'default', - stackByField: 'dns.question.registered_domain', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, + it('Make sure that we get Dns data and sorting by uniqueDomains ascending', async () => { + const { body: networkDns } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + docValueFields: [], + factoryQueryType: NetworkQueries.dns, + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + isPtrIncluded: false, + pagination: { activePage: 0, cursorStart: 0, fakePossibleCount: 30, querySize: 10 }, + sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.asc }, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, }) - .then((resp) => { - const networkDns = resp.data.source.NetworkDns; - expect(networkDns.edges.length).to.be(10); - expect(networkDns.totalCount).to.be(44); - // @ts-expect-error - expect(networkDns.edges.map((i) => i.node.dnsName).join(',')).to.be( - 'aaplimg.com,adgrx.com,akadns.net,akamaiedge.net,amazonaws.com,cbsistatic.com,cdn-apple.com,connman.net,crowbird.com,d1oxlq5h9kq8q5.cloudfront.net' - ); - expect(networkDns.pageInfo.fakeTotalCount).to.equal(30); - }); + .expect(200); + + expect(networkDns.edges.length).to.be(10); + expect(networkDns.totalCount).to.be(44); + expect(networkDns.edges.map((i: NetworkDnsEdges) => i.node.dnsName).join(',')).to.be( + 'aaplimg.com,adgrx.com,akadns.net,akamaiedge.net,amazonaws.com,cbsistatic.com,cdn-apple.com,connman.net,crowbird.com,d1oxlq5h9kq8q5.cloudfront.net' + ); + expect(networkDns.pageInfo.fakeTotalCount).to.equal(30); }); - it('Make sure that we get Dns data and sorting by uniqueDomains descending', () => { - return client - .query<GetNetworkDnsQuery.Query>({ - query: networkDnsQuery, - variables: { - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - isDnsHistogram: false, - inspect: false, - isPtrIncluded: false, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - sourceId: 'default', - sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.desc }, - stackByField: 'dns.question.registered_domain', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, + it('Make sure that we get Dns data and sorting by uniqueDomains descending', async () => { + const { body: networkDns } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + ip: '151.205.0.17', + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + factoryQueryType: NetworkQueries.dns, + docValueFields: [], + inspect: false, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, + }, + sort: { field: NetworkDnsFields.uniqueDomains, direction: Direction.desc }, + stackByField: 'dns.question.registered_domain', + timerange: { + interval: '12h', + to: TO, + from: FROM, }, }) - .then((resp) => { - const networkDns = resp.data.source.NetworkDns; - expect(networkDns.edges.length).to.be(10); - expect(networkDns.totalCount).to.be(44); - // @ts-expect-error - expect(networkDns.edges.map((i) => i.node.dnsName).join(',')).to.be( - 'nflxvideo.net,apple.com,netflix.com,samsungcloudsolution.com,samsungqbe.com,samsungelectronics.com,internetat.tv,samsungcloudsolution.net,samsungosp.com,cbsnews.com' - ); - expect(networkDns.pageInfo.fakeTotalCount).to.equal(30); - }); + .expect(200); + + expect(networkDns.edges.length).to.be(10); + expect(networkDns.totalCount).to.be(44); + expect(networkDns.edges.map((i: NetworkDnsEdges) => i.node.dnsName).join(',')).to.be( + 'nflxvideo.net,apple.com,netflix.com,samsungcloudsolution.com,samsungqbe.com,samsungelectronics.com,internetat.tv,samsungcloudsolution.net,samsungosp.com,cbsnews.com' + ); + expect(networkDns.pageInfo.fakeTotalCount).to.equal(30); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts index 81a1924019a553..abca6e03610977 100644 --- a/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts +++ b/x-pack/test/api_integration/apis/security_solution/network_top_n_flow.ts @@ -5,23 +5,22 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { networkTopNFlowQuery } from '../../../../plugins/security_solution/public/network/containers/network_top_n_flow/index.gql_query'; import { + NetworkQueries, + NetworkTopNFlowEdges, Direction, FlowTargetSourceDest, - // @ts-expect-error - GetNetworkTopNFlowQuery, - // @ts-expect-error NetworkTopTablesFields, -} from '../../../../plugins/security_solution/public/graphql/types'; +} from '../../../../plugins/security_solution/common/search_strategy'; + import { FtrProviderContext } from '../../ftr_provider_context'; const EDGE_LENGTH = 10; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); + describe('Network Top N Flow', () => { describe('With filebeat', () => { before(() => esArchiver.load('filebeat/default')); @@ -30,150 +29,180 @@ export default function ({ getService }: FtrProviderContext) { const FROM = '2019-02-09T01:57:24.870Z'; const TO = '2019-02-12T01:57:24.870Z'; - it('Make sure that we get Source NetworkTopNFlow data with bytes_in descending sort', () => { - return client - .query<GetNetworkTopNFlowQuery.Query>({ - query: networkTopNFlowQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - flowTarget: FlowTargetSourceDest.source, - sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.desc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 50, - querySize: 10, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get Source NetworkTopNFlow data with bytes_in descending sort', async () => { + const { body: networkTopNFlow } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + factoryQueryType: NetworkQueries.topNFlow, + flowTarget: FlowTargetSourceDest.source, + sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.desc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 50, + querySize: 10, }, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const networkTopNFlow = resp.data.source.NetworkTopNFlow; - expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); - expect(networkTopNFlow.totalCount).to.be(121); - // @ts-expect-error - expect(networkTopNFlow.edges.map((i) => i.node.source!.ip).join(',')).to.be( - '10.100.7.196,10.100.7.199,10.100.7.197,10.100.7.198,3.82.33.170,17.249.172.100,10.100.4.1,8.248.209.244,8.248.211.247,8.248.213.244' - ); - expect(networkTopNFlow.edges[0].node.destination).to.be(null); - expect(networkTopNFlow.edges[0].node.source!.flows).to.be(498); - expect(networkTopNFlow.edges[0].node.source!.destination_ips).to.be(132); - expect(networkTopNFlow.pageInfo.fakeTotalCount).to.equal(50); - }); + .expect(200); + + expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); + expect(networkTopNFlow.totalCount).to.be(121); + expect( + networkTopNFlow.edges.map((i: NetworkTopNFlowEdges) => i.node.source!.ip).join(',') + ).to.be( + '10.100.7.196,10.100.7.199,10.100.7.197,10.100.7.198,3.82.33.170,17.249.172.100,10.100.4.1,8.248.209.244,8.248.211.247,8.248.213.244' + ); + expect(networkTopNFlow.edges[0].node.destination).to.be(undefined); + expect(networkTopNFlow.edges[0].node.source!.flows).to.be(498); + expect(networkTopNFlow.edges[0].node.source!.destination_ips).to.be(132); + expect(networkTopNFlow.pageInfo.fakeTotalCount).to.equal(50); }); - it('Make sure that we get Source NetworkTopNFlow data with bytes_in ascending sort ', () => { - return client - .query<GetNetworkTopNFlowQuery.Query>({ - query: networkTopNFlowQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - flowTarget: FlowTargetSourceDest.source, - sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.asc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 50, - querySize: 10, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get Source NetworkTopNFlow data with bytes_in ascending sort ', async () => { + const { body: networkTopNFlow } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + factoryQueryType: 'topNFlow', + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + flowTarget: FlowTargetSourceDest.source, + sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.asc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 50, + querySize: 10, }, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const networkTopNFlow = resp.data.source.NetworkTopNFlow; - expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); - expect(networkTopNFlow.totalCount).to.be(121); - // @ts-expect-error - expect(networkTopNFlow.edges.map((i) => i.node.source!.ip).join(',')).to.be( - '8.248.209.244,8.248.211.247,8.248.213.244,8.248.223.246,8.250.107.245,8.250.121.236,8.250.125.244,8.253.38.231,8.253.157.112,8.253.157.240' - ); - expect(networkTopNFlow.edges[0].node.destination).to.be(null); - expect(networkTopNFlow.edges[0].node.source!.flows).to.be(12); - expect(networkTopNFlow.edges[0].node.source!.destination_ips).to.be(1); - expect(networkTopNFlow.pageInfo.fakeTotalCount).to.equal(50); - }); + .expect(200); + + expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); + expect(networkTopNFlow.totalCount).to.be(121); + expect( + networkTopNFlow.edges.map((i: NetworkTopNFlowEdges) => i.node.source!.ip).join(',') + ).to.be( + '8.248.209.244,8.248.211.247,8.248.213.244,8.248.223.246,8.250.107.245,8.250.121.236,8.250.125.244,8.253.38.231,8.253.157.112,8.253.157.240' + ); + expect(networkTopNFlow.edges[0].node.destination).to.be(undefined); + expect(networkTopNFlow.edges[0].node.source!.flows).to.be(12); + expect(networkTopNFlow.edges[0].node.source!.destination_ips).to.be(1); + expect(networkTopNFlow.pageInfo.fakeTotalCount).to.equal(50); }); - it('Make sure that we get Destination NetworkTopNFlow data', () => { - return client - .query<GetNetworkTopNFlowQuery.Query>({ - query: networkTopNFlowQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.desc }, - flowTarget: FlowTargetSourceDest.destination, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 50, - querySize: 10, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get Destination NetworkTopNFlow data', async () => { + const { body: networkTopNFlow } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + factoryQueryType: 'topNFlow', + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.desc }, + flowTarget: FlowTargetSourceDest.destination, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 50, + querySize: 10, }, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const networkTopNFlow = resp.data.source.NetworkTopNFlow; - expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); - expect(networkTopNFlow.totalCount).to.be(154); - expect(networkTopNFlow.edges[0].node.destination!.flows).to.be(19); - expect(networkTopNFlow.edges[0].node.destination!.source_ips).to.be(1); - expect(networkTopNFlow.edges[0].node.source).to.be(null); - expect(networkTopNFlow.pageInfo.fakeTotalCount).to.equal(50); - }); + .expect(200); + expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); + expect(networkTopNFlow.totalCount).to.be(154); + expect(networkTopNFlow.edges[0].node.destination!.flows).to.be(19); + expect(networkTopNFlow.edges[0].node.destination!.source_ips).to.be(1); + expect(networkTopNFlow.edges[0].node.source).to.be(undefined); + expect(networkTopNFlow.pageInfo.fakeTotalCount).to.equal(50); }); - it('Make sure that pagination is working in NetworkTopNFlow query', () => { - return client - .query<GetNetworkTopNFlowQuery.Query>({ - query: networkTopNFlowQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.desc }, - flowTarget: FlowTargetSourceDest.source, - pagination: { - activePage: 1, - cursorStart: 10, - fakePossibleCount: 50, - querySize: 20, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that pagination is working in NetworkTopNFlow query', async () => { + const { body: networkTopNFlow } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + factoryQueryType: 'topNFlow', + filterQuery: + '{"bool":{"must":[],"filter":[{"match_all":{}}],"should":[],"must_not":[]}}', + sort: { field: NetworkTopTablesFields.bytes_in, direction: Direction.desc }, + flowTarget: FlowTargetSourceDest.source, + pagination: { + activePage: 1, + cursorStart: 10, + fakePossibleCount: 50, + querySize: 20, + }, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const networkTopNFlow = resp.data.source.NetworkTopNFlow; + .expect(200); - expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); - expect(networkTopNFlow.totalCount).to.be(121); - expect(networkTopNFlow.edges[0].node.source!.ip).to.be('8.248.223.246'); - }); + expect(networkTopNFlow.edges.length).to.be(EDGE_LENGTH); + expect(networkTopNFlow.totalCount).to.be(121); + expect(networkTopNFlow.edges[0].node.source!.ip).to.be('8.248.223.246'); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_host.ts b/x-pack/test/api_integration/apis/security_solution/overview_host.ts index 0d648e665a9a9e..f3de9a6481b8f3 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_host.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_host.ts @@ -6,16 +6,13 @@ import expect from '@kbn/expect'; -import { DEFAULT_INDEX_PATTERN } from '../../../../plugins/security_solution/common/constants'; -// @ts-expect-error -import { overviewHostQuery } from '../../../../plugins/security_solution/public/overview/containers//overview_host/index.gql_query'; -// @ts-expect-error -import { GetOverviewHostQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); + describe('Overview Host', () => { describe('With auditbeat', () => { before(() => esArchiver.load('auditbeat/overview')); @@ -39,30 +36,36 @@ export default function ({ getService }: FtrProviderContext) { endgameSecurity: 4, filebeatSystemModule: 0, winlogbeatSecurity: 0, - winlogbeatMWSysmonOperational: 0, - __typename: 'OverviewHostData', + winlogbeatMWSysmonOperational: null, }; - it('Make sure that we get OverviewHost data', () => { - return client - .query<GetOverviewHostQuery.Query>({ - query: overviewHostQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: DEFAULT_INDEX_PATTERN, - docValueFields: [], - inspect: false, + it('Make sure that we get OverviewHost data', async () => { + const { + body: { overviewHost }, + } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: [ + 'apm-*-transaction*', + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + ], + factoryQueryType: HostsQueries.overview, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const overviewHost = resp.data.source.OverviewHost; - expect(overviewHost).to.eql(expectedResult); - }); + .expect(200); + expect(overviewHost).to.eql(expectedResult); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/overview_network.ts b/x-pack/test/api_integration/apis/security_solution/overview_network.ts index 60d300e168e4ad..f0b5c635c878cf 100644 --- a/x-pack/test/api_integration/apis/security_solution/overview_network.ts +++ b/x-pack/test/api_integration/apis/security_solution/overview_network.ts @@ -5,15 +5,13 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { overviewNetworkQuery } from '../../../../plugins/security_solution/public/overview/containers/overview_network/index.gql_query'; -// @ts-expect-error -import { GetOverviewNetworkQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { NetworkQueries } from '../../../../plugins/security_solution/common/search_strategy'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); + describe('Overview Network', () => { describe('With filebeat', () => { before(() => esArchiver.load('filebeat/default')); @@ -32,29 +30,27 @@ export default function ({ getService }: FtrProviderContext) { packetbeatDNS: 0, packetbeatFlow: 0, packetbeatTLS: 0, - __typename: 'OverviewNetworkData', }; - it('Make sure that we get OverviewNetwork data', () => { - return client - .query<GetOverviewNetworkQuery.Query>({ - query: overviewNetworkQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get OverviewNetwork data', async () => { + const { + body: { overviewNetwork }, + } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: ['filebeat-*'], + factoryQueryType: NetworkQueries.overview, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const overviewNetwork = resp.data.source.OverviewNetwork; - expect(overviewNetwork).to.eql(expectedResult); - }); + .expect(200); + expect(overviewNetwork).to.eql(expectedResult); }); }); @@ -67,36 +63,35 @@ export default function ({ getService }: FtrProviderContext) { const expectedResult = { auditbeatSocket: 0, filebeatCisco: 0, - filebeatNetflow: 1273, + filebeatNetflow: 0, filebeatPanw: 0, - filebeatSuricata: 4547, + filebeatSuricata: 0, filebeatZeek: 0, - packetbeatDNS: 0, - packetbeatFlow: 0, + packetbeatDNS: 44, + packetbeatFlow: 588, packetbeatTLS: 0, - __typename: 'OverviewNetworkData', }; - it('Make sure that we get OverviewNetwork data', () => { - return client - .query<GetOverviewNetworkQuery.Query>({ - query: overviewNetworkQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get OverviewNetwork data', async () => { + const { + body: { overviewNetwork }, + } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: ['packetbeat-*'], + factoryQueryType: NetworkQueries.overview, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const overviewNetwork = resp.data.source.OverviewNetwork; - expect(overviewNetwork).to.eql(expectedResult); - }); + .expect(200); + + expect(overviewNetwork).to.eql(expectedResult); }); }); @@ -107,38 +102,36 @@ export default function ({ getService }: FtrProviderContext) { const FROM = '2000-01-01T00:00:00.000Z'; const TO = '3000-01-01T00:00:00.000Z'; const expectedResult = { - auditbeatSocket: 0, + auditbeatSocket: 45, filebeatCisco: 0, - filebeatNetflow: 1273, + filebeatNetflow: 0, filebeatPanw: 0, - filebeatSuricata: 4547, + filebeatSuricata: 0, filebeatZeek: 0, packetbeatDNS: 0, packetbeatFlow: 0, packetbeatTLS: 0, - __typename: 'OverviewNetworkData', }; - it('Make sure that we get OverviewNetwork data', () => { - return client - .query<GetOverviewNetworkQuery.Query>({ - query: overviewNetworkQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Make sure that we get OverviewNetwork data', async () => { + const { + body: { overviewNetwork }, + } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + defaultIndex: ['auditbeat-*'], + factoryQueryType: NetworkQueries.overview, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + docValueFields: [], + inspect: false, }) - .then((resp) => { - const overviewNetwork = resp.data.source.OverviewNetwork; - expect(overviewNetwork).to.eql(expectedResult); - }); + .expect(200); + expect(overviewNetwork).to.eql(expectedResult); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/sources.ts b/x-pack/test/api_integration/apis/security_solution/sources.ts index 1ec4bfda8492de..228d0736f26d7c 100644 --- a/x-pack/test/api_integration/apis/security_solution/sources.ts +++ b/x-pack/test/api_integration/apis/security_solution/sources.ts @@ -35,12 +35,12 @@ export default function ({ getService }: FtrProviderContext) { .post('/internal/search/securitySolutionIndexFields/') .set('kbn-xsrf', 'true') .send({ - indices: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + indices: ['auditbeat-*', 'filebeat-*'], onlyCheckIfIndicesExist: false, }) .expect(200); - expect(sourceStatus.indicesExist).to.eql(['auditbeat-*', 'winlogbeat-*']); + expect(sourceStatus.indicesExist).to.eql(['auditbeat-*']); }); it('should not find indexes as existing when there is an empty array of them', async () => { diff --git a/x-pack/test/api_integration/apis/security_solution/timeline.ts b/x-pack/test/api_integration/apis/security_solution/timeline.ts index 8ae562a961431c..41b557ce3fd32d 100644 --- a/x-pack/test/api_integration/apis/security_solution/timeline.ts +++ b/x-pack/test/api_integration/apis/security_solution/timeline.ts @@ -6,13 +6,11 @@ import expect from '@kbn/expect'; +import { Direction } from '../../../../plugins/security_solution/common/search_strategy'; // @ts-expect-error import { timelineQuery } from '../../../../plugins/security_solution/public/timelines/containers/index.gql_query'; -import { - Direction, - // @ts-expect-error - GetTimelineQuery, -} from '../../../../plugins/security_solution/public/graphql/types'; +// @ts-expect-error +import { GetTimelineQuery } from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; const TO = '3000-01-01T00:00:00.000Z'; diff --git a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts index 559cdc8c29c09f..d3f40188aa6d31 100644 --- a/x-pack/test/api_integration/apis/security_solution/timeline_details.ts +++ b/x-pack/test/api_integration/apis/security_solution/timeline_details.ts @@ -6,92 +6,72 @@ import expect from '@kbn/expect'; import { sortBy } from 'lodash'; +import { TimelineEventsQueries } from '../../../../plugins/security_solution/common/search_strategy'; -// @ts-expect-error -import { timelineDetailsQuery } from '../../../../plugins/security_solution/public/timelines/containers/details/index.gql_query'; -import { - // @ts-expect-error - DetailItem, - // @ts-expect-error - GetTimelineDetailsQuery, -} from '../../../../plugins/security_solution/public/graphql/types'; import { FtrProviderContext } from '../../ftr_provider_context'; -type DetailsData = Array< - Pick<DetailItem, 'field' | 'values' | 'originalValue'> & { - __typename: string; - } ->; - // typical values that have to change after an update from "scripts/es_archiver" const INDEX_NAME = 'filebeat-7.0.0-iot-2019.06'; const ID = 'QRhG1WgBqd-n62SwZYDT'; -const EXPECTED_DATA: DetailItem[] = [ +const EXPECTED_DATA = [ { + category: 'base', field: '@timestamp', values: ['2019-02-10T02:39:44.107Z'], originalValue: '2019-02-10T02:39:44.107Z', }, - { field: '@version', values: ['1'], originalValue: '1' }, + { category: '@version', field: '@version', values: ['1'], originalValue: '1' }, { + category: 'agent', field: 'agent.ephemeral_id', values: ['909cd6a1-527d-41a5-9585-a7fb5386f851'], originalValue: '909cd6a1-527d-41a5-9585-a7fb5386f851', }, { + category: 'agent', field: 'agent.hostname', values: ['raspberrypi'], originalValue: 'raspberrypi', }, { + category: 'agent', field: 'agent.id', values: ['4d3ea604-27e5-4ec7-ab64-44f82285d776'], originalValue: '4d3ea604-27e5-4ec7-ab64-44f82285d776', }, + { category: 'agent', field: 'agent.type', values: ['filebeat'], originalValue: 'filebeat' }, + { category: 'agent', field: 'agent.version', values: ['7.0.0'], originalValue: '7.0.0' }, { - field: 'agent.type', - values: ['filebeat'], - originalValue: 'filebeat', - }, - { field: 'agent.version', values: ['7.0.0'], originalValue: '7.0.0' }, - { + category: 'destination', field: 'destination.domain', values: ['s3-iad-2.cf.dash.row.aiv-cdn.net'], originalValue: 's3-iad-2.cf.dash.row.aiv-cdn.net', }, { + category: 'destination', field: 'destination.ip', values: ['10.100.7.196'], originalValue: '10.100.7.196', }, - { field: 'destination.port', values: ['40684'], originalValue: 40684 }, - { - field: 'ecs.version', - values: ['1.0.0-beta2'], - originalValue: '1.0.0-beta2', - }, + { category: 'destination', field: 'destination.port', values: [40684], originalValue: 40684 }, + { category: 'ecs', field: 'ecs.version', values: ['1.0.0-beta2'], originalValue: '1.0.0-beta2' }, { + category: 'event', field: 'event.dataset', values: ['suricata.eve'], originalValue: 'suricata.eve', }, { + category: 'event', field: 'event.end', values: ['2019-02-10T02:39:44.107Z'], originalValue: '2019-02-10T02:39:44.107Z', }, - { field: 'event.kind', values: ['event'], originalValue: 'event' }, - { - field: 'event.module', - values: ['suricata'], - originalValue: 'suricata', - }, - { - field: 'event.type', - values: ['fileinfo'], - originalValue: 'fileinfo', - }, + { category: 'event', field: 'event.kind', values: ['event'], originalValue: 'event' }, + { category: 'event', field: 'event.module', values: ['suricata'], originalValue: 'suricata' }, + { category: 'event', field: 'event.type', values: ['fileinfo'], originalValue: 'fileinfo' }, { + category: 'file', field: 'file.path', values: [ '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', @@ -99,179 +79,157 @@ const EXPECTED_DATA: DetailItem[] = [ originalValue: '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', }, - { field: 'file.size', values: ['48277'], originalValue: 48277 }, - { field: 'fileset.name', values: ['eve'], originalValue: 'eve' }, - { field: 'flow.locality', values: ['public'], originalValue: 'public' }, - { - field: 'host.architecture', - values: ['armv7l'], - originalValue: 'armv7l', - }, + { category: 'file', field: 'file.size', values: [48277], originalValue: 48277 }, + { category: 'fileset', field: 'fileset.name', values: ['eve'], originalValue: 'eve' }, + { category: 'flow', field: 'flow.locality', values: ['public'], originalValue: 'public' }, + { category: 'host', field: 'host.architecture', values: ['armv7l'], originalValue: 'armv7l' }, { + category: 'host', field: 'host.hostname', values: ['raspberrypi'], originalValue: 'raspberrypi', }, { + category: 'host', field: 'host.id', values: ['b19a781f683541a7a25ee345133aa399'], originalValue: 'b19a781f683541a7a25ee345133aa399', }, + { category: 'host', field: 'host.name', values: ['raspberrypi'], originalValue: 'raspberrypi' }, + { category: 'host', field: 'host.os.codename', values: ['stretch'], originalValue: 'stretch' }, + { category: 'host', field: 'host.os.family', values: [''], originalValue: '' }, { - field: 'host.name', - values: ['raspberrypi'], - originalValue: 'raspberrypi', - }, - { - field: 'host.os.codename', - values: ['stretch'], - originalValue: 'stretch', - }, - { field: 'host.os.family', values: [''], originalValue: '' }, - { + category: 'host', field: 'host.os.kernel', values: ['4.14.50-v7+'], originalValue: '4.14.50-v7+', }, { + category: 'host', field: 'host.os.name', values: ['Raspbian GNU/Linux'], originalValue: 'Raspbian GNU/Linux', }, + { category: 'host', field: 'host.os.platform', values: ['raspbian'], originalValue: 'raspbian' }, { - field: 'host.os.platform', - values: ['raspbian'], - originalValue: 'raspbian', - }, - { + category: 'host', field: 'host.os.version', values: ['9 (stretch)'], originalValue: '9 (stretch)', }, - { field: 'http.request.method', values: ['get'], originalValue: 'get' }, - { - field: 'http.response.body.bytes', - values: ['48277'], - originalValue: 48277, - }, - { - field: 'http.response.status_code', - values: ['206'], - originalValue: 206, - }, - { field: 'input.type', values: ['log'], originalValue: 'log' }, + { category: 'http', field: 'http.request.method', values: ['get'], originalValue: 'get' }, + { category: 'http', field: 'http.response.body.bytes', values: [48277], originalValue: 48277 }, + { category: 'http', field: 'http.response.status_code', values: [206], originalValue: 206 }, + { category: 'input', field: 'input.type', values: ['log'], originalValue: 'log' }, { + category: 'base', field: 'labels.pipeline', values: ['filebeat-7.0.0-suricata-eve-pipeline'], originalValue: 'filebeat-7.0.0-suricata-eve-pipeline', }, { + category: 'log', field: 'log.file.path', values: ['/var/log/suricata/eve.json'], originalValue: '/var/log/suricata/eve.json', }, + { category: 'log', field: 'log.offset', values: [1856288115], originalValue: 1856288115 }, + { category: 'network', field: 'network.name', values: ['iot'], originalValue: 'iot' }, + { category: 'network', field: 'network.protocol', values: ['http'], originalValue: 'http' }, + { category: 'network', field: 'network.transport', values: ['tcp'], originalValue: 'tcp' }, + { category: 'service', field: 'service.type', values: ['suricata'], originalValue: 'suricata' }, + { category: 'source', field: 'source.as.num', values: [16509], originalValue: 16509 }, { - field: 'log.offset', - values: ['1856288115'], - originalValue: 1856288115, - }, - { field: 'network.name', values: ['iot'], originalValue: 'iot' }, - { field: 'network.protocol', values: ['http'], originalValue: 'http' }, - { field: 'network.transport', values: ['tcp'], originalValue: 'tcp' }, - { - field: 'service.type', - values: ['suricata'], - originalValue: 'suricata', - }, - { field: 'source.as.num', values: ['16509'], originalValue: 16509 }, - { + category: 'source', field: 'source.as.org', values: ['Amazon.com, Inc.'], originalValue: 'Amazon.com, Inc.', }, { + category: 'source', field: 'source.domain', values: ['server-54-239-219-210.jfk51.r.cloudfront.net'], originalValue: 'server-54-239-219-210.jfk51.r.cloudfront.net', }, { + category: 'source', field: 'source.geo.city_name', values: ['Seattle'], originalValue: 'Seattle', }, { + category: 'source', field: 'source.geo.continent_name', values: ['North America'], originalValue: 'North America', }, + { category: 'source', field: 'source.geo.country_iso_code', values: ['US'], originalValue: 'US' }, { - field: 'source.geo.country_iso_code', - values: ['US'], - originalValue: 'US', - }, - { + category: 'source', field: 'source.geo.location.lat', - values: ['47.6103'], + values: [47.6103], originalValue: 47.6103, }, { + category: 'source', field: 'source.geo.location.lon', - values: ['-122.3341'], + values: [-122.3341], originalValue: -122.3341, }, { + category: 'source', field: 'source.geo.region_iso_code', values: ['US-WA'], originalValue: 'US-WA', }, { + category: 'source', field: 'source.geo.region_name', values: ['Washington'], originalValue: 'Washington', }, { + category: 'source', field: 'source.ip', values: ['54.239.219.210'], originalValue: '54.239.219.210', }, - { field: 'source.port', values: ['80'], originalValue: 80 }, + { category: 'source', field: 'source.port', values: [80], originalValue: 80 }, { + category: 'suricata', field: 'suricata.eve.fileinfo.state', values: ['CLOSED'], originalValue: 'CLOSED', }, + { category: 'suricata', field: 'suricata.eve.fileinfo.tx_id', values: [301], originalValue: 301 }, { - field: 'suricata.eve.fileinfo.tx_id', - values: ['301'], - originalValue: 301, - }, - { + category: 'suricata', field: 'suricata.eve.flow_id', - values: ['196625917175466'], + values: [196625917175466], originalValue: 196625917175466, }, { + category: 'suricata', field: 'suricata.eve.http.http_content_type', values: ['video/mp4'], originalValue: 'video/mp4', }, { + category: 'suricata', field: 'suricata.eve.http.protocol', values: ['HTTP/1.1'], originalValue: 'HTTP/1.1', }, + { category: 'suricata', field: 'suricata.eve.in_iface', values: ['eth0'], originalValue: 'eth0' }, + { category: 'base', field: 'tags', values: ['suricata'], originalValue: ['suricata'] }, { - field: 'suricata.eve.in_iface', - values: ['eth0'], - originalValue: 'eth0', - }, - { field: 'tags', values: ['suricata'], originalValue: ['suricata'] }, - { + category: 'url', field: 'url.domain', values: ['s3-iad-2.cf.dash.row.aiv-cdn.net'], originalValue: 's3-iad-2.cf.dash.row.aiv-cdn.net', }, { + category: 'url', field: 'url.original', values: [ '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', @@ -280,6 +238,7 @@ const EXPECTED_DATA: DetailItem[] = [ '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', }, { + category: 'url', field: 'url.path', values: [ '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', @@ -288,48 +247,48 @@ const EXPECTED_DATA: DetailItem[] = [ '/dm/2$XTMWANo0Q2RZKlH-95UoAahZrOg~/0a9a/bf72/e1da/4c20-919e-0cbabcf7bfe8/75f50c57-d25f-4e97-9e37-01b9f5caa293_audio_13.mp4', }, { + category: '_index', field: '_index', values: ['filebeat-7.0.0-iot-2019.06'], originalValue: 'filebeat-7.0.0-iot-2019.06', }, { + category: '_id', field: '_id', values: ['QRhG1WgBqd-n62SwZYDT'], originalValue: 'QRhG1WgBqd-n62SwZYDT', }, - { field: '_score', values: ['1'], originalValue: 1 }, + { category: '_score', field: '_score', values: [1], originalValue: 1 }, ]; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); describe('Timeline Details', () => { before(() => esArchiver.load('filebeat/default')); after(() => esArchiver.unload('filebeat/default')); - it('Make sure that we get Event Details data', () => { - return client - .query<GetTimelineDetailsQuery.Query>({ - query: timelineDetailsQuery, - variables: { - sourceId: 'default', - indexName: INDEX_NAME, - eventId: ID, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - }, + it('Make sure that we get Event Details data', async () => { + const { + body: { data: detailsData }, + } = await supertest + .post('/internal/search/securitySolutionTimelineSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: TimelineEventsQueries.details, + docValueFields: [], + indexName: INDEX_NAME, + inspect: false, + eventId: ID, + }) + .expect(200); + expect( + sortBy(detailsData, 'name').map((item) => { + const { __typename, ...rest } = item; + return rest; }) - .then((resp) => { - const detailsData: DetailsData = (resp.data.source.TimelineDetails.data || - []) as DetailsData; - expect( - sortBy(detailsData, 'name').map((item) => { - const { __typename, ...rest } = item; - return rest; - }) - ).to.eql(sortBy(EXPECTED_DATA, 'name')); - }); + ).to.eql(sortBy(EXPECTED_DATA, 'name')); }); }); } diff --git a/x-pack/test/api_integration/apis/security_solution/tls.ts b/x-pack/test/api_integration/apis/security_solution/tls.ts index ebaec7783427ff..164de4d095b97e 100644 --- a/x-pack/test/api_integration/apis/security_solution/tls.ts +++ b/x-pack/test/api_integration/apis/security_solution/tls.ts @@ -5,16 +5,13 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { tlsQuery } from '../../../../plugins/security_solution/public/network/containers/tls/index.gql_query'; import { + NetworkQueries, Direction, - // @ts-expect-error - TlsFields, + NetworkTlsFields, FlowTarget, - // @ts-expect-error - GetTlsQuery, -} from '../../../../plugins/security_solution/public/graphql/types'; +} from '../../../../plugins/security_solution/common/search_strategy'; + import { FtrProviderContext } from '../../ftr_provider_context'; const FROM = '2000-01-01T00:00:00.000Z'; @@ -23,7 +20,6 @@ const SOURCE_IP = '10.128.0.35'; const DESTINATION_IP = '74.125.129.95'; const expectedResult = { - __typename: 'TlsNode', _id: '16989191B1A93ECECD5FE9E63EBD4B5C3B606D26', subjects: ['CN=edgecert.googleapis.com,O=Google LLC,L=Mountain View,ST=California,C=US'], issuers: ['CN=GTS CA 1O1,O=Google Trust Services,C=US'], @@ -32,16 +28,13 @@ const expectedResult = { }; const expectedOverviewDestinationResult = { - __typename: 'TlsData', edges: [ { - __typename: 'TlsEdges', cursor: { - __typename: 'CursorType', + tiebreaker: null, value: 'EB4E81DD7C55BA9715652ECF5647FB8877E55A8F', }, node: { - __typename: 'TlsNode', _id: 'EB4E81DD7C55BA9715652ECF5647FB8877E55A8F', subjects: [ 'CN=*.cdn.mozilla.net,OU=Cloud Services,O=Mozilla Corporation,L=Mountain View,ST=California,C=US', @@ -53,7 +46,6 @@ const expectedOverviewDestinationResult = { }, ], pageInfo: { - __typename: 'PageInfoPaginated', activePage: 0, fakeTotalCount: 3, showMorePagesIndicator: false, @@ -62,16 +54,13 @@ const expectedOverviewDestinationResult = { }; const expectedOverviewSourceResult = { - __typename: 'TlsData', edges: [ { - __typename: 'TlsEdges', cursor: { - __typename: 'CursorType', + tiebreaker: null, value: 'EB4E81DD7C55BA9715652ECF5647FB8877E55A8F', }, node: { - __typename: 'TlsNode', _id: 'EB4E81DD7C55BA9715652ECF5647FB8877E55A8F', subjects: [ 'CN=*.cdn.mozilla.net,OU=Cloud Services,O=Mozilla Corporation,L=Mountain View,ST=California,C=US', @@ -83,7 +72,6 @@ const expectedOverviewSourceResult = { }, ], pageInfo: { - __typename: 'PageInfoPaginated', activePage: 0, fakeTotalCount: 3, showMorePagesIndicator: false, @@ -93,76 +81,71 @@ const expectedOverviewSourceResult = { export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); + describe('Tls Test with Packetbeat', () => { describe('Tls Test', () => { before(() => esArchiver.load('packetbeat/tls')); after(() => esArchiver.unload('packetbeat/tls')); - it('Ensure data is returned for FlowTarget.Source', () => { - return client - .query<GetTlsQuery.Query>({ - query: tlsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - ip: SOURCE_IP, - flowTarget: FlowTarget.source, - sort: { field: TlsFields._id, direction: Direction.desc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Ensure data is returned for FlowTarget.Source', async () => { + const { body: tls } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkQueries.tls, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + ip: SOURCE_IP, + flowTarget: FlowTarget.source, + sort: { field: NetworkTlsFields._id, direction: Direction.desc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const tls = resp.data.source.Tls; - expect(tls.edges.length).to.be(1); - expect(tls.totalCount).to.be(1); - expect(tls.edges[0].node).to.eql(expectedResult); - }); + .expect(200); + expect(tls.edges.length).to.be(1); + expect(tls.totalCount).to.be(1); + expect(tls.edges[0].node).to.eql(expectedResult); }); - it('Ensure data is returned for FlowTarget.Destination', () => { - return client - .query<GetTlsQuery.Query>({ - query: tlsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - ip: DESTINATION_IP, - flowTarget: FlowTarget.destination, - sort: { field: TlsFields._id, direction: Direction.desc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Ensure data is returned for FlowTarget.Destination', async () => { + const { body: tls } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkQueries.tls, + timerange: { + interval: '12h', + to: TO, + from: FROM, }, + ip: DESTINATION_IP, + flowTarget: FlowTarget.destination, + sort: { field: NetworkTlsFields._id, direction: Direction.desc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, + }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const tls = resp.data.source.Tls; - expect(tls.edges.length).to.be(1); - expect(tls.totalCount).to.be(1); - expect(tls.edges[0].node).to.eql(expectedResult); - }); + .expect(200); + expect(tls.edges.length).to.be(1); + expect(tls.totalCount).to.be(1); + expect(tls.edges[0].node).to.eql(expectedResult); }); }); @@ -170,68 +153,62 @@ export default function ({ getService }: FtrProviderContext) { before(() => esArchiver.load('packetbeat/tls')); after(() => esArchiver.unload('packetbeat/tls')); - it('Ensure data is returned for FlowTarget.Source', () => { - return client - .query<GetTlsQuery.Query>({ - query: tlsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - ip: '', - flowTarget: FlowTarget.source, - sort: { field: TlsFields._id, direction: Direction.desc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Ensure data is returned for FlowTarget.Source', async () => { + const { body: tls } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkQueries.tls, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + ip: '', + flowTarget: FlowTarget.source, + sort: { field: NetworkTlsFields._id, direction: Direction.desc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const tls = resp.data.source.Tls; - expect(tls.pageInfo).to.eql(expectedOverviewSourceResult.pageInfo); - expect(tls.edges[0]).to.eql(expectedOverviewSourceResult.edges[0]); - }); + .expect(200); + expect(tls.pageInfo).to.eql(expectedOverviewSourceResult.pageInfo); + expect(tls.edges[0]).to.eql(expectedOverviewSourceResult.edges[0]); }); - it('Ensure data is returned for FlowTarget.Destination', () => { - return client - .query<GetTlsQuery.Query>({ - query: tlsQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - ip: '', - flowTarget: FlowTarget.destination, - sort: { field: TlsFields._id, direction: Direction.desc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - inspect: false, + it('Ensure data is returned for FlowTarget.Destination', async () => { + const { body: tls } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkQueries.tls, + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + ip: '', + flowTarget: FlowTarget.destination, + sort: { field: NetworkTlsFields._id, direction: Direction.desc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + inspect: false, }) - .then((resp) => { - const tls = resp.data.source.Tls; - expect(tls.pageInfo).to.eql(expectedOverviewDestinationResult.pageInfo); - expect(tls.edges[0]).to.eql(expectedOverviewDestinationResult.edges[0]); - }); + .expect(200); + expect(tls.pageInfo).to.eql(expectedOverviewDestinationResult.pageInfo); + expect(tls.edges[0]).to.eql(expectedOverviewDestinationResult.edges[0]); }); }); }); diff --git a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts index 1ed9a03ecf87ea..3eb1ada8da4594 100644 --- a/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts +++ b/x-pack/test/api_integration/apis/security_solution/uncommon_processes.ts @@ -6,10 +6,7 @@ import expect from '@kbn/expect'; -// @ts-expect-error -import { uncommonProcessesQuery } from '../../../../plugins/security_solution/public/hosts/containers/uncommon_processes/index.gql_query'; -// @ts-expect-error -import { GetUncommonProcessesQuery } from '../../../../plugins/security_solution/public/graphql/types'; +import { HostsQueries } from '../../../../plugins/security_solution/common/search_strategy'; import { FtrProviderContext } from '../../ftr_provider_context'; const FROM = '2000-01-01T00:00:00.000Z'; @@ -20,20 +17,18 @@ const TOTAL_COUNT = 3; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); describe('uncommon_processes', () => { before(() => esArchiver.load('auditbeat/hosts')); after(() => esArchiver.unload('auditbeat/hosts')); it('should return an edge of length 1 when given a pagination of length 1', async () => { - const { - data: { - source: { UncommonProcesses }, - }, - } = await client.query<GetUncommonProcessesQuery.Query>({ - query: uncommonProcessesQuery, - variables: { + const { body: UncommonProcesses } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.uncommonProcesses, sourceId: 'default', timerange: { interval: '12h', @@ -49,19 +44,17 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [], inspect: false, - }, - }); + }) + .expect(200); expect(UncommonProcesses.edges.length).to.be(1); }); it('should return an edge of length 2 when given a pagination of length 2', async () => { - const { - data: { - source: { UncommonProcesses }, - }, - } = await client.query<GetUncommonProcessesQuery.Query>({ - query: uncommonProcessesQuery, - variables: { + const { body: UncommonProcesses } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.uncommonProcesses, sourceId: 'default', timerange: { interval: '12h', @@ -77,19 +70,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [], inspect: false, - }, - }); + }) + .expect(200); + expect(UncommonProcesses.edges.length).to.be(2); }); it('should return a total count of elements', async () => { - const { - data: { - source: { UncommonProcesses }, - }, - } = await client.query<GetUncommonProcessesQuery.Query>({ - query: uncommonProcessesQuery, - variables: { + const { body: UncommonProcesses } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.uncommonProcesses, sourceId: 'default', timerange: { interval: '12h', @@ -105,19 +97,18 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [], inspect: false, - }, - }); + }) + .expect(200); + expect(UncommonProcesses.totalCount).to.be(TOTAL_COUNT); }); it('should return a single data set with pagination of 1', async () => { - const { - data: { - source: { UncommonProcesses }, - }, - } = await client.query<GetUncommonProcessesQuery.Query>({ - query: uncommonProcessesQuery, - variables: { + const { body: UncommonProcesses } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: HostsQueries.uncommonProcesses, sourceId: 'default', timerange: { interval: '12h', @@ -133,28 +124,26 @@ export default function ({ getService }: FtrProviderContext) { defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], docValueFields: [], inspect: false, - }, - }); - const expected: GetUncommonProcessesQuery.Node = { + }) + .expect(200); + + const expected = { _id: 'HCFxB2kBR346wHgnL4ik', instances: 1, process: { args: [], name: ['kworker/u2:0'], - __typename: 'ProcessEcsFields', }, user: { id: ['0'], name: ['root'], - __typename: 'UserEcsFields', }, hosts: [ { + id: ['zeek-sensor-san-francisco'], name: ['zeek-sensor-san-francisco'], - __typename: 'HostEcsFields', }, ], - __typename: 'UncommonProcessItem', }; expect(UncommonProcesses.edges[0].node).to.eql(expected); }); diff --git a/x-pack/test/api_integration/apis/security_solution/users.ts b/x-pack/test/api_integration/apis/security_solution/users.ts index 9d42fc0b9788ba..1b5b3604cb34f6 100644 --- a/x-pack/test/api_integration/apis/security_solution/users.ts +++ b/x-pack/test/api_integration/apis/security_solution/users.ts @@ -5,16 +5,13 @@ */ import expect from '@kbn/expect'; -// @ts-expect-error -import { usersQuery } from '../../../../plugins/security_solution/public/network/containers/users/index.gql_query'; import { + NetworkQueries, Direction, - // @ts-expect-error - UsersFields, + NetworkUsersFields, FlowTarget, - // @ts-expect-error - GetUsersQuery, -} from '../../../../plugins/security_solution/public/graphql/types'; +} from '../../../../plugins/security_solution/common/search_strategy'; + import { FtrProviderContext } from '../../ftr_provider_context'; const FROM = '2000-01-01T00:00:00.000Z'; @@ -23,47 +20,46 @@ const IP = '0.0.0.0'; export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); - const client = getService('securitySolutionGraphQLClient'); + const supertest = getService('supertest'); describe('Users', () => { describe('With auditbeat', () => { before(() => esArchiver.load('auditbeat/default')); after(() => esArchiver.unload('auditbeat/default')); - it('Ensure data is returned from auditbeat', () => { - return client - .query<GetUsersQuery.Query>({ - query: usersQuery, - variables: { - sourceId: 'default', - timerange: { - interval: '12h', - to: TO, - from: FROM, - }, - defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - docValueFields: [], - ip: IP, - flowTarget: FlowTarget.destination, - sort: { field: UsersFields.name, direction: Direction.asc }, - pagination: { - activePage: 0, - cursorStart: 0, - fakePossibleCount: 30, - querySize: 10, - }, - inspect: false, + it('Ensure data is returned from auditbeat', async () => { + const { body: users } = await supertest + .post('/internal/search/securitySolutionSearchStrategy/') + .set('kbn-xsrf', 'true') + .send({ + factoryQueryType: NetworkQueries.users, + sourceId: 'default', + timerange: { + interval: '12h', + to: TO, + from: FROM, + }, + defaultIndex: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + docValueFields: [], + ip: IP, + flowTarget: FlowTarget.destination, + sort: { field: NetworkUsersFields.name, direction: Direction.asc }, + pagination: { + activePage: 0, + cursorStart: 0, + fakePossibleCount: 30, + querySize: 10, }, + inspect: false, }) - .then((resp) => { - const users = resp.data.source.Users; - expect(users.edges.length).to.be(1); - expect(users.totalCount).to.be(1); - expect(users.edges[0].node.user!.id).to.eql(['0']); - expect(users.edges[0].node.user!.name).to.be('root'); - expect(users.edges[0].node.user!.groupId).to.eql(['0']); - expect(users.edges[0].node.user!.groupName).to.eql(['root']); - expect(users.edges[0].node.user!.count).to.be(1); - }); + .expect(200); + + expect(users.edges.length).to.be(1); + expect(users.totalCount).to.be(1); + expect(users.edges[0].node.user!.id).to.eql(['0']); + expect(users.edges[0].node.user!.name).to.be('root'); + expect(users.edges[0].node.user!.groupId).to.eql(['0']); + expect(users.edges[0].node.user!.groupName).to.eql(['root']); + expect(users.edges[0].node.user!.count).to.be(1); }); }); }); diff --git a/x-pack/test/functional/es_archives/filebeat/default/data.json.gz b/x-pack/test/functional/es_archives/filebeat/default/data.json.gz index bd73f4946575ba6170d1e890ebc3b22a49743a0d..1886818971deed4699a429d6496ff3e7dfa8bace 100644 GIT binary patch delta 142078 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 delta 141962 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 Faeoz-^Tc-np@l{rI`dzuB z-tgqk=hO*JKXbW{%o~{WjRnzuVI<oMD(zad7z)OZcjtz&8dh5hq7_CQY%3ILby-`G z{>E_R-;O1*7L0FBx)2=};;4-9GvsQ?maH-6SNa}b(uzU&eB<h(BzV&MdNBF8@B*;# z-EX%&I&QyQJuK{;t;?Gv+j1+3o&>jgk_`FRw?Y^a<LJIDcciHGt{>ltPo>yLk<Hiz z9h(&)6ncaZ+7D#L2dSwgMVw0pTnDO6hfj9mpG5l0>?MRW8p$O-OR|^Pe)~=S?2O2_ zF;(O4043%GReV}mnq!$N+>`@jZwu%!g4r`}fNz7_BW!5chiuS%((m4klV+e=%y>A} zN;(Qv-5|@t?yk*=;$4&#PqwH?MgCEhU#{nEkuumJV^>b?)>Wv-(ORcEOa?dnuwi7) zix`CCF#(B#5bD3nf*N)-UPB1oj~?oes(ql!=#3MGW%4uBfBz}rGcO{AI{`qil^|AN z^IAE?wA?@e^TiU@s{l!bb>>bCzr>?ei-MYCokbY9Sl<rH``x_E<x0&^$M3(!)r`5$ zLg~7!H2jlookdB;QJl6u*(A#ObC{8bi?(M+VLXjRDP?^JnTKODFVP&L05}m?Y*L6s z0R|rEaE+J9FiEEcJeFaY9KbT%jpv9RExro7CyCUN))~%6&BTn5V7P*HHz{bp<QgVF ze(Dg)7gDrJ{MG`$kxj!p&74|3^}_&(4$~BYU$I3|*{~G1IhFdnK?bTtepO4PH*W{n zbx{sr%u3QlAH%4a{9TH|)ry$WW&Z#h`^$_YhIA}fVcVc@qIYLv0dP}MEQh4d=W1T1 z%j{%OD;wRLg2S8$P+Wt`M<+Cg#ROS0CIaTYLkULEc%Wi^xpuIk(jvM^WKcxG@E1k8 zwEKxY^vYEcQ)kT50HHF5x}QcwMl><n;YeJI@E{wHSc;e$<f_2z<;L@T$-1H7?y05= zVq;Xlclh=2^aPv>6bohSU3!bRsp2oG*K%|CQZKEVNP!>k56nHT(4ui5Qq^$EK;W|f z`F39%v#cu&`EN^>g0#HR6oW!j#X)=jemUZbxp=t%@5rN?gZ~)gl(5`Q<8o8@Qs`fn zBy%ZgDt!4~>C%Mo39`G9s=F|TyUY!+InF*f$*!_-j=zgR>V^PWBUg7}I#PFI%?&<9 zdrSMgME*RCFfpVLNdQT}IAs7$DI{50nlLfH1yLc|?V8_ayT~LAEAgN=i5rRAIHg+> zRJ%%Bnu4X3XA4(XU<Of;8|t<{$Y@iy4TDsUvkQXV*2O#1mYls;cXv+!f#u#n86ca{ z?4zzpC9a$sm#1;Q5zHLEsC}MUo|$xsS)1sPJZT|!ahc-w@^N(VcLGs+)b76>rf=7m zg>2Jm-<9|uPf0H!T7ewGchFYQRwcStAsGI?d{EKbHbD9HGPJ#-XW%YU0CyaJ96x0u zKllYiBz9N3=#?3eY&9%?0`kCa3rz`4CCN@{W__A^&*;9ej?CFuU5|?&%o#RC!Ue$z z!KuQ;NgmeEb=5y)b{aNSr8<>Y2VIXl2nzF`K%)ck;juH@g>B4=t)RcK|4%bEPGhC4 z4|;8@lbA;Am8$G`|KS5beTEA`^nm)TLCe%C&k){1Gy*mKS1=C2(B8q3Q%$J<*G0pa zZx(@-c^|Sm2^#yU8EU4m&A-Mjjy9b4=DP+pE6(5rZ7uPiUHTWIfWovS=W0~u3<Nqr z13=C6&+yNnP0vum@&v<X;#v^>e}}-tX^a-2*9Qtkm-KJD|7!M)pxX}tJJS&~fZ~ku ze7paASCT|Qpz)BIz@V9Wg_$CE;3_B<MUYXdu&;|V?*brS&;RK%HKU$a>~Fh&z0v>G z<$H<0F1vq5C%rU9^EQNkMsHTR6s3L)rQ5bD3sIk*T#<f@RPjF}^v}@h$zXkb1DuW6 zSN?v40BAh_6Vrh*dqlxsr`+FgfBfyW9=8m)EZwObm+_5%u1`n)`yq~$z5U03d~D^o z^01BncxC>!oX-DyR=k}V<Hct0-(c%<Tx_d<PK>wV|LZNtP<abxQ5MF^EZOStx9V2Z zPcu0y@eOb*s?ZFNtP@pa_h#n}T=mQ!Bs_iR5=*u_L>w7ggI%Mgeo^__O?~h*W+coS zLSO5eI;-_s5T&bh7buHU^T6RqAXa~Sr5bj^z6V$dHwu0U%XC)E?It<2ei_xQBrR@1 zp(x%LvY7ZVTUCyt`3Y4@U>*HSr|X8DXE&bdQf5!s3ll_PZ~#=KL*(_+M7vV=f}PNm zZEr>=9`fTg+HLnGIWue2S5-CyRc7z$)ZsWb)5J^M_yxiuNTKePE(LnOhv!)YB6iM? zlJh`1eYqBGNTIN80X&XUTuJPF61rh`Rndlfxnb=b0uo14uvWvDRn2OfFDfcE{O}Cf z&gDkXbGLyboo-e+Dy9AO#GiL~o}b50PNaTebl!QMjBRq#S9s{yKB#_7+qtQ5GFc34 zKlqbnbtk@+59nK?c&gBEJ`$`%=G)6w&tL!;2suQp6)Cb-WAg6L{w)>a9HJM3w<|cz zo36=HXfMT!<C^WZ4Q=v(CbUYgC=1NM&f~)U5el_LuHI`((nO%)%*@TUabO!h0$ai- zCTFQK<rYP*LfObwHCp|jR}0!K1z+7i9^ZoY(jtl-1IhH%9-T^+z~jln$S12Z^Fv^Z zQLE&y49|@1@}bwqa?!9fte{BI=;T#2)J7}m?uXT<BooM1le{816?9q#N=ih?r1yms zR7p_M;FPu~lxUe!NYdJ22|@9Rc@&DumNZh9`JCpu^tUU}6krJvh*Cfi7o%3HwLgVA zWe`MylhFM5a&oyZ>#&3c$Fo)sfH^WtmZ(OB-q`7QffPvHmrm85QZNKJ|J%#=soKmF zG0GzpYY#8cQXRY$0Nw1R9>r~$rs3sVeDeFDS$*S-1E!V?OPSDX{aO0X{NlyeQ~+&C zFnhe&P5<OqNOB5p)L}zf3-3*7p_vpTVjv7~BV=#X&y3tXTpk7W`8$6bK%V3%<yhyN zmI#IvKqs7BRx}RHT4IxZyI*tCK^kuRXKEcFnj<2pjvsCcJO7n-Wx|)S7Dlt~@5PSu zg*mhpX2sU{L(X+;_qQx4-4lm{fy8_>r0KGY`4mdMccN1EoJ*=-u_Ff8PhAB79t?l) z8Nw)YMUr#RLPNYfs<5SF;0{`kUE?`f&el$e{38vftL`Kuc}~x*xIqL+*zM|k*`<4g z5_IXpWu~wP>AF7Lk3NSzR@Kuu3BSr(xz7&P>(KPReRLeUV{Zta9G;kbk*yORKzCuZ zF-UO~>4O$CfXJk;px7|vic4Ux`<i8AfH3~WmrGLiPjVet^?F1AfYb+V5a_YiSLWS+ zXbhnPPg%h9;K5bLOV5SB+CT4!VbjN;W8Mg+kA%<C;>g7!M8e6Al7*j6Rh`-=T)D-F z#o3f1gj8Li{E1x2^=E9c*I6F4nFgaw03MeVQsn-t>xa(n+f|lG_ymlH15>B`D8Jdz z*`17eHmV|yQ<!E`AkrYxmcC_h*r)O^N^Nh@*z^ve+^{Mks3ZebW-ZA{rd;GbpWIm! z4}2CILb#|fLe}6Zd{%}Vem~mh15pSGu5JWTtS{J-^Fppp*s!^v-=sksPAL0$x%xX4 zhQKC0kV${Wcnl70H^PQ>^?z@P4NIP-NS0KTGSxP}3ezA4qzxiONO^xr+b6U~Oq^s! zkri2n*2;7;u|>zg)56c&ozBk_3+z@h_!_JkM@JdoEG5A<#MZzS1z*58#lvnlh}D#) zn~=UpnumF=O<WtfgWe=k1<_2Ut%S~LhA($2X$z;fTO>az<Vs(@7n5;8oxs;`hQ;S4 zJZc{s7pu(xm^^v2<tRn}8sSfwAKR90Pf3~{J6RhFxX*&w^meN4_;<ZggfzDiDTqTR z?!7ckbx2$dmxLsqPdAsGq%>xF90v@kDMkVnI`yDmTuWFS85V*_Mr<5UBMmHEkgB(c z5ud^sKBh8_d@|K$1I2VG7#SvB{6pp^3Y~XAyy8uZsWqDPwi&tYUmAn}M1%0K5W~Rt zhoREAPpBN!3w_;C-?wEu+e!ilRq2K+zyF@9kH7-20tc1ql7iKYFz`T!!qf0o38`vr zb&Pe=Ii8V2Tt_y-(3G8fc&<n9)Jy7|QUfrp3G4c&U>xB!*IXtzl@`|<i~P?4lL=1N z&V!6P+#rFVr(frd)Z=FG(!QjW#QTJbJ|QXAI&)6Zhn0dG0U!W`!li^LGG-+_3)u%) z)JLGIr^AL%yW_dWyAUo+)l|U~Ht1cp40SRC(E+LHx}~4cRxA`ae}LeCwnSo_+><vr zpxrOMFU60U$}|L_R2i*78TgY?WAuOEfaq;^@95+1TzurGU$%96e#k93$T_HsZ+L(l zp;pwuz)(QCXCI@KQ`ph*%`f|kvD)E$7CZiAZoRF#Pp03IEt8!uR9{TZF8ukzv|Ci* z4uK)rmk8XVmX)fgjAv;M@S8+3Y3(;W0QfIHK;>!P4S$S|=|To8@j6x_ZU+78FeKu= zJqq!Nbjp5gIAwabAg|s+f-J>2gQA%~Q`rGcqolk=l9mu~q3xoMkx%3#95y|TnT;8= z#?!&1VdvPIk*7R}f+k479C>s^t9jqVq)7@{$EJ}!B>iNhe#`*SF>GD2c>G(*1qr!1 zY9frV$)3gWpITX?zu{1$cYIe27ynt%{`^m6N%O?Y4AZ)KJ$lu&2bZ`ueS#y96z6`y zpVZ^DP-^LkA=iOh#iXli`YDOv)XSzeq2=ZF@JV+0;4B>i(Ngba_SnSk+*GQ!>4Kws zdKL*dQeupvps*46o?><JQeD<QRT6&_SgI?1_hA?i-MJ|u4)3`X_FOL5rsTV43Y@8G zj?TEDP6(CI??Ko;x|i~j-08v9lK>SpL^~*T=czvL?Xg+aB->UcJ$%iy8lLC7*zM$H zhu<o5mNQTDpI-d^jfQihXJzzC{9EKO<q|aTJ8->BAEC$bK}}U^Dq$t0hI(e{;ve<4 z;@|2mYTUonTjn?QHbMPg>Mg?Nf2p?tMc-$_=`F;w!*Lb%{5GmxEpX{h!>ISN>#j)> zinY`r{j0n04&ZL>)?%o$)VJnp7Up9{0Z*Cr2C+p4B(vbC-OSAD)`7H+)m+{&6@*>e zk&EG2K6mJ6A})@&H2dCm+%HoFtaF-+yblK##|6Ig!0SZ%@6u;Omz!?K-+Nup-+q?% z>JJ?{4Dv;PTO1J5T0{rww`VbSr{!Rp0?lz!CEcC|5pXc5D<a>~im~XC*)W2Sfhxc2 ze+rYZ_0Qv-c7+eRP43>6Fv<it#{8(iz!I12tF5(uR)QF$MVtF!vKz>lK+?>QqX>nL zA%x7hjC#%%TAFHN<)bl-D#I07iEG`?K9_8oNF~1?hWd)G#Rs&&;;yfzGa7M0Y0D)y zt3Q_Am6GEZsnVVAA-mg84uFcla+$%_e>jeht`GUMwy7@Zm%D&PB~KCZFOVO%+WURW zZG;{Dw#nMbOE^|m3o|zhWpGy}MhTT<aA!*f)e~5!Igw{S{jhj+k}w~^FT|9_wSXG= zG^zepA-YDzBtQWpjaBF8r`h8ixklw7GQy7uCE+0g{~RT~JakMJ4M;X~f=WbqVL?I2 z+BGSZ`52kAlE;1!U;TIcvIs1sisPooFb6iPQz{X?{NK^5X!7n5R5@@kt-5@q-Yd)c zzVB=V9pEj`k&D60fdE3<h(@}SwMOX0XB{x_vlvH@B$LT=8!=(d{NscqF+@7nNl5cI z=;*d{C@7#rM27HT0l4&-Eh>s?o42|X0^K$Z-S%I$rWc(N@r8$Si<2}#Y)%mw)4t4| zUT#PSl%YX<&EE`74YR(P#z*%T*i*dz7qDmP52CF0)UGf0rt0An4M2SUhkTzx=l~aF z_-&Fs(mglDVT70dXnM&blI+Hu0B!^T%ATYs0d4+11Qw)ENJMh~)rF+Es9FD07mBte z&kTHlI>F<EeiP&Vay&+0>hC~w;7wn|n>qthY&5kX)fW+3N%HTil8Vj$f?$TpozeC1 zOUKPEYxC^mYynzgv>#+}M(vVwruZhUl|9Ab0F@x&kxwJ`Gqh<*TALwp`jVrC8|!vh zxl@jiKiMK@{s+o=XcRawAI5ix)CtLpLZRmR^o$#W!+3*Ms2|H6CbgR1=qNiMJWK&6 zn-MOB1x5vy*aDM?gQ@*45o>_L*3kf+#gd+dR$+nDigtSP!JK{!=LjziQkD*2qiPSg znIEEktjCg7Ruh?ZvHd7}njwR%-Q$Qkr8lF`GGr&AA02Mg>x2Jij0X5m?B#E=q%<Sq zD5f`MYGw7{@)v^rU`KOPsJyxPVmi~CtBVm9mVpLVt=@^&ImsQk$LYLgdi4YncvO?p zI|G7%o46dH%F6bL`~$nnFdyYL?WVV)8vQjV%J-<LUw<S%aWiuAc>Nxk%!htW3IcM3 zLdF=@CgxWxH!MA~^YN$4w99a&DwXhK-An(?zPSG%_Qm2q*_U{Q7ty`{VqX9d{E}Zz zNStsz&ZTE1*~MdC17*jM_P@z9TD56PgKqk7@|^#hJh^{{QQQ|eN;Q9d&f_{=39ygy z#gx;-@5rgD<c(jC_{+XDt}3foyXb=MgH!XOphxfhe(i=a7hJM~yC{-{WLN6=`OJN5 zLeWdC7sy+|@f=uRRW22mE+^9-<j~pU`+~_1u$9drR$dQWsPl86V*VUSG*n3ALy0Jw z(#L&b-jV4gH9TV6xDHyV;9viuy^hAV$4HBTO=k5KTK5y*)zroE#=x#|Iw+5BsYHGU z<x%-bZemNpLjXfznI<w*X@!9l<C|D^E5yZC>NOY~4zL;(vs%Gg=&2b&*o@G6wv5QU zqO!+=@Hik$R3!1s<M46`VnfzFs7)3<mZcfzPDLtty5MEUt9v3Q)to(mwKhC`tfIcd zv(OW_$Iax2FL<O>c`7C?thn_9;tovWS8wlwnU>$=xD=Xz=vRlBab#M>;fleAec8O9 z-4h0mX~6bbI2xCk;YG_<H?<K;<B?qmhjipFb?MFC<Y%~IPA&;c-QByqFl>uA3^G9` zK#q>Hu^?1FR9k(sg-OMqu5~UaV*@nL3EF0{*nOMk(DtTxrICCzzQj$DE+#W6ZsYoi z_+PUe*}~Y6VIVqs=WKf>8|Y7;rNb$iL^M{l{4ps(VS@KC`7i#FHpT+=W=AzqKl+nl z79VJW(=8%(0CvoqDu1AFwoFtOSZ2zfZ1AEnxnG+mY~?jI{-&ATE(%I*%-<Z1f2~bE z-h86X3KzW8urOt?0?lsLNL!njwHVfJ@;Z(g#!T(X6ul&XfRXwH%5;hrL^q#&ja~|w z(25EX+>b4Y#uE;-Er_Mm94&~Sfc#!GX^WfWSsZrr`Ti8cJ8tQp-2IN2tLy^7$(>03 z@ZUq<0OfE4vA4u@Y!9ia^-i5Z?(Hzi05e#x${=~HLNXS_rDg=GadkLqI9OudS#&q~ z8|(pgMte1?v)8lN8;y>slC-!%Pa-0EPQ!JMZmnED{+2FVY&T7LxqiUQagwGq&>p1p zUwf{$>KyxY-g$%8$3^_#@+eGPTC7<`#LcmmsHiyD_(;BHlMCx;YLk>U;=n_9ipDQU z!LXgMIq~VDS0pheP0=jqr~&_^Q{at@G%~E6H1n!AOZkpPS)9lcw<UctInJt}%r{ry z4dNHvZOCbxBQz#OM9hEl)lVy7c25TVT`3f-WeJR^*^CSmlAu>?Q~ia-MV2;7(4?+J z$P>WHOv#l^=-+`D=b$Nikl+z%4J8UrhDjW+niJ7ly~+eB0!|7<L&5NG<B-CY3xn{3 zPbh&6s$D}=9H2{)|7a2z#H~@D9OmhklbXl5<^U%?2>ea&8$WD0f;ENsdkL8Di)i>a z5{}RpF@KDlUAb-4idW-U2d`<`MRy~5qBYa%tB+Ah6FfLRj9)Vco|*%~wvkroEZU$6 zd%J;+^YE7DfVbptJ<Jg?GwWbWk5_X|aA2-8w+7<6iJ*<A^3e~>cNCVc`Dnu9=`^rt z;3OROVD0*ZO;=)eQ|igvijA{qHB7#dx6u}Q9!NMGy-5fG{O$ymS_!pWO%(Woval)p za#&g>^*LH8L@dlwuEhg=Q6yUt^o;YsL%*%;hi*qh(zW0K(vAvzABVNuOX?L!yBf1B zwMxw1tq^<JY3subTmSaxr;~-&$$t5ML0<6j!KjFq8>pnIJL1lTyHXr6ro|NjD@y_F z*Df2M-dEeZ-?q;`*~wuk)yyb4(<!{P47>g{?WI;_ypud!DvOrTwX5C&dNDa$*Y2XF zJ?1S%ckc3Vtx%@P_=1@NQ7W4|Kh5NdifY~+bneRK<Pt26%E_Aex5G%F$%_Wm_lvo8 z!Oy!#c-Az#5$K)3*jM0sv3_T`>WRwuk_c}*g(I7N;y7TwW%IbbSUtBNIb6ROMN||0 z(7cOJt&h3?S_oEk-JA~rjCEzK21rora6eZW*Z#JSj<IGsZnlaw)cl*dBezH8dX39? zP*1;JaMV~6&Xjdk4jTNz`(&?)zp=5n*hZpV>SF$#jIwGqgB_O1y{Qhr6|GtNKi3on zMz@FIN)$U11XfJ7{OS{igJ=vtdp2S=2s6>gU;+0`jVOEj*7f}Min7>T97CgrN%W?~ z2^9=#$pUfE9=tfAv_7|6KV4~-dDn7JP^{RqMKa(^%9Hl5a14FfPocpr=H-VSFKd2o zMEYKyb=vI>0Gl!|N_xLu=8s;zv1*N&NR2Lqh4P5hImM$VE;=|1*H6nwelrvdUhLgd zd;)5TR^9q52WdYEgxGV#b89)qo!^1(g?c2QfS-Ks=+IZ;QML{uS2BpXB_3BY$jO?e zVS!=2&^3%;iO|g-^v)lDFP1!*zKz>&PfK~I<H$fb4G3KfCe}H%l4`P!$0+x~XJoo& zwj)AL$<bTb!4t+t7l0(o^nU+s3%d@?DL{@lBhv`QuKm~UatybmBw|ny#!U_x1++*% zoCgWT-SeiU$ah_gzf(l{(MCrRBGhu5Fqa-y(eIE_6sleM#a2>u+n|C4rv0vv$#|c? z2d|=Hxzc4y>9}#Lb_M*~*RY>X!vGU>0QGm-?xz;lxc<J9M(!(}$0;M=dS1o%#$(AE zm^ybl^BnkLo=<y-Jc%PgHk+?zLlDMh4S-0Wu{5CzT&tND1GkvI9|Wk%?XV|o_54T7 zkx$K@BIq%Tpn_h%3$m%MIU_x`Pwy7t4_A9j%1Tw*#(9=cp`I^?^DR@Y-Q6ypF_h~v z82PHxFY||u=M`qm=K%8*p`k`_f%9GJ;qul6weOc-=9|ocd$alvVDD4b^*vkm+&jKD zIpPG{vt}QlnKh+hWw7YfmJe|lsvg*}$~99;ebbR;e{6?da2?x_i4?df=B-~^*eW(o zsgL?GUmq{^I|WOx^#1wD$ld+sh`9TK`}v3KRjAr)Xp}sR^%=lui=Ef#Qo4n=-3Gy4 zifm)%W(S%LM%?4ez;M;$Ew+Q7+Q_k4K>RY#GEZs5oLRL<zxSIOf=OTqM6tp5e)o}v zj(GBh(YcH}K9dgpX4FgjWLg4K#KLN(1ik^UB2J_(RS9ZL1~b7rC6g{~_KCN`AhQ&n z4O@+&;=4J!0~cV{WHW*xU-`pg#nMt@RA63EcoISCwd~j(GTp=9kAWC=eaq?Uoyg1? z!Uu)@qr^m7a)EADQL9{g@c4db7=KAj0{)Ka2MEyW>0-aWaBL0h=%XEin6q;(DHqeh zM4JMSsD>@$;cm~@>kWH-&sqi8PRQT>kIT+KEJ{{ZUz`Dng&zh~J)p-=<kqs$nxu<m z9u0E~Pi13%pdBpRt?!(q;1OmBPL#-w;D|E(;Cw^#+0KrJQ^*qUu2=7Q?LLwVXg4p- z74*4((s@1~uz%Rw7`VJ#P9c)k97WFgfVU2M=u25I?Yn|^dF{@knRR`<38Kn6DKub= zv!xdPP9G!yOf3@5AYaE6Nwx5)qrBk6+Dl)HUoEePzhIl*X=_@iNLCBTynjgVFAZ-X z7?Yh=WUF^JGPNXQDo(jy5LIRUe8&+#J4u!y2!oe3bYqNw@R<JSk&@0prjYnfN2Y@2 z*<iuX;o*7pgJ5@WXWXl<FP8XjFK)#0&rI%-8>i1FK*3-i7P)cI)tyZ12qUwXsCt0> zNO-nrpy$O_h9@IUv>t*5V(73#u_YpQ=Bx!GOpxd&IF6*zEkzOQkYNXp4AKjA4D0Y3 zm}5^C!{poO`iP<U!!-+ip0CAf`#lb<5Np*v4y_j@=2SgtbJ#KHB@}oA!Xx&vF{Pp# zcp|>60BJ1+-@t{oInP_cUmGxQQ#i^M%p&m7uHz+`nZuX=A1>c>z!r*f3Y{_-U4}lt z=vGwReBauhF^QrgL+Afz0`i$$n95~V9*U+77JgmVF*r+}FHR~oe&Jh!VzEuWjd(iK zu$G`@6*v%c&KHwq4=Z7kY`r@=oXAyBCzToGe{M1#G9|n2kW()!T=A&_o7<AyrXqh4 zTlq9VI-&?EflDX8iIg|t-?h-3jWq5CE}bB$s=Ty8^-wA7aCQaY&%?7ei<BB~a$nI% z_=*JzUiW^?Gl`4-S<@%pMhQmxo}n|g0|0zW+^fQoSudBu^*i2w7wQ_hqLj)<tu}Jx zH0s@mW98-b^mVgxo?loGmBa_c?FD+o@3v=<U=lwf=LND|{vh5&ak4%gH6rNZ%qoZ3 zOrQvLk72TZEy;MYB7omZQSsPYLgH5?3Irc?{a@+}_!ssygs8k>tu8+0ONj?|KZfIP z`>f0Rrx`svQ!tqQ`sf+HI;!=t<dQ3aTNlKAQSA}`B45w~-v-^m^GgyE1b)ffeBD~g zoukz15`3LGO*J`i_{>>>DdQ9qT&F54-uxM}V?3%}#ne1lCDoJ7v%C^}zVZ0r+g837 z5QB;qeCW}BNNb9!w5GfYe)7^bbEquSoaE9g_MWn(<vpje!gOPD1G{AHXU4sy55VRh zLHE>)#bk1u5lm!j9>&8(ofsQl#&5?d#yT-q1SQ!iM{RPURuU|w!i1STQ=V)T%S4Fc zIXj5C=VO&SvA^lDbM>ByS}JboFSlwufWEI_N{`748rXItlO2b?2j@#;YmbdP({$7l z$qT=t9jy;TsfkMjKlJicWZkFQwLUAf)v3O4yfpB>90jkiAkRK2%p#0*33~9GeRb5? z8`d(5H`w=^g`g5-{UK@lGSSxcBR%tmDl+0jdk!k~6EkMrc}pNY@bpKiyZxL}7BFF3 z{%Jof6H7dD6!|<}=CyV4eB4uzfll->J67WB!XNjgA8m5A3%ypI@s)Jc-I*cQdAoyW zAv03~RVNCbJ*<saylKRM@6X{2uB{)Y%uj;$(Qe!_?$14JXE!Tfk8m+?(nj1IweOFA z)9J5gn+^ONQFwiwen=*pzFIgD3kKBJ^je0sC@R`jk@Weu(?T@RmtdS>8Utd~aHBM4 zvvW@zecQ)vTvzhh(6j?5H`UY2aVRGe)7QRLRVSvmH0hJDLzE^agzdfHC()#6zW4wS z2Dxz`dIWv+Xr+F|jmW)t--KL>3vOQ?NZv@3;U+H{jVFwoRZccDX(VUaD+0-@ij@5M z(l%x2AWLX2N6&{+9m`M+Urf?uR;4_gVK5YNQ%}>il~1FAO@y)n4%6MCl&RrQAwg#1 zb2_*>%2k2!55nO@!Cg~L_mPc}@*jlbT!2ypL~vw%zOK(=nW(!JjT=HdLYZN1PHPWL z^Z&<yG}`2}dc>zslkepw7b{Z-<ZlhUX_iRpI~hnVa^ABMGc_b5HU`(Zs!tIV(`mf~ zf%N!)Y|V<3yv;9zHY1}wC<DX?@P_`6ixwo4zekq~R$*UJWT#hS*Z8-vtjJ!h%+6JB zTb5o>o{qH$;sbz5!bQY?nQZ%FR35rjiG|g0V8$CCAipy8gbku5{xP%6r6@|AMcKK< zNzAvVlf3`ivV1X$N*aVm$W3{JnwDp@2mEI+-?W9_TpW3CQvZ)P?fV}CDc}IZFo;$t zG0RYV10bl&{sISop@Za%xgbe!P8AN$J(0gmh26gcDKW7M(E&;MJe<xDoNuf`$D0uU zSBy_6$BoSUE1cv1Kbks7!cTGlJs=}1<3wnxN_i@)WJwkvv;xF&*cS=jMD=!%fGk#O zUBjtR!u2fnF%8aJjhAg^2uEfUw7+!WkJ;YZW|;1Emt~(Ag%Afc1)tl&+EAmh3T-xz zX>aPT7a<+BTb=@EiiHjN&6z58lTs#0*d9@(!^aPTFY3lg5XP$Cc-qbKGB8_rE;m~{ zQ<HmktU+oj@LzK3o-!f-*1zRco0r2}`GfV&S{JVY=?Q_KhZUwqPFUT#RLJ~iap8rY zrCWZ|LDHLC$rfmPzf;>ZY*sZ>r3u=&1C@R|xqCTmaTT88jx6C%)8d&IG%e0+>+*iA zps~^lpqIjJ4X<N-X^M*I=EFEQ8J$USJj<~!<G5@0%DGsKUP#S-hGiimzWCpC^#gz5 z=hL`Xt98y*-;;B%)5B^1MVe*m^)oC6;5+qnwLfrIdW0>J%>Fum(eetXkush;Y{)h8 z+I59D43Y5)r&{L$+_+9g?6pk(j@Ez^M~3}iaD%ZIvv}AWHF1{HPHW##v6^@W=h*+6 zu!7QYVUFxd9Cf-Al}RKRGlTt@tn|BM<8jVM459`;cY367*V4Po%5Ae`<wU<3*cw$! zPTZCoAVj36Hd}+w$@I`%$i4G6Hvi_#BKY!j8ogoLu{ELh2iV5@ZMS^&;A6UcP#ZYI zPQ}6!d-=WTX6{n0r-^?m<SaJ#<u&^!y;#?`(uGL)>V|Hc_(n&xSJrrg<jY=;6^if0 zrow|W_z9CpJ5b@`R3o@^lE<>gz~s`YEYn5x=1{A4_SmX4S&}jxnRy5%C<(=W8%m<U z-;Pe@Vk2+)vvoZ3D3C8^g(j9}YCT2MoZQS=KNRbRpF}yD^dp2pIN^N*WP^!M!=O5s z$ykO7oT*3!_Bi^jc?w%Tf2HgT&vqIctM$jEv3aXDHgi44eJPtdQUwwPfJ8x^UI8-s zc)zU$R%#osCS8np0<I~@RQF+sQg@@$?7xYVplJCRP+qX+WtY9E%W4nE^8c67re@XC za;;*@Pjir(tJ9nRATj^9f%Z|4`ml?**guwTAIDCg%zi|Z{Xfls^ei;^>T(2XbEd!T z6!{@||1lLdUSmr*UKI}B1^+Cq>MY?4#QlFvwtYXUfM>DW-Dx=Z|M86NN7ciQrWcH* z^X*ScA5217{cjjl<_rkMeZh(BD{<_V$?Ro+g8<$lc?+IKnAYLjzAS>1S+f&#Gke^> zj53iupzwX0gHs6@Z=*JE;5zQ;KbD>_hAWf*<{1vm(kjoo5Bgu1(->~1MG`o+{Pd>K zbd~yao-4G|KUQMmB(C_^)AV<^T!03DA#GsR7^yi^x1Q(HNrv@QV!0d;k75R`uNE`u z%sPzj_7c5a#(#HdeIZ=SmF=3l*GQ^6q_QL6c%HA_0OC)G_;=uH1!vWl1Y(XU##~x4 zUfIt?*UC}QjnYAk;v0-HOan7q*6-*vCB#W@*Sphh@5W5;)4TF^^APa&?xE_I6-#72 zvEgFAUmcIh{~v2_8B|xdbd81(AP_7_2<{FcxVyV+aCdiG!QI_8xLa@!?(QzZ-R*9Y z=X~dU?|JW!TXkzzA$tL<H*DtU**(VS-=hm4TFl(zf6_5r_J<jNch6C|Hb`MP&SW7A zUr9BACB09o_oEv}5~r%fVVWUEhB_YBo@&8b!BPJ?T;SScbLDSW)H6?I@oDMTkIkIW zXhT6ov2Q|O#mLiTWh<2f(qQlA0Xog)3%EG{FHWP?X^m$|b(=5&jH#dO_Nj&IQsb@d z2P>(Co#7{?2JM#e_-hFhCo{t(Qw=!_u@`+h`Yuw|V>n%|9?m?sHztc$J@gvVwOmdd zhrw|Yfn#MUEsc6?`h(<$xN2omqmxlj_Tp45MX5#Rp3)@Tboa#P%g2oZz;;Kgarv5i zy;JUP@^*0NHUmwwi-qTc+Y5&2M0zD$)p{xZuJw$dqYo?#O2SijDQe*{!Nz5oo_SmH z6RMjnC%)9J*7-f}&!-{s6Cc^9l(UJ=-m^R1HoARHCpYfK@`60y0O{RL*;58C&02QL z$%D)C1<S3X0nwAZb$I_c0J+)};{UH~=ua{<U+@3YOb#bqtUzIW>%t%1u--&A-q+1S zhyk4hozx(gB-$U(M50nSH04_rCjgN7;h7t%oI%fDeAbi@ty*7pHX)?f4OtEIY#v2b ztMNO73l%M+HVHX`ruJ-I%%rNs6L;i`SBWHkT?f&`v1DMXRV1p}WR=?=41(_H#goR; z6iEOaE19G@84~g0^?_6u!Dv#ZEoji{U)X{M%^p`j2#(w;T~AiMs031Z#8r9>+M0dM z%%+F(C?-_n5Z^M!@DJA)GUl_xsB^|C>?LMhJ+^;4ep)%uaBP01Yt^l~*<(wbQp8jZ z_?FnJmNFM5th)*K$U+R3yIhOXL@UY|D!l}#Ro(D*B;MT)WLTUCSH+t@`5AeO#A&z) zCDHN@M$;SpJkdxV1w89#k#lT^2o^0sI*r(Zv$HNNZUr0<Yjj45;xF3b;&Pz&kY-2c zr^a%bF+s(IajOK*7f)NsireX-&Kc8RCqF;NQ!>~MAGL1@J=^D^jEhB`N^{|tI7s1} zHxD}>NxBEaa`{<bGe)FhOFwPm!Y$WKtsO4oj)vmrS|<HC2K3_(4fZ<!DJ~i*P}^&r z!Aeba%`QBBleJ5cT*ZV<zs4MQ;v^ed$Y~3d${t+XG(E~RH6UR5Q(4piRTgLaEI5kz zU+apG<KfD8p>M`k+(SHW<7K&><%d-y^0D2Sjl5uu<SNm-3h9?EZ);L4R#RM=lHJGa z<AH@WPZ`WBvkqpVwqT-iI1Q1R=~OZ5?8(v;&l!$7j42H4S<u|%X&goFAg#08CPD7| zzr7%sfAxYQt+RdR&0t$}EX-}>K`O?0svV_=ou|S<7PLyUP`g6G(!(AL0m+#60Si*{ ztkfHGQ-y;qTPdWDQSP`l1wcddu?ls-N4<B2H_MJomK5U+vo~&CnmK5F?3-(^t@uic z{okaHiHZDC-=#}f+-+S<&p%s;<jl~SC-Ty#|50!t_>Ag<BpmV}2?wl+ZpkV_3-8W* zbQcFueG$g=u%qz8F|#G5t(yi7aA#$2<a&0sc^SOZ+AZR5eFmIS?O;FVtkT1(AcXYf zeA;nbP96`wngUM+tI#t;&z0P|tD4mGXdc{2LRn8WUSw2q89N_NWN_mIIx3nt?I1D) zis^KHybK;Qe!Esut=zMBeA|CY7)j3V_4fXGemawLC(F7vJz`;`V$tYRTtr)6wAjjN z%+M6nqJKv)A_sgELuJ{(qK`}vPW|gwb68<VAjbl?&WJGo@_4oPl#qkKc@D+?a=$wA zO;01kP55|P`Q`8&Kp5U1<1lX;#$Y#}spYz?H^P+@TH@EioUEia#jU9+#(<N8cKQ|& zT8klP`oJS62Jc(91U{U6|D6#Q^RJ~&Lyln+pq$g}I*C^KqwD>-w#y6=A%lM!HD0iJ zcKv@cGS~g9))oI@WI~k5GK0*GB7=X<xD7wpujx@RWPeJ5w@7r*Wx<fxqzEM=dYc{e z=Bo3&zt9^_6Gl=;p558d51utb_*Y(3m;6O48>;5r@B5zZlTmn(C#vbULW_#Uk|io0 ztEfuL#Vv@%n7k0}{e<^BYb+Jcf3%!ad5N!N$)!pPSaQ29lF$DlMCAr*#Gfl5aF<HH zZLbY57in8Hi3kAAnaGQ2UN0DLd%E1)#P^tb*jxN&E48)05Vh*kkbH1=Bu)s&E~r4{ zCnr|S3*;7qgeD1pgeGLR!`4F}iy2vgsw9I~Kb}P&kc$-r!Ek_x5ZNNgvPbUh<6hSA zp?qMj@5HAlzD2|F2&c=p_+F7P!Xi$XghU~FAKu=VmDZV;9(v3oRloO`{jNE;P${Rr z9!hbkAwE?vLVZPdUxv;^GC&eNAFr=&%m8TeZ!$7yx+!%7oGYn;KgT#ghICe%SeR<> z8k{)O-=#Zyla~Crwt?r@d;{|aVqn=3EWBpzdk)8sg^>1fB^0e+S>|f$d670eJvwhG zQx@xV=Rv(R&;SLF3f<Qp4;cq|3Dp})K9#<$EsA8#Hz2<jSc(`$25=A`jHJZ27T{Il zbwH_`<1X~DIK=Jv<ksiBh2^N$BhFU=yn7I?(+O$m{N#owH;-syhbdyt=Or?y%Z(YO z3;9;y^>HLR?502wr5=4K0<fsypTE*|LO9<Ss{2lfAC6RFWXvaz{91>V?2|e5_x>4; z(o9Uk0Q2wGlwcuG2LMu^=AO%a`YTqw814uj{@?A2pg&Z2FSGW)`v~<<<oHVKJIlX` zzOVceeF<Ipb@7Hz_fwV=?d_YE5apsmi~G(=vcdT#l_&#a)2tuF4R<W1WIrL)&~hAT z)zG+$*V{-Lnk|55poRpFh6+V^d;FA^*;MJgq+P(q$&0AxZkwWdUL#=Oa`7iGBegz> z{?2v3gyhz_{!W1^WD+@9K22nj*QBUucjY0h)Q||(3#epJA>D_wn!!~bOOyinP4s*> zhS{C@{>+m`_y^YyInfZ+RC(&$3MEy#Hg#27DAl)r7VuV8?vrYEAxR{$Sv2v46xM3( zcPd)IkQ}+OdZ3$oC6i88hU9`G=FU(vYAeW_!wa-jQ#d}xw}hQ{pM?3RbLZpq51SPS zM@oc<a;3I#adWdv7e?;fvK!61Q<09*!GaEr_=ZE)&!>;J#Yq=oh3J?89htr#h}CIg zgX+Qnq%Q>m1>LoU0&YnSwdb+c;|8(TvIaJM^%gT$yb$XJ>wIu%ruFHlXI+v(&a$-) z5FZgh>*ejoj6^}iv)5IOauzBwg10eAkWMYwjVxt8k|Y=l-_0TLx*jhGJmoQIb8X(g zC1|}wjR>+45(Tp-vEMOuATf~3*#Ky>=oz<NQ4JtdKct{K5&Bn==LjKwkYdgGCXe^E z*@udpzd`T1#)ruZwB{u(2>f^&HXH3lq6V;kAq`;vR2U8o&oV^R_lkU7<YAGO)WLXz z$=+1nNP}N4>n5lbNfLrs#9y8sUds5BzRD8&@+38{Eg?{#)K<ekvor!WFMAF?^A%by zvla>&>{nf872uYh;)c)3v{m+}N}D4s;A#$CqqYES^fO*@)ezMIf}nx0Pw{=<3CUpp zjE0+GsKOk5@F-O=1(o4w>PoAV3D^C)(u?^+0%(BbvRk%5SV)mesDF;ibTG7EC{mu{ zZ21q=yv0P0V(m1TG-aS*G@D?cuks&;-k_;rnrZFE&EoqI)Z_+3M{QF=fK4pclsYU0 z_IV4jE>yXe<j%L$hH0~w;f$Q1nOLfACn|q439#_s0$yfMFPP8ol6ZaXsjKbNBw|{q z(h~kH0&S&CZx3R-{fBU}=jPK9<4FkI=RtL-xLu-+v)ZG(tCeC;l}Kzs{!0Veh0zcq zeD8p=F3_EE=o~hRDB*TNeYO5g=#Rz9*H?=bA^Q0Vq|SJHA$cR$<x?Hz#a8AkbB4R@ zGpc{HOx)v$V`lwvKmt(wVHCOX`58%WGn{X*rl4*m@TXl#kltPH{J1QDGWw9lOP!mu z=7=#{8*bEnuc-Ug?T^U{oTaH5pQ`M-2}{0BT4Uh1N&_Rr=4WFaW=lfTI02R^$K(N% zkG*+P8pp(|9F;*^4}tu{+YY^^tf4I4KUSO;ZJG*EcDp+8cThJMU$ctRaVYNEj_yk! z$&dK8T^T^Bdr)9&7K*@nHtaMDBB9WONGN~%l^D-BBk#mQ!=vPxb1X9xd&*MS+QLQx zgmXUJ^rjpDR6lPPAL?F4*JoGz6nB@46U74)A2h8PN&==gWEX9lEQgJ|UH8w1@y08E zz(((!x!0PMbG<Ky$$<WW>LmkIZ%CoP!Tj7K22X9G1y%zCua;8Fv&moWUgkU^CN2&W ziYPuT3^NlV+TjiZPW~MQMjWQPc@T?EY6~(nSL!@HzU2bnA2E5M`lC+?ByCOA^dO?o zmLTkfA_upwv0#Z2qK^dzZ6eWiwJ&c%)^DS?HcU4E*|GxqI8YFKcl|!&QE=cy8FJBy za7+<<@isLne96SzeCyjLj{BT0vPw#7!zR$Gz;QryuD!b2rX}tNFW+8`w$s6qKrx7r zim|*}csd|F%RFk?X6x!2ZP7G3ye%ocVSr;t)7ciQW=EQDF86jrLHc7pfm;7#J2{;7 z0u8tnx;qT8?he0pIlAhBCJe=ln*xBdfTb2gjU;JqirUunGwJ6kSeh%Se={49NGcyl zONv&2kqS;v+Njf(<Aw_VR2!KJlg^qD?+ht1Z5tzB2qwmFyxV&avUs3(l5w(XuzCMP zbJT&h?$OG1^0=b%&5=R}pFJ@H#zK$yjlCVj8$M6Kl=ZGKF)lvo({B>vj<?j`KSibw z$cXt#N^kGLF8+3L<J>D;D^{=^(H0OAzX(oB&t7;`GwcV#8OV&zLO#V^)6Y8xsTO?} zrRjb<C?aRTu|Q1QX^X7bpG7TH27|vp?O(7fg2(A!nGh_ku@&)MTD+T7&Cy-A^D&wA zOcW9V;7Tq|*oZ-A65T00*0Yk*LKeqGVCViW-JXxMfegV#?Jphr{@YgB@ucD1q+l<p zIHI`dH5Ml{_JiUZ>CsO4`;NSM6K6}<A2!#D_s3_uyyY*O;gho`kjFPSEe@6Py4wFZ z+?+V`|0XKxoZ!iC7z-KzD|fVH?=qdlmQh!PBUfC$2Jx`xOEJ(`oVq?2Qp?VjsfmKS zxG0xut1K)l(KOX6Wn)g%NXlMBK8<Q+tN^U{V}wA(MfuB7GvmxvgVUC#$JOYUmz%MY z_W!TipdApkUNh``Uy_@I912p(`&Xx7ydP+KyfW+FzA4LLukg@KYeaMRSN{NWxN_Ic z6#<Ya2#tdf`l00*Z<@Pk#cU;ic0K@m?qi6ga#PBm95lfO5JE{#<78l$?^b#==#7vf z$9E=Y@RLk4tx-yAnAV*rS&~pnLc9y^xWJuf(0KPRdCu15;l`C!p`G_%=Mc4qc@E(w zCgRw-K$Hchkk-Hmw*gtsM+V<sZ3bMn+3Sb2Yi9xY6HB)Khp>gmMd=JKoC)F2Vyny1 zKvOs8t3f6Wn%%21Cl;j4@y?auGhy_#Z)N4Hic()-z$aJK=c|JLV>i<O`!gJf%!3;L zFaCcG-m3u#1s6UvI}|=GMXpRLg=D>fDA+6i2P3MC$zMcF1aE@mftiZ#o0A8%8Wx^M zTY|`K${p?3YwYAuGr&Ag6}tJ^qsdK`_BdBZ^XM)vZ}nhS&7yn~t2sDP#DT;4Uu7O= zl4O7_&g`Uh;s}niv|>i*N(3`yj7E6Dy7nEEH+U2|B{!5DA9Y=`o|n?F>jCR0vZet- zZg>mj6%x&PHc4^lzZ3<h6g^ZEA4QqdYv1cQCSZb<%-JV2AavCN-Rh&4J^TdgH<?os zknIT{1seVp^H1VF0+2(UIeZnXLH|xS2}2cYJCDNED`5!_wQpyuSVRjir5c;jFli6# z)wA-|$<op(JnR|bl_=(-{Y%rb4H}f?La-KVc%cdfzAMSRq)Nhz2Js{Yd=;5yfxKjT zA|(Rk@^Ksn*Z#60FYQu?g>}~^;Zgz2w!v5@TaaJH`zB|P=-+jwOG(@=vYfTQM|1Oh zeGKS|$?@WXORn|D$TyoGob7GT?>9Cs4{su9Bj_-a<8=PIRa~*{O~c(aUY%b-_rKaz zD0xINvN@Zv$RpGfi^9900U9gMl8krLEGQ&}uZ>H}SOpv@pN&wKe>)HUV?1aLG9C>4 zm+@d>V#R;iRZwe8I$>sQ|M2M7{UsZ*>o63ZvEvW(gV5)dcy}kPD|)>O+@2)I>xby> zQY1eN!3u+PUcmI4@iDlGH{Eya&=MOlS-5c1Qw!tcobe?n*}-T*>XUHh0FhAes0y8H zXn|mi_lVG#hF&6MTyuf~RTh`rAX$BvziJR%^<RQ<&-?y(5rQm8Y{uWjg}l-d##5Q# zpce)><jyaiEFwWTHl`txEO8{z-^1W7rE-c7>MsDqFaDpFqb@pva^K!4Rp>A!;mQ5| zLme~qQG8N?XtD$O-+U4qtzd_u!(j~o`QLm}B;(}YQ#}(OB~V5D<4pq|b<7O%n~{eR z=sZrH4@99y#6tq<Q=%gG=0Faso$;@Rs-F%=WI>!4J_>XI-=b)3*gX~nj|inXYpoOu z!V2V)>CLPSa^z`mZ~kn|*=T{%tm8DIry2YfbV@>2X;=gq8ofGWf|bNVDuMLmuO^uz zFcr~gN})eCS=d3wH^hNde_3%=0=fpL&xfwmCA!McV8!OKih1HC#8f{TGz<wou4{>v z9s=|=zMn7(qM;O%LHV0$9kGRAC%20V3GF(X5&EV=k2ld7_5|%<Yv$S3NLMQCcZfCc zQ<pp5z-?cM9<t%2d1n>o*fmE6v6n|3zj&GKKt`KMtssgYg$r7^u^b%eB!v<*yx!s= zt)WU22M5mMI&_tFe=K22zFcrReNE>DyebaD{Pz}WMEBHkCNZ6dTd)L;)sZgREnc|; z;rVXfAEj4a2*3V%^@P#*>j}fQ0`i3U`JbLJu%iFv3Bzajk>MK!=II4or57aTPz$vI z@iF;9>~4XS9X}>%XI9;$f>a0#6X39~s`Jtw)gNbRXM;Jp=|7x}dY?E3GMJD1KVp<Q zkTligj~JzAI_zofA`e};J6MKfSGId$<__h|eAtZZmy*!Mj!co>;M0@S)gt5BRf`9! zT>-w{AdMdBtDE4(kN@K)h`*GgoWq1;BU9`6qBI388BQ|4d-xw_f`Q%tHWU0~cG5={ z@oIK5dJ%IS+3G>x91hB(&hXJ`EH|h9yCH_u&6Zfw7hbJS;%7{tR)>?KcIqN0(sdx& z<mUo%re3LegwY^s9!QMxj~0dYT(MIOM5qQpjOrxBP{DkY|G~?fNc)rMW~W+Dp1_*o z>q7jTtS@umsGiawZv78Fwa@QnY5_4j(sDh9`&it*ZqPsRFA$-+CW;JQLYPbEh(1R$ z<^VZ<r3zb`rL-|eBmW0BKK)8Goh|$Q*jr9?7C?1%$-ITr5W1Gzt~Foj7l^KxHh1EF zn{Z|vR$mWNqfpRAX-%=ZdMwniWnVPXj7pC_BxtQf-T<1mcfv2d#dEpgiN%$<uAi%e zll?)~Cd=cJoQ&|JbVjzu8Bl|ooE4+K%7Nm<XNmsgW0u8h{n8o2bOhfTPc8g)Ma?Ej zfn5;syh*_wskfbUZSwpyC;>a;bEUqfJ4{D!)x7vw(41mt3G1needkgSH23X8FZ&wu zX`%0Jx0iItIx@0)<kGD`y{c$PxOBX1;egWUcnrD<oO3dzQ@m@1iR1mV*45pq*~7%s z?&;3{a#Q&BipGoF>`7ZmL>kCW&;BhNPzsS2>9R0i#2_(3O1ev7IvJcS5#w5>pIny^ zC9#Ro&oHJfYhs-@>x^`un>_25OuwVYCdl%!Iw`!&caMttog4b(c&Y@NyP{`aPV)RE zdfsM=AzN7@XsC9ilxk>Cd>XH{;Bh2BA5}855g`^nJY&KvcJcPtjrZ}cTJI|30k6dB zPV#DN+9Vmxz|XsST~lwU$xCN2;S&1&p+Bh7U|KTSW++b8D7?A%biX2o;o0?xZ*qN8 zg+)+2vGE2ED><gHdJ@t*d<+#U89bARvO;6=tDd~+5;PlM4x#-SJ0E2@zuj0vrheLo z)(k7eDC;sZ_+b~Mzwf~avH-C8aKdN{Gdv{3!rQ=H3(!vi?TYvs7NL=`ksw-PmJsef z(q)#EwDDyYBqV`6B<xU#mxSmCkAfFmx08j`y4s$jHh>Fa)8$&?!91np0%RkLz9>0S z!#XV$!({atzH?05nw7{=>yi5b5dBUK?F9izv^KlkvWWc-q});mX4P&QPpD*Z0u&j9 zq5EL;4G1WI-t`A}JHqQjR8?_T5io+@dvj9u4V+hX77|CsQJsB)Qe@~=tA|lH;Gn#| zU{A|Q`d*B8Y0mY~gYlkk6wwtj6siZ8pvSBS(ba#f8v8wG^?Obq{G%j%jV)sl7=PbK zh!9xOj>yQiH~zjrL5U9&5kA#9T6?4;KH$ak{FZDYoqPhXO_E4FQ{=rffBWLfyXIeP z3~yJ~yynP#pbW2gOhBI?p2o+1adN|>jEoB^+4`BGSLj!Qw_ySHD1`a`+79syG90QG z7q6F+m*v{7B18W1DYZZiZwoHC1~)iXxsFkhB$h9#BEtuGN`0Ce6}2}$+-(Q{I17a^ zwC|N{>i?8VMY{(TZz0L2ghz?2+Au7wL<Raiyj7jYRjrIf?So+|#iK0l3noFT@?NV> z!X|)G^(qO?|Fy1aS=GtnRkDTQo)=i_2gfDO5ZML8^)O?3TedmK+F^7YbNkdG`JIEO z^%hTBBO?-kC2B+J$LxXY9EGQR3{7ZwfXZKt;CgUA>utK2j3|c8G<eUw8EV~GI_8U% z_r7z~kvDpWRy0e+gfRM&voh!6%)+AvRg})4*CjGHAY9oBVZWt2R1P8jiZaddJ-x8P z!Ea#_qdmm+{Uh>q%~qBOgfnnv{cBR1B0>Ikbq#T#04;{j;9dSmOP|oR8CyWhv$d<| z^Tzr4@Pjh0F5!T&%1Ky8IsZ!2kLk?S=T8!7);?)YJ`gON^_co3`+fQ#4%lHKsk(SH zSX$2H;!6QBcLSK8=s}$3JtJw;kACZdWowb<2|&G^Q<ii*{wU7-S<5!9Z3{<k(JEFb zEJY5mf7*oNg`Vb)m-3_#eAq$Ik$ya@7Z7KjzMqBKb-AsJ;`~+n6giE@<!(hLe{s6( zkws8i7w}V$Q2yM4fZ#ErQM!(SSUf!1@qN`CHK#!6uV#ZC0YE4e;`CrG-mswx5`Vq8 zbeOIvmoHW@KJ6E3HkG4p)>qDfw}SyHT&eZIfQ4ll-+NuE^bHCHGSdnD?aRjda(-oI z7Mui4r-SWYiC^XPPA5`Z(29?~rXQOphhv|isg=7Tiac?N0u~sIY;K*(Df4}lET&FK z1PvCIHWv!M+SLdhRE-S{VT(UJ$31{12j3SiJ8dd7xz_M_5>XCTHH8>Bp>0yO(A%d1 zRd|)_=Hk=$_D_Rh?duGmblp+viuqrPX4_KKk!ARPedMV|e`&pHHCyr|ly8H_SSaB3 z5DEThD0-{>>1EttP=)EmhH&_^98yNMw}foZIgKij;nLCQUC3xc`N<cq7hN0||F5YY zn%e1?(K!s?(=FKH+UQICIg60O)sLr*0nCWd<GAZ#X4yg}jFCXd5FwD+*fDSO`Byc8 zrZL4+owz#w!DrtWPz!5uO~aG>PJvKMmv8tO-(|q2RK%Rq@8ZE1XA?vyV&LxSNm-Lb zoz-MJ;(b3cMP|y1Y=@I4iEZ2EwrqU!v{x?_L(}N>gRUM^L5MkSjg;$=>=RrtfTM*8 zk4LU7UWh(DJ~ETmGRwh)tgxR})RZ|}!qN8;)XF+-3i?ejB55Q8T}pq;c2yo1enM0b z=bYGbKZdK+b7ZkF<y%7^auy`6VBUicGQ(vkKQQH7AP71wIKvn)LmHJSkDnv4w@POS z1l5{^m$w<EnK^|xIt*8CO68A$@RX0D&|tRZ^)@Zb<^ht7_hI>PU&kE?%Fr-ZNfJh{ zG0f~QHQ9c^`IVe_f<}8CYYBYQv85^)!bUU<yMK-qIUHvRZNzZ^m%~6<m};8HLAbi1 z8Rcqoc{m6dT<sey|B3tjeEmRwe|~T=XN+jF*8y)rt?nJlRBZZF&Bj;-NNI41IG!)~ z5!<HxVZlL{A!zWYbL7;}2yn!<R<YeUyOzsXRBHU_cpS?)k0P~RmMLj2Q1;B}qkbgu z=HYrV1CwJC^4pmT=CU4pruc%Fnh&;0whmN<PxeAbbB6)6`?pRCB=;>E!{zN}zW3t8 z?&WxxXD}qbJJJ_0(c_effb|nuwK7=`oJ%*Q%8KTC>z<xw$85*h1qzD>eR9u*-)xCX z-9BR}sXobN1oVa28m?eCRD`M&)P|G2B-_|=*(9X1CDZpG$DCUG>3Aod@ElE8txDv2 zLD=QeS3CLCqDg!~wy|VdD91l!vayH<bSy;-T-R*OMk1{9J`+<r0$BnL0ztM!nhVw4 z;*V6qf_&553Qyk(L<;T$iU1iRA{kC=!74fas7#S2nJXIc1!9FKAA+N3f?8WSqL8uH z&|roiu)@B*ADHrRC3WdgbCycUkSKbg<Kzq@;;QLwoG4u<n?=|@b#FrBx8-upP4N`g zra(_{Ve>34r*AZ80_3bQXpl(cJ@L@{I(B1i<Jef#36&-uTij2I**cV%qi-v$LuY#e z^S2=rx2J5lFrx9sO?yuecB|}G%i()CN!mgB0v7QV<gW-SSu-XGYMz^9;D+*fesP`e zY|<4(t{Ad4DCPRAA0uIkM?_Nm7W5yYnJLG!qL@EC2LC3c0!obxYz!KBW=g1{s-cub zRCS)v4%$G7(<iM|?_0tHf*&_RO7&M53Iz8qp`xKwBZE`zN9uCb_KQUaK@we59F~vb zJHL{(zKgfFVN8hK7n^<bEI29fP<?0qh~Cbg-4`eAQAU1*qcXW3^0|wTD53=ExWegZ z<k3VjL;#kHKY>p1W(BG!<p)7;f}_qTBmdVgeUc-JTfM^?sSNRrWcF=7lI;6d!cVvb zhw~o6_Z5Q=@=Sf3@wY658d?5E5rSpl#|sKGGVUn~6%qu8cx4DxRW&8nd<-CDPHLAx zN%IFiACjtQ(2p&vtd6!iO(K{9KC#?V&G5ie-X4ByT5k%c*?pJMAiXj0W((|`9sE2N zh7w)GlQG(<8u5PzNJ<buLjS)3q<!4nxQoV%ZIz3t-0=w=ng{~+p7Ygp=8E_b*_F0x z+k{B~@YPPBHr!L+HXi<C*-ARn@pj0?Eh4uyzaem*t;X<fXu8(dm6lvy$a;S%#9W~z z&u;DjA%Az5<_pbgGjN5^lpX4SwQ_sMw$;M_>w`kGh)z=zuVHq$L{G=Ju5{PVS|ao@ zR<@R>2(|MVOqs+f&AlI5c2(b!VFM=M0){A`667?XF#&E*L?N+zy}hk`xf-iy#OrR{ z2=N+*FQZ={EC}6#%^vWJdEHH)0$~m4ZNFI{SoI3!Z5>oU=IQ(6eb_=3)B2fefycrS z(#*$$+>ML1RP-ZW4Bbyr94mR*tC<hh#Sd|z(kKgimbFZc4or|wGfOa-C{v`5@||F( z%Y=^-5w%LLF9B~<aJh&QVJRZ4$tdg|6Dhwfu3)5#11tYRAD4le2>%pU(G|#zp2M`+ z|2(;yeyTV~VUr-<VyeFAYHy_xQ=pTUS^tqNsHq&7K3J73gT~9=z{^bdNC}M$$oiT( z;rg^yxun;k|LjyZLbns!kk>yY_aCQGg*95T1)iU#PvdS6W`6<*J6xu}N1vg}C0zSu zF|<bO()?G^mAY^gp@YU-Ff2Bsz=|eX??@=7g}U}8w8z(wJR&efv#nHFvISP1Mo;5@ zvmhV2D|Z+vEre&@BM+^QCs_FaH+N6^Zi!EzqN^*PiYXG9&H5Hoy2XZ9<KVE3o?#>O z3TW0y=_HP?P5Ttt01Ba9vX`X6-YGfbf(O7x06I2WiWI>fL&l9u65j?z;R>prFIfK; zZBP^I{XH&{;^=g(Sv{v9U+7eH?5$aSuB}Am`K#-MBpZ476|x+dZh^m;mDi}W0d=|0 z52&F3oH8MtUN}wqoYl=3oBuh-E!Ps(l6g^QQ)ax<`MeK#SiP!B#u66Mpd-SNM!M!S z@63Ij(JZ~xRq_Bj={!EEhg#q=h0OqNI`gE}yc8Rfi`IeTXU?cZz$)AHaqhaI-Qixa z&_+62c0G>6@)SU;^JQN{nfvkusrkN&xalSF=Wb42v-ykT)$Sm$!WD6~C4%WNod3x% zEU+%%6_(P~Q(Pb;0=}R9kN}~Qn!YYpU{{oLMZvT}28F13@<E2C^2?C}I34?!mgAYh zc|q>_y~%GjY{t_<PnHwMKU+``r{L)AX5Q9(`7k`B4xk-9D0gIXJr!jvco+dk`{UlI zJ}xa^AdCz<9CS$Cf7@Y36@S?>Ri4DlB1Fq09Ce)FbQC9yw-x7`S&L7ms3%^kDzId= zno{{4XcKyk_HEGCzu-NoZY%yo*k^)qxuAS>f??x8fso%3uHA?}tlk{DfgD!+TMUMh zd;Vm)cEAXpGv~#Ipf|;G%4A$5VnpMhS-PcbsI&KQGmWnK!Pgm=5A+w8^bK`Y(ci!1 zCkF*olT06m`I|XNMx~hTG(Bh3)wL;!HcEE?eqy_wSd<G*pNb^f3KlGz^XNlmU(AZ4 z{8Z;#t@d7E=yPB}=rmG;Zgna>SA}{dnYovBLm^O7V;h6&2QCOYHw--Y1*@HaeFYt~ zAoBCz1(iyO3OW<^x=(l&*Q+7R-YO^64_HW*=^}d6U5=-hHEre2I}wWH%pR1pkR=bN z9@sD^PjCjd(1qxgQ*F=dmD|so-fhiM&T683Be>o_bX#Vs!Dt4a*mcVh-D;=pI1M*< zU4c#;L$r~r#Gzf+`VDt+&|3n-AUn<xykO6=0J$tyH!91JhTZCL5po<Vvwl3~$_FFQ ze79~Ushgpt`T5LZEy$U+XZgpQ^PABV>9%x_hUSNpUm0j1PLBKP%w2kS40RtguBhUE z$9LtH6~*4sD)+<W+1#Z5Dl>;#*E<S#;H1yvA&8$u_N4OSot4kX8dvLO;Sn(Upy2g| zeN=Yu!$@o;-S`b=X#@@>i<9;y$&4`kr=gQ3KW;*MCrEMrxBgdw$=V2mxo(uE>J1H) zIR$>f#Ic@51+;Q?N>uo!ac!PYSCU=NomTj!h_xQ#?TF#@h_!J7l44TO{Zyt30D0aA z8q-jYA`^u&S}>nYPC{FGh6Yr@kkv_5UbmOoyK=3UqqfI{wx{ds<%5-<gY3X*XzI() z21ecxPI)5jxf{Dh`j)a&+bfza80gPqQ5kblHxyu9dX|Mlbe8>osy^Q^_}AjC2fazP z6VVy3@xk~F`H7CLs^Z?k${ABu0an|x6u}waz0e7da@bvv&_5fZDJ#*9ZL8DY+0PKA zm`QcCZ|US~2bLa}E5;aaC`?-Uw4pE*$|N%!716oc+tx91t5LXMP^7sT<dQic5BT=u zxG{&EQsSf~L2WY(1(R&LG@am@@833F+cA-S0p7fC%LtHXz}M}cwnPmP0$i~mc4A*l z?4g7Qr~R~6(Gwnq`gfe&ptL@Poe{8Dz0_=etv*oN1WzQY)KxsXeOh0-XZ2`f`+Sr5 z1_}#xL}@h%Wfxi{YbUpOn>n0rY9UKOOsKR@2|14(RJcjFBABL*G!>2vrmTt(`9)5R zAxEy~rNBdhHw_~~T%!SB2nd+N+2ltJpg<K;{d5acNN#!&kwTq~m)F3{)-}Q_!p5iI zz2!neO)HT_4J?2q#G^n&4K#(wu2P^8Rfs?NAgd~en7t9swB<=7XY#86vxyi@jMgB; zS0PBSh}nl`wa#0ij;a7s&VZ-L?^FRrBKZSv(C*_UkyFPUbP3qZ8^CNw2X0%gl!kUG z&qZH6XgzR4x=$AymW@7?HhmE~D8CAxC!4#aG70&I<O*|(Uw(f=-u!a30KG>SH(?u) z;3tS;G($##TA7Sg$<m?j#j{NZMzQNtGzU{KMpuoJSv7@!cX$Vu5$WAZ<{M*-Crz5V zR$0?DtjV@RWSB>=4SWs#qRKiWiju=4q}z@$kbg+)9E|sNS9A?sgYZKNpXPipvl8#x zSTUHs)=Y|&H+@_86UEY9%1r`|XSzo2!PV}UGf7&t1=xaSObD~cBlP+7-tMzADrUhA zmBC?+sCP6K1ZP)Y-k!t|2#RU*fHGyWg{skD0s4#(FA%Js0}4cx)rTa9d`VcJkzh?= zu>c~bC{HHfsg;-?39^=5wh6hG{U+DZD4RdWk(2?E-C(SJ2*X&WiT+z`FH9}Flin&x zt#dxX>-OcW_GllUQKAB&%G<s8C*3sP#RIveAcS-Cgd3@YK3rsFuSi3t{UFfH8+9!Z z^~F}+fT;iDng0{k;qKwEr&I~Q9yb(DU<H@6wh{s?U8ycNskq2J%mXX?NB#K5G(A*x zh%!2xI3>?Aq*6UF<&M2nKe%^`tSo8Zf2&HA9TjOlN2%B>X*t$xQ2sSW{;{ijSd=UY zR<h7&0NFc7J8L#lV*l{hjCs@K%+534t|T@RHFZSwP1`u6_Yqd=2w1PrCPLJv#XuQ* zon1wmeDfqLO1@pD$U~lp%h7HHT@=~8&!2GnJ*Be$GhBrUvBTXpdnr19sV>DeicQtC z(ds32Ka97^Ud8Q-*S{12U2F`{Cf<>bs!m*7Y&^iA4pC`D&n^H9svMz9hXBM(N}AJc zsTP7t0!fH{1Gbe$nCPGNwq~q=5005R+Nz~c<%sLDn~bruMCn<Y)L}<InBpxN;!D*s zmeRE`KJ;!x3gk5ZZhclP2bdd3689-AG0d12ujULqZrmS*^6~(54sThIFe>+bQA_~9 z8GK4drA$Fj(0o9(nZ$HP-sw?DH@h$X2_E<7(#%cF0S)y_Hsk)V$JF;rPOnP{ZT3B4 zVG|UVMMn%zBNaF|1!8r1>vEw*XZ`O*HCC-bVzuMeGTbT*^#ZG#LZHDvpTa?df0pHq ziP=U3*jE)#lj49n@Zx7*Pv_TSfrlseJdR;ot6OtAVtn!~A-c5X=DdG*kZB>S<&Xq( zM+!yR4~K=0(Vb8XL1<KaDf)`S0$i>iLQN;q!UECm<8Y#J9m|<?jfL;HZO(IY#|RuR zI?cMWNViROl5*J=B%7*ll7HnTi?zr*pj{@%K{4Z<Q35!g%pcQ?E!lj;$TT`;3xQI5 zcjoC@B<@UQ-21M9`X5r5g^E-?=2zv_3Aq9r4N&_+YmU{}OmL{!L8J&A<9)=G9~AS{ z6D(lKND@dFoF<)HPm>u(m0I$yd?j24B_=-+EZC;m`n8TZY1Pf(K;jN=oh?~%(W84F zC^W?wrU1j>`7_vUIq;-q!Wzt065m4_la3aei=H1-#>0SZtJ;g1Hr(T6W~+Umru#yL zIh;Xe*3opmNVSGmp=nyo$zCJH<=sU#SyuqN2*-FYXDulE($j?v^oeptES`Q#Rv6u# zSBmpb`bF_i`qf41<Hps3{91zoKt-ro&z~(ahXq=Rtdzgh68sDe%hIo(inVz%Jg23P zTIt&yuZ%lf&zrdtyy>dP8Y@142&rL#ii|Xs(TK&Q$8Z0Yf?;2a-32P!Hiar<G{ToN zBzID-#|d5yxa9QkafkBPw9To+SM!4k@fSytU-Sa#im{Q%gWQ2+?1>V`K9;zoqcP*M zX+p=oi#q<<UTBXY1o8+;&|I6VAxbpp5_>BtW<EZS{V(M%jeE2k&`cMtffc3vD!Ziv zL)D{OodaUe4XGw186(N+)yx=W@=SWedQEjnC;Py{z_S`-p{LhVe%Dm)LoG?q6z_uk z<8FpK8|Ol-9wz{+A8Msmc1=V=qiYyy)}t@PD89_4PHSqFUJt>YSK`1y+v~9_o#yj# z18R_8Wr>JNs*w06e?izu66Zh&-$?)CSNoq|%HK3StonZ?j`)e@eDn3LqJ)-E{MY92 z$T;ZrYnqgpQH<Tjotnavb)pQlS%#dMLmxPQ`61>I_DTw{S_2{EmTG3QwLzhFC_TRD zqc!hoyHT4Pr?GF>Guho$@;bek%evv17=VB05ks>&eAmwNkep_Nwf3<Td)Tdku7uka zWHdPuflz|vQLpq=@v~G~_USX+%5xpulXjgsoLo8n)dEMb1QTMeCMEtA#%Hu%75K1O zRJ$BMzI}iZ^q}WHW6+ct3x16isjt+?xh&B!S725wd~n!^e6BvY7eSniIZChspHh{N zJkhVXe@=cvIdf!xPJRU3{1b2eeSNJ&ZtKvXSEI2ppjR_r3-6GI(>zBDOh~N;LFX(b zzE()kt9X?1swUf{l!4Ct-HUv;g53Zn!Akqt+AaUC^AE%`|D#lmKxe%`XHdZ=iQp-j z1?AGh`s^ScB}f!pu+tO%+V0wbn{+0tG6NhzKn0iMYcUi?hpah+yvRK-DkkG=I*dLV z+?KYzOA*M~1p<P@rWF+u(Ei;6zUg(=6mX_wV!o@*ZUiN@`#E*Q3)Eo%KrIGCk;;jP zuSADC+RR2jo$TmlZGTz9Ga$-(jI|d)s)a3s06;V-Yx5P&1eDZ3tq$G)1sx(bS#<`q z*`YvaX3Z9`X%CNLvWi09Hpv=oOosH@P^hMPV6B7?d%|+Y>%3p{Qso?zjq33P;H7qn z^nO#lVpuxr>Xs9mFtqt@I|wV>HYqCM;XpW{JqlA6J+GKsWsTaMK75GyPtWBJJ64v3 zEJjpGcQd)mw??+K4;^gEbyS+6(T5t(yzPm4E*Pron4=2^=Z02n?!Mk?YVN}^q0;_z z1I=f9LubooLlEr*8ZbV%$!D(nMy+hMu)XhawW@gos;b-|t$Nx%UCyN83<vGw_j6}7 zcg(B29Fy^PN2FBN7{V))(+<r^Lavmc$((-(SAHL^g(DMx@EsyM_6oAmM~;U)GuAJ* zvvfsJZ0|;hzpafZUVo-e@?*cf-tXlHG&Hmm8pHRww9iSxih!`z^N}?l)m}vv#Zqb; zyT^krq_uyVO-v#<%P~4|^WY}Q@)HjW5&k`tFL>uXP3Jh@ck5swmgu-rGJ29hL#_q= zj?6nVCj5!hF5={$MtB&AaDk8l;Mwz(+3N!0h?3)hd9tk-ju@mJX?7MYAGKoMr1%kC z9MfSNBz`=r{|$6yCw&-$HHKsWPX-%4L>mrV4vm?=z$P0T5;B22W5`7RbFtcpAtwo9 z7$wYbKQk$80#?_EhyKqc!!~0f#6li=!JMQvUiy^WB&Wa>u;K59pvy^|Ip~%w#(BmJ z^5Dbfv4)7uueW%;<R(O)j5H=OZkz`WB+ru#<5~W_{ddFb5W_7s%Es@8GyL?De?qYN zdml&!PzYV9wj(IvhVElSBqlqY8?QHc4bO1YxJObjGbwvq;sZmqY%=fCe>@*DKDgHq zCL#YTgfh|ZhQWXky%sM$<`4Alzd@sq4W*BVPV|8!w8;&i?};|#`j6;B4NC;StaXkL zp&A>;FkIM>Sniy=iZke;Lk;U^ChZTUPGUpwb1K5?=)#rGw!xScugq$3{yFmR<*df7 zrX&q6F=!^OBv0}dGwqY^L}^O_j^GYX&cHl7vu%x7{;^lRQgXBN6!!HA1xOJ`cXSMN zyXlA^(6^oI^FgPxJC#a&PX3*db_z%O2dFG>4j+_{bC9`Tw&a_y4=;=MXf6wv+f1Dn zr!SkBa0Dg?*QJDI@ozC2s#Z^~Hmw|<OxNxO=cbCvN*TqbQ&L7W5}4RPNno;)3x^d} zLN+kd%K9bDt>Qts{BpXD(!s4uZj!UD=aUFC6Cy{NeL$K;<Z;1_$t+moo8>fc4g7bi zdda_aJicYJ7ib~~Ef=r<j48LcQSe~4zVa8hJz1Itc@vJjG4x-BLe!5%!bi#XHvSc+ zF5&$ukVEmdY}8pkVXuVm1(pbfI9FZaHla6yl6OChGH~#7ly!0I;1s(yuQU4Qhbfym zUlrw>;aIo^6P^9!{2cn|N!VPwJNi=3@udZLwtFb@IKBx0W5!AtS>=4}D3&T$I*J{U z7B5#hk6F@}wP1s4z+EF}%{nABz`;L0i5)a+J?MGfaz$1jo$Ec_0AdfImL07PeyWwN z)R0bZx7A{L(v)^TYAZY}!qp+PO>Ot?W)y?k*kN7iDK{$|ZT4w)b7wwU%%WoJ<&Vzj z4}EwSXT#gf7|6bFJD&2ZQ`c5L!r^~^AZ_Q|wINTddctJBwHIr?%vx}|8DZRI-rdvl zJhJ=^l~D>2BUdHd4gd!#<%4P?S+2!{$I!V>g-JTgu^>$jKt5e*)1vmWkKn_OM^#%5 z-ioh%NtDAZ^t2~WKV&N2*;d8lp50Xre`YCzt|z!*BodKGr*kQ8ngJ_=-G{IRbw0}L z$zOduwK|G9L3h)-DQvK&lR52u@%zKmN`>-?-*6@)*2<P4fcQ$mkt|_bNmpB11yOKB zR7p4FF7jD>4<Fgm?LUhSl}?BI+}30nR=rcg4t0}x<JdAjgwcc<AFxS$4VnYYTvZYX zHTlH`UY~4R?UxRT^G5Y*GY-Qq9U%76k=aM-{=<{?nxqT<odXEK9VS<ITa|yzqs!la zyo%UqQAY=6x+sf2lqGr!@h*cB0iTndXp3KPDoGz;m_g1sf6C8q1UT`!#qzP4P6><j zj8jrsI)mz0(rpJ`i_h;^t$l?56rX7f;R>vUqN;rPK=yJuV5sto&QI%?kB`5$w^MG) zxw_vQGT4J()}K8rvkTCCKb4zXw~RzTe+w9yt)?8-=cij!i$W$JTrDb>(fi%}1}rHa z`{BqW=*llA%Hzn$ms5~;J1L)fuNIIUkSyjk$$!5P&)%iIcU+Xh7Y21^QJ6ffBUY1f z#Yws2vOO*WDoCH5)w6%ldJlOn2Bw!C1(ejpKDLY4H;4o;DFt^eo&ni#RvPl>vp{#O zyAjt+9UXB9-DV(_x+}pD7QcLKW@opZkPLpF0adfP{kou9mQxkc35k)6PFzLGdxou^ zuk6fhrw=zL+Xux{l3f909h2xeZP?Nl`ci>wf6n0soW37>d>{Nf!_amrJ=Wh+qqw*9 zMTsHeJAYQ_<R#8TlIfo*5TJYuP|PF6$yh=F1ab9kJ6_P7Dql^up-5oHCC%6alUB}P z1W1FwOYx9q#!!z35eQ_~FU`ydSK)N$&cymr)8C{O(g`1E8bXii4v=PaEvY6pKn%Sx zd_(^oy?w~D16Dfd?A_27EPa}Vew;RxIDH!Zf8vcX9}hj<U-ZEI@t;tHIxN#!F|NRp zeD-I5_WjTO*$g}3jPCXD%Fp|XJQT0Hv0YgGU#tR8o~KFH@%{V?FgujShB*vA|Gq+> zysqwT7%W6j{a@&^`3(6Aa2S68?*9T8j1S4=7{gOMulV-m-&fiIV~_~=5B8D#*KK6q zHhljeOz-etx6xy?1w#urEaUmVQO9#URQuDPZ<)<ZlAMsBX{$%KU@;~EA6CC>nHSAJ zD06Viqk9ruX@Fm%NNar9zz}x_4$E(ZmUUjrCU7n(CN*z_o9TUHZil4PLk~KRb{k6! zs{Z&dbnjO+VUKe~vn@#ZXSguxpHxyAc)2@}9o)D*-G0-^!KxQR<;pK|lL?c%+R3w< zJ=Tn~yl-iAtDU7`yl+Vnt3GnV@Y#*pmBT@3?EUdOqt+^1d~@gIc^aVfxH=JAUa6R} zcWSAGkO;lX&ez+hsD_iknr1s<Bl{Rz?-N@vhhayBaIl!&&3ongbzc*%EzRAcDSyAo ztC(3G31hdAAwem6y8p~8+jl$Ki~@C14yh?ITelpZo&0j%KBsZjNe%<PLAdqIbO5=h z7$#!6h2+9iQktnH?-Vefq+dX8?Jb$TY%%T1nL!>ZmLEkdmle;}bNl-F?n%hmXf&gZ zjs1&;r~7l!iCWnzO-rfcG8|4l077LKPt~J(%~=)@f_OeZA3bEOCV(KrYGKw=#SU<O zw!F_rOUVCm^91TUKUuL<#&R{BRnb1rinkp@^4vtc@m_uaVzhcCS*G(ITsB3$qmcQe z6CeBPNlPEtx6hMLT4TAB6o$53`I=8pwyu0EAEFwMePoZUn8hNjdT|qaf5_gL{k&;; zNL3Sac6Q=Qg>>5FFJ3NWdK{4~)(BHqWINHAE%wpbTN+mD@UUYxyC-dFH8WiIum&D` zFA}CLa~4{FeZQAoH0H)8@@7ZnWT(53wF`CR&-IH=cX^jttCSv=P8e6FfcwqMYO@wO z-DgjCJJoCh$Jh~;26}hyj?>&{_Ia4DLAo4B*tWx9eZ!kC1G@Sfta`LaiO@q!REA<- zoNUb;ViL)Hr)`lx#7cY|-Yui}(w-hL{R<sr-VmM-Fwq&AoZ<FHIH@XqD%>a&wdQY# z_O+{`7Y}**pbcH|PUS5XSo|See8KY2Lw$@20n6-tkjln$vM|Y|b$>1d-FygQ*;G0d z2Uz+WX?SF5s;;xf($YwMxb<Yri^_Dr_^-j@9IW1EP9{4{%*j#j*ZJO~@ztjX1|i|A zX9~z{05fE=l@7<HN7C=nG=$%yq54DP*T|-l5fbaxBX;?HE)&N0jMRcnz{fxY-lTwr zo`p_}Sd*~Wb)}lr_86MD`zrW~)kO`<38yp`(5i<*{|c;8Zxq6(T1~3`)dBHO_;19o zA;cLLhfST>gNP+yEMc&^YMb;Y-X<OL1F*`jqu`*}zmo<8k;)Mi{+>}1xu*071x!-@ z`HLcnl9%D1I7qd1v(fPVKg7NlTcRRP{u44R+Bad?gzRW*>QG$?JN}QIeu!UutvLry zyQ-Hyr_(|DkG7v8Y<`Uc*F1JVfE^6m&s22mvTRs*c9gTSM<&m!hHC*POl6lOX9U1q z*wP^*ek@yDPoOh)piV-!orONFa&xynx!;!wdm5o>c^*7&sLkTGOqw2m^kUO5H$z^) zrfSndL4{FRa_y-5af(rF)IPnA`Xuc_Sfu1P#Va<a9M#&p&`LuMF*7O9;1dBA4bI9E z9cdK}L{awz6h`^Na17u`)9?KJD(rRgiExqE@6YjkNIVvqj`t-K81Pmb(j_M^^ykPC zv<yo+#b_(Ll(Ug?Np&ZnNkZ_3A|*3sOT)Sfpfm^piQ3VTc4!#6Yh1grvpvw8NuZ{K z?7RlN(=rs}dcUQ@!(S4g=10NEW&JUk@$Xb|R5QdDPiwb`6`=lMiu`OMz;qSvE*xw% zHGj3TMQxYPfMj`SSU(9miq0w}KbJcFwLmN|_Bx~Farj{njeAt)evB3v=)+!|XJ+bh z{fRVSO5n$6Ie~Vz%SA7<{9;1voZd0ntfk%7uvgP7y)}R2KziqqWAU62fy>R2W55P5 zdJYdB-pm5w_i6cE>gB7gw)%7A1d%8(vCq<%QKiF?U(V@?$hAdSF{9-KnR1Q6z3>I$ z`lDmvOhEPI!U}&Blgh8T+pzt%k~$(^b2l)fQB(=M3nKY!im2Ar#KAzU0#wzgR#KE8 zc&_JKV!;g%^Nx;eqGu;g(e)r}R4`C9rM#vR{t2-E0cRxcjK=}0KtG&#CChjbnIvU% zC_z^&`gx={Yp)74QoYt$Ny;V$MimiH{lEY8I}G{G{H_@cR1OFTKNF*WEeGV^F0y{C z=4r=-HQ@#|vbp)I$cDX_u?}|*M=is*=Z3u?35pOf>YE|uyp~hVKkiY2t_X#y?mGhE zpgQquiMyF|P*RCV`1kztuB$YM5s^GZ7lGuAf5WJ0xcbT2M>!FaX%eIoiXD>E?D)(q zIq3c%%s5tr>?TJN#{DCR3VmFpmV77n3h23a=C+aMorw%0m#x(B<Z{_g&szc~P2%)- zzlOxZP{^Pv`?4W;Ao0K>=P~-~#{q;8s&8ug>m!o_ULW?4z9em-73aO5QAI5FSE+3Y zQVel0?k)o3wb6&JG)=NuD6tzwN`ks3zIY>8fWbH2Q@x?#nPsY^QV%m=K$5^N^Tm#w zH}HxecF-4BJIFOhVtDxyHZMG4p0{D>>T7LWbYtQ4doxsJvV<VpB(j7%mM(yl06(#z zeg&rMfUeiG3rv8ID4m`FA2iHiXjnII%oh)dfR+!YbC`qxKeuVQsG?UgQBDB;SIL9c z-DeauXmLv~;&!Az`(zcfitfkE21crnzNX){-VAFH#{i?YM94(Q>WjQ{m+8tx9P91K zYtX0q-<&rf`NMrG7S-^{feBuf>r0=CCNX-BHiu7ypkpt-`ulZPB$6K2k9_q!L7Cl> za@kq#X?t?lq`u-Mq<%EWsbX0r<8orY*7&|3@YN&en)}QICt}fqr*YdgP5xu^r_+6R zEry&}=!=BeKP6o7)emlc!WUByIx1ClU~7bmIvBLU1OMLPtqr`0zjY#!u&fKhuP><5 znQV)Y!0K0(A(s6KUgvM{X3p8ULhonmlthA2It4RuI%}&L>{2j?t$nD}qe!*>P%*Jo z5%_{B(i~Y_aAZq!aFehwyBhLV(no$J(2Q)_wYNJgJZ}ls7Qh*9Qvf6e1E2q-`s$3> zY?=TICpTc@a<}tTj8G}+%3z<<{wLMvfyE;+sB6L0-$@k78(J&Xk>*@ii-v7SlBB!i zHtm<T<oSQdI?I4Kx-Lj35S#>e3-0a`2<}dBcXzkO-Gh5@g1ftWfZ*=#7JP@ipY85H zm^)3+4A6aUovP>PDY)h*d6x-^A8ZT;>1iMEmCt1seK#GsmEeDMVUD0{3jTeILmG^p zU$p%6Ie_VKU}{J<-*gRlB0LV|!%s3jC{TZWh{3(vSQ6R9n!6QL7AU;%fVybnfRo!% z!6j_O!x4`tzDkQDok@;E`W9JqNM5$w>yDK3vx`TyapAZ-Qp==q<VEP)h?CGa#hiHk z<!8|rSr&M`8R!xb)tg*In@hM7=kt#td_I5j=N<YX2{};Go7BdE?No(cdeT|QqD^~Z z$|BU6z>g))JbT_&)B%+djCI^gUb|p_Z^7>*eMmk0NoT2ahY~9Tuy41gwbS5BR<)SS ze5Gy)OS#(i-h=L8o!|X?QM#P37~D5{ek!Vf?qsFzCT2lv^}M2HyC>@-%BCXf?3;u^ z7G7@cp_@O8Sb*=I^ZzI?-&t~5zfJiWvsG0JgrHJSN8p~@?`fE*S}Q@7RX?r_&)hbA zchA*uo+!|yXZL8+!*oD>dcCi$`Qxw){_DPFx`BQ-4=QPQ2s4kPMg$`ER#}nAdCztM zivk+;11uPXuitH$A0`q!suBw<=<@EyDTdd5@G}Cq;-LOgy-$3yu{jQ53)Kj|{QmTv z%PpMChxezb$Sy*EbR<|^$&;i)*743ixk9yquP#upPQvzCh7WQRiNTGm@Iio<HADZq zOuo7iPbk!6+j`YtfWhwZsrv)Bf1fY+D;(Xk8SiLoOFBx<@os_t4p!(n?m0Z*3dsYW zxxk!>gnYyvxRrwKYTU!){|DzB{@)9ekQZ+YZ2v4^|9gQx6UmVs1?G%>LetlTZIn4A z>-~g}NhkAq%9kxHLTJ|lQyxXA3Clzi)Xld?u|UTePq>OuliX9|POx+`<LqMff(`)w zu8n$MM3Bt-n1uB+e>)!ya)Z41=Lkri8WSGkWXK8BNE6ThZy`4y>}{dl&xCD+8T-G7 z=<wu*#IC30T#T~*^P=BQ{_nSmdhZK2z>N3r9|ruh20BeH#3eXJlu7D(%Ev4C|Gwr9 zR}qxRr%uo(fbMEs!((t6p2axqXTo$oDyXBQndoF~lQr_SylN4QS+3NVC&SloVCt03 z<KT0Aa!bK?i7l+h4#3;<@%~yX`e~f5{b}~TudbZWy>MnW>E*HH^$b{$?wE6(EoY-T zR?9U$Va|Ho#WKo!kiwjEj40U6Gb{P2y%&FOeuZT))*L^ZE+#P)TG{Nbuhx@U+59=~ zPc5Z+!DZRXxd$56$Q=75Q=2HbsNhEOaMM&8RI_~9bh-bYbJm`E&xZ6<=*=@A;=RcT zsWl`m*LKdU(-agBD+73~>B+b-_`j>u-m*@7Z|)+<*Esh328MEPJkPoSGYT>R27@8z zU7<V&+P2wH<_ZPt@r;Ka_{XJg9$UOwcDVAH6uLP)2->&lZ69;y5UkH8aa^*vNVOm2 zPFnb18?SfA+#c{+E<I{8nvRT>?&^oH%Uc~fj7l62_q)7yT!E~g|JnZ)l%4NKb1()O zY>htgnCW+|Y&DmXN>>_!_<NxJpMQ|)qwF(lKS~27^U%ohvwxv)GhvDU!!r%X754`% zhNn6{?Wc>=r|YfotnaNnn70pR{&$UEmZRJv46}{Kp~;tFr785nddpL>exAP8BJ=!O zw_Du;?#HAHzj%{OSXXclRp3}%cX&Si*}!sIWWHM}OPM;Wz`Nm=-D}eB@Y-3gR1_kk zM5TVt+9%dXBiTWYFZ?oa8h|LK04bizULi*sDxIb(Q&IAI+uTJpWOv}ype>X2t>Bud zsO~y;p`ZWiS;*lZyOe#{!ma1FdKQBN89-7tBEZb-!t);$boxMk@!Pb{zGBN+fAL%2 zZktR^t%zx-+7fw7?74&ww}vPy<63Cgd+V@#@$By5qM<p8!xoRyGVR+O2Ts1Ody|Dr zg@pC;&lVMrXNBCgmSUDA??Ogbe6#SKi)3%juotz*-A0zv%Tt}UDHY(g{P)R6N$ERX zoTKW!{vtlB_nM|XBrvb;zbi}z5@((lBXta%rJmpq<&_K6y~R&nd|d@}2`lGglD9mA zj8|enWI}dPEa#_2_*H~jsZ`E54o7x+dePQbue^dPwL9N*sF|a#A`jLl=gYd2)TlF& zrB&M@>6ith$zT|<!FT{B<O_{tDUtS2ta+GqmjeS6qqqvUH^4Dh_u%csaLW7s>n&*3 zjbd7(X4^E*pGX>ROa-i5%xNVUS`==~E|je2!<%fpPmj6V4HiKY6gz=w{M65Z+jG+G zp6q+zD|bIxxDBwlbLw*4=v9JlL@Pi7U31P}yZn_ASxmiKGa3M8S1Uq_yS*_qx8(0m z&yR59g8&LqgrNN^L=kC=Ufxg`V}HNN!^{0?d4DpiwFq50OifTXIi9jatjm~mVgGjG zH_rcc=VtW8`ndA$i}jQD-KPt(&K6YXx8OwUpr0;B+J`SU0WAkK;KRa6^Dm;bz&%Kw zi;8ZYlI&2<$z#F#WTu9AlJK(SeiF(RT5nl_di@%A^dtk`VnrU4+b>NCi^FGY%=%j8 zc&SLa$PPezY(@`|s$&xm9yde|9xQqfn&ZGAMAT{?k92=d99v)h^B4e%O^o;f<cSb` z@P~knk;JYl`X~pI%r`&kR919=s?K*JI%d(nQOL0?m_fkw7J{Jt0D=IWhDy!iO7r|Z zvTmOVyNV}#Y=7L2vS$E^B<<a(+A)kD`&F1>#tT*dZuKu_?tW_hz?D%Zgq2@JvYoK3 z1A%3h)L#U|+3h%Jl;h!s;c5pjT;PNta)y(N#HvEV#-HFpZXZ~ogwP@Y)dW%~fn6S^ zylxd<K2{$w!5)R%$skt#6kwr5ly^cAx_QjjUW1h%sy3WwbC=j({4SWI0>kp<oZ-+P zfg)|BIrIheFR!YX&Q8?%wSwCrAe1`iLoP>8Eo``A_b4`;wG|hn6}QLt19ICWqAffw zITQrSG)}qz12!n{(OnIo<c9K)YWB&kCQ|c5cU5HHp$)pq0`-^dzq%m%S8g}H?%40- zq?Tg0IfXa^i2k4WeLm~DaX*JZ8z`xNb|PAqRqvbg`%T6nE1FV0%#WxBIvZtEWvaE( z-6Q+6GsiameSb+4<FxCyjjHB3L+!GIp@KQoOe=R0kM6tV8z8k^#me=!yPuoWA=W0V z@i;>*lz;4+O@Pbo(|t+J))Yv$uXrfmd3c^$GM`&n_kEn(no8SCnu$7cirkud+}k@_ zYk$4>I(VwRF7P@DV!aFAJ1YR(?cCJe*E)VWFTDn#NFIF0p~u+*+OChR-#NZQE7&*P zk#|I;+5VKi1FrhNe;_6g$Yi*WwUbr9a~a-YUGt&@n2M8!ZUS64)|e%)L4AdtyR)^C zIh8gcloXz$#Wm}A{Iw`Px@gJV(>d!ik2}t~PcA(zR~syiI0Wh^GIbj+*6Jtb+drq= z&YHFH;3fC0XJJp%TGXcb4sNuV;m-UYxA-zUN}X|LfYWavs&Gf+<cjo0UCUJKh|Y(s zz0}UmmG%+#e1vE11BaIBB9+Ijme<$aXNlOZ^R2WIWZIe>e8qd`G@Krs{Lb?c{7*ya zFqNOpy5c6VisgMlTbGXp1q)*eo@rW538|JuT2$omLrt7A7`hXJIn!7qEjkt%-*>P7 ziax+5E(NPZqi+{K8)3k3hPcDSGyH$*%|}1dbaOD81>{7ATvC825u_}~!hF$WY8s4{ zl9My`f-q9>(2hO-0Aci2d7f;9#u#H<<eh^aRuQX4k5BnW0jkW7aJ6SmS;<dn6=?Je zsHPzI|0|O~prEut1OLcC#+33E4xq8%0*$>~0*yuXd?E|k?omb&4867>&4p1jT7XBK zMqGLIWy1z$lN7x7?A7>fr^AYz4l}+?W6})SRjHP$ijyr>5^0%epChbi&`3f!id}*_ z85F*vra?mczfzWI$={6*T{5X~6!5mwX&Q@`;y_j@r46EP+e3qv<#-dbe8`u&QN*3E zwly{k8bNO;47H~(3%{qXghg<|J{OCY^G(<-ja#DAKy8`%Irst90%U^A{+9_3lGM07 z{p#$SXryz6fEyG2dS{hfXL({C{0wv**l7L))qUY~Wc~a?D1bWTh~Zc4y6LRVl3+29 zdI?Lpq<Z_<Hez^YY)v*I6=GR@Tr)5tP2QEFv)PW@|1XDJS>oi;L|Gl97Fs+PI5qQM z1cPMbtO6=Cz{rGVLL4U$J0-$|HvL4jT^mTWQzn)(=rLaf2__sy6{kn(RFo|ym)A|D zkk<zY#3m|&Lh^zCc;ORGC>bf@XM|uamh<RQ;bDT5Oa+JORp(SmG=+%Xk$}0Tih%Z| z$nU?lA)42!X;p#MX(}MUAFLw*TNe<MPT5sJ_GNh|2JXZ1E}DpEg!<E{7plou*Z8Ql zvKf;3UY;~DN7LU?!7`ucKXP-b%?wt?19fSF6}Va0MQwiACnEdb%n$xNJ`K;?aQu0O zbdJp`6@yjl`jG%}j$`#XWU%`Onk^5<3nHg>p`prGA#|eZFc;Il^<S`WMup;pG$MJk zY_a)?F7NU7<Go%kmRpB)Uyc;;w|Jv6?C;hg$DfXY&*>Q)<Z&R+9mHSH9gwi}q$v#7 z2>WTCUU$tDyvzC1r};OhMR4#%I$~MXKejtvdfQI?pjw?Er6yCaDD0_&CBD8ycyV(W z!EWnM&LBqz`<tU94g8~!5&;WC38cLq<(nnXge++MpDd->4jHwC`I`Z{x|+Tk`REr& zbb-As{Y(w083`kjeWr%~B592%{!hI0#S`5np~n3r+Nv?kKxkmmHsN0x#}9Zj;JRgw ziyNLE|KvBwU?_7}GX>^BEi~6Vg2s(f5G#T<^|~ez%|xL9zEX)wfDQW>n^XE6fy~5& zj;40I+v}mAr4Z=uJj(F*?#v3b#;URhN}oce6w@<_LfL%uIE0ytGwf{HzrOv~#@hNF zP205vse}Z|bdAw?gvvwoQ62dq$zpYz!mn4!@+)rollqLwC~Wq8ik+v=$@Sa++FJi# zJ8PdEgf%w(k+)O_glOQ|M{91a@=RB2TmWk|DpG3bOrs9c>xO}JZS_fc<vd=BhwzK> zZ~&szkgO`NBmP(E#fOc9H)jXCsp&xe+=@G=)>^@M3Cvz}DMEA8p|{z&<FB8Q4=dX_ z>-8##T3aZ_<H$L)Mn4vwc)-10hrJv(L2jwAJ=08W*5^Rpx<_kME0t(;8c_*lz_^^4 zJ0MCnHuin6O|xgWgoR(EJmB7vz?7(l{8HyS!%#<1(QdxqA9gX1^NOYE+9j5|<(7Hc zqW(@rRsTcok3amw)wmc!FqBgDMok9+U$N%4U17rXjb*moVmA3IAleUXh&ks5mbU7G zW3>w+u9bKw<G4A+q+uv=>rou^fV!=_V27TD$G2jS<9z{VaK^`%%-$d<_&t#h{t+8o zBj}rgRHTu3R0VS?Lr?L_%xakf>*U9tx4vxH*g0c<t1Yu>euwRA#)<~@TFD7v+I`vS zu4locd4jP?E4G`ENtozzhbGo6L7V;))=bVU{vTV&O?r-tH5*YRUK?QBuI6Y<f_o*9 z0$ImiAmK;fh9o~v!EpK_&pmRu&%04A2r0^)I3G$H9a#FkUq-pyS#%(N&#Y`x;&yi; z<xOjV`=QJgPc2E`XUX!qPei*U<kl}q!bCrNu@6_qY|iS1lxB$@b>syY76^lNk+qBk zh(0*XAdde26p1Lce)Izf)K#a2(F^I)jO;7>7SbgZ!^qxWP7L{7(P}xEQG|BVJV~G7 z9vyUezaT?``A?HPvUz5z0B^Do%S=p(H1c#aszBX-n-G(L85*N@UU{i16hfylFWovj zqO|IT&%GGU{M2iUA3Id+w$8W&7}#&x*pSaIHrU8j?9Pl;pS1v{M*<CtTRwKzSR&(B zNvqG?G0!%UcmxZtJ{KI~5Mc<E+TzAB&u8n^ue3D*$}z`4`~?jop$H~rF_8%J$bMJB z>k_4~`VnQFv{ywW#nhR}J!RacT%IPHIF(U&sn{gwnoIR#l@_%)VAK_=P%KrAM2wF{ z82Pj#z8N@NQ2<Ediq~nY1mMQ&aF8*>@dy2hyJBJY#W0#U<8)!8{)wT94r=7Q#n6aA zA&3xNWh9ny2&9Wd(s6^pkcN!;M)643T&cIv916&oMn)Y*wEMBT+JM?5CgJ|yml|hK zay!L{lveHJ$B2Y^Zy(i7y0bnU>54KC5?I!$OjaMa27@PwX-T?=!qtz;@$@&H_GD?# zB0CPlmmA;^NX+&5eudCYLem3kxFYB>(cbqROZw!3!n?(RaK6=yEN@LlNZJuXJX)BB zI1aQQK@x%QYVn2*KEtIBe6=2rYYzQ_XMc5Mi<8)kRXxvZCmSsB({ge2@+UCYa`;E> z);@Wi|4Xti4V{Bp`OJt42$yTEogN`J)K@fcsNVX~80YRHU)Ji?V|9ksap8tHf!9iY zH#K|qvg#6j`po@wyQ}MnIG_5@ALA$+s%Vv$)XC_Rxyqv1!kd_73S1fWALPPT{bTVe zZ7(a^sXjeaQMsvOf9}few7`KooRkOW9EPARqXbSH#zG?tX65OOq!d-x^my7roodz1 z#-l8+ilrcB|GNqR*v1kdbkQcnP@T(5!p)}~wp=p%lcC3YET3TeCyd-HGV1)_v0@oc zsPKWv{g%_ff=dQ%A@0d<=XMG8IE{}etKnqNH#Yt*_s`o7k$`7paOr*~QF@3FW4A5H zWnMR+vYwQBJaF!c(ptQ!4#ML;S&pZpF_6`zUvXF+R^qIl{B;Yz@o_)W{>LrM_>ij# zifBQDB3hJGp<&Jqr^jtk<S7-uxxcyaSE#wgNP#A9ZGdJavUa46XCtS2ojV=sP~&v~ z9%%Na*GISiM6?>s3=Gs23ssn#qEM3T?tJtWCq6fB{z%YLIhrgG4-k!<lhgk08suIV zP~XHZyC{6=)th^InOc~J?{MNX)_SH<xQN$47N>uGipJjAkakkpCt~wbi%0e#XDC09 zJEqM$?S_jprMlDnFyHfVYBIOD1*9)*x}IjsrEoKi$}W6)48Yaaif(?-A1P{k_{>!K za+9^$kpz;EfQOsok)0KQ{-sq8?{eqyYQ7WE?3(|6R2{P!$x3g=x^7doip>F>U<@tO zlqi}$@7kQ7S~h~VCLcX<nc<{a*=_2$#bj##=7IV&F|~>LPoC!2;TQg202AA$2D{05 zS;j)b;-L34G3~F9IcEOlgQN&rgpR)UZSi!NRghW?0`x!Nt}SYuA3TIte?$F(*zewT z(pz1x{?H#f8iy3c9M0-rQHljh+U;6EE<_k(b{$W%_#4i^6YKX$5KO|4T^po!UCzSe zeOSL3pb=E4`&5+Gq8Cqx2S{(-cU#QzXxyaEN_ve*fwONHqa%_=Llvuye>Ao}D#iXq z4`c$Yg|#<VXjYHxE;>JRXU{O`o69EPerK;}6=L-g969&tmS~ht3ZFd9Rk61|HoMxn zcjv)9&W^sExYxlfDM5K$LuW9Lj8Cdh5TH424bn8(8O~MN32{mx0%WR6DCykhG)m2w z7pGnINP9|_W=n(5MWmilj0NHCRzi_cNtv?KoW{3a6Jkb-%o3%6mU<Hu_pY;ffflQb zVGYr(Y!R2LRk9k9Iweo=$l{y(V-*AqBz!>@g9w*&pT(E8x3$EaO}Djt81a_PXsP@L z>2c@OcN!C8mx-h`fr6fXYD4Y$&|E=^h%j(rIHUZ8tWxMVvcy;ATM^|PJZw;mfRjay z5Q{Ys1yVZHJZwh{F)4@ALL70RnC-L&GdIz+uDSsJd1<3&mLWHrr2OD<t1aT7^7M;` z2q`Tyk=FOTS?28pX@)x<;yglVD;ACl_O!TSlklNDG0k@1X9S@EyP+kO0SEw+Ea3K| z)nVQ0kK-?t4Q<MJU1|C*>|z5jS>M9!%W>Y8Fa=0b)!6qo7!s8tVeiLeeHu{uqO!%r zP<Y9rHq^Ys46Ae!Dv0uDr6^*E)L?}rRPw}bP$`g6VQ#!{)naR7V8a~c!fGwc+Hxoq z^1voNg%?49Q%PGAjwwvec2eBXmDX7eI}&X&k?vT;>EPt{=a2(QN7LSG{ts0ukVvCc zeu;2_jqNmc_|&~OC%ygB?kg%}gnwBen%iX5@o;Y>lLqDS&(v2&q_aOjE1=Qw{K`xt zVUy_9aip925NZpNwDFD_KR(ohX{5|cP_t_QdZ^@%iST^kxQv?a`zTZUo0$XREWzo1 zW6pP@yOtr?r0H<DTklWK<g8f=ND?}qzuB%K^WS?z6`Ki6EAOplcF<)+J0M5%yUR7> z0x4a8Wj{LB%$dw@l39Ls`E#@xk&vf)A0eR-2iXuX>rn=7FE*oH_6iA9;Ejku{f`HV z;o`|u<^k_Z+lhx!w^7ErMY#F}aD}Ciey<5?0h#!e`d^K~bXsqXCBu_c-Mt^605gRX zsQxu^=}5)WD7m?fub|gSC;zF|*wy$}|6*(I!Ws!+q)5S0=PJaGC0?hDM^*5pXJ&OY zwt8N@%mmNxvtrMBZ0#NSJX*7vF{T&}2d5mjrqKA@cddM8o)9g#|4)Ny9X~_jRh0Ig zvWPi1P{sXVF5Nr{Kar6oDH{t04`59rATe2fQsufYMm<U63;1#|u!T$;S)v7Uiw)NA zy{S>Kb~;f8){LL-0>f$v5&zb@E};oCU1RjG)&>&BxTN4&m{8SdldU?}G4b;HpJez6 zf?&HTiG-_5GeyIY6o?=cc^o*qw~Hztz#h}}1EoB=<DGI^2fPNPX#wuQI#Wb~M4O%+ z4Su!RVRQtP;N4l8ZzPe}{o#$9+|LQ9tc$od(eKY}BI;?Z2I4gyYIS254xD%nLO?X# z#f!(DrtqA%(3?y4yO(sV3A$n#bS-cu$K3U7Q0T3?G#he#0vg57fy|BPiPiPErsL}u z)95!xKOShZfrK0I*V4c7BH7ZQ+^(`D$aZD1GIl?jeX3W(tj+u7zoH{1AaX;IVXCZF zt5qWQOX%zU`EJruSxA77*fyRVFCJ#or1FW|uSP*7vfYS@^d7`SmJh_~pAGN8tMvE~ zm%rE3)cFu}BCJD=Jo++Pa`GW6A-6=}nJNH=?7Cdacb*pJoj1q7w?h2q$CBS0;2`_E z7qSoTeu%&}qs^6pi<s1K_VeaIrH>07qOVS+WSP^29m`4W7AJz$b-Wr;whv{3QKSf? zgzl#NVyKA75J)QBr7(9}ljgVVN7)E9std~ik=#91fH)dwF0Q!V8<#KTi?)PXJJtuF zN;OmNe@yWkTS0}lbJvYTlaVPyLk!eH3Jk{;elHB=OwJ}wx%<`f)!;j3Mva~fr@t%> zURoaXj6G*=Hcpf@a=8`-CPW07NL8&oG+|;S$TNmRoERou&1g=qZ5gK&w*3Q5B=7hu zv_$BFl4TsFh-DmVfAgd`)!hiyFj6X@#1UOoYkvtH_%S%YX28-gS%TXRqp!|<zW;wX zAY@V1r^V%0oqBb=UmkXL%Wackg1abGoBo|y-)#s>vrHXx{^Fd1bJ@SOu_etM)gQrR z#Ozmfp8WWXIRV)Zhdv!SO?p8JR~e;YEjeS|k@a+SBzy!sE{lFlT>(-v-Y6^AY5%3H zNO;F|6E4EwLl#Lq?xqzf;Y4~@zN^x;4Q(HYB$;fW!IjTBYcc7Qa7+(%ZZBD9ran3S zbtZesCbT8>^uHmh@@aK*<h{C{A*;A)qHzYvF}p1i#D7v$oDr}<J7umAGZ|*eG`uC% zTCi;K<muxyQY?v%qo9J3EqhUfHC2~v`V+-Kr@;Ak-Nvu)&a6pFqIlUWAm9oO+suD} zD~LRQfh(Zd0S^VtMF9n9o*55w5(6%ZTGSWd?l(X9N+UE5Yh&$uwmkX?UCHSsxY;%` zi*W*gr7`DuuAGggX_HLTJ%@%0@;UNF*Z+x6ZO-VB*`%1QlDW5l#lZ*7wLezZ-P*Qt z+VeCNPK>?<*R6SQBt>2&SARdan%TRu{{UsypYEQIRF0+ZW<a$L=9A@GCh+`cFDq+e z&G|ljuc{r_EWiByOgC$ZX*f{iT#|6Q-^_M}h|fAcFX**Q=~0(0O(x>Ufgc*&%K-7S z2V?p6U`(vM|5S@a3^srCa=$y1wPvtI*@!DczJOhhjAD9tIPXq7rS#}vAt5v|zfM+} zOxgLkI)JH-8+cYvW#)Q%WQ~Uzt_>c}`yoY5_1#W~xrT``mzB1aNey7v<oPKDHe4Ie zL~3mw#?<CDX?Xyof}E<dI&FWh4}Mw)5v0<y+_$qDA-|ofKH&g;_=Ayp8k2nU-Ec37 z0qB$owDPiwWE)Kb{bNimQXQ>ak1x9qk2*7J+Fw($r5+2VstjD^3XI~1><g;Tm?#CE z^^?T)lgyr5q{0(`1MsH%+xruztJCA2JC`+H?|av)y@P$@YrQ|muq{$@1#RC7u9zvN ze;9*(-P4@sWVZ_{Ohzw4n@d61K>U_?JA^(cy&%yN0O4Jpp|kdUx^}PA-VLVbo7T|T z1d4oRgkO|*%+n`2G-XExB%j$k64QyY*6f<mi842*&ycwT3{OZ&(*feoAtM*9S~7Ba zCkAwdPCPiBpBq142?&Crfui?2V_#hV75SQjo*#WAvakEpl->}+B#Y|rgjh3=X0Y(8 zsAad%>T)r_Y!NsX9j4>?Y7CUMyNcv-RJ?NGdg4BDnz!?2*aWss+GeO@+7K+=bbG2C z<<c4e#Ftl0NcwjtyJKmzM(mZ!U%fBmv%XRNI@1Va!Mc__`6Gf)=+dS5Gmf15db%Vo zm^>xc;$43&H)|7T%_t)3jy9!(A4xiIF~yy-BA#X8HvV2e52I68a$Idoi!!nV&By8w zs5K7`%WLFyWee!$0w(=gstNm^)Fk0eH<{t*KpPt|bpz)Rth2d&Qi7=yM&u#gdA!lj z7E}Hj6II2u@dX+lfebfwLskEq&pnHmnV%C>o15WCi%zS59MZ*~S~+3-Xk<;L6iC^O zBR08;+P3s|f|UO{zB}N+=8$7aBRbKZ_<inxm<vRX@{=~Kf*FIEDd6DhY7#a2MU{>6 z3xL`0o#OgY6eEeBpDl8p?@81j)rSHr5jg3F34un5HkhZcU?oCHK`qv7EL{3IjFE_? zZqg&#aZFe~B>V~0&z|A~SS})F9f7ZW=%>g|3Wnzm2oa>*G6CJ+=0Q0rBD%jVb>p=} zbx{i!8xag5)N|1OKHc9}zTz!HB?rVL<X3weohhk_3Usr(^FD^2Q~&1LV0WcCv93w_ z)ER2VyBDQ{$GTX)b+zroVvC7;Oovmz%7KsD!Hi2_Rh9i`hk_TH*bTW4!+1%zWI^?l z*+<%zcB#KwueaUnzpQSjD%TGVF5CtB{8(d^f&7sS3JSszB#D#yKo|-tB~C1ASUg*< z7-uFPBXU@L36XfdWh@%y;@503dEsgydr<jl=7LH&rT;^0gffnBR3P?~M6BOd)IahA z2@x{|GO@C0SCg|Km_d3^?7En+bDqv6Hyd&8fZm2!h@0T(lJ!8VxEazFBvP+7mXIw3 z((hTKO<KwH0RR@shP%q{JNAVLmj3EnE^g?|Cn{(Dmv8Cu{4kcGQF|#!gat{Sep=Ln z63S{->CA%?i6P-=hDPl5a#L1DdVIcRxQS)kE&`&A^RV3$=#%t<f3UhI-d(=C2ZwUY zMeUwI^sa~7FoE4LvCZ>x$a{*~r+6<afbK6GMY1ji4C=oV6)>FT=Z+%^>xqr&(<QIQ zWH4m(Av#GSY8yWbh(ZE=HZ?>kL^i~I*n@hMQ>~-lQG{~>9E{e*Z1Yxz=B4V0iN&TQ z;)Eb#V@SlN5=joW`UuX(W%<hY#d-WmPlVzuRgtIkWN8kFL?!hrNpL?3;QM=VVxu1Y z;=uj@p!8Z}2{DvFGyRlrf^vg5ZYTg@fi$>KK%awQjQrcig&*I)EjGb4_zePN`vg-R zr>4w88iTgYnEb^uPmj7XAr$oPMFvd4H9*gVxCW`*9J&5Hknh}lp5FWMm<W7+f_H;= zCXDtK!3XD<%H*6%>m|%MWAAXP-xNCqa!GuN^+ch#xgM)>zwiH=pY(+m_(15VCM<_Y zRhjFLm!WpTREah}+^bAyUyUXjn%bj`&!ye=rRWaXQwbra4pd1qpCZjL=E;nc5}N#8 zQiN~uw8KAp1!vE`d4uImzi8S>z*k8XWH9_tK&qbf$|>Gy{VyvI_y?A^zZ+z?b=Tq| zd1V2L?5Qq;b)NEc()qN%71MXZWC#B#rhPCnRzWS_dvPQgmKzEB=hU<?$-DA__;ZO@ zyS7`Q3?Yqr71#%0$jRHJV5Qz+QGTlcbtj!5U^49gEwNXWj&uk*fhUkz%hSz@&#>-G z@Ohiwp|9J!!*;n?F{V~LoRm17^qJI2$|ho)RU2=K*$sE_F0f5Sbi}`L;?J3XX)gXj z&YJEkWl01Yyw^vkUCAi@^P#ik<MhM1U7<R7jfJ?LAviNn_N-5%hj;wd&jn=^%}poX zj2EqQSXy%;fd4kvW<3U;)dJPfv<luwdX)Yh&6$I@IM-Rn;iGbkORcU}9M`g8Y3WLG zpBrwDF?zYeL-V|){i3-SS6w3`v*T!tTn^@j&-Op$yND7rb}qKGi!+V07PeD%Ca1IP zw#bKR@?KhyAHh*e!A&Z$X;5`_DUxk#lKVyxD0<|=TDM%jNpq=?;Z>sdZhzo)-F^?Y ze2cYArnOCBWoA)-!tyj;iS1k{!C~B_l554o)UC3}z9y8vXnKu@N{h>!W78}eKis2= zbRwwIRrqaj<;Pl9>1gHtNJCf4llNtnNJk&0;IiT+m^~^?AsqSrP_v9@ok?+%cWb%> z@TO2<NBDQ?O6kU9mX~cfWP;Flc0Fl-(RW2}=)2hb%|9*wj<YZS9^;hM#)<lTpu&vP zHgobOQMGyh2O?ByExxJ3^JGob++sg%hmIe@|BptsCT~Q2!Qk<?4#QclEt5AgreL>& z2_FO4{nvMbcb&A#MAJx+@L9D5-K00M4rh>GPF8u$5QpxN{zbhRWqk)qTiaxSrYc5= zNl?Ui^nE-&G3K`XNyXJEi>+IIHtM@Kv{Ta-6gr=QJv$(o?{!yWaUutw%#q2Bsk9pf zT?1*KwMBCajwkRYPLW26S{)8eBmp<Ts@=m#gG!cR%`8Wo{+M@}^ZngjN9KTtznCOK zBEI>?s&1{?x0ac(t)9ivF%PAnd4-aTHuYcSD7oUx#=Sk4cIC(k^{a8+TKpyR5e9YZ zs@y1pnWAux;HHZ{)c6+pkB3ldeLlz`?|XG8`gF%*sy{S_9`nY2CI%RH=l~PcH^B)9 zVj)BxS|-6ORX!#{M6c)<Ta7$Pg$y%nVuQTuL$chjx_3`J_tF><|3g&jQ=AwJd`YIN z2zZkJ?aGk6BlOu>bZ-0o$tf4B&u+nc)qUQGz*Cr0y5s(u@QtHy)l2E|qw)EHafHYa zu@vWt{^3GZQ~-J?vVE&#P_;Qp*m|lB4>IW%0%yD!2n}udb5+Sw;`C+lu{obI#llcw zf^|hy?kE<Wd1b&7oO94!(O$ldGSEO<_xnmQyD&{c5qsS*Cw&*Rp!}*}347m&5+o__ zVEIfM5f+m-CUwM&kSSf`3>XZbbJ1!%jcFTY)1<qi8=w}K!y%B=LRtl;p-}4E8K)~= znWt5sm)+JAr(8|K9)_ceOk-w46DrY{SDD&8U>&LZi^Npn2P=s5C0z~O1Z-9rOPenC zUfw=A9eCo>v*YtY=8s&kuwz023D^_n6F>81-&fr>=hsYjj*<HFKO8#{L=EKETj~^& zQkkV70SEZ-GY3oKVe;G3qd9G{(=fwou9c5j2OqAEV=9|5Cph~%dko;!L+Fd!*jOd- zc44jpN+XRT8O@X6Hxor9K^1D<x7rYA_UAy%8VLd$`o#U0c3XbqJ%Zv!BH58N4sMXh zft!%7{5N}FBK8+gllkt^J$Iv2R~nTD&=p|+@aYY{7cOg75HQRCI4zOfYxdwCCex&` z`(is!K-yuz>?ub{zYMQ_K)(!_-OUc)nR^TxdzZCvW?`kLnL78=ISE;aUTK%ZA4O|3 zzC;|#bB&hj+gf}J(V@3jf`$#8M+-!>t_{Oy<Yoz6M}@6w6`&wmu!rSmFGU1OEm1-f zvk~Q>DTL4i*CUR0P`mZAj(kTb0vPHCVfnF=NDu<E7xSnB;&Bw9<xvA|k<Bq(V1*;5 zuwmsn0$sn$g{&sM3|LWoAfFGMhX`~%7IRw_9b%_NO1b=jUHy0v6^F-U4IWf^O!xJh zS_=8@mnnQkBhZv`x9IEjXg+#?ih#QI8Qm>9=FSw(EqXTs3+@U7s~Y@3&IHQ8X($}5 zXjOtssi!ud$>Neou#D21OX<hZ7L5E>$3RzsP(qh=@8*+OdMP`p`Q^nZnc#!bpXWJ3 zoLX(rxcLjClJSWbp4a@jFKum&-c@yZMHjbrK4$VgiB&1)a<uc4KW58;FQ`{3IGFG^ zDMit>3o`>8s)@4S2-tCva}(vVDn8zCu6)~?N%;eN<NIK#?X~*}+cs;gF?uJ+(3wEQ zjmES-=Cr45<AL#R@*=xd(1`#o-O;1hYu|l^{NB}gWpCnv54d@K_+x1bSS&w#UtFyp zJ=V}?VX>+#Q9~oy5J2q%ym}+^A&;$mBST&k%O(c~PZ0DlNVN)WP&r>Q5+f8PUHxj< zsWOjYnn%Hx29IH$=?T_uUd%^2blQW+J5g>+S%f%-kH4CQEa(}Q>rs^KC`$-pjwcLh zVs~qeVNtr9LCfSRL?@=99KCyJ;%)7}L}YxgIrMH;9*@XP(WGr1$l6eQ-t6Pc8cjP} z0S*rC!yEQGUY1>Ns&90v%D|8=1h4{&5Pfg<$I3<N<*T|Z3uNvKdwpMIb;>&|1(PY* z!V@tHWLh{|%Ren!I_W1LXjbe?>NB_ZhRU~8NIGt`hgp0SFhM8NADKNYoSkyWBHCf9 zyKZcMzKv~I1)7lnL^9}U<v?*u&1644V<^v1<JDL_g|w6SDL5d0SW`k)d>l?kBDxQr z*o)5?uR6~#g=&W(x=GoZ-AJ;ZM?pIJr_~>Yr-JdtxO47EfQ~MBVw~@JwdC1K2f%qM zw|I@4OzWz*6vwDY$KNWRxu(NYb_xzY5xw%5c=#}MlMx9}Oznj)qlTh@_=3{hzia*Z z;y;xY;K}~3b-*4iRc$K3n^CrEEzs&Gu%}=>@ms1&q2w(5Hj@33Dv*+M0`cx!93+9e z!m<AZQmt+v+w6}lZ7pIcY`;9-^@@yQv96C8kffB#67s!esEfSCBz?*?uvSn{e#vCF zwKV(&68b^_b9uaex9Q1fHsTJF@ey}6O}B57@SiIhb^;`kZqaq(^H{HoSk>U5=uXj3 zW1?arC>sG?LOEpmWdWh1sUfQ0L!ku&a4MQudXv9N8}6@t%Bl;(!ZRe>JK)IH=PLJ@ z$P}J-a|_~u+s!lyU=z|}Pwj`kYhax~|1KWe=vhAvOep30NAUNf56=r*GZoNExY4T( zd&qEgOXYgd1S?O%x{#*{Y6rHsI0=ZA+>Ql^)Y&wS%6xldi_o<&BK;w+Uj1Bto1wvW z{alh(OAgV6#TeGY=#OoZl346d3(dR7IWS|6FmHUvZ^eu}H%z;qsQ?#B?H(FEAQ}dq zbz9j7%vu>9Zp`cZC*r3jGIi02_O6+$|3qV*HyNo$Zkgg;qWdFjtsz&L<6ocCaXB)Z zhvkn(IF4u+^hm@H;!?o)eZX5@O|Mddfq+vV@{1sLRQ+XHOQ;hJAx{)xks5DN94Kz6 z*xy7Ru!z<xYR;MAkt5?zdC5S7WuDZeXoP<ZD9QVu#eF2HSa0#`pR_AS6opb=BUCOF zLdt%iDx!nlv7Ff2XHzI#k3k=HrWT7z)$N?^<={r1t@QsG=TCMLK*oBxSg%J>ebH-$ zLxsv}ra1DC$w8G2TeRX!z1h~>0t^QEc>eO0-N(2OHclj)S-(KVXep=|wVyet0<M4k z!(iUcAYEit(Ayo={EHMgtz5XP@MC$M{)=D+{{})Z>w0ey$tjQ*HkT&SztDYH^QR)U z)RPyDs}c&m%%h~1$WqVg^$KHg3`Hgd+ASY9pDbEdynT^LAIqbKDdd@4MpacUdLz-e z<(|Bp8Im>312_qnWq=D8{lJ(MN#^E~VjA}g7xN<5(phLD5ABB)<6;A%jF|5yiQck2 z=?kf(S=>oC9TVf&m(w<_vh5(p-d%_J^@Bv){o~OrRmXt3sCD34g%&w(y(R`udaXzu z+8y7?rO{fCaurh7<-}*zU~CK*F;Wq6Xa*YXSF-OnnAK-f!~m&Q0ZT4U-}ba5vxJH4 zNWAfrv-e`f^ZnUc+gf|K$NHDFyUaHs=PWb<@V}+`@g-hDl37JHUIJR)@noadYerOZ zN)ivH(MyRSLD-9fee&4H?67Clea2+>{n+qC3wB4N*ImK=<ILQ~9J@`)?QWKi=%zuI zmunmAy?AX5uzYQT@;-av1BE3H4pMdp3fT4i$j!a-hY&%Z&tEx%S#hJ@T?m8qq7u@y zyyJ9tf9OY0ImKBZ{jlEvoRz;GczHiQ7OoL~Ma({dmOCt}f&%MhhKu<;YANo+5sDV6 zL-SPxr5vu~j5sne8Wxc}{;N;_9@svy2cO}X2j37B3qbv~)aCS5y05RItR!n;N#pe@ zs{*(`ezoIrf7G`Ci#H{NqWG$}G#~Qr`McPI*7MZL_@|hFceGx}W{=!4)`-$Lj>77m z$*id-zSM)l<=I6uR5jEbwJfu|GiVgl8vRMOfetU5S35^7;S(L0jbNI3SLF;gwrXHo zLbvnPx<I;`C6=er2x7Ma^BU72QzBuFI%Hw@&q6iGAyce$`!vpGDQfB>uU;nA(<K^4 za~YKgr}I`rBejBI&MI_Do-EU~|LJ~)pAn8L`nb*;E`ADem3o&mc(Kkbc^ck(x?hT- zc`zCL@R($hwbuH4)3al<y)(Cj006+_GnQA=XQI*-trFzzU2qM~^Uo`5<mF`<jrBoS zpGq=alIILCDm-LkT=bwazaUp>Q(Ekh82ucT52fS+`~7BkHY4O8uMSJ==y@M(j%TBx z!Ys-M>L}GVX|;36{t86#eG4juT<sp}b)O|mSrjXASu7N#OG5>|IjVsFSgIlhMFrVS zBXK&`+BCQ*yL+YeXdW$TqWGEjkV9|<KCNQk{zfS$9g1Q3>|S~Y{Eo}=TY;edm*?40 z_I$aK@9F<BRV6J8nm<NP71=3<(Oo{TEBRgge@ucg0Mup@>+XFc#ZO`I^sY_x(1O}b z@nST8x4&n|zC<@^r!&e0hFWaDbtOa~)eK`+qT#DSS{HovI=#|QSy{0_iJOZ6rG8fX z?Wl|uY#m96kHoA_waDp3^$O@6a&0;`EVL?#51P4hU#0}`WYmjVlx66QhjAIHHRzN& zfQ>&_g)yEvoZDJO-g9IhP=5vwAM^ikA7Z5fE8k^S#tIa;p->_q_WMGwBumSlPSr!w zFa`U&ctRo8E_l-Tj?;?ylS^7w599A*(G2(B#cOjP>xB>oAaP2s&xhM7sO4{sNU5Df z=&ME&%?O<nU?W?y`C8-)@Ikm>g<fR-ARsdBHLLCJ*ot_hy=`qxmLJ;1$2vkd3peBd zlB6zuQQm(47RT=g#bkjPt3NI4`SQd|x_%5*ER_ODiK=G1OaB$S!wk%k%k83v%j+^S zsv0bX-w+~MPd9^{ser#Ph*8!5HG-kg=skZ4KVp8wJHJh3!8&y4VFLmY%oE+(aNrzz zB23gW#q1o~^#hLrB7Ufp>D!m$vSRKMf<&XRACW`9hiE~CstN|!bt@`JCXh7Ax^Gr5 zFp0Po%$5=#z|hq5(U39eGm6L+v0I=E7RGrUC?RK9Z5;LlVP>|=hw50J#r6dcJmZ%s zNTL#W%eKE7>1f?P&OnACoY_$V^ej{`?^eSiB{oaCmXW)KV5;*)NR?B5M@popEBrwB zXtGqwp)|Se`gEhIcukp7Nxozq{jmQWnnYqf75;+bc7J7XRDHj@f4FiqSBLP^Y01lh zdtUqd!}+^~X*N<z=4%vjjLY&0>ju<N1!aHldW}opo%(G-Lk>f&OTD=$phxcV{h+*0 z9Ktfwdz}S|N!)T6(IKdK&-ubQ1Z57V{sd%YhA~VD-n=2}MLn~cM=mR3c=Bjw0}iWB zTZ((oaeZ|dLB<@%f!R}SxeENGut?=%`2tkn(R!)=^WS)aT6{pi@5}dcN-Pu)#9+b$ z744C12*N*!^o}%mD-(nPQ>Vo!N4<{Yg)pl|^p;cKaD=f+D49H`&Y>_wqke(|ADoWx z76VC+b(vL7;DNWf>0#Rx`x6JpI@_sFT0=F>h759Xq{Ei5ag|F{#c>A8+jWdvRKa5} zPC3?Xl{3e5+ttGC3|D+FW7^M_f69Gtxh7=>!F5k{c9oNEM-Uk2Z2y#&YG_2b0__6c zM>hYPXv;(I?$m9Zjqs1Y4h{;!{i0vk)@ngR`Vy_D@RoK%=3QKaH%}SR!sB(b703j< zaJ)<wH}H0}n~&Z#*R>qgHeZZ-T3^W&70dl5$&e?|nt3^K@)Bp`jPu?<Ua(*wH^f84 zZLV^rD1DV^=mk82Yc+8?zMbrSraH$Tp5VaNoE3exo;Abv7Vt@Na4d6DGd11?b69IR zZ}4AGd0sx{=CPXlgpO-FkyaJ{owLB*W#dD8Rt1h;^&S!#x7*FhSs4Qs_;<9(p#YM2 z44<KSBC=`q{ohJtW@^y4rc_%-Mmxw#v&}3$Jsltu%qxH>hgDRntHh>l?Cd+NYECts zSVJqH@`?CFpK43FYFQDBc$R_`f7HxAq$v?Mr-cG?3&*cUu3H;94K!0(?Wwu{{@4$2 zFMO$gwW<?Yi^}TYo&mA6kV{{#>YvRtJkgsf?!VTKMpOw)C1kO0%|fc&z2w$a&xAv3 zJYO6xJp!~<3FxqvPJB){p!t9p!Z2%pzB#p=yXI%B2M5mz(+uiZ)SBd;fT*ZBNa^Wk zvII)uzZW%!!@pvG7Y9#}5N6Z3*JE4IY3JkO<OIb|pHJ@}W6JSAuFx@HJ!#)Ln_<kO zGIZey?|v#IQA{fYQ^t7B-%4=@nVl&$75KLXU(t$4J05SKX^P`B-ENRc^AGIBMr5v& zD)K?r0G6xpY^78k$>5G8KZmQN3#q3Y@z6vv36d8p@bc+8uR(J)p*sb$Oc{F}3R&mU zi=Fw=M9-AT#L*Q5w)r^u9gJ_@aJoIwvZnLrKRRy9kDy&z{Cp7rGy>i|b@}%M-mOKS zw1N@Ve0_fSaU#*CR2v3+{((2Lm4|eJrDk8~0>a{a5_Vwu^D~u-l?9-<hj(jOlstV5 zzhP6Qh3eq2E?IA0aem7TAdM3o5F9v*h(d@`E&8*g)T)@$JkOfRrZDN1H1R9o5h4Hd z3?7yvqt)A`UH^t`SUT)MIzsh_7nhSHs9kVo^}aAeKhG_WL`A`jIO6(}Lr}zTfJj4R z69_ujWR3$PCS{J(Q}>VU6l>`b^F@LaQICtIfgnD?AQlJqZoMGw8!*kfIK_(c+hXIW z^hv5Bz(8ZIn?&+J-KkK;fY^1nrK&dQ%!Ye0oYY_Et-j!0zU!T}xTXp6wypV`J(^t? z^(b<2`>qatSfF`mDcuszFkIFIkfraG0p}6TcdBadsJmc3f@Y?5&O@n+!%+J%n7^ks zlRBX89JX#8jWY|>_yucfR88L=+BlKfX6{liKn*sdNFYwFQT)p(iBhsyFA=SyO{eke z+%l2XdP)-y3I%IP>5iGjSpQtWjIW6vfSB`VuGcJ0)fCn70~eUQRH%StyCH<kPawY5 zIK@0U!ah}WZ`tc90_DcgW8L0GkEO<pI*}fN<zmdKdS`#IUHu3|H_`-GUn}`YHa&ph z-8RW!t&`Tggq^Y4X@Q?tS&Svo=olbeCdZ_pGvBdtLBD!Fuu;hu=G`*;=MyQ9?UzO= z?`%HpC=~pVm#F=o1_$1u3csa8DPaH5r~cFK7frmV67Q)+Ka<f_VzrghZbys2u#pg* z65x5_g3UeH;cK74k8T(}qwkfQZWS(*7gGVe$?M)Hd~4-OyVaUD@$~%_cujq-?oU;g zl8NlR)Qt`FUe+FbHT1B@G1{bpP)4OvwTWijHR&8$`;J54ZEAnE^=0S|kpYN+tqc|C zx<X-3YtgU%$&@BBsCa{%^cwer%KpOuhW;_KG;I{XW~o@x)sjT;>$iUNfv$x#?QR85 z_>y7>RH|ZSLYm^A9jm^lT~rn)hp;>dJ#7bALXw7P3mN`t)<RnXQi0l<M$W*EuoTd< z&qvb~O|4hxJu0m}v;Np?1p4xug|6a5Y8Ki!ns5SJ?tXu1)1EAYb=dOh)5h3GuSzt= zy3;hCa%{I<jF}o>Bhy5#HgKU>-QDc#bXNgFUo(L}m#6!W7jsujubIzI1zg)l6Zckh zxP4`H2zss|mJJhqZM-MV@^15oEgn<HAX_0*vs%u3HCsbVk`M<s;5(L$U3Zs;>9h~q zN4cqdFN<ki0W0i&)uB?=I%)s-_+jY=GqFcPkEsGZPfoUK(gtF>^R62{6OF|<JXKxE zAUIqrY}j=$&;_8e6LbL>gOEXUX=(IB@PwEPLX0qnC2)0xQt16y?DVHI60P5yhy`OA z@)6m@6g&&74aRa6Fzsbn_|q<oizT>VVGSW_n8LU&Rv;|$Tt`<ZEV2b|*bx>XL*AaO z2x7YJvZ0GPiQTNk9tvTFT{2Nq-3%lq@uy7YWPLBI8QmYKk+qSH;w@J-i_c@Q)sQZZ z3FE-(t7R)<ZWfFuv6m3ncZfG69;(Es-~z1u<1ne2E%?QObNNou_Z;)w1c4EMC@`Hy z2-Oxuf6yx8+{x!X#duxF|4A|+*05*Qw`%pgS+O>g6rob<=M4+4?yi>ou*3jaFnr2n zc=x>}r-n9t*{BG)*r6qamb=|m(>lomLtR;bK6dHm<yLENA!`L8YrLJf0j^0aZQ>-g z#gs<sgvk&JU}^!?M6IKA<4wp*L&y5@<|%A7BXTkVYG0d1k2lrjUgWfs3i8&?w5X)j zlZe7D`pmRIN?;kayT_)uR%A3EAD)ZbW8Yn2t=8+)%kD(S!)u${1$|js8L3m?fT%L3 z7LIS4qnXxLY!%Y28^%QpA27qareMV@_;H@xU>Xw0Z90L+!TRiBwit6IjU&-r`#yD^ zbUKI?-KbuLI&81M-q#GgAToo&D$nfGk=UwSS8{wDrKIknPu-V^*<0CI=*g0bwN>lD zI4L!1mf%3plRtKLy`-?24%TiYf|A!&-=uMtk!jhi7T^Ag^cOL8q9QGaOkyo-5OgZW z8vwn<a~zJ&+SoF)t#T<YI{~{__v$qaP?pT2%CG@Zuyp4j65pPCMS!S}{TJ;Afln&} z3b+yK#__6jo3tG4&c*9B!w!Xr#sQ-_^3V;TT8k$^4*_!D-Et9!S>)|Cy%$Vn`n;WB zi_feJW1?dtN@^-p=o<uJVe`(#6z0tz`GFy!O4^}zR)TFvhAPV={ytKJ12X}uWU|Je zh|1`TK4f1}vP1LD{BUYWq{Rq#6f&R^Mp>m}5OJ&`$&zIbDU9%q98|xegjt7>d9v;& zc61p@<o)_hvFly1=EI=StUoPecbBmuS;A*=LZ}gocW9v-tZu!qK}PKSOE)+}9}4Je zq~Yb3OKbIpgLPI-#ar&SnXd6)1>@$x!WOrH;P%1m{A4!IuPTsa2sPsWTtHw9C3rdd zDP1)qNh}~neg9(>_z(=Ytq25?pX6pD5&t`cCja17qh}&$gjem7y|iE`MWpJ|SdGvZ z9?m-Y_Xvq2fsvqTb$h;<Yb{UmT@(OP(L)<-28DR7@%{u#9ktrexyh_x`JYevJhL2= z-KB#jYzF#|_<^{h+Z-~teOV@Y>V$KLTgs-wNf3(J2{hDQw$@@YOLx6cgWli#iX&64 z*e~wWB^bJG+}{x*5Oy#N@N(!AZY6!ckTJ?}o5-B;Rl7vA%5c(^(eB{Zse%UJXV|0> zd!UI)zx9ggV+ms=Ykkdg3nNjHFk=pJgb`ZQ5Q>vf`#eQQ6h~T=0Q#O(2sT3X^OSu^ zZ%E?Tyfy)njTgb<M~yRdqGFZ=Cak{!1r21(E!>C?BE!tYE5;Zi3djZQ$*a2V)FB77 zOh5G8!J&ziArYnE{CMHIfy>1Hy(+&QD{?UQ&?t8I1}JdQ9Mc5|M;0{D<key8jP&=) zyFC)70&fq8sJ~a9>JG~j0aKphpi>^2SO`UwU@BWqXn-v9QMRtmMGK-ctjwZS=;(S6 z_K0<Cf_RB|`AD(NT34CX1@Z^nGQWSs!UUm*uWgiXnH9-`{9^&cNq(V1Or(s#01+Xm z&(Me;pbH@QjaY<*p#BeEZvj--)@6+bmjD5RyA#~q-JK95Sa5d<ws8yY?hXfcf)m^| zI0OkU!Tmqv-tMovU)8Ho6mT}M=Gp98GUga_qGfWs%Dk@ovUJI*Q;3CF^>O|hqHoh@ z5D-SvM(y_N2fcm1huXNp=}cBXUB7$reG9H*A#wDKAavZAafQ&S^4gUr%&?{P>;gM8 zB?n(g)+#mBb!Bg&^6=x`uvMc~NJIG&(AjRg{Y@#Gugy{l&M9gr|5(tJ$(~}qQ(>k= z3>6eeDvG#UPrSS>a_!MDq~N+FE|Pu}#Hlh#^@`;gs$tP|aSdtGREqRTs&K6zj*N;c zlxUx|3K`=2pB8alL~FlYum*FVP9GTauntCxBdl6WV}Ow`CX3PAup|)%OrJ&u>j5$f zacPU3&g!gA72mzxJY26&=!=BY;-mS^3sC9G;nU-rr0f)pewqHnbsNacw^><z+Q0hd z^+%L3*Cyv*gcsU4i}s3`r&~8fI_uBY)^-vN-<1`emu#iAe`4oSX8$h)sj?@*LrKK5 zNjQD%0B~H>LY{-1#JGRmMjoU|i0&SLt&Q6ZSFLOM5=<mEiXk#e0p~+%N&?2U+NeDP ziD~0bAdTyhg;^vAMn4|Gobc)^FyvQBZWB85(|DsSIcZHd3c6L0`;fqp&+UsK6Sogd z{Z2S{6qjh~_Rf5xup0uI{S^w`06Q{k_MeoyK&Uk2a9mg>WEAt)p-4;-sZ7}oQQ5xo z<t(C^osDOWF{hZ>b3*aFLXDnB41qd^jfXG&F1Ri=G935sH5Mo^)R<X%DIpbFz?Y)x zQpbjIZ%zOE*O+<ok)|6jTsfV>>QWWLaZ%QhfbqYhk(h>4nyyfY=g_#Bv2kRKS!;}U zaRt=j4!$DQe+YM6g_zsUj7<T2NqzM1rBGZjQvJhl3sKF7R~JWN#!Wft%r?@y7m>DK zA>X|n(I7oFzPuO_@^>_db)^3>7<Vx|`_73qPy`O;{}Uy&YCSm0nf}F5$sb5wY~1O7 zDX*YONvXl}D_Y4MtGDN@)8c{p`xnhfzzC-_gQosF+Nc{iM%7X2zwi6MZb?4Q-&@ks zw>WCL^Uq(*@B@PeNXe){r&;>Sm)_F#e@9D7i5(@GOlYf1wc}1-tjO@QgEeD|`3snM zwr~;&KJXnO{5#r*|23Ks&)?BXx2#R;9AP_fGsc9+Ux6Vv{-G!)FSDsG6$^Z~z>i5f z0KQ%SJSW|K5doIq#{~T59RAOnL!>gJmk4)V8#lEC2U3tSbvCwP4g2L4MVenE+5Cjn zPR31n|0_sG*uN+;aFnzxF#oMM?jFOIp2%kZH-^7)$dBW^{Vjj8Ee{1an&vz9_hquJ zH&U3vlg6;oY>z&v6Mvq&@7*+M^JL-vPB1reB1zA4>dk?%bQafmn$uju@hBBqqO4|7 zCi_ht2{bDams3lXJGaUU^AW_-&i!1=pXF)9gFcINa;SfK-!$|4R?cmXVFaYnhUTHY zzDih?YH4uKWiXq)iX{PnYK$MLBO_)^=qz|~e-{lK{|^2BtrA|#_3hpQ`xk3gTcz9m z<cs7cGkv|3mL?DN(|UYV%OEVv87;?{0?S`U#rrP74d)SMoF4h-D{rIDyZ)%#=)a)O zY1b{@zy?TslRM+Er9|j3-V8_gTFopggI!47*ls&puUk6d#bjRvOun9N;=j1qVn;`W z+%C8^HIW4tQhCWy=`0&pk>*MzEpPTS;p#pxm9@ssTRdcs;Zi2GklOAdL8X7t9dIjM zJn^i<j1?z#mQ!`H=PQY-EPtk60}_-u?^n*J<Ik+~Q<%HmT=A8TwCg4{)NGtBPY~KQ zuJjJ$&e%?Xba-2UeIZlC1qtnW!NRM`_4~;W%>_82za5MZj0?tLOCO55#|GOupA$CT z-?tl9O<nv5(D7RaUAvo?Y{>GrZ^nu+YH1~v{xT^O3vju=9K5wn4X36BdHk5CWjLHa z3ry|{X@vKgG3?b<Y-+q-O$IfEJTJov=uBA<Eh7QWmcZ(MwZZ_$)p9OZsICe%b)}?k zlb!qy7U=Wd)I)!p*?_)Y=hv&}N2NbU4`UB@LW2B-6CE#DY5jf>2mOkxtC6PWb5$-@ zb0>RcL~Srm@K%maaIt)NzZl)7ejVyo32uhK42TP&7D85OTbns_c2U2{C5vxfxY@kg z*ekoXs{#(oYpDy9S0#CHB~K-?77P@Jh=#?u=22EPFZB15+)rbYthSoUo(RM_)`Awk z9lJdfrt1#mcbj<6MjvrJ|8!h-b#uR|Z#p5<ONz>9cI`v+u1%Nq=7NQ7KZsZr42rd+ zXa4fX#e<hQD>w=#Fc<CTL4>}2$zQ^$R%5*l_%FsuzrO8Q`_Ej)%F<UhP7(MYl&3ui zUPAgWO7{AC*a?J^qPv&cWeg&?O&H2aki)wSP#JQG^@!hH{X5lu72Y&-w7DwDR_SN+ z*P|+PZtRRlk5{i$ATy4joDMf!NB2tB;q$=CWIXF+ue4U|WVG10EUu<+%<3@%Rtl+i z1M7XQo$=f=6&JG|>!%W~FkC{gwqDU8@?(*yrlqdlt1T|m#oGm9y#h;z$!Sk(E^{&= zicV)b2B8VmT&FTLw|2*e)P+Ox&M$SU_b01Kt?h=T9Bas2#8U7IdjkV#j3K>;4AAJl z#W3A7=k5rrH61q-gWp#A&%fgXde{96cRk1jeOKKUhlhG@sC*ukH)&1hCKg4er3o5u zIX`JOAKP=|jm`^5V&swem4xavEu{FymyC0vW8%(M!jenH2%&T~t>Rp@(h((5nbKJH z4;{qD3D5e{NfcDenoF#v2lty;@2)%@rXW6no){H3T}!TwJ$*c!=E7hBUem!tp{pMQ z(Wlwdo<GATLNx2iymlSlOAcci;kZqJtNL8n8hSj<95xXm&GrkOSzOS7D>Zq0UF_`H zM0**S>AxI(x1YXHJ1ud&Yj`>N^&;SsoPSrZYo)QMR;YbIQ~LHg0WmilkxVBfczT}H zEKQa)<vi^djiHNNYVo@#KvpC~^q|$VSNlS_(YRbDl-fC71E^2etCBUjJK(L*Hoahe zw{FE&@0f}Uf=7@VbtEmgjHg{y9Yo&bah(c;9o(9$0(6#EHJG#@q^|EGsC{f^t=qyT z^50H(?Hjxt^)Q1-w@Ja;$w4?NdgI49R=x6puMGisj{F$4akuyYpF7<WY1*Zmt*&Vp z^};U8BkMA>kx)-|z}C9=pZnFjePR_9TqMt@f<!)Otl1cLA3E}g1a&m>FcbFd4QO%M zVrj?11kM0yu0_%~@P=3l+PTD$ZuG!mHH570_X#!<pI(2gR_;hRde--I<^JJ}xA)5C zoR2UlodksRh#NkD!&?pTG@n6jP(=E<IEaDn0U{R;vifV_#Kx>AzZVK0l(^m->%ol_ zI35kwZ<-R@zT<I$2iN6&v<bPQd^HLYtidxTUtlu>XJe9d9^0Q?ItBEc-VOe=-I|h( z{CH5%^R@8g<982<ctp&^;D-T^LU1o}^FmVEevJaq2dz>rfNuU<+4Z#n^Pl^%^Fo2U z<C(ko>9+M}`-7Xl!*@@a_cHt2$4^51r<Vr}iu>Coml+c4rOWv9+V4;Ioc$I;W_=1O ze$Bg!qG##dtgv4fO>l*X6a6_4j=6mlX*w3VDD&FkSZ?TD%FGEBa8Q5Q#aenGc78r@ zdU%>)Ikr>71vX!1OBiuqR+2v{{z67+YxrX@$z!ebTy;l`qiG}9A;>hv$|nswF?#W- zY#^1!YKSA@6<61ZF6IX!T4G?>scKn?R`}LN(OI>kn2d$RP0QwD8mVi+Tfral!yg_w zqGAM<!|}lLVF^m0^mw%prA-cs`09nYAj}ue0!O(85S|4!tCfyroo@xx`k<jLKRj}r zEyx>EwqZbvsr;Z_#t7`Qu6WG&7y>)!Q%Ev85|&m(qTJIWiUJw<h(h`Py&PEXrY|$3 zW=Ru=dQFR9xF}0>C@*^Je!YJ@G&S(?0(IJ$^ho@flNlj(>HU-gK{th~&9Y86g^orr z1Q3rpBcfaSrO%n{Jst~T&c@mg=aP7D_=AdI|55wM9F$?%v0}!q;jG@_AqCEAGMPnf z7)=qaQl4L2yu!*Fh$e+OTbpA`Vz?76l3FwgiA!O%su(7P2A*_D$H&oINN7Wlv8)ml zr3qskWLhMutYlholjhi=(Bi`kWH6HSsDK5JIC_E($@E(4+6Ncla8;CoQjO$Q>Ae}y zxgFu)zSbq(Vp=c*Paama>qQqKK9YgMjLvUYVx$Z|p#$FVU%;Y@S0It3;6bl)qKx1l z;PgSNA^jy;hqYX*MW=2Ft80y@&3qGK{`tng1k=C-M)N!f&44m;>k9y*xfuC+i2DGe z4FQt3*kR?<HvB;Ci<BvGkj(OPA|@A#G`ghM=k3>W%p<eG$;BWf!eAsq64h?heJU|= zQEWovJ$i8p6mijD6jX$osCG$;!XRieQZX}ZR%Z-W=Pqd&qXM%<lRfSz2=eaBV5H2v zlr_=!ctu>kBwtM4bOZGydm7FNGx)5|U;JoLz!dQ7KOTuNNHKqpy;b^=))`NtSOxyh z-x;qkLK=axGcJxx94F9u(!!aVH?pX<J$L3q+Rc&jMV8rOAuNsG@NDRQPK=1*7v5Zi z$P^T1)Lp?C150%F2(qF8QCJ7MOjr;}30q-eMXj@Ds*~})5HyuuzJU29*?q~T+`MJ6 zz?#Fm5)_$(Jw4WW+{kPB;LPl-z~3)j4CYV!0hUvZLdof5a`f>sjSRGyi`FIP$%u0Z zc!;hRPfK<$XG%>Z$>$RY6hN))e(EPC6;;ITf+5*`A=`PPvfhos%7n)|eaHCqSc|6P z6xZ4Bj4&?aOrOmyeHaLz^$^F$^!wS)7pN(<TPry_*56({*x_&Nye1$-Le8xQy0gDR z&U)v5rjdDZv^JKrmGr_A(hHlhu^#eI<QRY;LpS?PC@ld8k8T#r4ZOARuWNYX)7g@A zq!=f2aHU2!!$mi9u-2+J8rd~*jWC{KfV}#I%@S8=KryQiLrz-kT!lO22q`K0G^cAo zVWSLShN?4*w+<o2R*2h>-cD|cF$KR9;LY6>gM$N;2PpDGe*UNo8)6~v1c*{a9Y`mK zo%JuH>hduoe~IQ`0_vg+C?Io4@DoL138Ss$r-8welgJ+R?WruVjKKh=nl&h~LX_^i zJp77TYR|BB^iIF5hB3*e39UB?>Cjh?&;DxD_Oedk4KiDA11orcj65h#^>9G4g_RJ7 z7v>dQ*}M~aZco?OcDFZ!SOPbDajSmlALpO0z)wiC;(*HmgbQhr;cAcYFKwC%g=ooU zTSrTrRECqBgQU=Y5aB9^otDLq1xvWiY7ZQ~`<*s?uV=%Y%F~f@7#Zv!w=`Qbzjq0% zcR><#HG2QiI};^oR+UmFMHvs_=XPb{!+;@x@#G3RCzN&H@UTyP6b8PdQ}d%f5l)5J z{q~A`=$X?1%tYnk>Rp&mHxH^=d?dZQ*-TGkE37m&>l^$<54ri2g=v9#3)-ZEm7-E{ zSa@x!gh^J2B{wsrN18f+HG;b;s_jBdX?2mjVa0lR={eYRRf(46i^v&eTW}XaL~W|= z78B7!`d)X$QT^z!Vsp`W&Ll2sCOPW%@^TfoRv#ol_IJFE?ODNP?ed4@G^Il&EAXcE z%dv~xK)&Cy9Bja4TlD)WH+&`Ms>Qj;Fj;L=@m8H)^X6%*2s};#udrTI5DOi!pEQ21 z!B&(`643l~oVU=KDLC*+>0_~vHt1}@=5n><GOd)#nA<0>#J)}Y;1+>S$8ehT<PknB z$7BfDSzx#3O06R1{SAD|_O>A4WFl$~decb;Cs0(kO@cuGpJPkZCyE<$`v|zC9m|LN zkdpbdv5wBqjGFBJRV@KA2diJuGDNVSJwGlhK3)|gGT<y;+dg7{ecDq%_6ZqMNokY9 z1ULDL?U~u~{(6v)kgdzqAfU9p1~k0nmEkjHUUQ?2eW*#GquO1OOK;wYVsfcly)hlZ zixk|OPdwOgRVLK^^rZCt<wL2~Wy{tT`R1Y|E3J)vrOVbrTFAAZWxkq|(q2Q^&1dPJ ziRKp@GIx2mRb2G}<`W>Z|Hg9QP%k3U=<M(sPs~m;6RBxYI$snK|69Fy7SLA@f6N2* zf^b8AR5lNfB1=X)tDaUiO@=8(cLi_Sf~^E6ap_VZx;k@+xmw1|1+RJJBE;KpV*DW% z8qzJgxv+iJGCHIjk@0*+1SeC#3B{pUDO!PGb5Is;N5jZp!TV<M>YAIVdp5phM8xGJ zjakSNr(Ga2tkUt`tk!X67zmjeFC-N7ygrdQjQ;lNsAU@z-#sGqd~big*)$o4igW6d zczOSG50&6^gDvPFP*=e9jtJ>v@W4QCF;RP~PzQOr?5(lx%9e2v<6yzBGlwS*B3%6L z{BQ6?Pmi>GIz8k~X;9Hg#}OOR%eS{N`~%%NliogF%q5G2*4=!8f&en}yJO2yQkI@( z$21W~yFgtzHhWg7!MW(QCkbj-QMRmv3>uTX#-!(?o+3fYtPWdOglLpqf~okuP9^=b ziNV8lqJlDZ%E)<toUy=JkNAPTLrmJ_MFh-JQuQRe&H7RC(O7MSHR}>gFcOi=F$;$n z%MyL;*e~zphT~^}+eR7_)^B3%|AR`K@DgrLz~9lR;&MJzbW5Q(CZh9ITqA1di44mz zq>dxRXpOhUdAz(2!V=*^75Og&p^NxS!=fc{p|OK5Wh)+ZD#{#)=4CJ5dG~uz<7}_8 zwL5)9YKQt4YV_^o)4NS#z^S$FDf7v*#v~u^UMAZf;lIfIvQOAPE(kNpXEnnYR=Ahr z)>!CfaNxK04``Cs9JWM6Pq!6LXER(K+I~~;SXTdRz7TpT?VP}7vzv&Yi--a`1*X}~ zehW|h0}OfalPNs0tuWy}$(BCHW^-$oV+Q1yy#lewtUl<4%FSb!?{YNw3h9w!lI<V- zm}U!S-B#LBoS4lu<E${-$o9+n2Q(>J4&;y?RCXS_kRGPJ9<a~2d;cKSe;?OId7H`2 zqX<EuPD|hd`9GJw?Bgng1!WDTXIY3%SZe|kT|!U-|3HmdfV|o7^-_y})?#&*9{}s| z4~h&tBu`D>j(#~zmD@{2KE%EK8})1C;e}>MHHfcK6hZtQi|{{Vfy3xlCgui*0VPmp zC;;_>bomeDyDuY>O5SDg2>=(!|0i~EfO1F&P`?!?>T_P-aP6OoP*zfC52~Q7(jTsw zCxiM&9J#rM^M?=RJKgE=$%XN2b8{t!@9=MI1E%kcH*xuC+K)Jg3wLMkXSDb0Ivc=d zgRQA9hK>M{KeSq%gdWeo<;I%vH~pj-zw3DcTt}_h8@8G*uHNgdi95B<+qTIMteXm@ zQ}`|+Ps^5`ljY+{0ZN-Ryc*awZ&_FE!I=mbEjc_e`E`Er4#b$=hF;n|g!Sw<<DxRn zN<O`ci-!)q(9rK@$BhFdu$)t}<@OI8;Jx;l>Sc0aGv6@na#x;PDX~nlTv*6lb_H(o zoL5g<_;~lO<i>nXTbC?7Gj}(dx=3i1u0yE{H#!@tvJ<GB=AI^G_uJfb4z7a>liIMy zTuPUAON81iY(-{8W6!hMTe)gKR8)Gd_OLC6_GWi~3_53x|7G>-`_<g>?#DkRYZhE2 zaino>$`o(YYWFTzI}8hylLWph3$6oI^{U?{WtUHpJtNoLaXzDjEI4u8RM%*Y2|UOo zsL5`}Q?Lk@=psw5e%p-hJ+<NT;BVTv4wkdLxDETUn&|K@=9Xytvgk`W-OW!+jv3vp zY`l;C4N(ikR3MJ`HOF(X@ig1zLQ+&JKaS^jHjOu(Uq(Oy0diHTl6p=}u^)N>%#RTt zusq^`<t_cr&FH@?cJM;L$Tr;l)*RQSUtbl0_qYw$-H(%zk2;xQMM61i-QPpXd;0?> zQoc(alW0vFE23v^jY|8=qQpsypx?a3Gy%7Uc-%u88C&X7QoYlvYyb#2(^lb)iun|F zqEMu5mDI?gw$F=8pKUDMbP(E&Yj74f%=qk*kIE3pSn+i)EZXKPYKvtXX4O=&k7zJO zg)qe%QIHEkV&m+IqRd+yKh$~-l3109X^Vd5F2f!q3&wz3?pUFz4l1(V)A1Y9=1x(I zGl|>%X=F_@$mN^AdB*)3yUcC6uxVoFmC3XsEUTXEJrUM)LP8~T(7tQqrPMj2-=|Hs z8x9L}5+R#PAu2Kocf(<Rf4C?yI$0S!|2psmOyD>(UN7kuD~2VnE!^8q0jeHP_Y`;G zm<I+U390rk0O|Y_1Bxo4K_PX>A}7uRnp@i4)?#rJwG^?^OK5;_l@K<0FxPt#$!UV` zPKBWu8VI10c+%YBA6VsW0O-DHwlmjF>IwWr?)&Y}q?apRKEQ@NeTybu7b-O2CH_3` z^$WOJQR%#JmsmJ#V#?q@+Ez*2mSuR#-eAzdr!p}CNMhle`B^ta6Dc??taw_>o|X^q zi+SVW<?g+I3~jYB?3mkY5nr`UjTtZVMdO?Fj4pcoZR)<_Qgy1^+|=<<mc8uVG+l26 z_m=a+(6DGCO|c19YZJm{jQu6$tz>E4e2N~eK3U{V2FotAVV{jNcKh@3B~M30887av zWf0J5mwy^uydclP)=P>Z!QC_>u3W*fuj!^3A#VPnn^3PmBfMt(%jf>0ul9aIX!ZWF zO8^=Ii|2?XlM+rQ5RI5mp^B{{(oEguyW?Fr6F0)i!tejy<q((XRa-*qJ4unxJ<O4D z2QfZ!l{aQ09Vs<vGs|zT-d6p&2d3KaZqaW{)56mW#RHj+j6<@*XV1c}RPnFGZ~s!V z=hGI^`BS4R)ia!dV=bwi4nu_cho)_(WAhw%-jfXUbKS=|ysee@**K3M(7jLGA!OVe zW8+FT_?p_zt;WRoL>i__Tc|I!=RCoAM6;C_PBi}NwiBc>Bl??8#Vx?IVatW7ACJ|Y z%`-BWt3t7V*}8;9EFmmND^KZ5u7cTykdIJLrDfzs4n>}*LSZQ_gDOkCYBJmuw$gz3 z|J>lf{=31!2t!w15%`sK?OHBC1fp&3QyMTZz8>Wp%M=vgbZ9o<cNcof3G9TuSv6Z& z-U7Oy-z)7h!p*f<^*lBkUY@X1MPX4VQ_*pOKD+0;txvo>JA8hSuCmn+wIgO?V#<CV z$<B;h7nKt=XePdt?7jbah)SUIy2HWH`*(*E4Bp`ox8L^-Hg;9fpVY8|pzO&-1}OCH zdRjV#m^uArJ`HpgWQI|hut2Wj0crKS;USV1ems6Ph1?=jF7}Xc)V}W>M-VQsk}1_Q zq{pyTQnwj2+DFiW!A*qk0!27QaUufwJIhgFWn^$p@<kh~egx*i8y!IO7-+qRr5qK6 z#z_E)O-}S~8n2oE5289)3=bB*_d*sLHG%L(xDH&VuyQ}G8UUA#qQ;y&DtRNd&?3~D zg}4wD&+PNPU8DIxGQ4)B1N{lql?eHn0r`{eO+v56x<Zj@)q;L(#c1&0@CR+^TGeku z!=aR9%(d1W0K3)@tH~FHTHfcyY6TGNsh-apaOI{(Sq;+*<DDwDHen4~4lR|^?WDnQ z*$yNv8Y3qq4Pe%Mj0k1Tybh)HSMBJE|7gJr7ZbO>M3r?$wH{K1+g0-I>0JoZX;cR> zAkg`Ax~OkfCf4!1f@LUiVy%cHr<ypZIpNv<EsAU1-lOVj<tz<_P0KaEa_xhDgg&3+ zx{sx1glv1r<WZy$?#gnqU{iax7F3!MGrH^VH7Goj3gA_;gmB!4fZtRj*kyk4+xD5} zn3YX-OVoGz<ddJI=j5J5OW#CCRV$(Q_dR7$uhAiyX;LM}Qr)Jjj|ch`J__>v4BQ;q zPIBX3BK|1?`J4qx?IJs9aro@d)6~$={@vZ7+r)YZ`@Q+zeAUp#O>0vLGfc3|AXE!s z!FUG(@bh}$iRSSiCE>KSzvS(Uik#1<Ks8yqLNmQ~a0d&NJ)Y#mX<(&7Ks}^8R+r}P zqH~)H7+Ni8T%eCkTPQRb-N)_@W$sKu0hnNZ?=o$r-X6xIGbo5FJTAk^0u{$%8gjQS zLJNA8OLNx>balv!odo!car)vYiZ6F%KviQ6rBgt_j8xhUaVD1QZ~6F>VFZ<pK$f8q z^;Syp7@k94QJR7sIa0JQ<_Buqdx83%loYvi^2rPhC4tZ^A+pjVyDDs4M5A$0rV<ES z9;HT%xGQykJ|W0h8h*tJiiwc@>5HM_CFT;t`4OgcY9DzTQ`k{EOU97Hg|#2=97sWz z2D!Q&H^df!o!hn2Ah;2-8t_NMA8oAt9}1F#27X9#o(wC@L>veL*80S*cKFt*bK!w< zw8bOY(&mdDPP<`y!f!{`LVi?M+V2p5;-6<_@l2jg3S)X9(DVNO^!3ltpS@f@xG=<t zzI*=nv4vl9FGThUrL`iNATF%m0ey%Tjd8V>fsS8j8jeZtk3JVwI9?m{(5|+4eJo3s z)!eZd#mLo)-zX$GRM0?PPi{8)71CZ3KXIy~H1YCux-(Pvt}04!VXf0LWh;A^rL@eM zCgt<Vr4L(a*^DV>c?}5=(V<;l1bl^6=&UmI4SD2T)Ulq&hCBP6MGO}s2Vn2A6zmy@ zfmB(qz6v_MP4Ot8TTx#bL-ldVo(;XYuW%7;y?0#nej%*1M;+fqFgq!$L3Koe*sg%( z(V&x56N{SHC|g<BakzLS9Y%vT`Rc`BRQef-ce7T&>chzU4jzZuuUB`b1^34uHwWvD zpMaa^gOZ!iPm^X7<AOFIT0qI%`N-%a5%Ad>+DlJ@e#o0<?Twcs-sn#}Yo=J{aNhxT zb&*spu`Z)W#!`!ofod0rzP0#rAtki4Hz?LzG6{D4GMBl^GK{R^GiuP7yq`zD7~$sG z4%BrYki7eN6T#1yjGH$SEK7u+7TR|?pMrD0r`gXtEy{(y>CQ~;d;>rd=&;4m$7VO@ zk_;Y3XbPiNcTLmB+#qJE(&+f|vKku_;?goGWkVQ@%r8l?`P013OEL%q^gO^N35el7 z=_{hKydlE?i57Q`ABhs;#&y8?&g+y-?gI(3N(laVSucp0^U}`-q^Zi{-d7h-7EGM7 ztLq+O^{9x2CYpt89)RxFU>82I;6C=)MAbu6_Wk9Z(Bp(s_S*B^Maf6DWm@w09aVQF zWKaRaj+w|?brv>3x+e<sRxrg}W^bsQ2bg)j9TRzm!X%Q6)Z}PGy=~omiv|65_oqP8 zcgv#=Pius{x7Zsi=AXE3R9L;kBPdBPlmyj`ODG>nv*N|Og|Gl2<V8`ProCgW_gIyS z!dOJa4|^nUvj(cLeNZ_>XU%n0$Zg@71Zm7mlgVM~U{wwL8AT8;J<EMAb7`wq8{lYs zF%Yph{w$>WOGHK}FNfo$D%bRd<VjNev}deZg-hA#;V0mTvkJ$fELO*4vczbsbl}g? zf8o8ejoRBG#;ymBb=Q4Bk&HqanXXKok1Kk!(qxwPW)@>Sp?IrrGEg~swpT7nV)duJ zmr+C+xiwMgL(`yIqVO!*8%kp5O~XpiOz3Yo;H;ZF4IHN+Xiz!AvxBWkEiu{!ta^G} zk_q7>quVR^P`RV^=^noej3fC#i_o#xi;B^)hk`j>=SBcDDP4e>MlNeRR|cA-FiEm) z#nfJUw-~QszBN)4%Wx-VEJq~$wWW|DRmFN}1efOnKDZN&6u1-3JZ!QFSKhywKb;eg z-a@Il-5D3P%lS07BTPb)@}_abwItW3Ik}Ti%>6IyR!p;A!_TMs#Oq!>9?Rs!KNlB- z_?Uo|z57|dGF_kJ&bC`XiLja@yU}Wl*0W4qPJq&-J4==HQccA8a8h}RcUxYK2AWMz zji0jOM005-B%H?eLqTO5R|UsA6S>B;KzC6tiv~{1M$eDKBcB#>-`sQ^9|`7blJVeq zilh}JP;GIl{byIbmLHPbe(ur%G_Uvc!YO)B9LKGSRGh1}FMN*-!7@D}Ca8&Z%ituz zLpF_lD~XYo0F#zLV`0-8pY+wAnu&0r5l@>=Z+`9(UhDqayiS4nn*1cq_f?tgb1(IM zQL9R*>EqIwsmF&(>2NA@ut*!`_Ywm_gaeU>wsmRi!@~J_0U%^%#3T&JD%n-?PT3I< zTrmmyc)cX75%>P+)N9kRQJScEw?Zb3LbTv@bkdUV^ZVS9anR*!JVi^KY`P&>m5mej z9zk+AA(qot5g;HBf`!MS8I%sTK$e6gKSm)3CmwJtcqFGk`^8>=i;Y_qy)&Z#O|yq{ z_DJu$4})cBFk}$1j*(o_$%~}uP-G^SR{AE5Cl6m&58f=E-jHe334d<?=wwu+_2<*m zvg=LWcGCOx*_nS*vki0C$rn)wq`*((CYS;fD~Y9ZTLctnFl*chM_~SeEBaR#sCLbD zMQlhg`jWfe$S>XgOTVshxW9%lotk#bP=rRHaSrC>tMbfGV|NCuJPQC{3Dx$~P@M^% zMyikNcc_5`9f`wJ;%Y#?N35>>UG12p8yuyR3zn_5PSDTsxMl}|6GcGFgin2XR{E`D z)I$XNc%olSNX$AesZwcmoXAjB!8QXq`ZL?@MB}#5!@r3lOa^mSg*cSLaL1<K_{F&? znD#lxkW^!8lr5@q_}vB3T0fjWAeJ*m%J7^@=Z5N>BPs_aFqVl9mwcfz!BiEvHaVC2 zL|Rx>vCm3K^Al_we0)5;U;;YPOGH}-U=qR?W50X{&x2BkX{|n`RB5(_<Uz5r%5kke zG}uek-`xr}`eV1nuH^2PmQ>DI69qeCDwb6CZ0hqv^=94YRL*yPQ9tzgbliGzT6^7= zjxT8gct3@N_#ZrV#y)U+s2p9Y2>((a_GAj5h;4-xD_sZd5K^J%gsFaR7zEG-BQt`9 ze*sXuCpwp-tP(tVW^@u0+|=Q{aLN4-HL~;LisFvwk@o24gne#B)sCrz6`B}jl4y+* zzZUY8Il`+&2FNo_CQQE9BT{jvY`Esar%R%78Y5!TsqI}T1cw&*O7HE|!N+2tBD8z? zN|z`#E`$OU&7xVT9(PCLOvK@Of(8ZtEYrzbqc}2n>T15fk+Yh5++XuRS^wsNz-gfV z#=sqh$O)qf*-qRpL<18<x<yKG6`=`y0~lQwL@2u4zYYO#LP1jonNBvhT#<I*M~BOt z;p@|4tpAvX39?r=&{^_Hx<It{F9VRVJJu9oI6F!p)AJM=iYmHx*S;l!yq#e<v!NgD z5?UdbL&opc=gjo+LdM>z8_>1NuD2mtZ@HLa(^j9)V*ZSXFVrdR;(Y!P-wK&NOMm<0 zkCvZ;j%|>833NJA_~o2cwTjG0gmjYwi}oJ6bxk>5RrmE~y^}=<*rDJ#bq{R)!t`YP zdT4z1C%RMcv)4}z<i<1IF)1`o1@mc|KYux<A&!hU>p9MXsbm3vH6SgsUfO^*o%i+4 zlwOlFKVGxITx5Pb<1D9L$4<MVjzHXklf&4$HB?0kgr)#1QhnkbpN=W+f_#W&9u_T$ zvygYGqF#3?5>~cEjyd3y^FcV*HWV){O*G%C=ODHK=3~!lNsi%kdwOFSynpq3U#wg_ zA$9UdgCvIH@l?;StqqfcT2F)_BgN?>YMH_?kJ|Ro$<-j1o_;Qp(!Ts@uRN<cdFMNE z;>Ax^gy1@z=}R*=mH|VT=RsiiVFWd-!bR}EZiv!YgBD;@9ha&lG4_|^^Kle#9xQ|s z@}zRPtox2Jo_H_%(`q1Go<7(88JLA}5rl8zjo+JvxcbKjQ78mraPL17h?5wlWvOYX z72Dz#bFDUj(;SRBRfT`61x|X3!ZLK)VX^L<B-Nh9fMBU4Y1{|%5SU8Nk>Io(8BAhl z@FsvSPAIkgTBg-KVGtE7O5gvU7Le6CzJOepwvcapEeWG?b^A1mwetnk41X6>qmc*d zjK3Zi@^k@4P6;ecs(1{+#PFEN`$5x~i~n_A&`6k?JVuJ|K6SB6PO4JFC%m+Zoydc0 z3YaZps+NdekOzv0903Ly#IM*Zf9ojeqA7;`$9q(=>L^WH_|mN9S%MNk-lqEJcc;GK zoF6zT81pwN_z|2GbdvnPNx`&?r2bBuH^~dvq~(b}Ti<;ckIz1NyCW-g|0>Y0#a@hK zqV#MBFU8s!#CgX({=mftO9spEykp1$1lggZtD!XKUOWzoDOW>m%W&;_UEwvWnm>B& zBVYp$8iKg!a?o8U1|ErU0s0c-n^@&8(-%~kDJiUK2(sMc9#>Qu#CqK~<d~7MwyZ`J zH0(GCuUb`xaAX1YTthK9ct~gn|5A}jM|D;Om(UioMEOE!0IUe6diZk4EvD_ak6iCx zun0MY7-uR%XMgRdp^<~hzzzi}a40<0isy#>@2qdBG9d;o@3+1xvg&M7F*sf#UY{zL z@1){AKH$VLVRbglBqT1caKRUnX_LRR&fuh0pfqA=AUGk79-;LRDXsmM1fA8{l~ha$ zAQOX&P-mAMW)vG>MUnLXjf~4HasEAsik;u?4Y)OqFf#+Zc5%eUSFvD4f;J3un0nSl zY^K_g$r^$pu1Q2z=WsE)3}5il^BQkb5vE+IqyU8R9+)J;_}9XO%d4T>1^z*-(DjWs zeosiayp!jX=MN=Yn1$fzfxj8>53J6<(mzBCM&QZ3ABLlv{G}V^ad`o=2x)Ngk4)9r z_XwNp$(&1o>wJemJTLGfumjJ4f>~7$H%#>xI~s`vrF0&@2o_Y_8<}*!f@rhswJZYf z&$03DX%vLAm^-!;T#8~WbiOiezA_i;GL^H_rp)`SZ1P`1DY}7c*kv6RYr9wk<Yjzi z)J@hH18sC;_FVS2)^^qHEM16Sc-1sphF~?CJ<0tEG6%ZCso29lu4R0u&%CUxlw93! zW&Mz;q_N)}KRsfb|JhOyBm_znd`K%>U?7Z!wJpX&ifc+(6dWyCRx6;PZB%}p3*F4j zo9l%^=d1Wp6F>pXW>+dZ_YA?68JEKQvB7O6#I>+Wgz}3h9XF~x%0IZRzcd(3d$Jw4 zY`jY#2q2<Vd!~pWgIH+^l~?TWWvxcC_z9d_b<A-wS)->)9%|A2zfmu5CAGcHhxukF ztjZ(mLom_DwJ?-qMD}0_+PVsHBIot4;!Kez*1x@y>;>HU&GA?)ojstczQ&OKF$z-C z|4idHlxj*Z&lU;Y=eRKZwDte*&+^{1EN9Cthg#xIxv_Woi6fNXxPe=DBdx(z&b+Ff zo9B&G@)I{-vv?IxZw^+h3NQ!PMFBON2Q~9hxZfWG#Lp23;Y~~Kq3>04i>B2Ru}X2Y zOTQId;Kfz%RLr<)SjjU*b;d>}XPu|vsB4`p|0@+u*PT1&|0@+u(}AEKF`_lWR~@pQ zkORe~GU(9Elx_#{YChBiVH~)y+O~AuoyY2TJa`GbD2gp%=`Sgo4T;2|L_}v6EdaRl z{cL5tyWan#SBw9fcm%L&3~UfO`WYy?lj%Pz`&YHSkDBXZeV^fp{j8(0tlLDZ_)b0G z>~!^(>EeEB6ZPf#<U}K|dN%NM(AX^9nuCJgDrsqhs<?XL`CFN74!A|AnUiD~<?vG{ zPktAb<<q%>W#hw|H6TJU1-BZmPPl?nmVa3G66P13#|jpO4ueIZFQ_!`wmZSvk6=-# z<bOn=WAU$|P#xt|eO))$ITn{Y{o?cXrSlBl&Im8zDwfOOxgp`qNR@GEd4H*_FX%Ww zR+XFHH0d0lPr{>=EFwooI8ctLm$Jr>M*f*p1=zhsjD4Hrf4ooBX#ZlBm;?$WKblGI z>u+SC;&%7O-LI<jzVSXh9$HNDV0FtIXzV4<)OO=_Og8F1ldzGG`Vi8j*1fYEwqWt> z<SOXp@#jvg>Upf=k>xh$5L|HPu-h&-i~uL!_I7r_>{Iu-^5J~h>eNR8S8O6<r8<ny z*TBKEZv;J*p$WxFFGu-TLYghk{e+v9p|TU^_V%gH$+~P}fsoZXvE{ouYLUdgo!>n} z7%6=CVQuvjzS4XF()c_-wTQ{@%!CWhVvJi9hooD3j8h%X%_XNzOs~)*E?dC#!|mOl z>vBOza=+NdVQI^)2zEWwH!^f0acJxbEx;Kng#)9@8fln=j59nTx=5I;!a}#~h3MrW z1!ZkY{7(&T8Xw)Qip#s%mFK6T+~>QWUvE}@_6Ex*nAR_DgtQ0RZE94vJ9eRx8-Il@ zVt1s0jlEKzjM=PR5Wq#rLD3-1sl{Pt!<o9owuSq1Ym*gGie!WG1)!6Kn(*T867cs0 z_p5+0rh{$Njs<b=$W<pYOYUZYLOCpc(teSJ)e-2^s5!(lEAKoypDI0`*}C|2@T=(j ze(RzMc-qtdj_=CU*srnqfCwIJClJ)pyVTV51Nqv-b{u3_?e1txM)@JRSsHhWw?Ti3 zT40!H;9HTXy4Hn>`?=@USaID9a44il+*1Xww*tvDuFHYtzb=Nx(a3s5(-W4W<n7Eh zE2Tw&?PGC}Pg6R9j&2=!_vXsNCZJ89?CQLtRLR1Y$W*XMY*)OgT0NONKGa;|=9lrH zlUr76@d91zM?+hT>Ys;@sEt^#nNDl0?>;<**MDJAbNqv<#wZm%_3++o0073<*URqf z4x`7)g`!{hZPp&SF;|x+>awxzcI%An*gw<p2g41zZn7)~JZ((=gnz!Qqe~VJcv|uk z!cFK>JUY#YNB=nm0p=76g_CoHi`|T`p{QMRj<gK=@{SfAKO#QvK*3bsl)Y)Rf61MF z0=bpT#i&~x=)b3{-}u}{3Ha5JT;x4-$PVvR6|9i7p`vl1M+7RCy~P=lW=7{N^^aA? z^Qgj=L4}YMjnB#4D;S=X`78PvuDlP>dGeDsN2}NQ?P89`PHZ9e9*@Dl%P%z14@%7` zCl{K*fBKeR1N;MEZ{0SLi0bPJO-*~KGRxohb*T?7`jLwX)ab>Nfmc=klDUq_@NH4w zSIf|KMJ^2-*!4@b{OdzfZf`q7cX%NrcsYfrz<|*Jp9a8RE^=wIs5+Pk)6q?alLeZi zXos1Z9yau8eE68q#<9#jhE5C0^85@@A)d1#)wa*`A?9m9;^fT|e8uDUQ4|e!n2MZ? zF359y0)Rw1QG^F#&t0?NTDjjzj6u~zS<wJ{7{NT@y|8ex0R$nQVZrIbk@>5pE*z|> zgXA4dq>&V+X3a&Lg!sFou@F&CZ+q+Sj^bzn`4b#bP9hCx#aNPV$ybAw(I*LGWn502 znpOCkE{~0=k3Vuxx#!fIQTdPZpSw?g5`gA&Mv|RRaeq!a;lJ>H02&KZ#{bKqi8GI8 z;m^xw7y?DreanePctB4s|I3`bm7U6DYRF<~-_=m+YX27~105Y^uVo1PYQ<Q+v|fO# z;hkq-+`!e!EasPy&u5e#w)34{Udt6tA)l&{JZ!ND{r*fkIj#Mw6&DhZZ3RC5cF)vW z0mWCXfYjjS#vW}mjZ<wi`CXb6^KZ%9T;dW|d4p8_i%#Z;0_T4D*=Euo+GegieYjR` zQe<41!k_t#tyeVt8#`{2fQ_{u`#L0hL5XcPj59ZtYUUls4AMc=!&=Tr!_Nrt@$}o+ zJFaWWmz%i;cb7lc9CzmzRSQ5Bu;g>Ic=OKG=i8X8TP7i=7?(@i6UWqwH?kB&n3{!y z(@iOnE_dCR5CkiUqF!m8OgfpDHAGef61923=7+f{S&eP6*1l98v&|qFTU%7-H&K$( zVygZ|UbR(3l%GQr{ey(IYz3)Qp?d5Ml*KFiW9be!dFnFqu+(+hBU%CE*iW{Qx^>yT zEB;`$HD}WUXrM^o>*wJoto4u7>BvU0gbnlO_q{DOc^#kd<xQiQc0KY9`KQ?<B{+E7 z{TBG86F(FLk@hgGuh@|G2S$_NA&^qK)vO4CTX^m*SVh{Y^g#WaQZD4)^xJzECg{cY zJKihL#)V3y2x<xVyKCUay07o}yKBJWEbnWamqRp5jrCVMCkcAsTQ`4)*KSVVy$~^{ z=*i#pu4LX2zC9F#-XQ5&^m$xKiOej}R%`#Y7=r6ChGEwAk2#mSG8KHJ_*J_IgqKfA zT5<MzM)fAJWK8SCnHjHb6MSwSkG!SVBZ6(wvH^MMD$ln1$8^c`oR{#57g+G|1f6-q z1Lu_fxk6xDI}SseX^;GtxMAB(f}65Y)nj)~E+OG$e4Fz9YeoY}P(36!7o5@fCNczq zj(Xo_BbD+jPY**8#t0TGwr2Y%$=0)@QJtS+cg@YMyKu<OW~%31u;;xXCynWyokso! z#C40GdZQsPJwDML@V4VpgZ(s;>7c%v74qnvl7eBX%n^apD@I<uw0d<a_f(nv<>BFz zs9F4-Z|7E0QC>xBWqVy-dpS>$0M+lcj?0$gr_twG-I%k*ou^HrzixIXaBX_sTff;w zHvtE}>g6}B$4lM*YQlmoDe>QiX8__!r3D;PcZdoS$5>2pG`U+=Y7dR&0d0{-ClOFm zwLig&_0J-W05>L~E<{MW!Cer^#X@9I5lWLDeWP=AA_Ji!JUYwzv`BJ|^Wba}3QfV+ zjKYATI_F#BA1;=1A*gXe#`_yuI=m_GDYS*G4-DuDLh)`Vq-}V@p(9tn#RE13Cu?dq zmmU&Lw4;$wSf09FrZp((GQte$>`7?F$TZ$v9Ard}k@Rk9gq>mzA%oxit}36=Uj$yx zc2=iuwyJ(Ny$C!U%DW6MWM2@HfoqC`uwu_p7b|79^|D=8OZcs?Rs}4sJ)SU!hUhn! zk)t9a&*mK{x*MtI7B$@E{D3CJJ%Zo$Z<=$kHvM<Q3%H^#Y87<I%>)^kKPL6ii@e>< zzOPf#$eLI3$bp#@J&)KDNq6yy+@fVLQ7&uw8RxJ<+dZuoyu7Qq7P+Oa<_1GtfFv74 z6aF<L%_e1F3!4)K?zA&Pul;0szG*Y(VdsJEp%iS^6A}`zn@nD=jRvG_>5;aXW9)i` zeH@V-A$dU28Je3zLz;<M8ODpH-=*T?bqbq5`)4pd^9u11woex8L2W}j(vtA7#LQT3 zZ|}y%?GJ^0%fThf-tME2lTaM?%}9D%-p6R2k+J6Svesel&M+SfWY1Mi@R>1XuU_j% z&31mV+Q#UpA)$_$;RFWvqbmkwl6c^FOg_)fp=K@TRmMX2b9xsFrwjJhOS01JS-Q2d zK}%Y%D1M8peEZ%AQ%dGjg7az+r*~TMU`OF==P*YCCHnjid*L0}Ur#*G2ee#6DPo)= z6nDR&%HWWo^QJ<@PIlQ=p~<uvB8!VsU+C@`+fEJ<ydFDSfzgQA6=(?Z+;|?|RP6lt zug0SJ@jSw)V2dl*c^h0d;70;}y*|DL27FL1s&|Yh1W6-;1+zy<ZExypeKjavU*9-_ z0#yjp@pk*RpVr6i$?E%Qxb~PPA<=2S0o!roZO+-jQt47dA+g|bvh(uS&nV9P&W}Ea z?ju06|Lc*oEhd*7tl<`?#UV3E&dtXS$05lIa91?^#gm5C=hWr5A#A@fO)v;gihfSe z=k#Tg5`xY*k*2~pnKqwe)C_C^mUS?Y==FltTr|>TQRi7i*4B#DSkXh)4x0`y^k8UV zO`siKO|!``HPkplt?cr`^7AJ?F8ZA#4D2Fnh=PqH)p5E-5hBNIvw}JlKFF5Yx!1VP z{Hkj=pZnGFXhzLSyc^#(Owe0qljf*Z`;OObA>j`B$HwH;g~rn3ARzD>P~g;Er^iOT zAtx0vqege8KnEYFVZ;h5dZf$bo8ORiUj+Y|{BibO;`Lm=A>23I<!8YY!?%b8jOgHH z4`d~VXh7FPYNsB3n}}x1#~kK(-I9Ps=#&40Se~8^w||Rh4zmOOg_I2DA2OnR$?OCK zZ<!5dA?pN^EE!iFHH;}<1<Dkpn~STrC&&Yzd=a5EWO`}GQ~=inc0=truwsh&ed<|x zZ-kCIRX9KaKH>JLYq8yNI4IutLH>cbE1<|0D1f2S;?-_m=c-rL5c>M_$+dIRvhc$E zTU)QfLD7%+W09fNAeC7#<0gSo937B&d&0ZuC~s^8asij~u57C~1#6%7x5``B%yDjt z6hKyyigp_u?^N9<BmS)|abWKU_2N3v>7HB%fJyk9pmNYSbS8@Vp}|`3oS~7Z)Y~i; z&uHVXb%@oVM4WN*KKt9NGkg3#5`|Q?lp094jwvE`4>YZ*GZ&+V8KQS*P)(w6>#VJr z5lz3r%aR^=S?XEd?P8$)tA@AbN!BGBA&!?}fuO}C#UJxeWf>f~k)oz9{{6dZT>u~k zraAI+b(*LH)(_Vh86NJBiJw~`RzDf9ay1hcB>DImwV&JmnwS+kv8M^@e@hhl6s`TI zjVwb|k4E6)0NluOW=A=hN9BKK*w>TIP~NrKz$NAHy3mw}_{4s;c(&R<yHT~beP+!H z>lCD!nify(19N5C3jmlbGf`i$8vdF$<(<n<C=kswAc=B5&cwD9=d$Pk)o7fsnCi6m zo{&VER3ep7`n_hz&8a(hkIAhntzv2hrz)K&nM#O5h-m7}-8mE$8v#k4J30<2iHL&A zj6Qti8*8AsCM8*m5fRAB*VZK+oIi?!TB;d(dCtuy&gu^l3&b>sv+@N=e~wf%r>OLY zLZgF%E7}n*LlXN%JABkft_J)1Y)*rn8uer%bj<Bw=X@M#4%7^OAb;Sa-rKMJC8EM` zMNPWQ?YJpmBZ!1i!4Y~u1X!=17XWSv8@8t@?)=8z{u%=M6NHEr1{tr#=lzF|;j{4n z(&r|?o~9@S-QZ*@laCk_Gz8eIA^ciJC^zc&pI8PM)Jc)x@g!Df=zmEnrNuct$|cBH z)CrMbMV4PYu?UgwDBP(1g7ya5C>Uv$eNHjC@0+Hy{`KT9PK9rRxp%}Bv!L9>jlkCu z0H_U+pn^ioA@JnY<&E_g4Ul<_q?W-aEKs9N|J{&=!H*;wE?>-q_bJpq<WSHjYzgmu zqoigHiCw;Tuw8X|2RlNxFj5<+D8BhSiH=6af~QchnFN9y1`Zwv0T(<U^zmND#3m#d zP#EtWi3dlCgMBBZ2BFUJxMU6(Ea1$-Uz+dV=>Yg=K2Ub*-+blyB3juY{?d}a^amEF z5G8_9<n6xAGVQ?cbzQ=~R%=)nU6YRV1;5INI*E`2BX0*zTQIA-hKXeKUIvGvl;L1d z=k+3x@xBq^et6?Q5|E>U#Id0KgTyvp?X_BhvCiW|vkz`yV!oAAJ*B1!5&%Kig?MBZ z<d--Z_*(4<e19qmf-nyY*bQK=>)azk|H*psjKpY6j#KG8q?%@I|MbDaBY7|pdYH2H zu>Cc{bIm53q6#TXB<o|Az+Wh-4NKFRmhcG+6XtrtR}!p9wqEzWK0lgSj?a&#^O$r` zi-Y`3Ll*Q=l<vS79w0FPZ)#AId1!>9#E4F@{XHbyl4DAJEL1@Z<4F^6D`$c#Vu}2M z_CHnX1wVAqesJmZgTN9wB7O1*?A4m_cV!MYS4<vH%ll4Vgeoq)Idhg=qDqHt4_h2+ z$O<o55%+cYy^q1~j30lC{G1UyY5@RQI8WPJ`2yJ^U2Y~Tla8X?jQv4xx*eB?=*{Di z&fja6B+4SF<`5k6uf$bf)GKlI4{wDPc`~(u^NwD5?)vJT3Wy^eB^CA?R`Q0`iY&U< zP!A^MZ7UzHK5`{n_mJCWr&+@>hB`p&B>HaswhGKzouj_opImDM0$|$8Zphdv{fLvo z>(BRDBCaX<zqD1szqHkkEGnxR&QEb3feyJb*Y$KzD8+q_PFz{QvWSbb?v;?ky^3U8 zm`VNwA(Md2L{cE_`;4O1+XmBIP|?!f?8<u&(1F0`*nhpuf33{&_5WCz`J3i+B>}<z zSea?a71dd=rfnimYl>BDr6GA*9}cj7y$az-X`4xuPCbn)9y(0GXRaHD_<-9B@~!VC zv{@zf2benL_12WuE?FHEoPA>Jv$=;U@9s}V=9k4@bW?p;-5Lk3`~Ff_9y2x9N9N={ zhMBd^crL40taExm{d5biUZ&6EgVAv<;NZ-9e^KOev46RD-8m4M-5F%+Y*5i^s7{QJ z?7x*a5Bf;Rx?t2~NOTu{dQReFMBM+Zw@REIi%Ci!-zT876Z-J<R;Z0YWq5su-M4Q) zAxGm^tMlejI3aBXW@eTYtp>Pjun!#?6^gTLn4+8Q+n#KIK4ovHT-J+ZX!M&poN(a# zCjRsU3Up$Q@{r732gE9A)y6n)h3_?Ctj@;{nKYgLmf`XoKO%z6M+!eHW+RLwPsvy- zt`sa1jv}W=q@^RLkNSjDDm9v=YB%@OQT1&_s5=a?*PKYsss1H^^HE+2;87fX6tMcw zee??+Fn8AAA1?)#27isAlF4F`ZP<90sb_(}{w3S(+aOzZ6Pu<Yo5rLQtKNg4!7eKv z-W~NrGWH9z(y=5JyDCzS{>@kl1;P|;e?ekjGmEJ9Hcmm!TxA?;`+KU?J_0D%L!=5e zHBFuocnykipe{I(?}8~FTq_=+Ogo8EMluuDPo#`K;h2&y7HUaO7i?L;BpsH&pjSG9 z5ae!N|EN*zc1KL}?xb`X5@u>7m7rwEXs!2%+QIbyvGo>UQFYz>uyhF0QX<{WAdQ65 zjdbVGor-X10qO4UZlsa!?(QxDLHZli=Y4<g|M{-_(#x4Mv*XNhuY0Y%*1Z{r)=_lv zVw4*D6dMvge6Gs)a3`9_DemG~hBFaqVOVpUAVfXdNgsV0H&>4I8hD+nVgfCWFv<HA zr{=148vQFH)@elC`3)BLkQdiBvxXHMR_}=c#iB-la_7Xas<-MNEdLC~6RJ(`BTKnr z!B(5+2Z<bv0Z*vB<Sl#@c2yN)gJV+C4`clJ=#8X6*x)PFcoi)T{eW~~7C81^T!PPB z@B}0a&ir$=K;QGZP~!!vpjl56=Y<UNkjc5)YRd!<$J!D)!uHtS+Mm|C2d&Lnt($H> zmYPNjFBuk`n}#>vE^wHUCL~!&CFCyj)hhdIM7>>z#Tq(KwHgR4=c|524PFo(0bWO0 zCAm&k4_Ru~gjg>8@(VFpXu&c$(bXy_#c%d-<0)))18(=X_7>vOB&3L~=odKb4I`{H zgQFIjOcp#m7m#<W-#SVLyud|CsaF1kZy%}17n=;rER_{UlGpNZ+0!SmBKOenfCO<+ zaKe&l;J8b~W#?HEQj?1#>dQX7Paxq@lBJDEMnT|W+{^u{GsDFdQc8eZ*5b~&1f<=c z>z*A=0Ie#a{wt`w9`|6)RqU3sRVv#Fj&FNb6q`y>7YiKU0>{{lu@|Gwh=V(G1-bWp zFo;NQrw|Nv3{xR*>AA7E7PYCEnyPB0#Vf_y1~K8e&A=HZfGZCUz3rX~po17$@+r); zOnJxNp(ot!;&2&fSE1ksdPT4qQ&=O8yn&oB!J^1Uj-UGm4=K2sHy9hQLNA*VzYm>d z+=F#I8GB+86d7qo00#+oW%r*{{FB@v9+yChiG@g>!FC>cB$7YK_dCoNlK@ff6SUQn zd@<R14`7339sKJh^|!S>BRd-gm1zI)3W#iB(EC#Kb#BN;9Yw$nK#~eWghtdl!;?Dh zYfSLuN7&<yC<N?0iwN=)KdF>@Zr@56;OjbUeO-nzqV8E8M4Afo;{7oemo3~2_C!ds z6_t&PALKH{It7B`Z@(wEI#SzPHlOjeDVzw(%NOXdj`E$)gkKALp!xA(y(dlgA%ky} z@AS+JfWj*<1%jg?`5)ns=w`qR$hIBk7?O<XKfGwdn<P&I!j@1{;zMMgB#b_&^)bB@ z(}>1;R@~;Hf~N-gEM!t}3iNG~=4y~{R_hD++)J}PQJi@<_97#EmF-Dym1FAPMWUG{ z9PWJ@N7X&N$52x|i>Z5LwQr{gfTIKyUIG5Vi&2N?8o*7uM)lFiR&FomfRQ4fx1>m~ zsEie;|Ks(;?&jvj-QCZ|os)O;{ieH@{=YL$r4DAyU{nxiQ*g_lzh7B(4%fJX<1qV= zfF-Pst=sM2AWg$RK^nD@dyAY4VonRRDScZbKvnYLj=8??q_=v(S(?{eC(fH^2iGi^ z<X>s0JU_cytvr8kA>(HsYaosPE-B@s?SrUV_Qu|EuREJcKA6#KvN2o0h`DxCdN54Y zJNb%1vT3;6A({ZG6YB<{<jIg^n(u1_)qTUSN)^_-8{2@rdC5GBaSbFnuAn>1KCFko zY@}$ueImY{781ARU*SUSD)yxPj4R8uQxK_DkTaw=E+ZPhneD66Mxi8}@<jsTns?4& z(q(u@uEs43h%54}iNl7&iudbNDuqbqv$$XnEdJQydiUPD9;U|cC0QgKSw<~7Iug@S zvnW8pSPu_*Im$Ho(jcD^R+NqalC9D$-t8F?Uix)j7*1j)MA|g_02&rTzn|2lIs!!- zn9QGdL4Sb%Qz&f=E;k5-JP_v`x_AEYh|oNzO+rsRIV%)x6OKF`W<)cU-v&OF^Qr2Y zwy~Ooev}^i$E#>vMrl53_6z`H1PY;j7U6~TfR{?fojK2ko0b^et$)%|Vs52liN>Bn z<hE%zJ}Qtha|q`bnnbRSj8s7xi-tJ)Z@>BlF#N5GT{J0_M%1H3Egq~DE}d+e8os~k zh?&WwPw#Jf=>(~Wy_c4y(GL!O%vR`(P^fAkb0&8HDoaG(`h*$6&VPoO(w4)erq7~f zLHxVQ3i3k|39EbTJC^EI-^t^?xJi_Amlh%&6En=18MyB|U0P&Q2KESP*EJB=-dJ;R zxHUfnc~58SZVqj*TWdAT$|{S$Pn+h^0k~$OC!bP1KL6+vgrs_CCgz4x>(Wm09(~V& z#7TiQU3>DOqr*s+e)g(OJA3EmPy1B7{335IuwMXnzchn8R(gHA*O)sGT8JJrGJa}{ zy-0pwD(H;S=eiF`eW0pY*XSy$#0q&683y_IS7*W~tBRL)h1D&Q9ai6i^gVGVBeh99 zd|+S;dMGC&{X)NNeSS3JGsNacQjl_{Htz%MA@2(1>cJ<>8-s&8vmp*cy3gW9Cafc5 zM*)fmvel3NR32Xv5wJsY#TBXz{m18$0av%<Ae0zsN^{VDOGyimHT67HeQ%-@@R49F zqjN3BH}Xs53O=P=pgwY7eUZdRrUDUZge{{w?1}4t62>}s3J7420bimQ@vs{U?;ZIB zN_F8`_o|)Gtk2_(=zZh-L}Fif^Kc{XqaQrHMBL|tw%EWT5PDJ>$VktgRD*h{<pozm zI8DF^okg9HXjd5vIcWJWd0%$?(#}IelOzqtmRC*2DNb*aFcy@=n~sycH>ld00p-n3 zl)Wd|+mTWO0Bp-8<k4i=KI|z(;uB-oW;`q{ID@|~@7Q=nBl8WvGVOoGlfY!bY{>VC z7!5Nh!w+PG5%oT@!o};Oaa2Gc!+9Q|V`TgZ%eY<s2sM{8I+y7mp*RF@h>|NLo<duy z_fZOB3Uu9(LZ18>NRC)6QL*^b%4s|U1i{c_LjEIpRiKquLKoW>GC-6CXx&HpOU>Z_ zY6^ffFnULkWd9`LTqtzJ6+=SaO&hP}i6SS*nvIG<1b^00q>)E2&C%akI931?r${U$ z3uhfeiB6bf-z-UC_<v#x8VaKC>Nut0TY;Ho5nKmHRylcUNQ}FIxm@CwnFtb8D5aDj zJ@sB#c!a3`(zny%#EgT=bTYFYbwj+8{<kRq#M$cTe-_V->d1dDp4L78ES}d{n3~>s zyFRH2k*{<^0f?R>GdV)Qh<T5*lxC$7i*E;d%c>w9yPzK87t&Ybu%e#se3}zT1MDbk zEWSIV=%xCIJq5-UvW&Vq;4k7@&=K;grch)M2Nxa5$<I@~ocNHGtiH<^>gj#lePq^2 z9O&PvH<gAIzeYje>`t~xZiDQfH;FFg8L7z&fP9!k&NB(mY$Ee~lLGei>=+seHYmw? z0ZENqKLrnh{Xq7J>=aWiC~4uZL4mujPRSTleQcz*3&x41ydPL(qq(~Jf-H4bA;GP4 z=!A`iEq@YCPShz5vHLmgDql^RZeU!`#;Cn4wAorALA;^YWEUCNSfssq|71IR#Aj6! zu-)4DK#d$^5G0@QqU-n)RkHrgWgq4qYA{=p*r%j>3W;PRx5{RtAV}(lUx<xhztvz^ zF(dYhHnrvpP(;d~T^PjQ3k>8qA8p7212c(T@i&!Bkg|vtM-hWMCw`hLxp+bR*?;z6 z>i*e-0e;u@{e0SlISKf^3A2b>TFZmw%#&W?^ASz;OUMt<{-3aydlhm;2`>IH+k5zn zRZ&0UXmUnzzch&SpdWGmXCqQl*XTsGWasyOY3%sbZYrUZBrQ;cv)C$3RX#XAzu@)Y zw#2`}(EC5dUV9Osiy6@96YY`I0Ewu92AeH$?msz1geE^vgf!|41PHB?RVd2PQ8`s4 zH4={e<`I>Ztkb8X{TM-YjvtJ-)pFH@6$HOSVLyNMA&9UCz2Macq$vBNP};t`_M}NR zu3S%QTWLn>y&QIbi<V@={=(5gV3sLoCqBpjxZn6FCQ~Dbp;%FgDje`-S+wk?6#;Qd zDoAnHL`vo))CgtGzD!m;V0}L|qZd%PGlf1Cnt!=)Q}sPoi5C~T(0y+F-uhOWD>(d` zfiKxMjTpFCJbY10ax^ObY~EV6CMBk`_~aW$z3XEs(G0Q-CJJYPNNnqFXLLVBMeOOS zUHg0SpC?p`R`RJ240(W%=#Wquh+x9)7pJuDn?_HFkYc5)#KlS;^DmRlpEeV{tk$$2 zroX-EP#oY)Fm2R{u}6x5{tHFC7=v`h+FZqWVpgp>NjqR038$e<c#8^@0V#@;S8u^u zXTxF@FsF2^%1}v%fTK`_AtU-YQ)BN+^j%qNHlG9#Vw0pu1;XuHaLF?<IT<z}$mYJ5 zuFGaeOL66X<+%SPnx1~qimmA|<OR^M^%x%8EZeNr_*6_d?eQXVYjETJXx~B>XC5mK z`$b-<p2PcaLPUXP4=?4>I>UUDzeqPIAkl|YlGjlhO>tD0JQ*zAx^B!eS|U4I2KHkQ z2U%pnf^~;qtsw%ByDuS#MLl@=j@xmdc9FS@t2x50$eSs{JN++^bG-94snfmBa=pLN zfcpA9`YRL5Y^F0}wimu$-7oII2O<1Lo?1}PJwgz*O^{enrHb)BV5Lv;ku8F6C5-eV z$zK3Llph)1<<q-F?qq&uJ>=rvC`~5ph-i<&SgMlmZQ_?xVOu=<d`k~rUHBs-2oVyD zjn|i6y`yq5$VFnUl3<0o{Ug>0k!ys=@J(VGP}vx4BO8FD4B&?eehVG`kvf!;N27*( z2bHx-h;_ot<c|a&@LPq?ACyZ9KBEFNk%BpB!K(@X=_)=x0uz39F)#tXbC9YFg%5=O z7jRf0(T-=yysRI-#6v{={%P)yrXLG5jO}sfAZI`VYES~b>tB(^-?EStU!b^L47Pdy zX%6^5?X^F(H{tc~_8MAAo<?Vm4oboXoem2A1-A5{`0t*A5q{6f@9&%-gU~bRzUS<e zUhb5B{&XX#ct?V3AveY^zM!i%8B#F5S73jNLtxtc0Fkgw+W)p!C~%t=t>a9%wb^yj ze$Z9RR`*raByKxQVTrMCO}ld?Vu@{uXqo@Oaq>jlZOkO3q2s}-2Ex)zs_wSTesMoY z;@lQYxEocX$8hhkvoZ2+;Jzwk!|EPRiFQ9}Z+6(+K+`3rfb973>bM;p>)|EuKk8a; zZDtNq0Q3uN<bj_QPhS=Fjas@jCBAa5&7is`o%M|OjbG$vEy&l%f^wUkjg5Z=kt@qO z?t&<jU)44i>C_#z%tX%HzqXT-=p~2UZk{!;e#8mJv#2(5u9!vU=Q*S>orH*Et^Fiw z2vuE#Q7l6Lpr9iXA5^N$r)hSI42ppq>fy3v1rY9G);t2LLU<s-D~im6&*;nSdc3mh zp+wZq!-Ry;lnP(eVzDLd4=wdd@97n5-zi4FpC=QMD%u%xB<G1zE=tWapDN@jP*k?! zC}gbL7svWgBwG|jIMtBPvyBu&_ua1TFjN$4#sVJqa&_tJ`Cd)Uvh+SVR!Ol>^U?%F z42|zF;dBf2W~tlsYPD;jYUw<Td3hP-=18)!(d};i^O?nHSnFf{O3HFoguB}cLG$-) zZh{M{ht)*K-5XQINBy@nzeKk`q-th_eYB?ZNZE<KK7Ne4!4e)@+xT&f6FTY;IO>A= zZe`^s=i^K7mi}%<m>Y0PkCauhu~+G=yFWn7m(G-4%2yyo(zzEUUD_YjfY;3yXU0#5 zgW<5VE}G?K70`b|YG`5^R<=Ddc$5Hux3^343|}RJKvxr<Q!<Hx+_^d3cxIsqMnQZD z3H1^tR<6S63?OTm)c41rRU`-`hFuNdrQ4!ReX#@RQw9pjyL?BwqQ2+r*!gz=w}DY< zPBS{+KF1Sh=rMhh*D@sw2jA?W_x~h75`oIFgv+2I!G<6@6A{i(*i$};3_B!2hctsB zUiA9{GOg<Efe$uxcx8j-QK$MKW&|>n;uB~?mGHcfzxXj8WDiYWW^9cVj{GvGZQ|3b z-(6i=?$ftt;`DHQef0s7F5SQbGYs_?a}2`~kx)C)kOPTMCHo~3&L^6=^V`QR+xrO| zG!e6xOHouJO+*J^>BE8N<<RAdc*W@1h;0zdB?8wTBp>D<L*3|I-OY{*uen)g0`{}> zgy;}^#<c7CnZuSx!W1KBk49+=DH>Vi8G9VT>^*}3c>2ro<(@cv462kEPx9>IXZ3c= zT<4d$P$ugc*MzaqZR$cL9n=GyCT*PmfUt`b`@6L4^)LTKo5GyHbuH*+Y%fltHp0J1 z8fqsLC58Lu=sLl)u7_OH%noD5DG2%lLQ#-nTldlUm^6$jl^l>*?>|Y9j{h48Lz(#g z(a?#sghO0{p!#tXit%Sde_)9>#DWsoo|BdEa#XsM9p>ZT)&xP2_9BJeBtqIlGo?~Z z^lJLk7Jm9uYoWgIPpxS^8`_irafWlku>V1oKKr|xXOK(45W5%HJ_o-y2`cdq=2#LV ze4&S;L%>Y`?-ho`A2W%b;C@jM)4vCyB$=L2IVOz&nN;F6>i>)nQ;2aDGCtgYk1r6Q znS^E{8k+onyJAUX-5?m1o-^G>c<QREr3te8JUUotDoRr77-&XiU*!e%gup%*KO_C6 z?Q8Z98mc>tFXmVjmJES1TpFKCYeBC<C2dnVw!M>TDcCW@LH9<z@JBFn&;G&^dyeKk z3GgM?(WqmH6K6YY+uxEaTqf1X0p1AaFRIjy-oZ^~&=Sv~YZ?$5!f>GETH3=Uz3@c+ ziy^RA$op^_t!`REVZV@=zzxY~bDI(z;o$$%LKcRPft#^R)mT4OGSuuQA_rcj<+H!w z08lLReV637U<j=Z1MmE3H1(b(@qLGn{O>1FEGhi6=%o-ih9_pX?~TmB997$lqbPqd zIFW;oMUR(_5*G^Ie|lyH-~9I)cnl>bT;>B$q8ZfF?{7qG3jY`2DpOHYu7*lSfPDfk zu`dQZ{B$$_fFlUZ61_xEI#m7dA0^`z-FS2;_|xyFbMx=B`D_UCbRy0cYrjo2`~9F{ zpZ&M7uV6%BTSwzP({1Sg=jTVw!k0t>o_^!*-$Nj=`OoM&Ohs)`**|b3UPG}*(GLkO z|7W`XW5Fu&&%ZgcPrxSjMS=1D&lp|~$p>Y;wB0k!h57fFJfms`#6WSs?-0Q`7!f{E zLI^ZG7!ORO{mOU^@*+odF<GtgsZwT6S@CIExlhv+yZ29dbw7Ws2W9|uxE>vkABOP! z48$cAyJMY$-GBJ?V0I$CxvS>3?H{i~0GwqDTw0qijio!!ED4CX=?kKP1g@P0f1L2X zDG8>1b&%G)3OTIaiYhR1fuFz5YMJNqI%Z_7$^<w`b-MC->(cC8x8f98HqRblZ{UC> zgjAIviI~N(7LbhTW&CZ0|20ETGg+Ix;)xSsTTEMwTsVD=n!fV3T^3Dg6Mw(kXXX0c z#K!$gtMg_ygKfU++}sg9?>FxjyYLL)>#KF61<fn)n#jUJ-H_Vm_b(SpP53wh>UlL( zV~PQuhIx5q^)f|uktPirCCVamjhyOv79g;wP<^Xu-c;ovlg_C|q|oVg-q5iryInKb z)(maAnG14?<-t()TectlJhKb@&5r{esYrUu=_^*j&S*MK{0sb#zt)e2(pO5W8ykC= zhpAiP?|<Ef-7Kw^hQQZepLw9;@fx~)esc~qN!)+=c(rN~U*}<+aOL5?k;rM|LO=A~ zf&Pa@eCxx-`sY!CM|-{*9_M`yz&auAhrm|>25jQDuhTSVsW9T1un@~ww<O!Db+*l# zC!saTPBB@s5H{g9mGM5ZUb0t}*i$lhVM%h9=*5PygibCN)I;oELa?YR>Mf)`zqk>& zY`<=3PS|TW*yH-LEj@kD?&=f;E)A9H#K=@)qo5IygS;qX4U@DLj4lzxeh~l-fg)a} zxCoR2vM@HNUA$KEJ?rCO7IsrjbY=G)ji0BYN(^z#;&Ulqy0s+Kd}s(FnG5lnzgV8M zmm8^jO!C)e`^7*k+a@dx)oUQ-{=!Jz*!}BU6f-@XUXM>fb8^vhut<bb23Vn~S`Qzx zcZ43WY{_j6TNY{9{qOSPwXi6!>CVz%ai!V126t^7Q;NCn3jzzP)wjMC?oaWQU>@gj z*Z-t5Q=&zROi%PU#`>Uqq(W5JZ(PGvv{-=L%yPfib#QOC9ATnSdY}$a%dTjxMqH2V zO#k@mkZ1Bqdt&504;MeT`$u!4L-n2|DXi&4q4BY!mDH3I^*o)0Jr@8WEC1(Q=fL)b zz{rw6lIOPWLoL+#&99j~v~Jmf&&!VCB>CQse!F45D)k(XQm&tt-stu>6DcXdNt|N> z@<<Py)c6BwijMvrW0CkkS3-2L#ABwwuPMOGFPtqh14oNk;GQ0Gn$ysSTw?hjxky@W z)H`X*&9yJW(9qtdrnK<#aMit#wL18<F<?eEQ{Ae@EVHnW>K_MQt<oWsBY+QUVTJKr zsNUzC);#}8^iIJ8Jy~Z4W`@v3(}Uirq`B`Kgu)}ffh$?9IpMzoDtYVq1O#eZ9NmWe zH{RHscfQp1&Woa<GD#77xrL(U1+pBt18;&m?x<|(Y|UGe3!}j-t?Ei(wfXzB2NinF zL=ssSdl{zZxx)3!^9AxXXlgKjQKg`kh2&TH(zuhL+fv{wTsf6$F*i&x)L+ao6t6+k z0R?dw7QQkxwcH4$eK8h1ZEyZTQr8;-p;&iX)r`ToocY+cH}&?YMl;X;!V-H1{e61F zr|WP2_JmUA^j0I=d@!@cn3n9DGtXxKz!Gnkq6_9((oeZraRWnHD+Uq=F5OBmY(kPh zNJ1qi1VSjoWw`sgM=HKUM_Qgy5{g$s{ly$Z<#*qAXSNc3{-7kx)kLj&h*|Qq_ydk$ zi*Np$YvXnyhIKv7U}Nk_vR8B49)mqeeiY}&eE7!s_}7!u<B$NP#eeKdFqqvddJRGZ zp8vK4mQS<?cw#G0Ohp*aXyQMX#c1^Z@E%*FFO-lg|6(I7iDQT`Pdp`25cF|e8ufpS zPJW=gAjmi?;l;m+O0vUmqC)h05NQ9UOAvdMgBX{wj)VgwiGYGhejA|rXHRD45_|>@ z{X;mOW|PW3WQ(lqB_;NU2RXq1WmPcjRqE_nu)i4%JA|9$K)U~r<q{o3=^-$Og4pSX z{~tU3<+pvZN>AW3+ukC)IRnEYBucz{vQYnHpkRdKUTz@C;!viuPxQyPS3Jx;*O8AH z2cNYRcU#7}Za}iOtj_o1sbCelO#>^~#t&YH#cb$csBsruHK3+TruXNG#eh%M^*JuT zDPSuvzux3;Db#Wt(ssJ7z1G1ebx8dLl6>RAW%iK46g>DQx6>9kOW3dhukp9-`1Z$k zFg)1~8Ks;dx*lcavT3uJQ@S1HIvvUoI!fifLZCrid5G?R*Bl~~|Jn|nre(l71^E19 z9wamKvluJSru4M;ZpY!^S6BT6XsPsik;mQ6_LKe1*(A?wRcK=zRcS&ipR3DNoym^d zbis`C&C<$ubaQtOs4DLJ5r;Z`vh?dXxuNZ!KguSEgHvLG>!v*#A$hnx<S}BT5__wM zxYLUUi<W3N-okw3$Zg7pe9SxMhdYJVq%E~yW7!WKorilhAKfk&6EB!=e01+|)evd( z;`Y0VE*6fgZM~=<hHEq`fI}|9L$HhR*UZu9lg+z{#+m80A8X%xswP~-5O$`&7g!Mi zpUqxBSu0@PusAN)=_h=L6zD>|EJO^|M{FgYw9QK6C*eWKbzi7HjR1-)^P$&4NF!rw zO2hQB(IUPpI5^Qj<#hfk)om_CD>aQ+Q@5cR-0>s#>JW(4@|LH$cl~UwpY*;s>wWjB zxpuwT_7tKQD_=WaK7M$l@7nAM{L7kU-qd>M+^+x;F-4fw&!mcjR!$t>gYpUL`S)w3 zBvySp`QEX$^)P+f=HOq79BU7VR95Y-MtAlExW}&8gJvZByydjj!e>`aUbyZBd#}kS zntWbv#8XXFC$E*e7{W7J?4lBEk9nEAY5rb5z2DHhy(^OMH#?DWUyt+9Sc>F25o-ES zWv&f;<BUc6sWPw_yj`f3&Hdx}IFUkF9{VmzRK<YkVXe}@yB|BvAkC{tYc{(7B+z|a z6;qxNf+V9F*2RzyFQFQVIVlt=Fu8WbN74kP;0)Ign!dY^;=vB3gI=S}fF)f7Brt+b z&(~4*AKLYVKeiWAO(Dh1u@y`mp7IbmumC}*C9)%Jwu;&HDJl-46E8L$mh{*_Ha3}O zt~^#Ngu#p8M0MxfZ215Q5%%eLF~3q)>0jHZt(2k75_bzV-fq#SZMKsV9&OK|?b~5~ ztY84Yz{AuD%vu&db1zzaG<nZomxd5ZrLnq)eNs<ZqiN}1B}(xGMfc;|hiXcwLj(FA z4&EH0YB)vaACxrn3vFwLBdNl^fhlH?ob&A5KHhz2Kzu8th7uKfeRJgH;&A&4Xe&w> z%bVKgAd`E(?V>ng6%Y+a9-TvNY-|x}uK~P%9z|zRJ9_9w;GI&d#m?jA;3*ceF>+L2 zM{w$V{^PPX?vSEl)pEhCp}7L<A|KEox!)eMB}9YE*Kl#|GMBB|!>`iD)@m<fji9)T z^uwUMYjATd_S=yDc3yLTRqS+YIMbM-lDqb$?0%*mLfr;E+#TGCg3XFE>x_85$%rMH zJ_y#bGP(8{?Y8EFH8jcy<=MaE(aR#HO`tfEogzSvarn8|7!a_ImKd%?xC8{7N9hke zb_x*tafb+SB0@5sI3ztg6i1InbSyCO&CNqn4O3Z6ryfZ7gWhh8*gU`O*ed5NgxlYO z=%R|0>?CHCV%>TQ86JjOOvHYg5ohi3^^L=`R_A+Q$95ksDT}`~hqkMmLe&UBMA%%o zeQk5_Y5~>`<hUj-*x>%DI02|rnlheJX)a7Lmr~8DQmJE3Fk7Sh-1RyBg1p$I#iE%v zWg~KA$N4gW^he>&g$<z%2X%6DDLexcwYvbWK0%qC)7kOQIJpwtl&a~2;XM-<@x>*i z`tGz5(Gc8-7ewZTxj0ikAB%0PY;R;s1nP@N7X5Nw6Ev0$XgkQKZ8HFj;xa#@$`$*z zn~<~vZO1<!_kE9WVrdSTz@Lb4YFuYcWKvceTDmDUbieEw7h0K53m*K&L%`3VV6=o! zR+RWNWo84@6&$OD>)e(ww>H&3HTP+=pWPt5sE4sv@KM|Cama8BEF+135^k4+9dDxk zd2IcMWS><(Ee$1&u~`gYFFUH1?~h^YjN2&5!HgY1#<B9;4xTwV1m2+;oi+3IjvrSE zE?J?7Idi9${|5l8IlOe?dEofx0v!97+C})+a^n1E7(No`>qgJnIbSE`%@kECQ|9;g z4ju@{)?!MefJi|jTpJE_663V75+)qY!7HgaeOr2LIyu^+3t|<3ef@I6W48Kz%>LNI z>^xQ>F)?nom=fY>mOx0^7||PeJEqI3SaUE7@;py0+q~mE==r2<mfK9?FdCY{yBwhp zCznDM0es`fuc-L?Hdt|KbhW=Z6l7hwAs~F&+3CqETM@&vW8-kQo&UA;fND7b=P%o9 zQjeb_%XfB;*m#u+yge$wq2rc}&}Tnwu$oBlwzwcJ9ut5!sL)eBh|#QLsXX?Jv{$cr zSh$!E%J<cDo;tg2HRfqGwpN_6(ump~MGm6|?2H-~S5&r$=@&U4kw2}kj4)ctd>NmE zR2C;_&3)lBB@{EI;$Dbdt2r7+WD;X~SBXWBC>r+2Pj4g+<gysnf7~Mx&qs}K%=)^0 z%z~*kFd5|%KdZ6cBeT3lhrtn}ekOGD<!q|2Z|>*m9W>R6-kJ5rnUHk)N9b#kstBXw zrZliPPBdC(!KB&FeK?t7MCFbT3nP1&HMC^nb=Ed+2?w$7Xvi=^LUU==2j>vcW~cjx z1^ZFF`jmTBK$ZCNv6(8(xp|-?^GwL#3g=9S!<C<VexpAXO|excN}IJDvH|sR|K26) zOlTG}4_htAMbcdA#x;+<IFy!)lFRrSOE^=(5C&6N&79Rp>#H}WFkXmhmjP?EBANE3 zFM5uGqlxu82UG^Wk?-JeA$B+hW~%`G_yCTHZzn-7ppxtB3%*n}7awwG$+lwDuV%c= zvTPqD&K_#T-yJD_5KnRTdGllI3+er=rE9R2*GKqkZffGP5$0CAt0n4i;_$S)Efed} zXSaP_9sx)~eD@q?1`oj85rTzGTv?#k&98U-5x*+vt?{ok#B7F(ukZ9)37Oq=6B~Oo zLc)kY0X^Bb4_6+y3wf&4ehFwf3rGAyjlDf?!)<PQ#)vBZ!buMl`D4WdOF;@6pv=uN z@O!IF33!ls=E;n}2qMqD6EqkWthD=9AIU(;7tgPbf*FgvFq*CrkM(=(#|UV~LJck3 zN~16qogYE+7t@0p3QPAg3+~rB+<5(H?^Is_fSt}tzR4HP<$M-;#RWzYp`u=qAdQMH z5{1XB1`=AbWFKtfrI@6f&Hl*b`j@REw$aR|pk(RrgFaa44?=0{3N7`o8**7pKE65` zsIiNvH#sw4%XB^|<g85^&Nocb)#F_%lFXWudpmG-j5+*Doufr|_Bl?&wg7dhOVl<x z6flGzDNgT=)T73iko8H^!~zDVa4mlsAsVf39An@eeMAy!H6)3lIucZZNSaDk;_A~k z`;%>5!rXn}tMsu-+H%V4)RdIeA#`zvn*^!0F6-Hn?|QFZ;UuB3g~6r!xVTY&0im-W z!@=dxzCvU>T8%rfVuy3`^!^6`N1!*Curv%@Oo;ckF|3Jq@>V%JFirjJb!lCkOR1NO z=U;Gqb-@#D<E7jrItGl*T))7aV2|(~xvvuaMas}R(Y1*x;i+tb>zDcX+a@1Rm+3a% z@F|$4Kd4e~@?oWH<2Bw$@z=)%eUfz%3wdb*9N+Z-`vT|s+e;tWhQtfEF3(Ulcb<*> zfhFDu3)DF$<b+?QQ|Vzom}|~9&>s+XgB0IA$n3f1!tq5hk2sRF!n^(fxXRUE59Kyb zvIivKX=1;P-<t-N`Ui6?sru4TBheuyTB`}kAK}kTJOLBW-{1%~$&PLV8E+&|{psm% zKW(6-E)X_(zJ?Z0>fW=F%>j`1|F#zm^VA;Gzk7=C{@v4j8;JWHW%KWZo{d<(9AU|( zJ(Zqw%|+mg0s$j}4=AU9FzEjSg<PKan<L)`gw(z`6OaG76ukNS(gtJ@gP?E$7`{KK zQs?W4@V;q-+#{){NrBz3L5ifS3D|t+d1cIRW1XyR(s~4yB7F&Pw)E4*VLn3cVqMOH z4Qiwi4Ttj*fJ!%{eWt4`kotMfjZ=`gckZgLnKRePo#2o=<z(6H7I68poL;}+l=!BS z{=L*;CJ%XIJZt>`x{We+cL_2j=I!%m{Z^IQ@AnC?R8?RxvfIBECviF^;w-P1Pzf_d z$!In1b1Vs=I^5d#wznD^nf4M_qGKTKrP3obNHot_0nbMG-BS;OdpdTB&KJ|LIGqXa zOO4?3zh6+inh3}I-sgb2eIW)ylqY7l0pq88fSkpIqNWHc>d6c6X}hBoj$)tp-s?Dc zPCS#v|D;u6f|b+l(@kQUE~FWcP`V-7vbsO$SpU#zvp0D+DDH#j`mnXc%`sD8qHxf> zk&ynl2=KgpE8b#Y<-)R%a0`C_hPh}-JH};O)w!7e9oja%*sYQw%U)P>l7<WWHw!=B zw9HPt&hCUC*R_E%2uhmvuMGQiPVa(ZYa4W-Ueq1zj>NxM?57&M6PmwEq6}U{DvV5= zJo)lHfq3oe`>G4TcYATHce1fIz{L{Q4Z}?PNe&?F{E6KwkF3>=?2rWq#*Xs+K){XW z!J)SFXf@QD^MKlnaIscCld>GWbTFWEck7>Yp9E3WJux7E>(II!#C`uzk5zW%nn~!o zRQtU{2frX&srus4&t|Tl-Avo;<Dw`cJ8cNn7DweGMzFp#ZJkZ09mIp+`gKn;C3s;A zTmXOk2LkemYM<%#`0erPJ5ONZsE!d!;$QBA)9eJz?IQA{w#%=@L}%giH06W#=87Ai ziYd0b%zC;kE7_(-H!2zx0ys#@+OnDn@a-(oldY?M)+C&+yB4W(1?x8J#CtV*HM(4s zMXUo>h1P)!IO#zNr1{vQHFE4zoNioQ+nDwaZ)YTm%Xw2Pl<NNSSE2pbo1^YG5zZ{b z-)LBQWv)$jGy1CHeqj(`hbg+e+Ik$E_1wSEvf-O?<D0>k;SC+J_vv$;PPA?=9D{fd z+p=^5kwNlZ(v1_4as));#iKc!<<q3q*`&@^9r|zY03<XNv6M}OCX%Ov9Ue)4e6iX7 z9C_Vdqhez&&HJqebh0-8t>OZI;wt}y!eMBB<^$8E5O(U|C&{t}0$rZ0iUVbAh2{YS z_!2~BYRdP%^+QOBTn5U;g)$0O8HMdrpT;TBL?kyGcpzzZ%%KAQa23>uLHccMD=qw| z*4AkoK$ycD&}p^ClIW%x6|C%rAzeNy4BNVRHi8k@f@&P<3>p^9VYp40^K8bfG3V@N zpZqA<<MnXsgkVjQTuoiG5j7H&$%5>}QAkR<*c9}{m5FFa%Bq}eq0<-D;%)Y%RE&9% z3d%bIEb(?`bv*g@vc{cm4VppUTb%`9K%>qE$Xq`%GR13Iw0JZw#kF}+wX(dpL<ic< z%ycp0sazVy7OS9|c*dEcPISXunBYHs0G}+*(ha_g{3J_18W>N!m<^tBIDsY$5-=rG zv~mO$%J{!|#Ck_8;(Avvv?=k4Lj>i(midn}Vfr6uqK)sjGhw)<4}fP0r=Zu)AQHrx z(+;R9E(XLbrol^gR`c>F^E3*k@ZSyPB9MX%tn-oDmQ*-R3D)49TsjP`v}_5V>to4E z(}($HF5q?&xJoH&t5{7H%@@=<#{BS&8?3g<QTq1d25(PHb>#EPCr-gW+d|o<tPzwi zs{DDx+8g2pUSnU)U4eD`aZd6cM%x6B^~*$ok>Q?3gfsz^eSwRysa>DqN71N_v9ID{ zFKQGfKJS08R{qA%<(@a4rZsJH+^+Z|I$_aXJ}uXXvA4+>h}C#qOA#PpslDsGez~Zp z3?zHtjJA%t2nf7|xmC)u*ttrDrAz1<@k^ndbg@^L+Vdjgp8{&IcO#2VB9WG?9IV2S zQu{7<B`xI)%K6KiE9aN*+A2mD3@i_roC7VFslx4Iu9y8CesFZ*kcXXfB#$p($0_Dq za<tRtN;QR)t}GZ)*?{zOV)fsWiUw?V^(BI>c{t?CRE>)y<zpfeX8b73ab9!Vlp3XC zcIWcG&h}N`k^@S09^j*XfM-`UOg6B<^QwhB8wpEqY?z-b5b1xA5OjKAZx(f##Ow5$ zl_M!kz@RbRQ|mV!FdJdO=M6d5HslfD3kV7GmuOx<=px$`Z?s7`rJ(Q8B8e`dUJRTZ zyKVOI={P^%n3y>sx8=f5V&by8vwno<K!V|;&W>9QR;1l|=4b!SXA!zJPbGhoMx;`X zi~dGHEek-YH~3yOO<!lNZ?puW!KpTnJ4Hh#0EWe{ztRtN{fq67LD+u3&J)|Wyz05< z3aheG)8W$Di8^;^#DNIy)}!?C#r7?^(0#c^>&xyBS;K&Ocg6#%C$2m(Sh&Kma_4b5 zyV$h+;&BJRMbd?ZS?<-EI-0A4<;HTj?=H^6O|3SL^V%-l1$g1gy1HZH6xCzwBu6Vj zLxulc*sR;tHMdEcP&(^;x9--2(vsKDDsMtxp;$_VB3sgEm}-QQp_C;h+5BM>gaS?i zvv)iLy$hxVp_GT62JH11<E_$l)OM4-^Y^k}$&zEfmU)~)GQtz|7s|NaBzyd9FEAYa zHiM5gd{9c71rYC-XGPlD?ueF6iubI>Uc;}jr-}xFJ+L$qm1HwZw!dFL>!(_@VIHRn z<X)$jpcnfJLKG*J*s8*H=BP`*Iv7Cvc$T%~V0yc?P<F8sRuwqV^CeAISs`$%f>1Mb z@l2~<#>znDVDY%RfzKiOD^H)hJhgn@Q~}n)XD2dWV?cpAXg6yra8`UDWq&fYskrs^ zMbmMTZn|u`EMdMZ%H*Ec{(JN+{7H!zfSGASvQYf1xDs^`ER<7!wk!zOStaFH6|mBJ z$4&8tSXS;QLSRbPeH6c)JB30PjZ@H)+EZs_lM+2DDewYQLZtiIf+^D}WG=*{a3Yc6 zsItC*)Nzu753^Bk4y`L)eKuFl`(fu_Q@DJn*ine6aB|<pGFHvDQQipq_KHtO(iJGb z>^CN>#I17_kz-X94|0m;?}EH^TC?1gL{2lPz3k5$WX2Sd>E&qEro9hn&pRlz*=H)T z*=K<KiLGfdGbUctJ6Q6x3z_3+6yj5dS_DX92x*1}w&C&YY+)eqIkZm48{j!$l6HZ- zDP3XT2sQ1>Z{0>Ko3z<XYwhWN+NMT398Bd~g0Y#7VqcD{$%XgHt!q=u%*Um??sfl? zU_GH=(-FPPJF+cnXeY&A-?yw$giT{y>XWL8D=D|Ey$2!&G#)%mh~BZ4BoaRd&YcKj z;fE-45BFyBLpg`k2-v43%P%IB=BF_QIM4{-&O`9e(zoL8ZtgtnPq)rFdwu-*c2Ng> zUO*3FaYdPOL=F!5Y$sP3oTU$mtxcL>k`9Zwj%6o*990uRdnP04-AymywcQDB?|f!d zl#H2<uW3()p)X=2*u%7EdE@XLI5<8)v$_6~HMUmQjHbK7viR8dR-5Q2bC4v^y(Q(* z?VZ($C4b5Ev@0tI9x2K4=<rb6KVQ!Okmh>tWFkm-W@0|9SQyO_^?oR&<mX~7@x)2X z{!Xb$aLvIXgPLv1#GXnll~se*TWcp&>z_(!Y~CJPksf>tVw`gf^@9dLY*1xS0%hV2 zZ9HYXvDuKl?4%k)z#;7`_h=zB4C;pXtT2j9vU?8o<Fpa&%d9XKV`0Xh9Wny<;@OoW zvxEIUcc3h@?&IMgC5J8qk~Owu&+5?Y$L#w1sZ=lQ0X*U<G@X|Q60i(r=g2>3s*zF! zU#3O&zmag4q-(EL3VTZlOf@~9X7Hg}{i@=A@-75zs0KminapgpFdx-0pC24e<UGru zc>8k=&ok9@MdH#RzwI36PM?A_*QRR!f)^vSZ-4OcbrlN_Y0aqfwym_j*2?E!{5aRG z?c#dAl;HR_ndp*N*8`^p9jCcW-XEd=6kEaH<TLy_?0R(ra<2V(7Z7NWD3V;Q<Hv;` z(@P>d<YH6Sz0m<6h*_aiqvxR(>(L1%zowH_G(xBDi>Cv&Uq?uqH}koLk#Ieb8p5v( z=A^{r5iiLR+nDZL+|5iCUG)szXUtx;WK7d~WCabZe_hrXr5?=_z9@jXfxW5bamP$5 zl}koX;o%*+dOb|s2%H2yPl{j=+aW?7`%Y*#8~=*6oVq*@wVE30@}ljWn1Y<9nIEHA zs`aFgxs~qJ{^R->TZY_xiV?;!yPLkr)ZWTQl1ko2i-|y}#);bt{E(6Gx8{+uW##9@ z2AnSkkp>G89U7n==I05@G$*+eu6-)5>js-NB{GI|r9RZ{Z~@t}!qH(mQt$Au=<fOK z(UvFQhBCFj@<#PW-L~;@$m$j@`O0Er#z~*!!jq|>kQ8(0@NAryNTu*1DJDPYby2jb zBedaciBTyIt}t$+Cas4XxH+8T?dM3F#54%YmwWATAJuYyvA49K>&7UCQ>#fk|F+h) zQE`;*EF#S!oEQivj!&4iz8(uOjv%ffxbBM<N8~LR^azSg+subLi;BAIwLHs`*xM3A zY^YX*!Idv0)kibHIKyh>r=Jh5F}Mz_4YP_)J7xM6(?7zTKlSVk=B#S#>|J1Q^_fo} zva^B$r5-L$FB$|RNBwAh<+wrULsHBFN)pwR9w=VVfzh<N6koJ?lNbeHozqn0wm_x% z7nT*NCaqw`(qRz}Nq+q^Xjtg#Dd<kxZuLtcR!P-zw0;tlLm%izC<qb=P>m^S9!Uae zm>M5hjF|4(?jyz|mlrGr)+=~(D(B?xE{jdYCWE%9A@iPef6sL6WB65I<3^|%{VHSi z?E&DNCO}9YqZHBcCbHP{eA47Nb%3w9=N!Jm6`O~raqDRV;_gGc%^-_)0%@N9Ya&;b z2|^SLaPQt5F=k=L&Q$-L9hF}v4IMkv)#Qnf#TvNBAydS`B`WBF6Rv6#_`Kwt0YzDX zfND>uRVs(gkT{F)L>c}V{(a(sbezw2Hn8L0Dr%m!t^vp$Mg<yXt$fH6;9B#vdyLDo z1&x+5PLwUD^Sgb4;R@~Y*LT^y93+y)6F!-D>SC|#zdr}BIE{w3<UDfx&}4Ch#$1_e zwr|ot4_a#ETzO+-{fL*al7dHsP%>S8WH`a{O)okim~g)UUmI>f*dClO!BaT}05T?g zuaAw|n*=bUY~zl}#pwDQZzWz8^dc(_U*L*rMbtu%-7|kx9GjBvh!0eDdB62II@|W- zpCn_Oc#H8vJe0;TIVb;P7XvvK){n2WE=6rv?2~vM<nU15QwB&y(=*aeM=N?e4lWUP z_%bh_F04IyD%ucF<+q~(Y$B}daz306C39Yc6i+$Ke`qc`8Fc%`Va?#)z2rvmVQ^5J zSWe*Qqi^?jwdaqm^f)lk2B$_N+{VyFU(y_IsJH7;6*IO~6}7$xD<NV$SPZr8s+1qW z+HBtubiI3>9F=H98a=noLoi8NTOX?VNr_H7-pAjDek|u4$-sh96$tn7cZ}VKDQj5q zAbL1-xIcrQ*3(0!dVCXqZD)w}LMo>~RUc2JFr9(@T!HW1OM-VfBf^HsBC^k9wEP0! z8d{)9;iv@}xU6=b3?GsO@6f8AxVLcKT;4AXY%3E#9)ITI!$T7|$ZCJ7pDEUBy{2E_ z(a~#+>YwaoE;P13g$<O1R*%bYz{L(>PfALAdC_{(3Qq0AO4C_Q?O*t3B#^+RTpPZn zdpBj?SMLh_C8u(WvPc!`;@r>G52q;WrfE1!(E;5@d8UX-yT&{oBFKw$ijq_DE^3n= zr@DDoW46<c@;L@Oymzgc_e)xXU%qf5jM(f!-})m7YLSKB)q(*;WC@>iuXM`jF~*Yg zifV5#G%~ID=v`PpiK*~&MGv75uShEf$Joi~d6a4D!Ji`Xka4n2e;hPq?vEAKRPVa` zz4NPGTa5fkOy_9ZOl7xDB1_w4rg?)t-4C9pqO#S`sAYc)Wsc;?k-tGkoZLSA(JyLA zsswM$d!#MfOM3%cO+4$@^{K?ku!JmjR7NX5CM^uLK<e_Sup6=Xl_&XakkkJ<q>Z%* z7v_->rA5pYaOI&$ogKhiN2u=vR!ATEMDj+K-IpyYRez4TKCa$nveGbIS%+9yZsh~3 zbzikmwzkw4<#k#S-g)JuATS>JYVK`ocn=yNNw4ClLB|0o3{VVEFP$;*3aX#;790ud zdpof{=iP=cKzYt9X|8eUM?|Y=v|M7OVA6tuoA{CXtJ-v+_7slU_aM;8bX_(~Rd<e@ z#x16%TYaQWw_kh+Y?Zo;F=xJc^Bd)oEi(0q#Qlp}Doe6a<U$QX=l)8${ne$lk2b%y zhmz#<Ec7e@`KcRLdLFi)L7_(3`8Y(R(bBq88sfSAEt%w8p-i(BQn-E~8JnWn9|pKi z>sKZ!E2Du9=P)5*p;sQ4Wk5>v=gVvN6>h3zo%D4Tr%Y5MsRNC3twXWSL$T?V7P@wi zJ5`^1%jt#}xOdCqspWK?Nnp!?7d^>&%auO&W*C>iYz&dK6&6%w^0S~8-A6w1-HREd zZ_wY`{YwsE?Y5DM4+r5RKgO=bDd}|;ADa4ixV6No!7FG)dl-Kq!$j&1j1^L;s_v+e zxx?WlEVF2Q@lvGtptEeR7=LnUrFtn&H_<2Kj>WO-2ky?#kWbF88zDg&v!q)KMdTQ= zi4kwP0j|myxpB5UTfCLSc}s@uCFOc=Tn-vTf6fuVLvXhM*w{7-%tSQ#t@z98V%&M< z7^##CA7VX~tc+!BLCY$LAOWR!BGF<>?^)x<()>8sYEpP3U-Ht*=nA(l68#h>Zg!7j zuu#?QEmDp0d8iYDlQn`PX3QmUweOwvJQByR1~5sLN7rMNwALpz%u*R8TV4J3#&hVQ z4^$r1<)s-Hg0oec@B^82YQ~AJJplgey$8HQGWYwv1+*RM*8B1=KM&SbN_w4{)!|Aq z!|5CfJ&>JiN}MU_E88i*O=T@gCdv*|#!nTi;~Xj_?B^1jLmU>}9%_N_1#JaE_Jwou z0F<aQW>yd%Vemd}S_y@dk*YFMuz4l|@dzw1W7qoPO71dA#*SxvNoS@ctSujzN3G8W z=NfMc9}fp_s@hio6Q_jE7i3!~#uZZ^TzknW&r)89po!D-8BPVKNy@xv9+<l3UVOB3 zzJn@8E-p-Wu)y+xb5+*Sp+xUnRUR_|d{p_jV(2ot>Ecw^&Y=Vvs2+75ZZFSI<Qh@W z6lFiPI^NGEK$p>(=JZ4QbyY@aLT(C*w@_UZWv7exR^nGG<f^E*TE_jrQmCHQu9Z6i zrNohG1P9JBdRcYjG1a*5ej;@PUx@0hj-#j2hXGSfM_n?`rIl=3pu#ACY<>i2h_G}? zeN~l5r*~+kopP;EQ>fOc2wF;3ueu4`yxJH}c<bu3taHkVyCP6K{1f#yn1;U89&YK3 zlD<_N+uk7ZQue)&c}@*%jfTloi+~Ja6h;lIO6XB8Xn!`BazXa0aNP9L3X3mRV(Npy z4vN{iO{~lJw<Cm|(2;w@<P$AKK2#g=9DkDxx@`x7xlqq{+o}?m_0&M*vT-pK$RvPx za(7$e@GO44kRFlDT@t6W<#}}qI}VQl(WeMh?H7qrse}*F+18WzVOR!@EA_d$KOTo( zkUY4^kSeZ9ChbZJxdkq=j@)zG8P>7xx@-)Sd>gicgn~DGpuVl83)pWkm!KtbR_X6~ zL?+S{qo?s|8~S(rFmlAbLc~M^wJrnc{0llTY++rwPXs{PO&YJ`gh!{#G2wp*z*ja$ zXw=z%5rD>c*N7(qKy5fN=tmI{>3GQV1vk!Tjd)AqTk%yzK8lvBYPmEftx4<15@(40 z*wkPOQnucH7qSES@ca0KK~x!0V17$_r2cWC?d@I0{V~}!`J~mJA0&9YENh9~0-}5@ zuz{TK(E9b!_R<pAC75Z5nIxK^R$zw?F3ZB<jQrF$Yj$h(@aQ{9=ZyP!a_>N`W3$XC zJt6>8w!qP0bqeE>)VSH&;=y-ad1dYSHoWS@#?1!hlgEF<^}x`U>9KPPIOGs{46`wR zxR(hmeazOWiQ{KiBple8%$m7KwIGsR%4%mUf-XcCJkap*U7q?H<FYx~Cfi(R4SiOI z55e~37K<dK%Y8Y@3O%mob=`*(KP%&tUz;u%wZi2xIB=SMO2*9Yd7s|XXl>nekneIX z3%-_XeX!cQ)C7EtphH8>jrV`8nK5;If0*^5!GRWkE&i|<&Nm)U|LhvUi!W~9j)XY3 zH=VKg`Pp-R7Q>|6v2nt|B^Iu8^1HWqcgIia*F=Zze4p{-BF0=-DMd~Kv{&m>?h5*_ zOfgNzv3Zt{%Os)#WZ)2FZYF*ZEGZbGkJz7?Tv{|+djcR{wQgkiY7^n~iQQ$^<Lfmc zhO6(L(oY-JG_Zp>weg=ZQk7OQ9K6o>n|H0Tc|Wzf*xI^!czpf@u$-}HN_U5Rzjz?n zZSG@0sIw^2Lr6--deeZP1KuCiJ8Wg&{k>fcHL|vL^c1Fo%~T60{MlbZ=u-Y5jT{fC zKLS>ttVERB85_;7?^oDTR@gWT4me6A98CR81RpYk<uB`$uX~WMhFZN-e_c5|*POTr zfW)peRGwm2C>xKPHiIy(=YEoJmX38pwfr~BX2wXbV?IA-gp;bzMI+)xl8!*1F>sKL zkS%9G{`t$|0Bk}FCIH2!y7yib8^qfJy;YukUw=u{cp<%y7fcTQx-vWsrY~Q+j3Bgz z6Z@o0XkAf=E_}yW=d0+(R`_kWcX6w6sSa($&5dPOhUp+`?1pp2{JUA@I*mEG@W=U~ zy1bNQy|HdhOOya~fIOu>cA@@?W4YPT&1Qm_NbVaW_NPO;@Q20#h8~-0YaP{P03y-1 z(5iR9XxYrT&ei*o)ey#cWf<P4zR@No^IQB<-K0Pt@6TgRx_Bl)A*&aORDRO*cv-W( zvvPxTHs~vX$Xi?I1I9=D<zLsK>zt}(0_Q5t^)pnAWjX?nKMn}7-_V)vl&sL%+ZBsi zitG;k*!w6HN=eK0a<h_IO+SDg9q2U<+pNUOr7e#26y?j&a+aWFh~=}acyEhZe;7XS zKw&>eyII+xIO)$QQeVN?Dz+?9f+&-T7@NsvF}^$gqkBWPZsoNyPt*;}8*P)(3#q&a zcKR6XhJ=;yv<EWDCjIW03h0upLEcvWlUb??<udm!63szp+n?;S0O0Q9TcF-k8Cy++ zRu9Km*0hghAZ6`6l0ZL$jz<QXj)%Uq;Tzx@&RESO0hQb#RyP<!QIxpu&U>7xwHL9A zS<@rJo0+#Ltho7C0aRg5m)sA!mwet>^7$b#GRp1VV#R*{H(dr_u0|A7U0qs(G-5zS zyuJ5_$#-hQAz*y>O^P-bK0BceGbDf<b&%%f>ML*@(o)m<`LyLTVf7Akvg0j1qk7Ox zt*zoBzRBbW`v1q*TL9J3Y;B{#J-CP9F2M;9+}+*X-F0vc1b27YxCD0zZo%Ch0tAQf z@4V-H=YHqEw{ESfsoB#r-95WzdR9N{Sx*-rR$Nh2LKpMzFyv1$O(L{~ZiMQ2aynQR zYcKFXG5YXmH;3KMF&N|Mo2;v{$fPfbqw5Q4>-=JhTYwc-_a?=BMABlYfeT>>j$;8B z$jUl3f9y;#6~`U#TPT4jUTyVH%=3AMqNZ75G;#-<jq&MoqkB1HVpd9&ASv4;DY5>X zcG4G}U{Z1>ASWS9%L+zD#?Y{!Urs9;LsGasOUt5FBZPrQ!cFC@tFS-pLr_xTP|yhL zZ>AjH5)I<SO~wk;li7y$Le3V-$T|WH7>7*ysIccbnM2C(h5q?cP^W7pYt>w5?!Q(d z5l+85(VglJM%IuYoL5ch-7mILGZfi*=StoY$oh7rlv^;oN8Tdn(QZ!o(wR0F-Bx@{ zxzvaK*+|?X`N`D-n3Oslt{zk}+n^lNUi(uMW-rac!}s*g#;F!3^-X*2rKJvl;Gohx z;o$f>?x`ag6S-Ar`NY97!SU%8;WCJ%sbdmaOxwY;93k0!eX^ExXfP!sty81{$<0B0 zrSoMpCmVQhZu6*ndwn@rUS4FI5!nQ=)$Pd^L>JVY3LLb}iucofW<MZzz)eDyo$XTv zrNn=8(5spFUIw`t#3r;Qi$($*yT$$1o`R02ai@xyk*2!}o`RkUIa!;6mU<*zb*72{ zxUab<JmpmKN>Gd;hh2qW%3xd!;$b1N^B6u);4SSPQ?VJArv>PS!R@h1igIHsLLkx! z@<k#lBGU;9mHY@Zi}H=iHh-Dn<)t${NLa$6pDuE<NuaBw8YF}VkIn#ifL2+cDB*^b z^NyfOm%0C;0wa_tm>4xnkt7agT0zTn!#4iZW}Bv%E&{)C&uLVhU;F{BA6{hRI9Tim z&2N2on<(5y{0sR;&!5V+7RpJ6VP7a(wnUoWq$enX&bw=0xvKT>Y+-Eo7u0!d>apLi z0>tgF_fBR-Z)cJ-)&Tvfh=f4*wU+~R4c)9uMLcffK38Z^h{qvDvDwLb>y`bBqlA{C zL~0sDw!gxeRU~1ppe5KKN)D;Wn4Fr4qGUxUl@0uvIkFo(*)L)i4lwTj;At`cxDNp- zCQ(}{bdK&3H3V<FRwE6D36&mt@3z`}0!Oe*vWKV}driLp?anUk&2vX6;`T1o69gy; zk}l%47}7`>^xxMc6-iyH#tr^*7f+jlo)iCvyI9}|<}MOr{KH**Tn*6aEQdSaTA1tz zx7p2GpsD`k`FCzVmCHZSr5^QWU>uDdew$b+{L5eo76kETbUOB>_A3Y4k7KaF>edE8 z{U!dJ@C($<!!k*ZvOGyCl5<;*s61|9)u&qNdZS7TgUN*=snHTh=e9gcRIGCd-letz zKH;ILJ2WemYGUoia9UEu4Kbft{~sw>=LmoKQmoPQGd}d2F^IG){kCBo$Ro@Y{c*@5 z*9#~kJo*yfR**;FP)8zU5}xC-u0?=mGs(M8;H$2ewK|DECVVOa?Fmps(+$PRJS^$< z4HE@F9X2veXoZ>fx!jBm@p(*)gvhRB#Xp9JD9Tz6nf85(PDJSbOfGP1`o-Z7uoBPR zfFQ;>Y#u`967+t;>)U+c973I`73y=^`ADS#^`07_o{cF<4J{q)+Gk524+(&W{*e^< z2(RB1f%}gk6<?83^H(TdyqtdAQBU723r>N_b1Eu`*d=K;liXB@Cc2j5+twIxr}UB^ zK&c!~zd%r+?sbp?UNO8Ta0{9`&I&|N&b~#*Zd3(145<lG*7AeR-+bZlgV`F7XiEtz z(kYv=^#ZPBb?~iJ-yJEy1i!Aia2SNa7`hXJgv0v>T@yG5y;%v2KJn2?k%ANgZe>_& zrfB<mo+>T;xGkE)ll`@GN0G}EqJ;b)%TU3VOLU8JRPc4~Daa(@TyR^tkUvt@oYr33 zfFiH!xRNmXsic#31dmlfnI?>*)_+9v4OtTyJG^C~!qDvQ0X}@xa$lmRvQ9<3i*Lq; zU4jD}@a`;H;Lx%(Ymqu$YZ<F{Y82(!nepaHDsW?JJN~hUU!V7k?JJ6>QBhM0qGGpg z*(Su#&1);aL0X8PpTG?#`D<@BM%2rln!pyJf-N`uH>D8kZev#pnF=gw@jjK`sEo){ zNzG17Hn5PY%TiD~O<)p3^H1lzV?f^mZ)JL(Br#z?-QEittglMMX2E`>^}YO)#CNoR z`v<o)_5RsBZ4~}llP$~2#=f=mR;h?U*r_&GF*uNo;;5vr>*o&#XHjRlxFFZpgt|Y6 zeul4h3fn^PV2nJyuW-~LEIc*#76EDj4p@L&n3{YVWse-E{lcn(|55vb^^0t^nXy1Q zEEF|?-NlH1khD=mv<!$F8`7}bv=)G^?cT?H#(+A*<*Az*Lw&|deSb#@`9HlPun6%3 z63t2?{iI0@1k7+yMTG4b&Rjy`$*^qR8lBUYKM|ePu$kn(J4-b*H!y|(Y7I<h<gTI3 z@6<S#sA0y>*aG~(_Bvi$lRnDRP@_6jklC>&#<j0q?+Gu*)Ye)@`>e_`62~f_B>f?0 z4sziW%&7s?se@0TA5(r0Kh_c|?ha{MO4Mz9bMb7?(_hol+Wc)g$y(o4=SNeUs}bl& z#@U4S4ZM7t2{Mupz^sleeU*fK*L?<cCZM#G3QwK?=O0pQOGnmkhpEkvT8DG*crxm{ z=hvz(c@`{kIbr{W9L!iYQY?lW$)@Dg>abs{SjogD#v912t`+aua}l<pWMd9YWRtp} z>CJT{I)DXu#S92|Y&!PS#OE7dOy(WFI%|UYGhE;l#b2NjF6Q2n)4Pm9+<Z<`h2h5k zV7`_A3-e8B#h&;85+Vy3YZ4Bqt-=PO1#kp#eCZ*89<fA8V-fm1wS$s&rF?l?{2{*y z>+Enl;no8x?zoVqbiK>_{6Y}R1jA_(K?Sr$m!pps>*5l%pj_&h!UKp^g5<BT<<D1U zVL}v{+2a>)>DFEpqv$?pFq7YXTY&k_xB5Fvs$n1ByKdsB?uH$gfLX6)HZQe`9b`%E z2&se9m9)ifUz36gCG_xqD4DrDBW?MkC)f9>tH6A7@k@33CpSWKtn~LR<<kCPvcXUN z&U91o>hC32cJh&Ag_i;8Uzb)7z{-u!^W^cdXcK*nLdu$8T(De9@G5_OqUdacUFq@N zko<J6wa`%%m178$ZxwHcF-Ph0@0XD*F&6Td0-vzz_2DQ-T<d2C_0zlI>P6?E-p^kG z&-I@_Id?wjq4-7WRC92Z)1iqmcpH_9yqwEAC0(+{{O*;Z5_to}IOg=d*N+z<zm}6A z=5uqYP{XcmX>+yb_~6bvz6fDzS{G(2*|CBib3N~st{yPIaKV!xOi^hgq2rLV!H(<N z5a7f8hpAzS@-Nyq@6Uf|-%rQ?rhP+{Vz#FT4=oWbmYkngmk&~PLgGg9Q8rWC0eb>} zO1<=WVjL4iK~ZW=v=xl1YAxD_yjQrJ%@Gf$irD`{QYN4e+wQ&qv${*qX^tDYZXyuQ z{!%<Dx3!l%7?~G54vv@3=PbNqfHdI;;fLA_3BxI0_fB5Dr}(Ku*x;452<FYI!ut}I z>?;lsd(29U_ttrr&m&Oj;`nquVAI{sW8b`*6?H!~xmkc4Z;57o!YjUZ(raqiqQ~u5 zYW}O1-{mT0?8{WqY48&kp-KNCGlAsnqxYo#NBri_i{wwCBNn!6)N0iWbzAT-jv)Nu z9jp&Fw_e&?nkvK6eizqv?l}I9CRUlG$-Q-9-JRdhFZK`buF!$cIG@$?W3glKw=}uA zaWp<xnWS4_7L8tLb|Eq)f;3l_nx#{6X*58lgZ7gkv*X>%iAsYSg>T=wZ;vh&HnnZL z{WMeMydKJ#$D$fz?HWOjko(q*rvwa03`pwrhLn}eW5x`LX0+8BMp~MtwV>$D#Tfr{ z(aseOLoGwC+617<D}}OU(pW2gS8U8mD}t#Bq&Z*3s6Uzjqoe-&U|f5l=KJNw#oP1Q zNtXWajkEmS&5lR|fH={h3UaZY0%SI#%Wz7#hZJNsp_!6ksI@Q}jSy@mmoFUoGi6@` zo7@y8h0+RVtYUHI&wj62y8+Y~<QUyTeE|y;13mFr9}U3j(mh`PnX%K1HY!yb2WF&= zueeTgBx)m5Yq%{%?{~?oGw@r+kJoLZ>v_3Nb)vx{yRZ^<kQ-;+c^Hv+qg8vD2Wl8{ zIEojCW<eSY37NjP_Vb9TW>R%u?Mqhw&oH-<$$^dSV>o3ALrp`?Py!ryb<!X)E1ce_ zBAgT#5p}@S3Rb=D04XIhrezC7E8d5VUX$yRpO{^LbhBmm#sBr7ZTZQ`Z@v6-yRr~T zbAV2|_QgE@n_+Jl0~*6p91LN7lqy5ISY27}-o51@BF1|a`h^i8kxPw?i>Xg}nt7VF zq&84QND>KeDctJpb+uXLK}s%?8*wm|njWQC*jWG_UMF`$O7YJI4Gmm@H@>dV4-4rp zJRbTvzH#kUxp3EIEht*9r(C8pSqw;=rh#VQhL_^h^3398Q^ZuSQf`Ca4EK<Z7xh_c z8EU~LO@Z`U)^iL<r^QV$Cc`^+=V1&nlD#R|e;c99t%GW+`P6h_d2eHI+f96SbFTqd zaQP}`ndRGU#+j{%F{LzhZ$5tQeyIaF#N6vorii^PMPq5IOOI(gAhDCOgQvnF>Z3HN z3ajWCndWDC<OM2HkO;r@SCsjvR_&gcsC3I2o+J6c>j*gL%XjI`qY7aNVqB+&c-2ut z_1FEhM-NFK^cErp;es<z4o+dDOK}009m#rq8YUXt-%j3v%EzHQ5y~m*pRnf4f9sUs znx1=;HlcZX-8u(7(52}w4|o{gz)7g^CT15FS1?sJpQxRy>a>k|5}-yLxg<bGm|>ua ze-n^TR6;RlIs*}XUc`Nxcy#>rTDIwVUo%&7>E-iFE%)`$z`?4}_wHHI??*8JqdMw= zT%gKaaiV$8w~3a`hO*m@E7=Agdm>_Dim4^0%mMwIv)j?w-K^DYoGGbI<Kw;!4mO8Q zWf$*OewTufK<?M$)R>DhmWb#ez|?x;Yn>2Y59>nX3fu55H<<He!wcp-p$8l{Zkz`j zqeoA;wWI~1=loRsoBRN@odV|)m-p}Azn_}=reM(hP4ULmR`{jys{J){oH}RYgj{ow zN+)!C;Ad&hQ#hW-r9^_2LY3l(Qq-;-oc?z^9u>!!KfxSN!_ZiLs`K9a3yfbutBy2w z)}nn+4s$m21sL|rYGapguqH+SPYOi)sKEy*skdgjH_h!X;89~5^UdGh$R-NEVUyEU z|7MimlqH$*FwYCkv`dlpfaj8m?G;QbiF;|oBueQ1CSQYypHMu(4AzFSW5v8dZ}QRT ztXp>#Ct7W!Sv}TQBbf02l1r{vIh!z}3pa%YS3o@lHOUzlp6l_ki*eoGCeHV(*V)l= zJ6rxg&?NBR*re>~6Z?`2&t~V`-P;8Lt^L2CNzPO4%eJ3t?L#$LdL^=RN*I;WGQ0&d zd3Z{tO)`>|y{+olu`CtLjM-6Ca(bjFVV|9yqUY5n(%IXC2};1&B<mO(E4hziUZf;K zX&qEXk!a$U?XMsIFo*##g*ceSdkr6YvGvsg{u@u(VTMw@Kn6EsJHO?|c*W)VfMm<y zWQqCXu(*P_hq+u_K;piGLdu~zO`>$U>zv|EKq<rU04~q%P0^vp_z$@jIXszEHSmfk z&+S5v2NG+<>;8_u-u@5HcOg%KT=%}uSGP%nIIdR$aEnM(jiNx7l^LubPUj_C;K?6{ z;78pz`;DfKIO=nKgS(wguC;?r-<Jn}+_&`HtXKDS|0*9|?rsxdk`=GDz^nVIY|qZ& ze%+;$QCNy%$YNjQV{gB(0T#U33>2vdRzi$nL&=e_BJ@&2Nkd76;W%MN9Aw%|Qrcb^ zJPbVLN0i8TtS~^7c~@4EfxST488!+!O4VG<yP=y?Y$qg$jL&%Us&F_t{P%(#ar8%~ zxE!vC{jE^-d7W4*Ho38HPh9K%tE}4t3di{W3foszuiC`whh#+Xb$U%z{n~U3pQ^G_ zV1LtUVp=1t6u{G|B6(#7iep@lhUx8?wS(Y-;QFM2WgO2u^ALeiC?vPeAm?z5hA*?H z({tM2Bkeg`byAeK=(gy-S35UTX(%-hVd%nu&5TW#av{H0tw+sp0#TQ;snU|ES8aAr z=Sz=T&Jdz>0f-r~9<rX52<O{EQ*A;1&*?U^{jQ>bz9ibN5$S}MbU*thNXO*XY7DwI z3M@duBg)4(9B3J5s2&Mfz8~OE*K~#(2b(QvElf-~8ota74HL9_`e<L-(eisUsi^iv zxhQf8oC8M6dt7l*L_tQ-da9JVxy0ge9JvWp<(y0B)!G{Cl$-e6#jXlCJ27WMwp~uw z<~J<)_tr%7#8`7}72zgkKvh+h0O8IQU`l=*tK*6UTDQv*UjlHU!ONfSY;ykjB`5Ic z=08)a?m#EWyR@Eu?`ZeoXK(Lhki}Z31na;T(J}QCnBh7;+KHL=Lb8r`CclCtydgMW zX-AMI!}Y%lY+UrI2d!$tlS2)J7-zpW!W19P)f5w4n$&4m99{H;vTHhsb53X|0kUcj zlv*mdD*s^%_eCPDj_-tGt7H>@#vyJsgcNT@)l0)fKTsDNRK{{X68@!>R#Focg<ohU z;2LOD4<f5r`9+SanW0?C?!6|iT&LE&f{9z;VcuLs=i)PwYjL={9y~=1drSOuWc^!c z0Vo`~DyxOURYjQ8IdTJZc4aI~pH#)r=i7brU>eSb86WJ(QYqsS{S>pQ8(#fU&Oh}V zA#HQE_4?<jQVGOaWBcc!KR`V6Vt%#_-1lD+M*cSHy&w!*iN-T-;FZ%v5;9BEuZ2YB ze5XM>rJXn9vXY`bC_?Ms1yysM6k5hYohQJwmlb*>rWcO}y88;isI@`i7@m0Ds!VCU zSA&;Rs8NsSu~DV~+_eJ8ghK1Z#!w;Yl7vE8f6*+R*Udlbh#I}e9?cV8#w=45&lkqG zEn2RPVW(a^RiS{Wlyn@QAQQuY$3lp0g%Atc(bK7tOrrn$V*Q4WZB=p{N5Pvl$DeJ# z-Cs8P43BkEAOz?&qLfGDfWb>#6m~TAQ9-CVYm&k&LA^$Ht&ckDxG{mC0MOd#=<<`P z*x%g%kZml8CJs9eTeAUD%PRsz0|Pf%H2Qt18p?vb1+it+Rn@n<Iusib26^F}ZnfUW zzB-|(ay5)Ij}A&tHCf1zOzNoud0@e+*wkMQwvZFRuqYp$=6<+p@b`Xt^<)pKKvqm9 zeXBqQ69#q)?2c%YYzxby=@qLbg(z3Y9fxLdG`1qp<VN-DCu$Ts8o|kOuKi=pk?c)` zke}$A?R8%6#&N7$d<E9{o}T=EMSFB-`9EF#`SA?WkGu#^VW@W2Y4ihCy6Z4W=Tv*@ z3@iZ}^a4b2xzr7p=0uFlB9F~4GDJyzSTxD$vO;6nX+daOvfIy|%p`>e<*R?7OWu_z zTE23|`@A-jfEjd<_QymbQjth>Ti}%6W1@aXI3^zmuCGO=$_x$2QiF75{?%AD^-+Z0 zAXrdA*y@>E_I&Vn<F~ogQ`Fyb7WW6s;qt)LYL!6Yra@P0Tui}9i`b<NH(hJ*fNK}# zoK{6!zVE;>!2rU5mD$d~5e`G0qh7~nsS3kL81OM#qageNS_!8e$y9?Zylq`ZZ0Zof ztolfmm$qt4J+r|btkxP5c@lZCh&gal6}9B|;K|nYWA`)*UC}vuN1;J@hJrvOVW3T~ zeC|A?N33`iA&|9bRctO!I>Vm&b~cnb7Q1M5f{7eA!5+7hk6v3r$E}S>T_@L``kI@; zZSB|>aqOG@2jrVnVto!%n<AJ!IC)5E)3aq*NV96j&j|3QkSCdTIwS9fno_Vvbw$!o zuTxt~pyeThoRJCDV~u<}`NjJ1umA*kNbNkyZCJ=C(_SgTJV`}0yHhMFk`Z_s3>xJ} zY^fe8K|@JGbIHV44*UcJsH9+0AtyVU>Q0EkCJSD6tVLLqkYv{Bo<@QQZgE9jUb&QE z_iSiU+8>BNMyo(sRSN1@w)YTP5L(M0w3OgYVN78bK!WP^V~9dugFr<i-+`ry!~p0l zXy|HZUb-mF6D_mf&blF$%*e$EACKwMO!5;=_iqiSFIufWgfd$@u1mPttJp6Ry7PLT z4}{#V3Sy(ykDcIWJ+K;YY&@;G0ae{Cfyc6B%ht@KoCyh2C?z?oclTi%ev44S-(hQ> z^eij_|4bhbX6dVDw7!S*c>=_)%%AUMNug7kX>z<i{eBRJ2T3<od8SzJJa-ec(GDK{ zK7zs$4iJWa#1&2>=E6}IHXMsuP0qs-Ru)XtKnygl_F^eUd-*2qca4@9!J>bs3@@r| z<!h?N0xccC_RAWcPL2b*KMW672gT^_{ltQZC)@1BzQ*0zOMmKIXEgA7dB`<2*_9Ld z@##-xtv7rfReg03yx$xhJ>%3`;(_57pCgW-Sxp#P<5yi@nTuctA=(yGx0NDgM}~*s zy0t!K$1&sxlF(T~G^J+4le*%qxYj^b3+B8L2qyx##_RJ%bUZXXMV8_%jKU$6PcRzF zZ@n3!droz4mV-3(?Qei#)cm!!V~7dF;MO=tsV)<@PjbQh$<#PXMpJIC!(i$9<$Cjt z&wag%>(9i=+rfr5{zq1ZNq=KTQ>3w87&~%Yh}k-4$EKMv4;X~F9z<w(Yogzo<pl6Q zc>=WINU-9#n*F>?lv$Ya7a)G}ctSS4X9t%gbxcT)%q^a>Q2<Omae0P!kRpg&Xv`Ey zi00tu3qRnTjY19{P6SfK8xp307Hb?<Y=v3O*%+?kPJW~N(~h88kZlDX&1`P$;!!L? zuxD6u_e2*s*@U4X*o*-V#E1DVTm1gL;}kgQm>ZYM*!w$blo+XDHE3`k`d3jR0Cv4M z2O8R0&W>I;fZpEi$H%MV)BE@cR(vklxo4Y*d#VQfwd(yK0$XR!g(LH%j53kny0_W9 ze{v3Bc*4WKzZu5-LI3l30cIjOpW=uwoTI0%lq9HDy>WInlhJLb=rz6kaY&zZNYDJ~ zmbp;G-PqSa3@Bkjj{M)52$%q?dAD8J(C{e#o)H?5%Mx_B-To+Sw(|N>_isFR$jw!< z>qF<W)^r>LGextA{%}WO<1PxcIU{Ke)HL)pPqY1kG6{$#NJ}QIiQx@Y+!wb9k(iRn zVYR$wkW0$)8J_5x2L~S`ICvU-0Wt#kg4MW)NSwbO;XW4y6$wxSg=IB|b!=-FRfGj2 z7L>@gwf7a}Gq*{zNdE}_R$#3%?8mePC|Ya``ANyd{W>*j3mV93$ZF?Py6oH5s4%;> zxL?f?ejK)Qp&?v7o;?qY!=A+)42M0=lbDj8<Z-`X!y1jkTsy@@f|P4&GoUKDGaE7r zu|Wp`;7}m3r6J&sVhXA5GHX^OV90T3wU<W8bjXbGFaK@*_Iz?ivHP9-Aw!S>$}N(i z$9u;wjxQ-LU#Q_vaKSC8EM=1$dAz5YEuo!lb3Zi9L_>&zKIN{>X}~|_u6vJi%DojB zw*dDsZ3U`nx;6)@-DQW{6$|V_?k}kEoVsAcv|0dv9n8#KIz;EGfBBR95Y=R#`?l!f zd^j6~iv-mL)l2$~c~g8iKXWgC5>+pYLEFpW6IZW|yyO{C|4Utm)sqA3cM#nYx?dYQ zarI&J_07;LNgyw+XZ~n?G~3M~^oR=z4V8McB+9U5NJGn1=IoB^K5U-w8SWC?9|Jbz zXB#g3XK*ope3krq7}GU(6;2p8H(=fcPf5HaHgi@xuwXEk50=`f{)`Su&;CjsQ#$Z; z-OmSpmT7GV_@Oetv0xCqeto_;7=xEYk(}6_`n;xyt{BnaO6Dj)<48GpgItQ{%t5_q zXUnwSmb7!Rs3`v9W=D4SPXSso+A)BB@Z<!^%1b3*+5x{)cQt+7y5VU*ly=BdXf<`E zWyg#^b&$DUQpn*Q9KX6I6|Xr2V<s$ZH?0m&S@>s0GLFop?q3lqu)2oMf7CU|oc?ch z4dKTBt*(LULGAVNmXx4b89;qN(`mHi*Z}DJpvE5r5t0PLOscjuZ3MgANH(><HJS7H zgE*RKaZvdeGSctIUo}sYi0c`LwVzYTQ_EXX7i|a<%41;m&;^sHyl(ieU?Eq98OyE_ zEFq73RyiBY|9mq-SQR$Xv3&d<DvhGXPwHjVkbpaRw`eX2jm}I#17xvn#*v{wBO@Rl z29OG2T;mWVFuy_mGxUVI;9*Tdh^?@T@#y4|Nzr6MWhxo5otWE-$9PySeMS5T4>s_# z<+k#{b?IxHTBZ{Bzdn1e3sm%2EPtJ}xMaEbWXW(?@CPukkv}fEv7RhFNM%*IxX3{x zSpm8l68QTjE!wOyBZa!mwvMh#{=m=1JwxVY*z|YDeK)3B5K0EZlhG@n+_1i;nF)%F zY1Ood#969E;?T~abEcciuAjF=53wkp7CzWh<#@h9YNWVq((?<%X-c_fkYdqCm%T2Z z405iR*l7^%TqJUv#kS1<swhf<sjMhx6)7DD_(m01xXxRBM=QW+jw6UbyyKk;F4cqz zwDn)QL^g%PEL@c=|Jv_w@#6r1Tz<)K)dnASnIvR6d?|0>7g1I$Lcinv#9HJC1tAjj zI%y~wci0sgR)i4vcVEG%n=SlxGEXwIL~;@AQ{SjVM;-LxKrRW_lIp-46A?6{0RiA? zU=Uo%onRIC<TH<)CMJk~z5N20H45tNaNvIq>^$IUA$VB+{X->Bp|qrY1esY2xqcv? zdBh%08#!V-CLfnA9lLgg>V<Zk9=+&^X>d1`2fR+&Oz@egqNz@<H7vu{>$O^Fte4pE z@aHD_grTF$LJdO=3*ZB>!;<vH5PNLF9fGiXT96IGcgx}yxpiBpL-h}54gaPV`5HoM zXAtpdW{AfWS*;+F7GUk=^1;YuNKh6R6zj5=6V6dA*lHloP#CGu3MT$>?f54NZYU{C zpvK_T4JvE7mlcC`ch~iuQ_Ka?ZVvHB(cqUK|B#G;|FDcEF@--LP(dEh8F&;f{Z}d+ zx*4lIa>@Oo;3Z0uSx~(!f~yCc^u*8n-X*|lT7_QKISDeSzkd~|6@N<$5KgMru32ky zf;;8>;EefRY5u=~Acp@11Wg6J3<4n~*AA7}->@$-0T{lk%R`*z5{?k?H_1}T$&C1# z`Ktnhb5cR`+)Bx6@Lv?j*QvRg6rYZxntO~)>TbN&_0r3!$*1q*k8$F+xDrcNu|5X9 z$#R?q-);5_vQ7k}z!jJtw2$*B#Eozp6Xz~3%o=RH74uR4>tPldS*x=1z-qxhD+_f} z2`Ht!5_y@<u4&xsmf0=YF%#*L4~0pNcbD?<?s=zP-^cB@%?KlO(u!$P+c_n#6sFhz zL`Dj&g(Ra=2#|-@{-cQ_tA)qW_o*#BY{jn;-Xp*H&AiDTIP?ClLbk0Pd(repwivxR zKb`uZ5(#|&8yBI{>Mir(#l_Mqnq+^FKhy~O6K!46iN|x(|EX7+j+7(Ah0V?Q2THYD z!)dFB6Us0#dtkA7Kno}S!iz%3Gfkw|Lt)7j3Z&$0Lu;OH7_Sa+r!9d*xJbY^{KaUx z;x#=&Y{uf{(T<^miSrDf@lI;}jVC5rdx|4%5r|wL|Cb)lo%`ZXcnQxiUX!WFB!iC` zWy(n3Yh+5%?Gq!^>MA(JnnEiOHBMP5IX`vTVO%Zd)BW#cO}*Q!Kj#A`eIehy21M`d zD+$@v(PMvomt$V=I=DIm$3@yuR5Xv6r8!C(l5!r;ytxeBqH?EVV4HweQX~J+rKJfL zO@Hp==Lfc!Rj|?^Ib6GB2M^EXm*+cz&PQiguRr<sXT1Er=#g;wLl+8gKZA?FbWZ0d z`3Ytnzk?G->?Fxk&8`9FvS&M993}H;@6=w~1S%nnfnf*S$A~G82vo35sYj!#&=lX( z01IE|B)q>}V>+Wr0Qen;yWfW<?sqjL;JRabeEu5egik~sMdy8*Pru@FCmelosu$|u z79YgsHRXoLo^>ZRn4;ckaX;~=`;w=oX3x^INWlN~B-^*0=X~+a@6|*x?Ow6tcBB91 zDrbY=6V-ES$Vb6>huBl$*9anQx_@W*vU1&U^}V7IQZ``LSvt%2<owjeQU0?R+;-Ge z&R!nGM>=>S03h-N)@*9$oj#ZT7aPcp82@BhBmdi_HFlCCc1~*L1hs%7q4tnEQvr zaDAI;d%s84r~4yw-(OyrbF1P;7nn9AtCj-(b3X?CnM{uDeb>ryjRi2y4&(2FVI+8@ zfWO`Y0M;XY|BoK&EA#y_q1y>&O}9M^Yt?uQP!A<##U}N0Et?BGhk=W0uX$=cZEAn5 zZfNL9`dpXJEc(M%oGlt->`}GPSXPQjmy|4mE@vef+0yFn2M5`4H`3vkVEWFNU*0bn ze$V%N!|=KYy0m~s1}-N~1x7SQL()RBsWqUXOru^)#igOFOdr3H91=&Cu%bH2K_=a( zc_M8nEmTKYG?E}j*1RKD07juWie(Nv?_-n>)xz(Aio{^4)Lf6v=I*X1FXb#+yH;u# z6mnvPlK^VI^dRw5%Rs6yszKdmP_lhJjb)M)7Mgx2jUjTuy8=aZr8HbURzuyV4?yE* z>elEwu1sU*lDdo<JR01=>7;21I#}s)ybNPH75XrW1V)`$QX38AkRR9cX=-S%Ux?*# zVpA&GKr0m*O)7TBbi3s#G6gz99}C_YT|Ahu;}4Q}%W3SvtpBmbRa_{6VfNB{8adT* z)>}fbpl;Ey*6`B|g>2W3*LzGaum*O|qcEgqFMsAY!>2xbFUb>`_wgI!?^*wTJ$N;` ze|{Vp<BOw@t3*kxB$*7*<&&+BSV$|Hbn6#pSl?}MZ#AABK<@C(L=|$z4&(69SV6?5 zPZ&!#)hvMT&*-deNVpwk3BK=bzuZ_gu+YGKz4d#I@<spQ^nOK5O8smoMFeOt3>WR< z?PqJ5%hki8S1W}mn_I9qXp4?Z%9kwLXe`(t;k!{dqC|WTE$c!fElC2ity^r6otZoX zmy=Oniw147=VJ`WHo|>Qf!1Yjh_*kH`j}=IA5&4TM3)ijK)!k{<MM0R?}~M|dMdh7 zIIH86fK|;?|0HA<ky>f6oG&2g=3UK|<Kr6k*W(+$(y|A8d>=cUeAp!lx$s&ive_kU z;pMOsRY`BcoiVZ_Eh(~UjjI2RS$3*BhGTrhi{%+X^%SCz_aylvvV*!g{zze9t<!m& zPx${<L=cI6(yXG<4W_Yer%C%!<W>Z%j5L=+Qbq(bm?puDa4Zxjz?j<y_K_g{wRE#y z;u%)?UK^^~A0<dLKl^79pr{}vMj5e6kTt~+xfo=1G-KGOZ*_vN*ksdNS#=%f;E9b; zP7|O!%0GOT+3#Vj{P5MpnPe#2iPSky@cl<xIE-xyuFu?m^CC`*RGvO+0f%!mJQ-B# z+W%JVsxz7Xen(Bt#vCKQ+KC*&-5f)jrW1VYz96L%Jv$kJ&#Lv*$Yz98^fA+2JiSUZ zrIAgnGx%OWdpI0%c9P^A6w%ngEo1_9QdIua=Z!<WBJXKhc?Y~pxucy>fBC8rB-83@ z5o`Ft{q6bDP%>c{peixD*#wUza)LgO&vSL2QX43Mz8iH+U=Qx=H-T6O`(KCdPVH}n zD5iv`MUQgisqB@Rn#q>sK*yw3oaMZ$m<yv|977ofO6l&Ntw3Zaqd!k<-my!`cyUq| zf17NhfghL5al@v;=7rVW?)hbnA{D_IT<ow{-k)&<IG69hB*{m9Kek2InT(y^!@KZQ zqcs90+YIYm{qQXcwbG5_e(L@Vmf%2e#Z+eDtHT7-<E%huTZiXk>mx`E=G9ebv!HM% z7KTy>_#&^r;{v+_wbO95)6hA*TZBd&$}V@wx1x?Eu;7E&OsS?7h4Mj9&l}VG%L8r) zTnD%p%b(#dl_KbpSA6zTv%Vlo0_X)^8?E6=%`JSfTDSqeRE&yykk>+$l(}2KLS_zg z4Gg-vOA3ec%XU$_{}8-=jQ%E|!<GAouWJ+U>EYZ&A#?XfQ!Og&U0}UwC7sNHo7@E* z4H^Tt6ejTyw3d)>+5r4ce*jh<gO;eP?EA5VVl(7k<`r2jR_P>Jr4(e71mLaS^h0J9 zVC0A|=E_mePle;0s5Y`EUXc)*a~c)Yr>>hkE>QhgCJjrAlwQhUYS8l4mcPIrXAZ;; z(;^L{k%nqj?z2dgOfYZMZmL<KdcKBz8lyh~N}#PL$&Jh2YU)!Ag1%pRxeW+p$nCx$ zN|CQw{w6PjT7)s>>`NHF_ZjFi0AAc(HzaaL_9vb%cun*So+Re)VqVK{fpBv$-sfHs zgUD_Bvys2CtoVHlS@}m+Guh!I%+w?aL@HqN?BNyhpm;T~8&uYd`#t$xagdNV-#bm= zcP@#Ye&psl-NWjJc<W^Nh3=bvlZ+;k@q{_?F;Inw!Xr~n{<|z4Q~YM$CM8<FxiHkc zo?o)gccBr=u(ztP^T^D7ikM>)3%sOWHZ79|E<%L|;CLx&l$cjxMFe(eMc$sS#~y)^ zncL%2$BDo$BAq(J-&+zbt;z63JL4nj<Z1Z3;Aw(L^!!|RTih}7(fC6aBEO0(w{Oci z?B$}(-DP#nr}tNE?4b@dCU~yihK`0QLax8+XWuC-#-3k{Ve4&PG=BYZcA%8JC(T#| zm$-s&l)yww3z1m=wOhx$`Qq8W@!kzsHLl}i&Nql{y0Ax}fv0KVppk^943aVIE!K=f z2k8$45$<=oExP#dkBEK(uLrq{5r~#>z(Q|Yrn8?Y^YVTJ%^keTI(JovAN;KJr9U~k z3m(~lGOwHWL4xrdCz530=J!-xta<Og{qf)-Pq@DQUmcFzf$VZgnrzW&Gf#siFfdsy zgz;3m7Y|+4gv!VQ*?wV;-0-QZmMvM@J}p`8leQu)ALKtW9X^YlBB!z$glg_Ma*u`% zc%i2Df7Wc_q0^2<D^f>il<<mVz6s|A<~Rn&Sj2Im$Lr*v$3t^$MJWSQ>k<+10WR!M zxOmIH17k+9osp7L)FTehuj-4^&H=XLHrD1Hrl4x|Pfct0u@mbG=&UriI55+Gu2jK~ z4}FWVZn=K!+1nq`{r0{h(2~-6F&wO@k1c;NzxOr{iCNb4$v)}LYB+vqZLj~q%jU8u zY?TYePq}{N#wl!7B#I3o?V}HK0%RL?67OLQ*D12_wzVG_;+IKIBW9DToo1)E>SgCZ z{|*y^@_8yqD}6EcViEKm+8}4Zb_rb2fi-Go&C>gjot34p=lAgYuCH`vn7qMX?(60W zU&$S-tQ9t%c`E);+3bMDO-``tcaIP@m1ywvW(#e@2mTSP^tU;=-73D{2M|`O_murA zIxffB?|aM8(!mm|F-t|7eEF0%V!#~yvyLFt$wcv2j@Vmo?auX-@YQ9Ihy9=J$pHZa zY)2z9>pqXuE9>ernG_@xpK4t~>eg|T{gcK`uMalM2{lkU&Q3oE30D%fx7e4(ACew} zk1DyPf&4%PyT~~Z)D|CgJb+~+rTjrG!R@=}?xYYU!T`N(%OIg7<k3}gahG==G_I9T zNAbxe&f;3t4B<yGim$P`Dpx4`)FuYHGB1g`Wp@*?IXOPC49%DcJvWV6hvuwA8~U0i z@Y_meOhl%kW*DE2?(^O|vC!h#V=Qc~?t(rnENmJ&Mb%^MWGxIKC177rDZR99E-H$7 z9HuZrI=`5LTUnJDs@E@D{N|KOIi7}*_N$PR5S{YJOmgt{M73xuButeeuI4!<Dg=Xy zR7q4YOkl*3DajN$Ar8`BDwI-R3M0W?ngpW{T|(AdhO|URm_Q4)u>X$6=t)UZOJV2P z{M7sL*RR$g{9dzA;62>rflGvB@FJE*kx`03%+<E9RYEC<P_ijVwGwxB>Cs7?eTc-E zWdE@l;_>I<D#iWbDl~d_3%%mBwK~u>F^ZMiIN;cY67cSD^*FTYAfkwOQ(d+6WF~F* z#_r69q_qy};)j@iHNEM(!`%QvlS!EPC}Y~zAsPwlwZrpqfE|0EfE_!_u^-{yKFbk~ ziapCQI3N_${`0mx$pED^!@&J%NG516gpjbRAgdpVvB+!Xk#~D&g?rndXDda#bN;L5 z0T$BdtuUyISMIGYWY^7?<ytzzAeM57p;^v|$P3{lTenon@{Z<UgbWe&bHipgk%$vg zku<Cx)*jYXU`_;9Ne=HPiaw$x^S1YQpC6_R(~gI2I2Y2H_(2>PKguu4(Kw4*yY0*i zhL^vo^XAz#8<bVS?X2JnefY#O1hJ#PrL_8^q}j80>BbIK*Vak*W7_tXF%<Z_%itFD z5zX+}SyLK0H7J*HMCuH~zxjMC_e@t&Q0!S&tV!@<2(asKTZYFj$G{d8Qw+dIWDaJU zB=aDfgB+Lsf@_$=5I!3@tK0mJZQh(x+XLdD$e^AYCT)GVE{E}vwUBYy7b}dB`&JxJ zHfvla%Jj@h%`nTrUw3~`PDq;Eb}Vid&!><4H==^)LUwdZK9MTxpCCzViy4IMwU)#L z8(K}~X<#{rZ)rg2``rD+$L$%REKL4~%Q>!>k2-fI1?-~Yty=t8C28c+4?KMPm1=b7 zXGw?}d8)JIJ{jq0<gGhIwsG_%8iLgzt!m4v?@HB6uU9|nRZbEIUYeP#Sb3+uyt#UN zJsz`C2oWS+>gMT6-zqn35~sVBaz&}Leo_;pJ_IC)6-4MPf4UVW?5f7q1^bL`*x~<v z#|m$X_Jbr&h$Pru8dj--=0{$~2#Fe5Rx7}qGFFi_XifWPZCLG#?q$tZIdSk7L@!hj z95gC?&N{{ybD?*=i6XtbDgFHi4rpJ};d5lrzL0nd{t5t})}Rs;ZbTvUYws!qE;(t2 zR}~1}7!ke{G(<fjn&!>(5WGA~+b|Li+!)E+IL5m&c2_tY!6(ci?zka7obBG7AAeN) znze143xg7J6p#lY1Qi4eJrRpQP%bpRCqLaNI1SR8W+#D$gGPZTKw~GdbeEZSlokA^ zOlTUUGc6eL?*hT;v$Q;;572<z1+*M4^h6sB%E0u0)%ed<Ye9Kh-}3_T{;5n5e8MuL z5IHlfP$I9;4JPzN222bL3%HrTB@jXlCP85=XzZ<+>?mmLr#S3rB7eL0PlLQvmPOEi zuQCbz?+Sr`YGi@@r$(gzkA8^%b7$<}-h=OqH0<B?f$vOqTCxl)OF#&m<k4U+1ogkV zp%Wxd`mZ9W|GNm>L!siW7laHR_zX9sj5A}v5)XVgqSId#?jGf|O-g;T;Eh!c<4w@_ z*y2*7Nh~yZI?V;N5nwoLmaAt!Q@q{0KJ9)lyy1V6Wpdc?*u+15niZ&Mb~sb0dgJ}D z^g3izx>6R`FD`c~60LoLhN;SDI+_-npoyK?Mt9;Cg<i#pieR<Cy;$*sMO>`Jtf>r; z62X<qCXHoSGv!cgJ!J>^!`#fC2Y3Nv(?3WpT7^WJJN?DDs@-_q&|FtP9jz43^avO? z-}>0Xgw3Q&n+Kn?PqmsM(<*Z>r_6A78c9O^e2N*ag)ifa1?Ah+BU|COCud|Evwu`C z=?fr%IQdyAzmGJ=pQ&d3v*un8%FPa)RK7L#<W#MCemdzihDfF~DOENEQ69+5qD!I} zhG1ECtNCQD#ahs7^A;~)Qt!me2bL>@Gr_NOp>OO^1y;a5TVHzc#bsC5i?hG8tKSn( zSz35u7g3Y90`iU#*$+jGPT8heRq2wt8LEf}CL~66g*R=du%GzT)S>)%uDppt|LwTd zk>)O~>eK=9K%2X*he7qxlY6Pzo)Oo#3JMMD<2SExTeY;Gqjana$!Nj>QV7gecD!S< zy^0}}7^;`oB_c9IJMg!}sA{L(p!mw|#sl^l`^GP#EAE?j-Sgw$^6MNHO)G}X31BSd zfwSM*Fb!EhCo28gr)!^sr5jA#2>ai>WsBIgtPKF{j<*H;4mw%$g2&wbzE7uh)2H7M z<=F6I)Qs#*Y^TIgRQe)?F94|?F6RAIID-a|tf<}=uW9-(jKn=EXLTM?cvA#Zgz6wX zJT@B=d03Mv-f-gxv^#5-QogDtEYgZ=Nyi3a$`Ep5eX;Ttqg(gS7fs?Gg9bz<0x4R~ z&xJq|f7Gx1{Tt4ZxXWmZKS)OUo)EY&xS;e});Ag@L!io?(FaRLxLJ)zo+`fZ;%QeY zt+1p>?eZ>Mojfu;kgP94E2ME}{Qd?hkrZ{)YrSqLp6~sJ-W*X;1TJj9`){P?b{dHe zv=^e@RAQ5^ku}HS<!q9l5cVGjI(6~q*Xe-@m+CY>@y5}SwccybwXUm#{_;P!Rug-I zWAMrGOb8PJAL?CeKX*-i;n5^RG41}qFz-}2S7S)9YiL9u*3vHo6O1@n2@KlQZRtxD z9>IXIbH|#qRG-1KwUAVJlT44AEZ+y$rXmLz@{|B<c=IfTs8DfL>B*k<dX$ai&r(1F zeR(6!<>;<i*jI{xcvf=?ahC~6NIcq%c&vB~JOn%|AG{Hhx*o}6ypbhNW(?+8@MazX zPl`F8az~cF;gk&`0y-keM%3COOj27(u!sR_Iwig8xeNE)@7>CWqFcG6RPw5J9Y{Dx zxS$$a;?IiY9c`kIZVel4CbI{FUO-XQmsP6a7=gR95w^iV^#FCM=bdg;e{C<elNq_` zsPt0!pp5|VVfPUAAWQ;F7)zKA@Z!r-R$|U00qO~&x(q_}I@`jLVc35c@>Hi<C2Jjr zS7+Y4vp}I&+A^Y8!*qdWF^NtONf!V=jcF9Zjn(OzeZGoS3b}y|Z0)N+G<|g^Mhj29 zw%@apcT9ti#`xr#?UTcId&0y9f=KgB0f}4;a%_y!BNwe5DWu;+l-oAc2*lhuY0#D| z>0d>zo83(3zPYU!Fo*OIwP$GvWK+T^^>NzrqEzx$w6Rs}FiFXWQe9Gb>buZq_YzgA z5~B3e_P17s-ODK;wq7>^+McC~V~@4c!;<_y6=M<i`*4SHDMTOCrsTdXf*6rR(QgEn zMyh$$-2AIjt#7g;S_5xNqUuuZhbMZeHED0;DU4xNB$!$+8th$dZJMjMx1!CB%8`k~ zldtl2e0P6W7AcSG(0e#nwFijmol8<Xuw=-8dI6O?P?!B|S91{py0yH*aQ3=)9>04z z?72@9sp}HB+L!*RoNYqRYJ>`2uK5O1GBb8yQz8Gbt%w0^5ggRevbpL5%{8mm>o$oI z$#ryGqOFmmh^aO&Bo7DWQqY@+U%gt@>Br2|^H|CA@7LWbJFQ?EB5K8MqO_KICt__t zeeHb;a?H$LJ2bX{x0mG#b7<&q>GCJ&WZRqdrpY2IXn*W>0#)PGkcT#`z+M(+FWw98 zYm0iYCU^Ty;kV5<o{#$eZ_Zqe@%Fjh%}eV}_rE{=HrVvuVz~?c<EiR01>2dTcCJP* z-Y_q$;9x|Goa@FgbmecS%;+tz^6m~fF8$h5_39;pt}|8-0KxgLyyJhz&x^`Glgtj` zyXK%C5zoFxPU}Z|&rDs8Sh|(7G2gwnEm8?A3qV~!Tr9)ii+&>L;j`KK2xv__t`(v! zvR<zB<Le;v<FULyVzUy>=Zfa<y+UYEkT~sK+`O+^#0@s`@%5Z}oyIq4`dIe*WNUJU z$sFfbL_7=@Vip$ojduih1h>ZjEGv*PI`GS6fu&+!W*hDp1Cs{k<<}y)+M|<;Z4*`x zY7gpXKX(*=ypjm2FygQ>G_P=C(6aDdKJ*U@=N5&7MAUdN>e90n(tCKyE3Xf<micwP zKE5tIt+(-bzqWII`9kRajT4ri|8z;E6jcl9E<&&oSY1^DPn(xS-ucuI<&^qEe^qMh zKjxZw+V^rJMXYyL=Bz`9X2}XkCZm2n@mXr2C~gh=$QhtF-A`qwk1S!HDZAV+o<{jO zCIpjaYTX9@c5u=T<RSOYr_b1-DaOX}z|W?3R#vefj-4LE*tVNsksX(_x_rOMDaI0t z=8TsHa1?{d_x+~StBC-sDT#oTz*6oEjWjy3L^}No!w|yI2tjjNxa4a!%cWsgs0%5R z<NiJV9ts}wU`T}Qik@|@%LEjP&53)Y)WOd@{C;h=31&6Xa$G?Xbz8%#yz=5?4NgS} z>b~VimG!XOdI%0>2q?n6Koq=ERkASm)~~pXfYJ@_U>#!yI>cv51UOB?P};;fA;Zy{ zocE&Xrx+H<NAJK!!a;-=n3C{5EpPH|MpdgP>rq+sdKfMOzS@c^z%VD~YuOED+=2}m zbQG%uv^1KSDqCch#J9V#n%Xp?3@@|rJE!OHR3h)fHhnhUmMcirU;|Q8UQ<Fy51lGN zV$fGSd3L|gw^yB-Ry4z$L<)i}o>@jHnU-Fv+#sbmFKbpcsDxI|?GG$5*D;}!^B??r zED{-`S1m**8<{AQ>%9?_3syFP?m%#4q9<*Lj=vj%a?Bw@OTze?u*fu{n;HCj+@VME z>C2Cs8$z@Lq-gDU&<!EYA=YCSL_9$IJ_NBdswk)>vFs=-yo62%v<6CcYg+qhHk7zW z`tSu=NebRp2_s0u>&u~P(N`8EzC}m(J}Ze=+pf36MTI**Nq^+M+uG-YKR)L>d%q=o z-GxiPkM4!^wAgd<K{y*Px>oZFP`cAeAIeuxKVC|=mLsh}Kc2+ZbhSU8ASeQgM)?@j zAi{`Uyl!?QqA?6JE5%<|_473bo6=gQe>KeGYP)p%yruInwd+tQK`uhAvN^TYj(;k# z*ouGjeRU&ptUq%a<yp0@s|MqZ<-d&9e0^ex<uAVRyKL}UcfLJ5|30aB{S~_Iz+kcN zK<tyzIJwZzZ)Zphwp>4RfVd9>_f*-8jZQW{wXK)tQ@-li+>4*B`HQAMn%lq5(la40 zDw`6h>q#G+$8ohpzyf=b3xff^x_+ngteG?m*mzmY55Ab;h2S-FUit%abh8%U9{mAT zk^ZZ1nT5oe^b3P`IX*3#NcU?7PpuYtRbz6Fs3JXlULy@oKt1Iizr_~0WSx`_DB3Bh zqt?l>zJ~m5BMxg0i`DjBJ4rv4Ss022JZ1uGoHSXLGWMx)E7QWD-|*YtdT}B;=gsF$ zbKM<q$imnYqMRxygS7Q@lq*k3(Fo0Rx#T(f5Q;=YA>}2LRe~SmQrbM$mCBWfnq|ym z=+t;U2EBno4c2JyD7TYVkyzc+Z2<BLzmgFOw!WkJ_j{Sr#WM++p&m|wRR9ch{{Q2g zV>bVdbFv7{i<EplrioOmE3YijFU6a&zKM%7U$^Sf-NP#+o2%FUS(G1L1Wcky;Mmt^ zRIIrXsCNf>9lLJ@XXc~F&Lt_82t5V;mZS00V>uvXS$*PbhU`CrJS$uG$aNXRQ!W~6 zWF+79?vE2Jza{*(?e~X!Z21TZzmg)W%KJ+e5j`4(;INNdhbV%ZZ<hX4H1lRz190z+ zI-F}vc^I+GiT8xA;QaV>4U9ehgMc#Q{XW+O;qg7FcpPj8{T%qgNmi~dX(GTBvQSY9 zxcN~{dQRF-aqFJS4(<KgZYJ!|tC{a^@9O$IFtri*Yu(K|dGcqIyP-N50cB19<9fby z(WN_mdoDj#)^Gn&OsaZdR=b0C{vD;7xB^pclRBfwPk2w*q$C!AQtU&4nSvBJJ3k5f zA8Y(}J3ynR2}KD(d5%nJR0X{&Yhr~1Gstd;VW^zLQjTN!;{2s;p0JnVy^zHxWS;(z zld|l6YeNt3!q>?f^p22FW<nH}h_X5AB^t5sJcCruu`oMBErq&?>gM90B5p6MSd>M| zLDi@agmlCgiq%%nbBho^aRWYNqU<dVeEM*cXgeMhf61PihE2*eA&z8FbM02LoL1Av z;FJ*)uYkATHSmrqy59C@EcJ3Ng%l?i?bWQdD{UpxgE&=|aewN$AsFm5q2;85!{=O= zcX8Dr%HUR06?Jn+{n})DjA{EJQxoZgJr=?(uexV)5j%q4U^ToA5+P55H06uNkfmV$ zt&B|ti#o=8Zc6i<%ip=H;RfrdRa1)kglQIRZZHM7F;&&{aRysTf&Be9ffCO9{e`}e z-?og6<T#4+>wDP44D4Ef5^Y1zzO}h$$G5u%H+A6kPwv8EO>p!K_1$&`r}gIBQw#US z%(K2uPu<tsTGSS=W@-W^s5|+Yu;H;o-ZR49FUbxe<!TS&%1zr4oA8dp>UN$>E33bt zW5ZmQLaB)iX+mfV{|{kr9o1IXcKzb6!L7KL;>F#iSSjufh2kz7cXusLp}14r-L<&8 zLy;Ccq4)hh@AG}<jB&>PBN-vdPQu=6UHh8zHy6=i_x3Et<Pc<!3_Z?|^KLOEhM3_R zBf+(oMqn6@-^bPv2Ew2^4wt_@q?y8e&}#X-c;#lzuPS3MWVOJuX&Z4&lb%8oJ&hc6 z^WGjte5uvbBCTo8J7#3<fzY~|8$WfDS@FVQyPupM$-q7$J`d%T1w~U?&Q!vlX)dBB z54;v<-5HY2wS&jUB%|db6p|NAaXLuEE0<e-Ti~1H;kTrODI0UjItp1?P!0}wOIpoI z>I{U_g49~%(#plh3df!tR;e>`;(*jboI%n<ESK&}o5sf+4F$2`)j{z?@q;$ve8@Ua zZi-rIYkH~R-wZXc5RTdWsQ2{~{=upLgHtxU<=$TnzPufEcOILDY%V442!%c`XN52k z&|Xe+_>9E2TU@f$D~<IbcZpC*rGoyDoKjYW`g@LZjQQ3aUyp}Ss_Pc<s(Let`WUeR zfpm^-kXolz8+$^N`ZroXUx!+gr5U`wR%){31*j7VH&t$`J*aZ7&p<<1)_*so8}lNi zZk|oNqV~My$KYm6sC)we-En0CK{PBms|mA{_x_G%R&lb?APe*J+uPTUw3!GwVG6Qs zS-MC__e>hZrvjaOefG~0c?b0;4qa(4u<XB)&0=}g@>#y1Agw{i3Z*9KU7F|KyLRz$ zz1W#=Wmh6Kv4rWNua^(lHq|g|D)b2jEo^f?efdJCzJEBrtuX>5%P&0(V)k6~u1j+X zumh|n{oTnZ+HBTCo{iLuzgwvllf|sQl4!Ze;hH(`9<RkG_zZou9y$ZRG<lTtQTytk z^|uYBHXC-So2517o`$%YRQu_jwl1TLJSD4~Uz$3#QE<SOO1YlZk8=`qCQpwqcy$XB zhe@Pa?3&k?L6rc>vEYM_T<{|}`X!_#H5L>fKk$_aEE)_YXRjWIE!K`DPaUhG?`1;G z4E1F4tEM+IkQp!QA`3Wtfj{yllThAj_8l#`>(bLY58w&8dMnWuv;ENgMVj@|3C<ZZ zcWnUf$h)_a$dS>NCHY$AfE8I($5ePIO){ed#iBi&h8YNoPQ!$9uS7ZEh@5jWyhM}z zS)nej9>$Qz=yz!*mlq;Mr-Qh&s-)exzNoLB-Qmsq$JxvA=6+%KPHswq>yC&K9iJtx z><Icc@)K@G13`jhhb=Rrs#RF?L9{72B1fFa?X#S_90jq2HmTw`5^aJEe{P)Nrd7=b zx<1ah#|@y8Yp=mqhTG12RI$!T91|jWn1=JYvUCC8jS2Hxj(XUncWKsFi1>mu<uHP< z>G+(Z!ulCRX2c*BmT956?d=fsKdqOLRuoW_HhDBgqv_Rig)`U4(*A~dAZQSt#$Jbd z;-t#EZptWxL<QHcI9(nhhfZ}z(}&NSh_zF?=gGi}>C<zkVmcS;(01d@!vd*-R)_6# z$=5HDQIEIPF@-ZEBO1sgGC7ogxBTBu1uS_(hoMOr)QmZlADobQ|8SqouRXgRWV^OQ zkU)aBQ(_W(QI$!|>##$w={Rf)Lu2}=-0E3zXslL4UL+L7)aY4XS8a8!CU<A=KOca( z=QjMzc6aZ)`-KB9$EZT^UbCIV=ePOdHT#azP*ad|D2v4qoDmw^&<uA1gLwlX<R8?Q z#f=y+LzZadX5ZVV@5sWLB8VmZsM3)j5G@IdlTj#06(yuBPD7jHYN-6vKV%yF$>+;( zLO^a0Ruxj!a7C4@fhcq-MCl1pDF)D2ya`_C+tfgUNDmW&(NF`Eu_Wyaoy14_wEn$} z);MJ)b`m!#A$|wK(6>a{FRhChF&1$A@y=sTWeytb0@K1dW*(4MMFDE-TIJDWnHx&% z-aIAIeU53b`{$f#Y&ZK;-~f}NW7l&bEj;DaS&-S7d}SEV`RDSSh9!(b8t9aXlF@|W zSyMY!wr_ij)Pjv`^6*jFzvxg=Vk(eoT%0wASmttC9=kv;^;?N)zkZDB?!Z{}oJwpM zR2m)8pm><3mdXxd)eg>N<|i{B5V9_BSP*N}Cod4PY+Nr$PjV|>BQPht{cRX5wY-s` zzkL>WSdhKKu0IuSyfX;d6bQYNka^qQ3@5iAiSJ>!hhDU2iyOxQs=EB&0^14gIesVY z9#oeXFMp9og;javHl8Mv;sNq1-keRSJm+?Q+ReNDeY=0Bee8OCbZ~RNc5z|-Qy^SX z;P97Pn!9LCsYZkH(t2Ou{?8H)p>6l?lKbOjlgi!STjT*q7D$$IxK!?lP}QHTV19`G z?o(Q9yaD(y&(2aL=Z9quy9@BCz1$wy5SaCW(_7-vbHF9oC@gf7v52Bm7?vBKlitlr z!PnBjh$@FlF0Mu&iz=K1(O7+`+hGoAR~6X*v=RT37HoL9px6&M@Nl;u2(>@ttm)Us zET86zTmpVe*l(*>szGK@rF0s+&E?O_=RA4VDy&w@(|(_5C5FTMmqx8bBl?#z@6t`z z7`9aAo~n+XSZwqY@(o+wH+X77r})7Y8HLIXgAx=!4D|SP2#Y0DlC;ce&W=;L%vYd{ z@-r!_$(r4=#xAy-k#$uMwPS~8pl2XirmYCjjsXceVV-E;=&=Xt=Gv#T*YgaK?Xc_| zpPC3-EUbaTChW;j#Dt6z>;Y^njvooA%H$6E^a_*;cxvJKNDO@+h-dE$6<QM?3GlzN z_<e0yW4xNkC`Dgif8Vd#`lIb}_DkLXDW0v6ZG{T)Cr>^4ac~i#8jf70==iH=3E?3Y zgad$n!V{K@<vU|c1$lq28;6dXr+=rckYZD(0K4n48a4kq-g9WVk`8D!?8uVUw1baa zGw^sHv^}UBS+>t-=OVQa7fzPk3j2mtD5j%n2wH6~csg(%a1*dl1+r#uI-I#%0%}`G zP|Ek0F&IR#vj5PR44H`@^Qgad_q1043UPm+mw!WIK0z1344nUlRF2L+d!e~1RPGvZ z-4osX`2JCsM4I(Y{amF~%k{{gni304fDsv?JY@0@DtI?&f%8HfOL#=|iv@D3SY$MG z<jB+}uK9u12BKn!n!*EZjB-I#yMZFQ-wTga=%cX{V^N7kCDlIZ6a}8B%}V<NP_;@t zmT$^+-k6Iq5+@b=Le*k8K$7E;8Qd}Nh{B4#Bc8`%M))xqUm>(Zv6yo5VKB&@_;@iM znjA0Y&;DFdgNeMM+_*LzJ~3Rkp@D_NI{^PcS!%8VQ#5&Ec@Hy;!|2VYmSh2cN^vCv zMkND2kAtG9_6*v(HLYl-d1Sm9ARwz-=H<3w=`*&T_E|V8yz>OSv&Zj%<{?>}rLERW zKt{@_fh*+t&x+$|ODI6%qGdLdS|OvZ|87Ddx*`Ti%Iu{?P|IaAW0k9A^5c$J?qYS+ zAD>hGk|$CQWwk*3`Y1bp(3wj52X*P-Sen#fXEhc5l0S1PoX8#CY8}}xz;CB^vzv%v z`9#cFA!$;(l~WF>#<|VGnuze?v(xyQ>~h+*)I6q2IXuhMXFKEE3yu{xk=E+pZ@&Z< z#NA%OUiR;O)JWR~rLak~vL>OsF4R389{Y;4vi1_$a;&A63#|RrG`k}CLR=dy9zsMp z<X%O&m);$fRhbvY=uVkS1tjy{ZT7=3A-&g=A+YU|(+CA^_G<jlQ?II4L6)5{td6rY zUKc}!Ct@#MzV~+L<mLbU=ZF47$;4T3w&|%p$1}@{YL)%-u$!@{^Q`4gQrxGuDW4F$ zfr+nkeRvr^YH{I3Sh2(%<|uD)-@}Q^PGcvR)zQB#>@RcpiT!?BxB)tczAPm+00{>m zpd7ZU5)-!~#FE2>#Vi<XDoaBzdmr99lz|%fmWId;w@l19qZ>p+MVtjKZKM3FoW&8+ z-}d7JBwddxYmgqc7xD+g#_{FTlhqn=+=7hVD*7_{E#-|+t7w*K4a4>h0mGps$_y?t zSqJLmc`+FqJ1QGXd>mZj!`;tE6FU)rnQS5VQWKky^=v(x;HE(h6-(X}5s0QhJgP}U zm2n+_?z5GGzh*cB*pD^7EfDr0W(+;=1=?GxIK<bu%O4bFYSRcYYTdYvR27Ma2A<VO zFD%FN6Ya}W?tRY0Yzxb@iE9TzNJ2;&lpz{hcFXWeLA|%#DX|JI#-}1mlS%<_;>LVX zbJwgkKBJF%oN0zrD$sC>KAMIlZ3@_f*h4HaCRYwBG`?4tAK9@v5|_-A#&pYb6c%|j z3e^$9BOVM0qfZ5UVoRtn0t2MxO+AfzuJU#!F#)a%P9bz#L0wfOPyVMBy_n1}fYinA zK&q|7qubb7U<jJsHQCwj@E~yGvayuONRw?yB5v%kcWKfG-|5TL2N7n64dcbn<4DOl z(00h$U1i6qsTG37K*xdhIb^O|T`KsOXVCWLq_t|#rk6&iEMfpjiwAEI&pXWCu|W*t z{58ax*!(yU9e6?UEyO*laDxp*@D^t3COTEepG%KRhofxp!Q#y-KcHx%k-Eo^{2e1+ zf|$&(;?ko^)C>(L^<#M5q@<f&ApR=z_u_X2;bwXzc@CN6pA2%siMjDYA@;jzvXHG9 zGm+UyPz>zeL7+gO>|ny-8)+0eze8A`)j!dh{NeIWDV}!U|EKOgRDUl%Q8GTFNgyoT zd3UlKUIp}8&<IdL`X1<plpJQEyZxGCbZ%lB(e^FUE$EMeJqJeWq|ylq%yWBT23;Us zO6->!lybZ1AAwruxh67T#QHc3^IAj;VRWpSX$m*VgEkfe5)m^&_j1JErL3EvO19r} zJ9LlThTod(A?dvy2^X*9bblHJsHR_}-!>-lPU~&)eIkP%9jcMcb}UDGZoy>&dUi$f zDE<I8wY@c65lT8zLDR<&T!;Qf$ha+RnTo_|4~#=LF>#QuH27`sME42fV8~QBC@g#F z$v|Y&w`B1HMv!sS<nabNkQApl6g-Aq_!%9$F65xS6p;`lY#=~*aCvUb)Sfa@oGkab zu!LFOpx*0f<XWfxs6cwGvY;uHfF4AVc}GZXgkxWF<fC!pga2!hc{q-Tu;H_DONw)t zy>X#@--(V&aw?UTa`@<pT8E2ut%~#aYZZMu#<YD4CpP-D{nS>_?_dXm_cPnUqo?)( z7F%z)-Z6ZR(gWtVgZZ*4SY)TR_de_oJRP3DS*Qd=Vlo62f)_PnGVYy?zcdo|A3U0I znjD&Onti3fT`z<CW~n9y;wmK%hS#4jgQK-k6T|-)mN91meP$(6OPKClO9(xq!EB|z zT^i2Lf-FZZL0agmKGMq5ED%NDYW=P<$+T8JbEj|efgcz%R+|!{J>%xCz=@txP#HZB z68o8_$9;G;<e!gq=4re<JT0XUWIZ+gzHGK%=y_=n%KjP&r+=gAwVv_pRyW+wC3yYQ zdOw3RB<`Js!~thyea`&3u`tsC3@NHy%XBFHdngLbr!tN{x|9p!6KR^WbIal&#HX$2 zuQdgjGF^aRTfU_8PY+^Ufv=Dc5D#V9-bQ#)KMwLSVkcB~C^_cEX>4aoiiO>2N&V|| zU<BJ`r#z7b6-Id4A-s01x@cLr4bON;2P*?udA(hG@jaKDT7P)&++{l-UmWi>4VaU7 z4hmH0mIZm9ythN2wT`Y_Y-P6Ip<j}Rj1sk=q6Q-gQ-l1nB|~znqLE1$h4hofi)#5> zcQN&wwlEej7Ii)x;o9uJk@H(vMuGR^9sBWd1(BASu51n}K|>TA@z|N7<5>F$Grz5O zn3x-p-XozY;zw)!8lk-udzrJ22Htc$IWfevdz%-yy!`xK!^(ZNJRs;1MErUc^W>R^ zNdjzw5`?9c9MP=CPvysW)&qmn9U<hE%AnZ5HJ}k<ZlQ0yqYM%gld{7XMh05(<jfvy zu?7qi@ZesUMN&rRi$llDrTEAIgM*WEKz!gfdFffcU8tO@qzoB8ZX-6ugCemH5$A#& zr{wZX+?5-Gaqrkr){K7dxD_|gmD`mK4G<$F-S|Q$IOOze`3ce51ruoExF7=KE{X0D zE(ZpX;Po}x3X@KVdE;eYdm*8M%Q#w>A1q$)UuZ#=Qx)OJAWkBQX9wwsPhMdgn8Gol zr*(y^Ois`qAJm5KP5cfMye1g*k;wkwNm847z#fT^fD*`$@VgxIN@0$%Oc)qq&flS? z@E3a|=*_mFdp>#fR)QOg@#Gd9A`t0|$eatJ248Ny!^j|Pi0J4Q5_e?ZmIuYF`o>q- zRs<6<G!{|8$wH-|H95KT6k?@LZcIahR)j!QgobuUZ$tdUl@LUbNlh5)k-kOmgjxkq zNgD@2H<Pa`kRVop5MepK0aZ1UDba%CXlgk*5ZKtMj6S)qw-6-gxtb3ZTZO$FlaL|f z@3FDTl$0Ry)?65mKIof;N-HUmX^V%Cs*hq(U<wylh+mn!gQkF_5UF^2M;X6|@^`2y zEO$)61OMe^fO6U;kqbf+N-|wK+DI)@Tn<5GRJ(}PRxzs%NZs&y8Q=Lzgl~-CD(7GO zH96m(au{?=jH-CM>&{g{Nzdr0Io+)<xoIgV`91|Y4<-)=$&p^(klJGSEFzvIz6di9 z!#%KJae`cDNIe7D!gI~z-rwhHr0++N>)eo84-}DA{pZDejjO*ir9Cmz0(hoe;pqGR z&rJDVMn_tOA}EbbG?ks<5vrfy^df741dDiad*hUI6*Jt;ki}Z6!=Q!Hz8ATR6y)`r zbuT0H{kN1!7bGH}*aRL)g8>3oI@)h1byYeVQQks28qynG#Cd~Nc9;Q$9Ib>^2CDn6 zY>fc663&gYsA?MgLu12hKx+7@vGGAOt|6jUK~+I*+CVUd83u{H0cs{sygySzmplqH z&Ujyb^i}(E-*`!5(Kq^Wm~L|Ty0_Fd_{K+=bdF?5>(B*QA-RK)%Pv4Wqplb)b~R%= zIr`Nr^b%qwQu~cL+<n>SEf;LEMtA-rW-4;veEJhxer9b%enP@bX(@X4n%=!y;^p88 zD%N(RM@-30>FN(hof<L&SDtjUdv5u%2mXw@WAK~)ZGa*0|JwkAQZA7Ganb)BhlN32 z6HrW{EBrq_nXdkLC~3F6!IlG2B3-49&vxS<=CXsf)?v?A9(%IFfOdwBZmI)Huf{ie zdW$J<Q)Di&ll-?~ihqDtF_XEoFEf|(88nk~u~zDVq#z~}t?P*<CWE8lASUyFUTTCE zhh7k+hm#5&Attv94-Ez=d+kDheNBP-Waoq6jZCw=)n{*3)lC;@CPqKWv$bGOrnFxK zQ79}jAU1pT=DXWFgxMc{DmlwaNQol!N(j|I%P}G%MoutfWQD2dV0q!=3Sh+jRdRf# zF&NtxLVM|gz91?gIWfxmS<%5FMb>?eoE?vh<A&w}^|p$PE|2#Dt-onFdRrk8{3}s} zS?CcQB3Z;+MS`+pjl-eKAj-hUXbTJpIhhE&0W8Dg2};>L4M!(ZCz4w^gc_(P+$}r> zO2ZFHXrfLOV-jg{*hK#^Q#UwTbTW+h7$qxIr|&T^`!|Y7NB6{*3f#9%b9`k6Y&v(P zEA9N9>NpY|-$Z>tb{P3BHcXC4^ms7TWCe3aW;U@dGeJNy6_gF*cOnRC5t0CUYF#rm zcpP$=#KaK2Ss3FkgeCh4)ZD%E)a0C0L#TIIiBH9xY{%MLKC@kj9nn6sv$33blQD!< zpg+prXj#XUntf(_tB9_H*_kRF<f@F^&t%3BMY$u2W>q8sYhg*x4ZLKXh-29gs<$>J zV?M%hEP1I21@%jn4YTz@HYFMEHgZoqEo+FWqDDf!ej?MuUwZvuX*qiR{6^RL&f^Ru zq3rEQnP8dXu?}u<(R?PoaN-OiQ_-ivgC6deum(DeNWTDRV&ab)?t^G*b#NMfqGOJ| zl?%c-NScrU5@$ek$j<fC$4ex{Vo^8|18wEvG_hQ>!NkRK`LvPkQH(HOA$TOt2{9_s zTqSmFD>!lg#5(~gDhW(75VqACl=v0ZCiwXV1tf+ACs!uLd7aW2*Kn-1EuibYj7jia zGK6+Eh^_*%Vpvg(%Ver?;|U~AWM6QHioVPw1<2`vfqOldMaNp87~B6jm-QHZlT50% zcBVffRmD?n8LcC68o&0?>B~)0a(f7{t;Tu9XercLg*MFvp~0_&waqmPP$y30r~A#s zFgwZv)zK5LqKAM8X%U-WixZTdKy_#>;S8NaRcy>MMXV1c!^`=6>O`3+o~r~nTFGjS z{Ekz^UC!b&FZ>0Po@06&szFfdAeNX>TxR#9pH1dZ$^5sNfjES-^y6W#N_?)y^`qsO zna{)o%B*ZTizl<*DT`)aWs(##zfx#%rH8P0!(?Rg0}VCpVMBjh(}x~pP{p!WudJKT z548;6T-;i+K|`Jzf7m>H%M+>s{4`v))L2!X%TmTsXHQpedw-^x!|<<JMNGcOJtE>x zz>NB`-SjMc%X>W`ywYuf;C$yZPqyYEKCG#tDcVi@<3{U5O~5TiyEl8QX+MYBGkKcz zmxR@R=7Z@|d#o`IyERqT<oQ$X^$%f~J)&X0s$TD*NlJ=Vm93ntfzJ&Td0^X$%=iD; zR<70UWdatO<rHV<_B>9#XS4L~HL=#!K9;za?)m%FUX7{^0)5xE*SI$ABu*hXcotd9 zB`@Edm}=+hq4g*Kv8<G^Q}%_XNoWQmk-@)9NR_n9Cs3<Jb4@2%5Ds(|_=@J)Icp3J zS+oETjJB2x<1%u6Hq~X~a$ZCim}v}rtK*)X`mt+4)6mAT7Hmmb<MhPgTNKmY(rsMI zAhc~3UD@2X3hX;=GIxnB`PFTz_3|ju{@LrvK**>1a(VsNQwsC``FTwGryKqCz}wXC z)p_YeW*eLlR+U<;ojS;<J>6UD&-S{bk5B2wK$_o8Cv*Hy_7A%Vf3o%kJqbm6*MC%{ zBb0;G3LR<H55x`YH-15E)KzxbvGD9Gj5~|>p28u^M(U0FHx(2^;KJaJG9vd^5IW|L z)4G@cPK=8_fXNH(!y?f`pHdE+8wo|DrRE)OwNb~#B-zYlBI8ZxoKlvb75!=~`|v(1 z7jP8W+79+r<*<$3<6^(~siXJm-NC0M%e`~Ix5Duva7_dagm=xv7TU3r;KPNOOKAKg zjZqs4@_G3A7SGJnjMVu}hsynL55x=Ti`wf8ERqt43-&nLaE2^=9}If(FnuJU$mBo@ z`i&4D_m|5N@5`H^Pskha8|IW3SZ!7iz+)M#&*<4{g8TJm2ya%<HJVVr?*-V_;wzZI z0`QW0)9Eq&fq|oy7Zf_b59vBQPtadj9SDi2{5JOHrO`dQQo%#$y_BHSMAtxdM7^&f z`^JssMdhtzXGmfJ285sz6y#C;sU%`xXJ9Z0L0Ik~nY)lm8On<6Z3=e~!JHB_Kwj{W zs3n04GlV%Oc}N+BgEkoKl_ESDy98Y#kf2-boJmkhXE!YzG3~yjP7d?rnj=9!=tkGl zy3U{{XQ7O=i{6kYQR0hR2wRNV_>pr<D}{g@#6%8tMr@%>7$FS_4Jpnb9HAmy0EO+j z#wSh@FA||*q1=qaB+FVy2P?S*a7tR4_e<;e5+RDNk?q(>iLOCm5sI!sz;vm@k)>k8 z&<{-JaVTdFe66QTf?$MT{AU!Ug4OU#ZRFWfSFt+~9wqv>XeKbiCg#PsZa`(V1sSdV zt(lhw(#YdqN5SVzCggVzfCB&Y9~zE(M_*|2Ds*lZRsfzszB4<4iq8ABU=~l4;B(?{ z;h7w6pOJMT;8l_SzY69g?|dBcYKY1?hq`WhEMdmn&<O9!##`)ogtD4D*o7q<gCqeo z!3YhcYb>t@qtliTUFP=4(;WAsjN!ul0g><o9W!itGO%6d7+wvz8@0zH0P{Yr)WS%A zb}r1OxptQVgbaktN9kb(v5+JL)k3%sG;SxCzTDTMcoGs6$U#NcRX{}t69C7nq(GK= zoe!Z#_Q$%J3C_XvAZl(Ari?|^2;ZQO{qpD)c!`yAma0~3CUNZ%=v4W`*ViCA^09s` zt)!}SCP7%;y7ueh3?9(esw;$>0mb-sQyGcW(Qi`gNt}I<r-hrBho`Oi&&A<skgWtk z%;-D|t54}&s#d!~%bCB=s>c(r)HNz;NSJB1d4kK@(fBMy5;66p>)Pq-vLPK=J~y&S zV2x1=GQZn|r=VH9{w?0~!st>Sod;8kj@=&6CGP`W)D|tW1iW%D!<j;K)PYZ$bkDjM z<6}ZAY4`i3Q19dk%|p)T14bAh;&;8!6tmjwGoLxQ5}C;@-@JB?CCLW;S2g-RSP%8X zQJYs$_xv{O-w&w^{Nd;my771+u^-Zfh`Jw=({X$HsALrmuD{+n`buMdngGG0VbBVR zk%?R|pkV;0@N}c0Q<IqpQ4I$Wm{8sM#z<}o#w7QDqS4DoPBbJO4R9=&xuhOEP+`8u z;pVgv54shL)&dM)Eg>N{C-uHCevf0bV1_5h<-;{HG(DJ@ise8iA=+b$hmrJ;Mg`Bs z3~dXMz0|Oh{soIR22{~G#?WNb(d5%&W}yY-NF;#hT8?qgS>S6K7T5|2wuz!}v(UpM zMN^5lQiomttc^xZgf53Dm%r`Xvv;Wz&4J~DX&d|40i$InHc5owj&b$Cv_ox#4?gaH zHK2;v5`zt+aV}ADHF79ySg`nr{Jcsq&gT5QD9uZd2%;vWz8h?07E~ZNr+D$gb9GEr zAQ}?v94)GfR}MyFCxsy)stHP3d?OcT;Pv9WQ*uV3+(|FPUC7#CZcI~*i}d4#8X|4O zME{M+s6U%BrQi96DT8cie8%AKgGnSMbft+Ax&1Rb`qY<{s-?x5uU#Azl`7bcy9_aJ zioh2)fg|b}#&O@ii7Hd&9pEKvBA<Ux0LEyVa@D}?>~QCqpK8wBY24At$1zfdv=g)N zs;@@lZL&CaDVZ^hWNEhWYW+^`e3JI-W@CR!PvX|LB0f<}W|ZL513YV2>S#Cedoh1z zp?<G~ADgnZeB?#YEBv9JKiIxAw;Ej$)CXAT)}nkX$HvwgNb~c@w1q(0nDgZalCmzW zU}!nqIEB~6+=$#b<7>i;{QcFTuT;CcOrTZ>H|A4MA_B@RU<0%%KWl|ipyzLX&7mA5 z;M-peRVx21nao0_dR&`qXr}9Qho`RV$!X_oQvc_6`xl}4Lq%%&kHuf>Usm7Km~(>0 z-gm@6Inslg>2&V=$PkJ8N9{!~0FcoU4WgF(US9LdT(cW~J{;*ge36Z4-CIU^+98p| zD%53#o}=jRXMx|F7~^MByZPY1rmV=C+YK<xN%q(c+nwcYde0<?R`4_7uF=mr2$oH= zhEIGQW~-#_^srkqS?p52S?N_BOBZ3_V}jJo(frnOs34K0e^j{SNvri`186NVIw@RE zyKf$|T?mxLWKZRF=yT3?&NtSsHARKj=RlS-z2`2AgvZ$@GS`<#1fJ|Go$+D^OJONT z`4&Ak%|Cb(i4?Atrz8Iv8)mN~d%>CL7<-s%3^=RWn$B_LyU|aoFx@C1!5{VUHi=@u z^2|s}?_wcvFoUT;yypxa2aYFhowdt7Z9vs*YP-R7T*{i84onza)@%HnVH*!h1Q{cr zO&8x{8ADP1YH#^c&t<a$+Vp0tOigN=Ice%=Xk~nyg@I0>jqWZ=2`P#?9=c>G`4gD< z*Go%vE8HHgi^@<fC)%q2j)fbJ#&$qJ>PddyM(6C=>+a?@((47--_!qdeR&*m{-?*> z>wWRdjqPzq`%S0uiGucGy65|Y`m)!uCB7Wj!vopdPw-eHavNN>Ec@Z~q4%Gpk+Sbz z7MnFA^LsJwel;Y^j9AyO<?0qF=*8zK^z$F7{`L$B?f3Wyg{=b@>suGu=4gP?O%U+t zMc34d3Y)@;tpmU<On&(hED3({R=>4Vh6q(I4jBlhM#SU!3rr0x4F^mOB-lL;1y8dp zEX(@(xDS3a0{9Ya*hHI{)Etg69<t2jXaZ9jBhwg&o)Eqr$#z_@)G*t+#s|<O6OkZA z-vmC+Be+Kv^dclaI>_p=LcYm*x;mHy?TynDF0}$u`~P-pY80|y-jVnfcSmB3^I#JC z$(jGBR}&In<8a6_cq{WkM!->~+A^asZ|Zzm?kh<Nwn$3joQG;6YXq;43e+~qV1(iI zl@W}J7ER@<gq+rP3Qd2iqDXq7bR<uE<#8}}kD@s^<O>dDJfDq^)!0-p2xuFGU0bd= zoB7D1>hcx_zv|4gsImzvJ)m#kg_Ba|4?kKdbte*De~P41C}E9RQeR<XC=53Fs<I6O zn<pW|8F-acP_1<4_JZ2O4a|g!C;Mh`eOffyH7hSG-7R|e01dz8ppT!A%%=Y{ZxpZc z%1vQMCJi0St7bDY)m6*}SoJXnbiJQ%4-XbD$L6;6Q`VU!@K#p_%zK?YO65ZlBTvje z8f~SLI=*$n{?+l14<#{*wX!7bGQPT5t8La{o$tM?P*7jataf7C(^MMD-Zrv6-@Ny2 z$qHPg_^&Yk>Q+!ZTYFEOO?~E=p-UyL<j>To`h}7=qdXp=*+9#6ZTmM0w=-3ruRlKL z*0mVt)RpJfy{*|+@g>~0xD)xnklyO+*JByl-sJ1Y!v3L%-eB^1&cjuYr~?@Ao$;M{ zKWu4qnz{Fmz0-2Uo^UR*b|*6!R?U5%V;H6@^(`D{Yq|$CaSFng**?W_H;2Ay44o`i zMo!&@BdRgskrt4xa7;RQP`RdP@8sd^<l?l%?Jj^}tDVuqktrmffFLEZG>}l+J+wov zKxU@VlrEpJS=y~xsmJiISYpn6dWSTyox7+WMSbf0p}dKpk*&$Y1+_A<{0!Qf+LrK> zL20?PRrhG)rNG2S{_^*<R5Zg6G$(mIxTOlCwT!?O1#Vp^wd0!Quk=s7h2I~P=N=4~ zW|IF^!WC^#6F)_yt-m(r84g)T7NS>=-g2947WB&P(I=07l-JdmaP+FFeIxE~!HaG* ztu)2+MqFna{~Os1mdBsdqm#?4Wj<IFB$H+zwkU1RJ*=XM5An;>9z(z5KUPNTn_B_` zV`0*IS~0vgW!K;v`?gS~zG%GpnXt?VEtxBoJ0o848}twOirNld$}hk6?w3PiUUYQY zq-=czbRJtDyh7I>mo#(*;zDXX8qJ`hm&`m+BTnsYWE%Yx^i?P7!|FRfu_~#aUls*( zL8{N5)Dnjufs-_g@vDh&D8Wbv10V@Od#asBu%JT+8)ha)m5xadUXqB7LX}PQ`cq=0 zL9bUVE|g>K7rD9*)RY)Iq9X5RvDMzN1>p4M8i_aHbW$#N?iLt{PYUNiHGMM*Y$T_8 zt%WDMol;C&MKMP>W+U!ja=hKn7<@m*=jimhI7NXgk?$T@L&`khM$qU4059qPPUbRl zhaJP90Ln=QN|Hg?`Q*u%O2RKLNA8t(`8Y|9h+#PZR%j^2x6Dutz^$e02HkyUjBDJG zIzL2kt=;Lj!3d2eKkT<eXNKS|2imU|A6$0f{*q_BMh6Ra+@(#+^6_a+VZZV#Y)&r0 zY8<bRLRR8T%31+h$_VUGb+2894v%KG*H$81P*TXK0jyPh8;V-a{Fas#lTlXl@RDpU zO+LlvE<}R2HT~3zIwoUb#IwxrY9wXCFw^q}m!_un-#^PopVs1HYc4x;3`zVxPn9tK z633|@x7|9gfm{h^&pD5+E^brQpML#`!aj;qG|bg}5HOG8WC$yW_+BQXzj&uxR!ak# z)7+MwPQgV`)I{QE;op?~+_M=Ik<hg|RR1N@$7}2q@Qjff+wRi*v}Rj2oxU!}R$8Ad zl7L%jS_T^vEjq;Wp)+6La`y!JPj9oeulI^A4H9x@(+y@Jb?%Vd;y|dZ!VGq5d-ET+ z@9npO9RNTK^{-^2j_R9T-x=dROl9w9nG1W9gfLG1Xi$KtEiD9t*qI%+FM9}kPpKXu zlnaS;JyCqU+wZen`toJf=kclh<uLmlLGilnnwPb0GysKL69Tcb)b9a7w?X>nu4wTm z0t6yNP9VfDYz99t96nFX<81dc*=-LN5#V3Spzz0+Idj7xC+{%nRn6oWi0)kQo3>Yk zCi>A^!QX1tjN%)+Zc5%!$|@szmDu)<;{!FEQOy}qo1wMEOjGsRWYMDH)WO`|R@Pp{ zW%w|jq)1RqA&q@w7gmYYLXA|l<?$;(W)?{aANdZ2woTD-i=<qM{a~&Etnsn|D?tY7 z(s?|l;U3Z5$w+QmC3zlL+$xe>WBNoYs>E_is)*QgI;V+vNgPtpzx#jhDsPWZBYuZd zMJ-O}{uIDkR2osI*GRmmQNZaVqE}pd5J(lwN6)hr59=H#;8_(09rNuxL!di9AT^wq z?my5kh~7V_GhH?~b{>c~Y8(H~1*U#e(&%yg$m3Y+Rb;rZt|`^M0#F@bcaR2?I7e`T zci#`=v<QJ$9+LHrPhFp<{$K_vxSy@S%q*lE4AvvAvXB$yO7yI5A1G_jeo0Qr?iC^( z>^vT9TS~)5HCF_ZvoZ|+>}oSTZ$PmMK!GBF`Ah!htj3D~NQSW@?g=6a2INeJXE3;V zrk}=*>al^dZH6}$cN!P`uRQO+*l!ky9|jr8pEkswwx*K{;p+#)uZ;u$!~8<~n>WIs z(-OldzEO0`Q;`z6u$I3NHbF;t^A8t%lDeUcMB(W`>&7eDn!4&3?<AN>+I9{6hxo0_ zj8nN50Q0o@|KVv{s4eLHly<=&>$_Kwl`ypH9~KxDA)N$^BhxQuC@ZJ>FAdxpfKT*F z0{?|~Sw-@x5Ykr=)3yI^Ko|Um_#$K$4zw2OWES&G7G%M|f1|c`Nd7dg{zXXsW~=^i zM*s4@(EervFj1htuQ~$1>aE2%*?$4LVDz}`FTDE*_5a3j`^$pKEUsuQeEwg6G59lb zzkWs_{rx{*E%!fPZ56};CH5EJg+oZ^L{10(dw24ac7LJYng5TyN&W-<68;PQS|m!6 zS%47I_lE>KWImrb#bAj*A^JPM_4k6ia=_d{`AZncFy?mdaV~!%{Qp4=pj+0tPgIF; z9O%S966X<8hNQB(4`SHj;`EPEP=g%JTnJM148fO^!@A!LZx~ZkzQB(@Xa`H8@r<D6 zaZoV2?uLKSId(ZBJ800Kte4971A48G?|veo<g+J@!p;3l6*$x|<vW6@0=Qx@RbYsz z<Vmb4wqO#~HuJN5_5?fsvWg7{4hB)_+Z$(w%m0J#{SV%&^~(3!>XPkun`3w{ySKuV zjyfLUT5RT7Wk|ey+UyBQIG~-c`Lj8+!p`^IUEB0lb$qPS8ZSE1gQf8#`{T`X6ITr> zW0oOc76h;V7(b;;B=&GU+q?1+Rx>3=8f>ZIu=z$knpJd4XSA<o;a>7whZ^h?$PT}{ z?(O}1_i^Xo&o5^sR&*(EKC7Fr_`N;ivAHlFnq=QuC<7f80wvo^ML&ZRUDPr$o%^q4 za?s&c$4%tXNWZjnE#GK8wHzNuh*@{i-kzzwoyo2sSzb*+Zf;+-Byj#YmQOd7r11v; z*2uL9s7-NF%>|Jlsi|`d>TTY#LsDY&tIX)GIS}hOH4vX=uiog-54uCCGR9djYrP@2 zX|a7+w<;Q#Ol|zk`pK9r%D?tN$i<`FU-QG!-dww`#m^^^!o}+5s7*isS0%lyt`cbt zI7ZRKp~0GZoN(AoxzRYdkqRmzi0uCk<TQY<#q*0$OYL`_QvyApv3%T*I3tF|r(oLy zQZSh<yKE`;3jzXslYEmlXhSJk$DG(eo=aRTGx4SB5d^1}xtuNZ6O^q2)%;0ju<kg8 z79n%Xn@tOZt2lsj<c)?|+i8qSGi~>NlA>ZTSp{X=57`diQ2)|7`HwvM#n7I>=f6TE zT+ORxCxF=SRfvS)G%fopM9O#}W6PQPkdtdr(mxvY6I{N^kRM-V$W_i&*8i0u9l$c= zI_6&)auiiPeHBrFWMfZcWh6=2dlM&NqL@USF>_RxF$Hn~sonB<1%t!R50bFPsXX;} zIAHNbmk|<rk$FMZdF9|2V`_hsS}FyIpOMV3aiDgqUc;`eApSa?RkEwPMn5{~;A2G; zx0-^WSF_f1seg8p;}_z1qj1N!v0KtuC_YIx7p1-Q@jCQ{_4l9Nk+O+XL4_vLQ`nO` zRbYleP)Jb5OJY`c`%3#t|B#@H>4Tyu6a%OfSRloh7{!hD(EImnf9QsaTSAFbNV5;o zC7H>P1*#ZO85HodPdAQ?;>(BzD>PJ_kBrWHuSebr-dm&149}UV{%E20B?E68;{jw6 zsf;zv*uR~7)|$np_ehM2UU^`K%Ym-~hIr8bR8B4;wz5{Q;>&6HI|wZx>>JMdUyQ+T zP%tN{piYzp7YqdFO{e4}l_Ri61mi^iLjrHvL#VZ{coA7csbMg&QbDM(jZZ2wRBoD! zKrfDLnZIqg2L}@Q#ln+!2=q-L;6?hz>x&K<_oa1W_`>n<YAZj=Q-=yC5G#*iN)j>> z<GUjWkGNj4Cj&EE=GNYcP7axv)Yb8uC^-fDVe027$4{NfDz4)<jH}V5W@uHvBxGP4 zE!BJG_}Ifdojtbnw?oW^>R$C+IpOIuqClzg4YMgRi}DlB<=*jk(FzI*l@)?s9-f=p z%KiBs09(7{wV-GzGMym~`PrM0FzR}9c9+sEZuWX<U`!!fn9sBEZ=f}9C`Hgn5SQB6 z<d@Aud0%PecaN26^%Bazv|N5{jK27%J;Jg}lawmsbHTx$Lk^Pau+qQ0#eR9`w!cSu zCjWT-2@;QnGZ~z%W6$uKt@H6xQ6k5#xq&E+q_HK&m`VHUSa|J3{kgn3jwww{U>Z+S zQyK`&S32Jw(%dWnJo(N(sK5H|!C9v2LA82qsuslaXr)1w<L{@=aiRyrYE4l>hTJEu zbrtFG3p#8hBu4GSxFt0yepY&l?z?@C?njV|weoHtNc}V(B&7b)jmw9U{9h0oq&Dg? z{||^=T1Oa0Q_yi+yrUJ-TMtM@t<Ccq_DbkDE328A`HB3Z3Cm003VPZj{CK>2zD|mV zLS9_1tEN4p>44!pi`S49JV)|rmEe=TI@PzcR#y-|^GMS8M)areQNgEgQyg(o&pp4I zNbFixUySD@Uuz~Q*qSWPh+%Dc|L$^6FolPo^F&vSY8sCIX$*<tc()6b^emnCE7S`p zDj2|xXE5~ZMEW@5>ifs-__Kl7lX)GwoJs$;cXTY<C%rf9rexnf_3$b8^Xd)dmQ^ms z&79!rukMNTKev9eZQ7i8zcP~c6SV$P*sPV?){~qYCl>DeE=L^<x+4f(E(R<QH*uWb z<wtGIEM)=8Xwhj7J4{?Zt3|Q>aC5Ad^7%Hku>a8ut?K}$&au6*kEICh=t<Q-TGf)r z*jUwsMbBG~)jVg$Q7}Wc_Hb$&UWG8dHdp@I?u}nzm~As7V(cM?XEqlbxED^ihRa^A z<?JO#40?SP@JT-<$lG_m*m)gc8JfaO0w#z3yQFtqcQ4WH4QB)AbF2ICyhV6fTytDm zkRM-e$`RiLxT2w2eL2WqX#U=2)koK&b#TelkKf_<_E!AY=LAVNM+Kdq*95jjFxbu- zAT@&u%QDzlg&1}yDoEc%XZ%0yor-1I;HA^0M)OH2%eX%%2(3)Uco94`wc)@R(*p<l zXFG!x-Y<9NFHg)lq&e9)Gm0nQtD)~H<G0a`9X{Uo;LAk{X^}-;M+*fa_}x=H8;UD! zWLxGOn*FBb5H^J1t(+POxkewGDA&)|Clyq?*|-xjcCkr_sEvYD>J+`j@bwY_clUDO zA#%zQZ6>;*#@Gn!Pio{vcdtc|p@47su=Pk<IUoy23-ZSH_|!h?pjhKDa?0YwvfoQw zB<A2y%<z4bI^1!LpKlqNw^0&f*)uC;Pt4*Fu!3>z;D*xKSwskBByi(`MZ+sZ7YFj} zIovt^t6wA4ZYn6v90a9zc`5(?5Q2Pbr~(0#x9t;1)WC#G4gs(e&KiTfQB51q(#sV) zN$Q@-kn^SW{WjN?@YC5XK7J_hgd_J2xgMc;zp0FImVZ|JvD}~gv-`(i_&*VUTH-Cr zK=3gQC<F%wDxBr#X2!PGV#UE2w_;>fbwf?kC)rZ}GU(a)wYOJo|8QD2Rq7n_IA&V= zl(6;~E3Gzh&LsgT`1!#)KshYI@U$T}Wwl?P*MEq^N)c92dd3;<I1~T90M4zts`QQZ zROggcE=%&QQI<=J0*O-4K@jmq&U)d%8W6CO76f&!yj=m=C_`!>1x4jlO_^x(^NpjZ zMM}eH_?>NEzS=$}y;~tx2Sf(&at>G4O59{9cyWa<)qw-SuN`1gO7!lM#I)eaCeGd^ zjf4`@A}-E3A~JN0ODa+vhDC*g(w#5_k`j_qq_ic=jEV_-DKF9wzLYNnCt5HzBSV2* zP9HE!vIr<JMIhtI_E3%1T&VZPxN^z&P`NR{8(4r~_&sY3qFBq>{mt&g`afoYoE{($ zPGjh2HWubn2I*_NF=y8uQtN9#?E2acjy#NH&Q6NTRk}3V8<L9yYl#uZ<j%rcoZq6t zGO+|4Q^YSODFg)wN}wjEhPN<8I=q*v#~FXkUV*=8A-V#|iZP8qI=nI}t|@kh)>7{j z>J;G_!4|bcc}waRViF0QNu*hTH8tO-#Gja?*cjrqv+0BEUcut#loQ?cewI~9X#fJ> zasdC(n@nb(@5QGYQ!<{Kf`tXFc68>~61~X+1AsATe<xo96^!0Q<BJ1dDcVTg*I*6P z8fG8v@lZ8YMDkFfkwmqxL{Y3=B|kf2_nY3BK`>TVZQ1mNW0(Snp4Gv$k5?S*9Un_~ zxZ)hyYA|VY&~wl?z#(d*Td7qI%QNk{v3Y^tEOBM1q}-i2$9&>DRSwlN^5eh3=hMqA zP4VHB2f8i{CFesC{ZmZEMA>`_NjTgtZhm&dzP*!Q6}kBWSa71n-#b@fehH@({lNi; zZ*i&}13`BURE-KnpV(jA{u{4yBH4=3y1p?I({^yjGiH#nibz3XaQxT(d>Rv0`cJ${ zq$b>`nk*bCbhVz)Jy$a(+vIcmm@pioQQLeC@9)GYuxqMFz6t(hMPX|uXq5jut^T=( z(ZF}b)u`=1EE#D=|H>KKj=VwuSH|;rgLCct2i)$yqQiwAxO@!R;*cMX%`tA>Gf!@P za?Uc`+3~{P6yDxxpYGgV*4|jGK59*#;)$QUi}?B1a^YpToL1-H`faW(yJewY09mzI z-*aVa%UdzW{9y&B@1b33+r>5OQlUNhI_;nNC_wq>bw1M1!2*65FXS$B^?WYM=pPoM zs(o{V`T|Wa@1$}i@WmmW_kW!hh>|KS^rn#n2hy77(eaJzd`RsU$pym~_8J7}4C7qm z?MRpssux<Whha@x(&Jw0uCG#tm-dJMkuqxSq@EeS0vYP4>uAkA;m8`}r?bGPW*~X- zXvvTHU_olWn5}v?Ee=!=v_VzS1f60SECZ92YR`#m|83Epsn=7OgXqq;T(NCEFulXO zU$9NkVBfLjo8mf%&3@P0Bs$$GSz~#{x}2T5Ouf2RppJG8oD@=WQzYIo8NfNb<+8%A zJ*eur>2R$zIr6~wq<HfS!`?;Z1u*Amed9yQH4*)MoH6xAo6}6yF{ZTDMH!s<(ROLW z#DRir&(2a65m%${Xw6<lac>IpyzShf^O|zMs2#xMk667jh`(?OS7J2O<#lxlQgBlb zS6J}FsG8c?^T0X~cM5YTo<Siuz<_EXA~ygFsN@E|u^3Qr<OV}tyn&WO02vI5XlFOM z0gPxT8H}|gBl_Z0IGmPW!L$33`bX)bkOCi!E3%zC%|S;P({%C@fG_@!fCZ$?Q>S!G zm$ZH~yD`-}?tf26#y2G3Que=2$WZ&gCuE>5HsoK$Q-UwH*^%qsz7bI?7hA<uPnt0m zFwV`l+qLOfKhlz+3u4it$mCl*qaK#4_bAzIceTd7J(R`KF~gHc^l`H5_43;MJkUHL z*g88Zh&jgjN`fky7jDkYHfSmAb)M)cMs{*SmA-c3A<1~%QxigD6QuGK?r8fDZubRY z_Zx)nWDLaJwzMD;6YFr9N4&R8!vQ(1q9JeI|J5??5FN7TvmdZKzKo6;u^sn464TT; zp(O}oSKkxUWKx9Cx03blvd}YN_-LYGDWvw8n1v?GIHppO>%_zI$a7p1)6myb7%8JA zK;j-QdjyZ;ggKr-En@DCP4UQ?o%~M);{vW=u6MA@DS&UoJw6OFK8aJo_{`FAMiD9n zDl}q2l609UH(!d&m(NcsODEO^NuGGye|S6|Up*8a<YheEn-ss=yse9U!>r#E9cgC& zH(BbAmZL)T-IK|fsWk}tx8Pj>>AG*@Z4Y0a)R5cMr&$pBq^z#^Zz?jXVp%C+;HnQs z9A)De8zvCo{4ABt=w<L#00RSKatV>nWiAz2xSjY;cY}0nvFKjPF*6gO>*Kh4%pcQz zmq*%v6WtRd4I^ZRoJSO#-3gAzg<tm)`dt`p_u!1|TGqAw!FlD`%YErHuuKfj#-(Ju z8p@MRSzw!o7D9&*QlkCB>Ad9ys5SQ#boLc_QwL+?L#aTh`10Cg^3qp2Z>g{Z_e<Lr zDP&%M;^#4@yXL>rqaEkx8^X~QCy1ZMNeYVy-ujRnW}HEXh38Yj*c~vJ9CjVUs2a8$ z5^#7@+8bBaDp@8T2W98+HRWc!Y7O1NKS4u<m9MM?#en{)XbwpNQ~fv#NUML%3%v!{ zEl&?4jJ|>*B$^=nFyELK|J)3(-<q**$o_9S`&$|~F0@<mZ(Qg&TiP@wOW!;8Amy;I zT6X^2Ute0_%!_bKcIZS1gSil%{u(D{0P4S&G|~y(3H>x$DabFM&1{sh+mdzPabI^B z05m`sipqc$c0bsgD#rlW(N{m$Dm}JrWMu$N?3WYjusW&5kr%5THA`x@)FYqZ+$xfZ zX#K%)q4euL-mh_?ng(({_>XQKxwZ1qn{TnEy{;E&(Wkv;ihje<auoc2^{8^_kBtF@ zS6(SY*Em{6i0<v==Q(?&gV(0LUcmpT5fYZ|N)4Q`@}6L^5gg&`E_Zday5OO)OU|c8 z4x>RmQ&avqCc!QD_J$B4(T2%wxPgI!deANQa`|KVfN^!s*2faJFoYSU2ItD*h9Uvc z5xGUReyl|B918{tg~KU7u&CMu@k+Z_1oXz#icuk+5g%ByqQ_AIP~!gMxW3X#lTMYG zh?HdzaXGbpxMcC)dKsJ=AS9PCC7VYh<6u(2zKbh3a<B*T>eT~z^{)rAmy9mSKeUm` z{*9b?t4+{0O>;FY)<32qOLGK9LIYT%wY@NXR5X%McW4rHUY|;5{#=*<F?gf32J*cR zV51T6>fKOru@o^AfE0%U(3}|7ae!!B(EuTMZVaf0R}(&&1R>ajFQfFQ-I-xPm{FK9 z!CM(@o1}!rX7xqM0uPhIn9zfhkmTs1J`+K$WGKYEvAMOV1ibicPX9PJMkbZPv0t6} z&;-1E+f!hp{>1^rU+V_AXaP)A*)I-+gB9o^6`=n0=c16nZkosDva3H<yw5~ra}pj1 zh=wNtW952Sh-f5(lZ(M9B!~pOzS4Zb(u<}Cbco&^wUPy)xMn<}RF!@wcocokpbQES zSS*H(go&)<j0}u>pNBwZNiV%6itJ0}TYxGueY2Z#osfTQ3h>a&m>!iMcp#<1Jbj?e z6EjO8EMN_66ZuJm&Gs{`iVF-sRB;K53<6Lfd0BsdL{vjtr9rYx8*yK?&JuPog%Iyl zxAFR`#1_=D*KK7@zTU@Jfv<~CRHF(H?FyzMk)Bq5{huV*D4XtTZBhqfbjp55sH=Y) zDY$C}xSkO+*Qsl$9gj2qcly>_BO6nlp&Obf)#I6gZENE*trG_2N<EH$t)iLb6-Eu! zX0;#hbj>m@s|my8X9)bO1&HuPn%pD7`Fd3RjFwp}(c|*TLkdLq*m^|;y@|gQVR*fa zen?4WFW<BCG)!n!i=k2g-csU!hz<E(gU%RO{x|6CD}Cz!X&t3J3c1F`JNF!w-SQs! zTh-8lp^FOkwV?$E!P;ro;o+~tr0XN-;}387EYLT>C5^V3yzwq+{LtR}MVrTJw!_<| zFf&4K+wb^t$1a)YMKF{lanlSyKZ;YqtgBSXRCW1cc&I;fI&}$~7qMD@K=^gkS|zCK z^J*dOkyYL(+)-9h+eIx+dwG${x!M@)li6+>YV1q+{(65a(O3>&S<~e1io%n6;pepS z3KB4F@YkIomqkEtk848V1%{Rxoe0cAC1G~c{!`OD(ANU?=u5l<)c&JC35|si^xv>t z|1Z-$Yx<J=^A{62vicgWS?5R}-15rja&`y7q(SxS1c5s0zZK3UO@0@FXGnG_CT`>Q z*F}+{Y2=iU9#TBofR=>MtK3ib>B}#-Z(`C10&2gvzEs+ZddkmfD31vUU8hUbtCs`6 z`Wx#f>3aKAo5lE1Vvdb0P8W!0P~IZ=B=vq}{rn?THq35T<m!JU>m1wvAIX|Jy4eZN z{C3cPzjxWA`2PuB1flyJH{&AoOjx<joq(ZJ>6OCOL%<WHZW?bl0apy*>ZP6_%zp${ zQ8W1HwrVD(gIJRZyT}h@)rom_2JnkmqaxGJJB?nminvsDKdO2^3<BGtd3`i&*T!}) z>*;N5l&P5cEoF%r^$>ehWm_#r+s$tEYsYSNdY$UQoK=7u3gTu3FspNlRbZwW*$$8? zn@grx@`PN<jmkBjq+iOVjFuE-DSx>1<m2GWukpj}7c=dEOMy$*&ZTi{#(chH3+R$8 zO_u^u#Nz1^r=UxgoGt~$_+@m76VfH+l%T|PNjYYG0bSC}>5_W)xM;NGz71#Syqx&l zBcMmko1;O}j5;=#*UGY4CdL9t=zSj0TM6gJoaw|tD_2yq1>DLOacfFK^?#2|7*G4w z8ExJj)_%56|G9f7)cd>RyKb%d^}}sF9#8w@(JJ|rX9lO)dJep1u^zl?XM|UxM41G< zdN#g#XISqC1+f!tRWaL;YwK7_zv;9>uJvlYGqa|v)04m^X;i5_eNoI49kHMnFlEA{ z&H*qN2(6V9X0Ua3addKWdVh8G;nUj-pFA9o%p9B;ztww89eOpLh9IAJSw0&nl{jR{ z6JX-J)t#wDYEUsjY?3!L<3C13|Lw?ErOSmkR;_?h;Yo;6D$9lcHM6!TieAeu7o7QT z$W#{0Aa$%pVcZxLy!*)?94n>%EENAPc<1IH?o9miRRI?!8)^7%J%6E|EBlh=viqM; zqT_aYb3=Iwf^r|ndbZ>TIPu)l6VHu!EM~%;GR+t#yBY=m?64p-dZ{23BZ!Vq5*_c2 zy-}Lr#IZLKr=?4V&(Dd>5hW!3=crH9HWFtc)*kd8#HTy)>6ADwwshw`luN3RPILM~ zITCp+#Wed#Qkx(3YJYPI@L8;4UmVa=Y!$-zW%2Rh-F33($iT*0?OOIIww{q~Rfm)L z<0wCa#o=m5(|SquZhvSs4w6)d6;unZ8Nz}KDg&qO^@x~#^R&!Aj_h&p_6Eat^b5AT z?nb;2yx-u<kG<&kImf8Y?j$Pz09yxfFIPrxxC5@vxnlMi(SI^#nOuOC(gO(uqZe=L z_D1MImMCP-LgsAdnX|Nnxsy4IAanLbnX|zO4Vkl`=JzdgmJq=i7mz!<6q`tdFyzkq zcOZcT!K=uf6^JU#2;rO)q0u(w&O+ua6eXR)%4K%2A_c%oro9^?Yo6t#cSF!XpuJte z)85Wg{Sb6K=zn;q*SXUwo>K;$8}_PmL)u!|d1-G)MFI!Lc9cj3MuO@>Q$}h7BS8yr zU?gY(4vYkh1XV)w$ViGJU?k{nw+)QM3A6wQMA|JxVgW>|+|;i(gh&Qs0FfZ(4SL({ zO3d5;-`<%ur;%k_`2GG0!f`vID$1ZU4@6f*Nb;oeBqvL<JiHK$5*7l9L6-b*fBQWN s$jS+nARrT1-CgYpGiRP*eP^$|cef><3iLb;%|y`u1G<S+9pR)A0nV}n<p2Nx diff --git a/x-pack/test/functional/es_archives/packetbeat/default/data.json.gz b/x-pack/test/functional/es_archives/packetbeat/default/data.json.gz index e3feaa425e5dd323d684a64e1e08de0e5dd15cb3..7646502cf2be21f4b21096672ee61b2146ebebe0 100644 GIT binary patch delta 37730 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 delta 37775 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 From 8406e04a427983ee6d6e93300fc96a5f3a6e0c45 Mon Sep 17 00:00:00 2001 From: Shahzad <shahzad.muhammad@elastic.co> Date: Thu, 1 Oct 2020 22:49:31 +0200 Subject: [PATCH 056/128] Fix long tasks query (#79099) --- .../__snapshots__/queries.test.ts.snap | 50 +++++--- .../lib/rum_client/get_long_task_metrics.ts | 121 ++++++------------ .../projections/rum_page_load_transactions.ts | 28 ---- .../plugins/apm/server/routes/rum_client.ts | 3 +- .../trial/tests/csm/long_task_metrics.ts | 6 +- 5 files changed, 77 insertions(+), 131 deletions(-) diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap index 1c724efac37b22..dcafe092211646 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap @@ -167,27 +167,42 @@ exports[`rum client dashboard queries fetches long task metrics 1`] = ` Object { "apm": Object { "events": Array [ - "span", + "transaction", ], }, "body": Object { "aggs": Object { - "transIds": Object { - "aggs": Object { - "longestLongTask": Object { - "max": Object { - "field": "span.duration.us", - }, + "longTaskCount": Object { + "percentiles": Object { + "field": "transaction.experience.longtask.count", + "hdr": Object { + "number_of_significant_value_digits": 3, }, - "sumLongTask": Object { - "sum": Object { - "field": "span.duration.us", - }, + "percents": Array [ + 50, + ], + }, + }, + "longTaskMax": Object { + "percentiles": Object { + "field": "transaction.experience.longtask.max", + "hdr": Object { + "number_of_significant_value_digits": 3, }, + "percents": Array [ + 50, + ], }, - "terms": Object { - "field": "transaction.id", - "size": 1000, + }, + "longTaskSum": Object { + "percentiles": Object { + "field": "transaction.experience.longtask.sum", + "hdr": Object { + "number_of_significant_value_digits": 3, + }, + "percents": Array [ + 50, + ], }, }, }, @@ -205,7 +220,12 @@ Object { }, Object { "term": Object { - "span.type": "longtask", + "transaction.type": "page-load", + }, + }, + Object { + "exists": Object { + "field": "transaction.marks.navigationTiming.fetchStart", }, }, Object { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts index bd4bdb9ca3536d..c2c86ae05d57c6 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts @@ -4,51 +4,60 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - getRumLongTasksProjection, - getRumPageLoadTransactionsProjection, -} from '../../projections/rum_page_load_transactions'; +import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; import { Setup, SetupTimeRange, SetupUIFilters, } from '../helpers/setup_request'; -import { - SPAN_DURATION, - TRANSACTION_ID, -} from '../../../common/elasticsearch_fieldnames'; + +const LONG_TASK_SUM_FIELD = 'transaction.experience.longtask.sum'; +const LONG_TASK_COUNT_FIELD = 'transaction.experience.longtask.count'; +const LONG_TASK_MAX_FIELD = 'transaction.experience.longtask.max'; export async function getLongTaskMetrics({ setup, urlQuery, + percentile = 50, }: { setup: Setup & SetupTimeRange & SetupUIFilters; urlQuery?: string; + percentile?: number; }) { - const projection = getRumLongTasksProjection({ + const projection = getRumPageLoadTransactionsProjection({ setup, + urlQuery, }); const params = mergeProjection(projection, { body: { size: 0, aggs: { - transIds: { - terms: { - field: 'transaction.id', - size: 1000, + longTaskSum: { + percentiles: { + field: LONG_TASK_SUM_FIELD, + percents: [percentile], + hdr: { + number_of_significant_value_digits: 3, + }, }, - aggs: { - sumLongTask: { - sum: { - field: SPAN_DURATION, - }, + }, + longTaskCount: { + percentiles: { + field: LONG_TASK_COUNT_FIELD, + percents: [percentile], + hdr: { + number_of_significant_value_digits: 3, }, - longestLongTask: { - max: { - field: SPAN_DURATION, - }, + }, + }, + longTaskMax: { + percentiles: { + field: LONG_TASK_MAX_FIELD, + percents: [percentile], + hdr: { + number_of_significant_value_digits: 3, }, }, }, @@ -59,71 +68,15 @@ export async function getLongTaskMetrics({ const { apmEventClient } = setup; const response = await apmEventClient.search(params); - const { transIds } = response.aggregations ?? {}; - const validTransactions: string[] = await filterPageLoadTransactions({ - setup, - urlQuery, - transactionIds: (transIds?.buckets ?? []).map( - (bucket) => bucket.key as string - ), - }); - let noOfLongTasks = 0; - let sumOfLongTasks = 0; - let longestLongTask = 0; + const pkey = percentile.toFixed(1); - (transIds?.buckets ?? []).forEach((bucket) => { - if (validTransactions.includes(bucket.key as string)) { - noOfLongTasks += bucket.doc_count; - sumOfLongTasks += bucket.sumLongTask.value ?? 0; - if ((bucket.longestLongTask.value ?? 0) > longestLongTask) { - longestLongTask = bucket.longestLongTask.value!; - } - } - }); + const { longTaskSum, longTaskCount, longTaskMax } = + response.aggregations ?? {}; return { - noOfLongTasks, - sumOfLongTasks, - longestLongTask, + noOfLongTasks: longTaskCount?.values[pkey] ?? 0, + sumOfLongTasks: longTaskSum?.values[pkey] ?? 0, + longestLongTask: longTaskMax?.values[pkey] ?? 0, }; } - -async function filterPageLoadTransactions({ - setup, - urlQuery, - transactionIds, -}: { - setup: Setup & SetupTimeRange & SetupUIFilters; - urlQuery?: string; - transactionIds: string[]; -}) { - const projection = getRumPageLoadTransactionsProjection({ - setup, - urlQuery, - }); - - const params = mergeProjection(projection, { - body: { - size: transactionIds.length, - query: { - bool: { - must: [ - { - terms: { - [TRANSACTION_ID]: transactionIds, - }, - }, - ], - filter: [...projection.body.query.bool.filter], - }, - }, - _source: [TRANSACTION_ID], - }, - }); - - const { apmEventClient } = setup; - - const response = await apmEventClient.search(params); - return response.hits.hits.map((hit) => (hit._source as any).transaction.id)!; -} diff --git a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts index a8505337e8aec1..c27314923f6bd6 100644 --- a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts +++ b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts @@ -10,7 +10,6 @@ import { SetupUIFilters, } from '../../server/lib/helpers/setup_request'; import { - SPAN_TYPE, AGENT_NAME, TRANSACTION_TYPE, SERVICE_LANGUAGE_NAME, @@ -66,33 +65,6 @@ export function getRumPageLoadTransactionsProjection({ }; } -export function getRumLongTasksProjection({ - setup, -}: { - setup: Setup & SetupTimeRange & SetupUIFilters; -}) { - const { start, end, uiFiltersES } = setup; - - const bool = { - filter: [ - { range: rangeFilter(start, end) }, - { term: { [SPAN_TYPE]: 'longtask' } }, - ...uiFiltersES, - ], - }; - - return { - apm: { - events: [ProcessorEvent.span], - }, - body: { - query: { - bool, - }, - }, - }; -} - export function getRumErrorsProjection({ setup, }: { diff --git a/x-pack/plugins/apm/server/routes/rum_client.ts b/x-pack/plugins/apm/server/routes/rum_client.ts index 2bdfaa1421eea8..8dee8b759df26d 100644 --- a/x-pack/plugins/apm/server/routes/rum_client.ts +++ b/x-pack/plugins/apm/server/routes/rum_client.ts @@ -177,12 +177,13 @@ export const rumLongTaskMetrics = createRoute(() => ({ const setup = await setupRequest(context, request); const { - query: { urlQuery }, + query: { urlQuery, percentile }, } = context.params; return getLongTaskMetrics({ setup, urlQuery, + percentile: percentile ? Number(percentile) : undefined, }); }, })); diff --git a/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts b/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts index 425268264612ff..518c4ef8a81a73 100644 --- a/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts +++ b/x-pack/test/apm_api_integration/trial/tests/csm/long_task_metrics.ts @@ -47,9 +47,9 @@ export default function rumServicesApiTests({ getService }: FtrProviderContext) expectSnapshot(response.body).toMatchInline(` Object { - "longestLongTask": 109000, - "noOfLongTasks": 2, - "sumOfLongTasks": 168000, + "longestLongTask": 0, + "noOfLongTasks": 0, + "sumOfLongTasks": 0, } `); }); From 63ff0606df1d35978049ff9aa25f04c46be895f8 Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko <jo.naumenko@gmail.com> Date: Thu, 1 Oct 2020 13:57:28 -0700 Subject: [PATCH 057/128] Extended Email action configuration with hasAuth property to identify if the connector require user credentials. Improved UX for Email connector (#78235) * Extended Email action configuration with hasAuth property to identify if the connector require user credentials. Improved UX for Email connector * Fixed failing tests and comments * Fixed type check and reverted logic of Add user and password switch button * Fixed due to the latest design requirenments * Fixed due to review comments --- .../server/builtin_action_types/email.test.ts | 7 + .../server/builtin_action_types/email.ts | 2 + .../lib/send_email.test.ts | 35 +++- .../builtin_action_types/lib/send_email.ts | 5 +- .../server/saved_objects/migrations.test.ts | 30 ++++ .../server/saved_objects/migrations.ts | 91 +++++++--- .../builtin_action_types/email/email.test.tsx | 5 + .../builtin_action_types/email/email.tsx | 20 +++ .../email/email_connector.test.tsx | 32 ++++ .../email/email_connector.tsx | 161 ++++++++++++------ .../components/builtin_action_types/types.ts | 1 + .../action_connector_form.tsx | 52 +++--- .../actions/builtin_action_types/email.ts | 3 + 13 files changed, 345 insertions(+), 99 deletions(-) diff --git a/x-pack/plugins/actions/server/builtin_action_types/email.test.ts b/x-pack/plugins/actions/server/builtin_action_types/email.test.ts index 7147483998d982..132510ea0ce84e 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/email.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/email.test.ts @@ -55,6 +55,7 @@ describe('config validation', () => { const config: Record<string, unknown> = { service: 'gmail', from: 'bob@example.com', + hasAuth: true, }; expect(validateConfig(actionType, config)).toEqual({ ...config, @@ -66,6 +67,7 @@ describe('config validation', () => { delete config.service; config.host = 'elastic.co'; config.port = 8080; + config.hasAuth = true; expect(validateConfig(actionType, config)).toEqual({ ...config, service: null, @@ -233,6 +235,7 @@ describe('execute()', () => { port: 42, secure: true, from: 'bob@example.com', + hasAuth: true, }; const secrets: ActionTypeSecretsType = { user: 'bob', @@ -269,6 +272,7 @@ describe('execute()', () => { "message": "a message to you", "subject": "the subject", }, + "hasAuth": true, "proxySettings": undefined, "routing": Object { "bcc": Array [ @@ -298,6 +302,7 @@ describe('execute()', () => { port: 42, secure: true, from: 'bob@example.com', + hasAuth: false, }; const secrets: ActionTypeSecretsType = { user: null, @@ -327,6 +332,7 @@ describe('execute()', () => { "message": "a message to you", "subject": "the subject", }, + "hasAuth": false, "proxySettings": undefined, "routing": Object { "bcc": Array [ @@ -356,6 +362,7 @@ describe('execute()', () => { port: 42, secure: true, from: 'bob@example.com', + hasAuth: false, }; const secrets: ActionTypeSecretsType = { user: null, diff --git a/x-pack/plugins/actions/server/builtin_action_types/email.ts b/x-pack/plugins/actions/server/builtin_action_types/email.ts index 6fd2d694b06f78..be2664887d9433 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/email.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/email.ts @@ -36,6 +36,7 @@ const ConfigSchemaProps = { port: schema.nullable(portSchema()), secure: schema.nullable(schema.boolean()), from: schema.string(), + hasAuth: schema.boolean({ defaultValue: true }), }; const ConfigSchema = schema.object(ConfigSchemaProps); @@ -185,6 +186,7 @@ async function executor( message: params.message, }, proxySettings: execOptions.proxySettings, + hasAuth: config.hasAuth, }; let result; diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts index b6c4a4ea882e5a..a1c4041628bd51 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.test.ts @@ -64,7 +64,7 @@ describe('send_email module', () => { }); test('handles unauthenticated email using not secure host/port', async () => { - const sendEmailOptions = getSendEmailOptions( + const sendEmailOptions = getSendEmailOptionsNoAuth( { transport: { host: 'example.com', @@ -76,12 +76,7 @@ describe('send_email module', () => { proxyRejectUnauthorizedCertificates: false, } ); - // @ts-expect-error - delete sendEmailOptions.transport.service; - // @ts-expect-error - delete sendEmailOptions.transport.user; - // @ts-expect-error - delete sendEmailOptions.transport.password; + const result = await sendEmail(mockLogger, sendEmailOptions); expect(result).toBe(sendMailMockResult); expect(createTransportMock.mock.calls[0]).toMatchInlineSnapshot(` @@ -248,5 +243,31 @@ function getSendEmailOptions( password: 'changeme', }, proxySettings, + hasAuth: true, + }; +} + +function getSendEmailOptionsNoAuth( + { content = {}, routing = {}, transport = {} } = {}, + proxySettings?: ProxySettings +) { + return { + content: { + ...content, + message: 'a message', + subject: 'a subject', + }, + routing: { + ...routing, + from: 'fred@example.com', + to: ['jim@example.com'], + cc: ['bob@example.com', 'robert@example.com'], + bcc: [], + }, + transport: { + ...transport, + }, + proxySettings, + hasAuth: false, }; } diff --git a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts index dead8fee63d4ff..f3cdf82bfe8cd0 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/lib/send_email.ts @@ -20,6 +20,7 @@ export interface SendEmailOptions { content: Content; proxySettings?: ProxySettings; rejectUnauthorized?: boolean; + hasAuth: boolean; } // config validation ensures either service is set or host/port are set @@ -46,14 +47,14 @@ export interface Content { // send an email export async function sendEmail(logger: Logger, options: SendEmailOptions): Promise<unknown> { - const { transport, routing, content, proxySettings, rejectUnauthorized } = options; + const { transport, routing, content, proxySettings, rejectUnauthorized, hasAuth } = options; const { service, host, port, secure, user, password } = transport; const { from, to, cc, bcc } = routing; const { subject, message } = content; const transportConfig: Record<string, unknown> = {}; - if (user != null && password != null) { + if (hasAuth && user != null && password != null) { transportConfig.auth = { user, pass: password, diff --git a/x-pack/plugins/actions/server/saved_objects/migrations.test.ts b/x-pack/plugins/actions/server/saved_objects/migrations.test.ts index d577f0c8bbc6c2..1fa5889e77cb04 100644 --- a/x-pack/plugins/actions/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/actions/server/saved_objects/migrations.test.ts @@ -21,6 +21,20 @@ describe('7.10.0', () => { ); }); + test('add hasAuth config property for .email actions', () => { + const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; + const action = getMockDataForEmail({}); + expect(migration710(action, context)).toMatchObject({ + ...action, + attributes: { + ...action.attributes, + config: { + hasAuth: true, + }, + }, + }); + }); + test('rename cases configuration object', () => { const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; const action = getMockData({}); @@ -36,6 +50,22 @@ describe('7.10.0', () => { }); }); +function getMockDataForEmail( + overwrites: Record<string, unknown> = {} +): SavedObjectUnsanitizedDoc<RawAction> { + return { + attributes: { + name: 'abc', + actionTypeId: '.email', + config: {}, + secrets: { user: 'test', password: '123' }, + ...overwrites, + }, + id: uuid.v4(), + type: 'action', + }; +} + function getMockData( overwrites: Record<string, unknown> = {} ): SavedObjectUnsanitizedDoc<RawAction> { diff --git a/x-pack/plugins/actions/server/saved_objects/migrations.ts b/x-pack/plugins/actions/server/saved_objects/migrations.ts index 0006d88c44149d..993beef8d9b2bd 100644 --- a/x-pack/plugins/actions/server/saved_objects/migrations.ts +++ b/x-pack/plugins/actions/server/saved_objects/migrations.ts @@ -3,40 +3,87 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - import { SavedObjectMigrationMap, SavedObjectUnsanitizedDoc, SavedObjectMigrationFn, + SavedObjectMigrationContext, } from '../../../../../src/core/server'; import { RawAction } from '../types'; import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server'; +type ActionMigration = ( + doc: SavedObjectUnsanitizedDoc<RawAction> +) => SavedObjectUnsanitizedDoc<RawAction>; + export function getMigrations( encryptedSavedObjects: EncryptedSavedObjectsPluginSetup ): SavedObjectMigrationMap { - return { '7.10.0': renameCasesConfigurationObject(encryptedSavedObjects) }; + const migrationActions = encryptedSavedObjects.createMigration<RawAction, RawAction>( + (doc): doc is SavedObjectUnsanitizedDoc<RawAction> => + !!doc.attributes.config?.casesConfiguration || doc.attributes.actionTypeId === '.email', + pipeMigrations(renameCasesConfigurationObject, addHasAuthConfigurationObject) + ); + + return { + '7.10.0': executeMigrationWithErrorHandling(migrationActions, '7.10.0'), + }; } -const renameCasesConfigurationObject = ( - encryptedSavedObjects: EncryptedSavedObjectsPluginSetup -): SavedObjectMigrationFn<RawAction, RawAction> => { - return encryptedSavedObjects.createMigration<RawAction, RawAction>( - (doc): doc is SavedObjectUnsanitizedDoc<RawAction> => - !!doc.attributes.config?.casesConfiguration, - (doc: SavedObjectUnsanitizedDoc<RawAction>): SavedObjectUnsanitizedDoc<RawAction> => { - const { casesConfiguration, ...restConfiguration } = doc.attributes.config; - - return { - ...doc, - attributes: { - ...doc.attributes, - config: { - ...restConfiguration, - incidentConfiguration: casesConfiguration, - }, - }, - }; +function executeMigrationWithErrorHandling( + migrationFunc: SavedObjectMigrationFn<RawAction, RawAction>, + version: string +) { + return (doc: SavedObjectUnsanitizedDoc<RawAction>, context: SavedObjectMigrationContext) => { + try { + return migrationFunc(doc, context); + } catch (ex) { + context.log.error( + `encryptedSavedObject ${version} migration failed for action ${doc.id} with error: ${ex.message}`, + { actionDocument: doc } + ); } - ); + return doc; + }; +} + +function renameCasesConfigurationObject( + doc: SavedObjectUnsanitizedDoc<RawAction> +): SavedObjectUnsanitizedDoc<RawAction> { + if (!doc.attributes.config?.casesConfiguration) { + return doc; + } + const { casesConfiguration, ...restConfiguration } = doc.attributes.config; + + return { + ...doc, + attributes: { + ...doc.attributes, + config: { + ...restConfiguration, + incidentConfiguration: casesConfiguration, + }, + }, + }; +} + +const addHasAuthConfigurationObject = ( + doc: SavedObjectUnsanitizedDoc<RawAction> +): SavedObjectUnsanitizedDoc<RawAction> => { + const hasAuth = !!doc.attributes.secrets.user || !!doc.attributes.secrets.password; + return { + ...doc, + attributes: { + ...doc.attributes, + config: { + ...doc.attributes.config, + hasAuth, + }, + }, + }; }; + +function pipeMigrations(...migrations: ActionMigration[]): ActionMigration { + return (doc: SavedObjectUnsanitizedDoc<RawAction>) => + migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx index e823e848f52c2c..ae698f2304e4ec 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.test.tsx @@ -43,6 +43,7 @@ describe('connector validation', () => { port: 2323, host: 'localhost', test: 'test', + hasAuth: true, }, } as EmailActionConnector; @@ -72,6 +73,7 @@ describe('connector validation', () => { port: 2323, host: 'localhost', test: 'test', + hasAuth: false, }, } as EmailActionConnector; @@ -96,6 +98,7 @@ describe('connector validation', () => { name: 'email', config: { from: 'test@test.com', + hasAuth: true, }, } as EmailActionConnector; @@ -124,6 +127,7 @@ describe('connector validation', () => { port: 2323, host: 'localhost', test: 'test', + hasAuth: true, }, } as EmailActionConnector; @@ -152,6 +156,7 @@ describe('connector validation', () => { port: 2323, host: 'localhost', test: 'test', + hasAuth: true, }, } as EmailActionConnector; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx index 3e8e71991a5944..b75d809f6a3273 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email.tsx @@ -75,6 +75,26 @@ export function getActionType(): ActionTypeModel<EmailConfig, EmailSecrets, Emai ) ); } + if (action.config.hasAuth && !action.secrets.user && !action.secrets.password) { + errors.user.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthUserNameText', + { + defaultMessage: 'Username is required.', + } + ) + ); + } + if (action.config.hasAuth && !action.secrets.user && !action.secrets.password) { + errors.password.push( + i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.error.requiredAuthPasswordText', + { + defaultMessage: 'Password is required.', + } + ) + ); + } if (action.secrets.user && !action.secrets.password) { errors.password.push( i18n.translate( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx index 6856e553ab400b..2685e62eb9a6ca 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.test.tsx @@ -21,6 +21,7 @@ describe('EmailActionConnectorFields renders', () => { name: 'email', config: { from: 'test@test.com', + hasAuth: true, }, } as EmailActionConnector; const wrapper = mountWithIntl( @@ -42,4 +43,35 @@ describe('EmailActionConnectorFields renders', () => { expect(wrapper.find('[data-test-subj="emailUserInput"]').length > 0).toBeTruthy(); expect(wrapper.find('[data-test-subj="emailPasswordInput"]').length > 0).toBeTruthy(); }); + + test('secret connector fields is not rendered when hasAuth false', () => { + const actionConnector = { + secrets: {}, + id: 'test', + actionTypeId: '.email', + name: 'email', + config: { + from: 'test@test.com', + hasAuth: false, + }, + } as EmailActionConnector; + const wrapper = mountWithIntl( + <EmailActionConnectorFields + action={actionConnector} + errors={{ from: [], port: [], host: [], user: [], password: [] }} + editActionConfig={() => {}} + editActionSecrets={() => {}} + docLinks={{ ELASTIC_WEBSITE_URL: '', DOC_LINK_VERSION: '' } as DocLinksStart} + readOnly={false} + /> + ); + expect(wrapper.find('[data-test-subj="emailFromInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="emailFromInput"]').first().prop('value')).toBe( + 'test@test.com' + ); + expect(wrapper.find('[data-test-subj="emailHostInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="emailPortInput"]').length > 0).toBeTruthy(); + expect(wrapper.find('[data-test-subj="emailUserInput"]').length > 0).toBeFalsy(); + expect(wrapper.find('[data-test-subj="emailPasswordInput"]').length > 0).toBeFalsy(); + }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx index 4ef9c2e0d4d2e7..1e92e9fc2519c5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/email/email_connector.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment } from 'react'; +import React, { Fragment, useEffect } from 'react'; import { EuiFieldText, EuiFlexItem, @@ -12,6 +12,9 @@ import { EuiFieldPassword, EuiSwitch, EuiFormRow, + EuiTitle, + EuiSpacer, + EuiCallOut, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -22,8 +25,14 @@ import { EmailActionConnector } from '../types'; export const EmailActionConnectorFields: React.FunctionComponent<ActionConnectorFieldsProps< EmailActionConnector >> = ({ action, editActionConfig, editActionSecrets, errors, readOnly, docLinks }) => { - const { from, host, port, secure } = action.config; + const { from, host, port, secure, hasAuth } = action.config; const { user, password } = action.secrets; + useEffect(() => { + if (!action.id) { + editActionConfig('hasAuth', true); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return ( <Fragment> @@ -160,60 +169,116 @@ export const EmailActionConnectorFields: React.FunctionComponent<ActionConnector </EuiFlexGroup> </EuiFlexItem> </EuiFlexGroup> - <EuiFlexGroup justifyContent="spaceBetween"> + <EuiFlexGroup> <EuiFlexItem> - <EuiFormRow - id="emailUser" - fullWidth - error={errors.user} - isInvalid={errors.user.length > 0} + <EuiSpacer size="m" /> + <EuiTitle size="xxs"> + <h4> + <FormattedMessage + id="xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.authenticationLabel" + defaultMessage="Authentication" + /> + </h4> + </EuiTitle> + <EuiSpacer size="s" /> + <EuiSwitch label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel', + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.hasAuthSwitchLabel', { - defaultMessage: 'Username', + defaultMessage: 'Require authentication for this server', } )} - > - <EuiFieldText - fullWidth - isInvalid={errors.user.length > 0} - name="user" - readOnly={readOnly} - value={user || ''} - data-test-subj="emailUserInput" - onChange={(e) => { - editActionSecrets('user', nullableString(e.target.value)); - }} - /> - </EuiFormRow> - </EuiFlexItem> - <EuiFlexItem> - <EuiFormRow - id="emailPassword" - fullWidth - error={errors.password} - isInvalid={errors.password.length > 0} - label={i18n.translate( - 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel', - { - defaultMessage: 'Password', + disabled={readOnly} + checked={hasAuth} + onChange={(e) => { + editActionConfig('hasAuth', e.target.checked); + if (!e.target.checked) { + editActionSecrets('user', null); + editActionSecrets('password', null); } - )} - > - <EuiFieldPassword - fullWidth - readOnly={readOnly} - isInvalid={errors.password.length > 0} - name="password" - value={password || ''} - data-test-subj="emailPasswordInput" - onChange={(e) => { - editActionSecrets('password', nullableString(e.target.value)); - }} - /> - </EuiFormRow> + }} + /> </EuiFlexItem> </EuiFlexGroup> + {hasAuth ? ( + <> + {action.id ? ( + <> + <EuiSpacer size="m" /> + <EuiCallOut + size="s" + title="Username and password are encrypted. Please reenter values for these fields." + iconType="iInCircle" + /> + <EuiSpacer size="m" /> + </> + ) : null} + <EuiFlexGroup justifyContent="spaceBetween"> + <EuiFlexItem> + <EuiFormRow + id="emailUser" + fullWidth + error={errors.user} + isInvalid={errors.user.length > 0 && user !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.userTextFieldLabel', + { + defaultMessage: 'Username', + } + )} + > + <EuiFieldText + fullWidth + isInvalid={errors.user.length > 0 && user !== undefined} + name="user" + readOnly={readOnly} + value={user || ''} + data-test-subj="emailUserInput" + onChange={(e) => { + editActionSecrets('user', nullableString(e.target.value)); + }} + onBlur={() => { + if (!user) { + editActionSecrets('user', ''); + } + }} + /> + </EuiFormRow> + </EuiFlexItem> + <EuiFlexItem> + <EuiFormRow + id="emailPassword" + fullWidth + error={errors.password} + isInvalid={errors.password.length > 0 && password !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.sections.builtinActionTypes.emailAction.passwordFieldLabel', + { + defaultMessage: 'Password', + } + )} + > + <EuiFieldPassword + fullWidth + readOnly={readOnly} + isInvalid={errors.password.length > 0 && password !== undefined} + name="password" + value={password || ''} + data-test-subj="emailPasswordInput" + onChange={(e) => { + editActionSecrets('password', nullableString(e.target.value)); + }} + onBlur={() => { + if (!password) { + editActionSecrets('password', ''); + } + }} + /> + </EuiFormRow> + </EuiFlexItem> + </EuiFlexGroup> + </> + ) : null} </Fragment> ); }; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts index f6bb08148b3cbf..958d77a11c883f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/types.ts @@ -69,6 +69,7 @@ export interface EmailConfig { host: string; port: number; secure?: boolean; + hasAuth: boolean; } export interface EmailSecrets { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx index ef6621f98fac2d..f91bd7382b61cc 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_connector_form.tsx @@ -15,6 +15,7 @@ import { EuiLoadingSpinner, EuiFlexGroup, EuiFlexItem, + EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; @@ -169,26 +170,37 @@ export const ActionConnectorForm = ({ </EuiFormRow> <EuiSpacer size="m" /> {FieldsComponent !== null ? ( - <Suspense - fallback={ - <EuiFlexGroup justifyContent="center"> - <EuiFlexItem grow={false}> - <EuiLoadingSpinner size="m" /> - </EuiFlexItem> - </EuiFlexGroup> - } - > - <FieldsComponent - action={connector} - errors={errors} - readOnly={!canSave} - editActionConfig={setActionConfigProperty} - editActionSecrets={setActionSecretsProperty} - http={http} - docLinks={docLinks} - consumer={consumer} - /> - </Suspense> + <> + <EuiTitle size="xxs"> + <h4> + <FormattedMessage + id="xpack.triggersActionsUI.sections.actionConnectorForm.connectorSettingsLabel" + defaultMessage="Connector settings" + /> + </h4> + </EuiTitle> + <EuiSpacer size="s" /> + <Suspense + fallback={ + <EuiFlexGroup justifyContent="center"> + <EuiFlexItem grow={false}> + <EuiLoadingSpinner size="m" /> + </EuiFlexItem> + </EuiFlexGroup> + } + > + <FieldsComponent + action={connector} + errors={errors} + readOnly={!canSave} + editActionConfig={setActionConfigProperty} + editActionSecrets={setActionSecretsProperty} + http={http} + docLinks={docLinks} + consumer={consumer} + /> + </Suspense> + </> ) : null} </EuiForm> ); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts index 329bd3433d3880..f6b0f06a6722ec 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/email.ts @@ -25,6 +25,7 @@ export default function emailTest({ getService }: FtrProviderContext) { config: { service: '__json', from: 'bob@example.com', + hasAuth: true, }, secrets: { user: 'bob', @@ -41,6 +42,7 @@ export default function emailTest({ getService }: FtrProviderContext) { actionTypeId: '.email', config: { service: '__json', + hasAuth: true, host: null, port: null, secure: null, @@ -62,6 +64,7 @@ export default function emailTest({ getService }: FtrProviderContext) { config: { from: 'bob@example.com', service: '__json', + hasAuth: true, host: null, port: null, secure: null, From 7211f78ce195f1925ebace43905b1301bbf82577 Mon Sep 17 00:00:00 2001 From: Tyler Smalley <tyler.smalley@elastic.co> Date: Thu, 1 Oct 2020 14:38:51 -0700 Subject: [PATCH 058/128] Bumps Jest related packages (#78720) Signed-off-by: Tyler Smalley <tyler.smalley@elastic.co> --- package.json | 14 +- .../elastic-eslint-config-kibana/package.json | 2 +- .../src/integration_tests/cluster.test.js | 2 +- packages/kbn-i18n/src/core/i18n.test.ts | 2 +- packages/kbn-optimizer/package.json | 2 +- packages/kbn-spec-to-console/package.json | 2 +- .../server/logging/rotate/log_rotator.test.ts | 6 +- .../listing/dashboard_listing.test.js | 2 +- .../step_time_field/step_time_field.test.tsx | 2 +- .../number_list/number_list.test.tsx | 8 +- .../components/splits/terms.test.js | 12 +- x-pack/package.json | 12 +- .../alerts/server/alerts_client.test.ts | 4 +- .../components/__tests__/app.test.tsx | 2 +- .../settings/__tests__/settings.test.tsx | 2 +- .../event_log/server/lib/ready_signal.test.ts | 27 +- .../plugins/licensing/public/plugin.test.ts | 32 +- .../services/ml_server_info.test.ts | 9 +- .../new_job_capabilities._service.test.ts | 11 +- .../capabilities/check_capabilities.test.ts | 21 +- .../annotation_service/annotation.test.ts | 15 +- .../new_job_caps/new_job_caps.test.ts | 12 +- .../public/lib/ensure_minimum_time.test.js | 9 +- .../monitoring/public/lib/setup_mode.test.js | 91 +- .../monitoring/public/lib/setup_mode.tsx | 2 +- .../lib/authorized_user_pre_routing.test.ts | 2 +- .../detail_panel/detail_panel.test.js | 2 +- .../sections/job_list/job_list.test.js | 2 +- .../job_list/job_table/job_table.test.js | 2 +- .../job_create_review.test.js | 2 +- .../test/client_integration/job_list.test.js | 4 +- .../client_integration/job_list_clone.test.js | 2 +- .../flow_target_select.test.tsx.snap | 16 +- .../properties/new_template_timeline.test.tsx | 12 +- .../server/lib/bulk_operation_buffer.test.ts | 33 +- .../task_manager/server/task_store.test.ts | 198 ++- .../public/app/hooks/use_index_data.test.tsx | 10 +- .../step_define/step_define_form.test.tsx | 7 +- .../step_define/step_define_summary.test.tsx | 3 +- .../public/custom_time_range_action.test.ts | 56 +- .../public/custom_time_range_badge.test.ts | 20 +- yarn.lock | 1236 +++++++++-------- 42 files changed, 978 insertions(+), 932 deletions(-) diff --git a/package.json b/package.json index 26a7fef9cfc187..0eda8dd9f41143 100644 --- a/package.json +++ b/package.json @@ -291,7 +291,7 @@ "@types/hjson": "^2.4.2", "@types/hoek": "^4.1.3", "@types/inert": "^5.1.2", - "@types/jest": "^25.2.3", + "@types/jest": "^26.0.14", "@types/jest-when": "^2.7.1", "@types/joi": "^13.4.2", "@types/jquery": "^3.3.31", @@ -337,7 +337,7 @@ "@types/supertest-as-promised": "^2.0.38", "@types/tapable": "^1.0.6", "@types/tar": "^4.0.3", - "@types/testing-library__jest-dom": "^5.9.2", + "@types/testing-library__jest-dom": "^5.9.3", "@types/testing-library__react-hooks": "^3.4.0", "@types/type-detect": "^4.0.1", "@types/uuid": "^3.4.4", @@ -356,7 +356,7 @@ "archiver": "^3.1.1", "axe-core": "^4.0.2", "babel-eslint": "^10.0.3", - "babel-jest": "^25.5.1", + "babel-jest": "^26.3.0", "babel-plugin-istanbul": "^6.0.0", "backport": "5.6.0", "brace": "0.11.1", @@ -382,7 +382,7 @@ "eslint-plugin-cypress": "^2.8.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.19.1", - "eslint-plugin-jest": "^23.10.0", + "eslint-plugin-jest": "^24.0.2", "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-mocha": "^6.2.2", "eslint-plugin-no-unsanitized": "^3.0.2", @@ -411,10 +411,10 @@ "iedriver": "^3.14.2", "immer": "^1.5.0", "intl-messageformat-parser": "^1.4.0", - "jest": "^25.5.4", + "jest": "^26.4.2", "jest-canvas-mock": "^2.2.0", - "jest-circus": "^25.5.4", - "jest-cli": "^25.5.4", + "jest-circus": "^26.4.2", + "jest-cli": "^26.4.2", "jest-environment-jsdom-thirteen": "^1.0.1", "jest-raw-loader": "^1.0.1", "jest-when": "^2.7.2", diff --git a/packages/elastic-eslint-config-kibana/package.json b/packages/elastic-eslint-config-kibana/package.json index a4bb8d5449ee8f..3f2c6e9edb2614 100644 --- a/packages/elastic-eslint-config-kibana/package.json +++ b/packages/elastic-eslint-config-kibana/package.json @@ -24,7 +24,7 @@ "eslint-plugin-jsx-a11y": "^6.2.3", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.19.1", - "eslint-plugin-jest": "^23.10.0", + "eslint-plugin-jest": "^24.0.2", "eslint-plugin-mocha": "^6.2.2", "eslint-plugin-no-unsanitized": "^3.0.2", "eslint-plugin-prefer-object-spread": "^1.2.1", diff --git a/packages/kbn-es/src/integration_tests/cluster.test.js b/packages/kbn-es/src/integration_tests/cluster.test.js index 0ae0ac0aac27a4..6229a8add0d240 100644 --- a/packages/kbn-es/src/integration_tests/cluster.test.js +++ b/packages/kbn-es/src/integration_tests/cluster.test.js @@ -60,7 +60,7 @@ async function ensureResolve(promise) { function mockEsBin({ exitCode, start }) { execa.mockImplementationOnce((cmd, args, options) => - require.requireActual('execa')( + jest.requireActual('execa')( process.execPath, [ require.resolve('./__fixtures__/es_bin.js'), diff --git a/packages/kbn-i18n/src/core/i18n.test.ts b/packages/kbn-i18n/src/core/i18n.test.ts index ec08c82b502db8..3364f20879c2a4 100644 --- a/packages/kbn-i18n/src/core/i18n.test.ts +++ b/packages/kbn-i18n/src/core/i18n.test.ts @@ -25,7 +25,7 @@ describe('I18n engine', () => { let i18n: typeof i18nModule; beforeEach(() => { - i18n = require.requireActual('./i18n'); + i18n = jest.requireActual('./i18n'); }); afterEach(() => { diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index f90fcaec79fe0d..5871c81f48aea1 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -24,7 +24,7 @@ "execa": "^4.0.2", "file-loader": "^4.2.0", "istanbul-instrumenter-loader": "^3.0.1", - "jest-diff": "^25.5.0", + "jest-diff": "^26.4.2", "json-stable-stringify": "^1.0.1", "loader-utils": "^1.2.3", "node-sass": "^4.13.1", diff --git a/packages/kbn-spec-to-console/package.json b/packages/kbn-spec-to-console/package.json index 0c80c949c1d11c..557f38ec740fc5 100644 --- a/packages/kbn-spec-to-console/package.json +++ b/packages/kbn-spec-to-console/package.json @@ -17,7 +17,7 @@ }, "homepage": "https://github.com/jbudz/spec-to-console#readme", "devDependencies": { - "jest": "^25.5.4", + "jest": "^26.4.2", "prettier": "^2.1.1" }, "dependencies": { diff --git a/src/legacy/server/logging/rotate/log_rotator.test.ts b/src/legacy/server/logging/rotate/log_rotator.test.ts index 70842d42f5e1f2..8f67b47f6949e0 100644 --- a/src/legacy/server/logging/rotate/log_rotator.test.ts +++ b/src/legacy/server/logging/rotate/log_rotator.test.ts @@ -22,6 +22,7 @@ import fs, { existsSync, mkdirSync, statSync, writeFileSync } from 'fs'; import { LogRotator } from './log_rotator'; import { tmpdir } from 'os'; import { dirname, join } from 'path'; +import lodash from 'lodash'; const mockOn = jest.fn(); jest.mock('chokidar', () => ({ @@ -31,10 +32,7 @@ jest.mock('chokidar', () => ({ })), })); -jest.mock('lodash', () => ({ - ...require.requireActual('lodash'), - throttle: (fn: any) => fn, -})); +lodash.throttle = (fn: any) => fn; const tempDir = join(tmpdir(), 'kbn_log_rotator_test'); const testFilePath = join(tempDir, 'log_rotator_test_log_file.log'); diff --git a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.js b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.js index 6acb491f9a20c6..99b1ebf047d740 100644 --- a/src/plugins/dashboard/public/application/listing/dashboard_listing.test.js +++ b/src/plugins/dashboard/public/application/listing/dashboard_listing.test.js @@ -20,7 +20,7 @@ jest.mock( 'lodash', () => ({ - ...require.requireActual('lodash'), + ...jest.requireActual('lodash'), // mock debounce to fire immediately with no internal timer debounce: (func) => { function debounced(...args) { diff --git a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx index aec010d2dd3f8c..4d1033d12d2b87 100644 --- a/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx +++ b/src/plugins/index_pattern_management/public/components/create_index_pattern_wizard/components/step_time_field/step_time_field.test.tsx @@ -30,7 +30,7 @@ jest.mock('./components/time_field', () => ({ TimeField: 'TimeField' })); jest.mock('./components/advanced_options', () => ({ AdvancedOptions: 'AdvancedOptions' })); jest.mock('./components/action_buttons', () => ({ ActionButtons: 'ActionButtons' })); jest.mock('./../../lib', () => ({ - extractTimeFields: require.requireActual('./../../lib').extractTimeFields, + extractTimeFields: jest.requireActual('./../../lib').extractTimeFields, ensureMinimumTime: async (fields: IFieldType) => Promise.resolve(fields), })); diff --git a/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx b/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx index 82d4b9142fb764..7964da23d8f508 100644 --- a/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx +++ b/src/plugins/vis_default_editor/public/components/controls/components/number_list/number_list.test.tsx @@ -33,10 +33,10 @@ jest.mock('@elastic/eui', () => ({ let counter = 1; return () => `12${counter++}`; }), - EuiSpacer: require.requireActual('@elastic/eui').EuiSpacer, - EuiFlexItem: require.requireActual('@elastic/eui').EuiFlexItem, - EuiButtonEmpty: require.requireActual('@elastic/eui').EuiButtonEmpty, - EuiFormErrorText: require.requireActual('@elastic/eui').EuiFormErrorText, + EuiSpacer: jest.requireActual('@elastic/eui').EuiSpacer, + EuiFlexItem: jest.requireActual('@elastic/eui').EuiFlexItem, + EuiButtonEmpty: jest.requireActual('@elastic/eui').EuiButtonEmpty, + EuiFormErrorText: jest.requireActual('@elastic/eui').EuiFormErrorText, })); describe('NumberList', () => { diff --git a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js b/src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js index 4d322cd7b7e61b..da840adb75cd3a 100644 --- a/src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js +++ b/src/plugins/vis_type_timeseries/public/application/components/splits/terms.test.js @@ -23,12 +23,12 @@ import { SplitByTermsUI } from './terms'; jest.mock('@elastic/eui', () => ({ htmlIdGenerator: jest.fn(() => () => '42'), - EuiFlexGroup: require.requireActual('@elastic/eui').EuiFlexGroup, - EuiFlexItem: require.requireActual('@elastic/eui').EuiFlexItem, - EuiFormRow: require.requireActual('@elastic/eui').EuiFormRow, - EuiFieldNumber: require.requireActual('@elastic/eui').EuiFieldNumber, - EuiComboBox: require.requireActual('@elastic/eui').EuiComboBox, - EuiFieldText: require.requireActual('@elastic/eui').EuiFieldText, + EuiFlexGroup: jest.requireActual('@elastic/eui').EuiFlexGroup, + EuiFlexItem: jest.requireActual('@elastic/eui').EuiFlexItem, + EuiFormRow: jest.requireActual('@elastic/eui').EuiFormRow, + EuiFieldNumber: jest.requireActual('@elastic/eui').EuiFieldNumber, + EuiComboBox: jest.requireActual('@elastic/eui').EuiComboBox, + EuiFieldText: jest.requireActual('@elastic/eui').EuiFieldText, })); describe('src/legacy/core_plugins/metrics/public/components/splits/terms.test.js', () => { diff --git a/x-pack/package.json b/x-pack/package.json index 6c18902c773669..f9b193bb4da06d 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -88,7 +88,7 @@ "@types/hoist-non-react-statics": "^3.3.1", "@types/http-proxy": "^1.17.4", "@types/http-proxy-agent": "^2.0.2", - "@types/jest": "^25.2.3", + "@types/jest": "^26.0.14", "@types/jest-specific-snapshot": "^0.5.4", "@types/joi": "^13.4.2", "@types/js-search": "^1.4.0", @@ -127,7 +127,7 @@ "@types/styled-components": "^5.1.0", "@types/supertest": "^2.0.5", "@types/tar-fs": "^1.16.1", - "@types/testing-library__jest-dom": "^5.9.2", + "@types/testing-library__jest-dom": "^5.9.3", "@types/testing-library__react-hooks": "^3.4.0", "@types/tinycolor2": "^1.4.1", "@types/use-resize-observer": "^6.0.0", @@ -143,7 +143,7 @@ "apollo-link-error": "^1.1.7", "apollo-link-state": "^0.4.1", "autoprefixer": "^9.7.4", - "babel-jest": "^25.5.1", + "babel-jest": "^26.3.0", "babel-loader": "^8.0.6", "babel-plugin-require-context-hook": "npm:babel-plugin-require-context-hook-babel7@1.0.0", "base64-js": "^1.3.1", @@ -188,9 +188,9 @@ "hoist-non-react-statics": "^3.3.2", "i18n-iso-countries": "^4.3.1", "icalendar": "0.7.1", - "jest": "^25.5.4", - "jest-circus": "^25.5.4", - "jest-cli": "^25.5.4", + "jest": "^26.4.2", + "jest-circus": "^26.4.2", + "jest-cli": "^26.4.2", "jest-styled-components": "^7.0.2", "js-search": "^1.4.3", "jsdom": "13.1.0", diff --git a/x-pack/plugins/alerts/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts index a5846cd1060c59..088390c3cb6e77 100644 --- a/x-pack/plugins/alerts/server/alerts_client.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client.test.ts @@ -4130,14 +4130,13 @@ describe('update()', () => { expect(taskManager.runNow).not.toHaveBeenCalled(); }); - test('updating the alert should not wait for the rerun the task to complete', async (done) => { + test('updating the alert should not wait for the rerun the task to complete', async () => { const alertId = uuid.v4(); const taskId = uuid.v4(); mockApiCalls(alertId, taskId, { interval: '10s' }, { interval: '30s' }); const resolveAfterAlertUpdatedCompletes = resolvable<{ id: string }>(); - resolveAfterAlertUpdatedCompletes.then(() => done()); taskManager.runNow.mockReset(); taskManager.runNow.mockReturnValue(resolveAfterAlertUpdatedCompletes); @@ -4165,7 +4164,6 @@ describe('update()', () => { }); expect(taskManager.runNow).toHaveBeenCalled(); - resolveAfterAlertUpdatedCompletes.resolve({ id: alertId }); }); diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx b/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx index 2ec3cfde8bd681..eaf45db0a0b93a 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx +++ b/x-pack/plugins/canvas/shareable_runtime/components/__tests__/app.test.tsx @@ -40,7 +40,7 @@ jest.mock('@elastic/eui/lib/components/portal/portal', () => { // Local constants are not supported in Jest mocks-- they must be // imported within the mock. // eslint-disable-next-line no-shadow - const React = require.requireActual('react'); + const React = jest.requireActual('react'); return { EuiPortal: (props: any) => <div>{props.children}</div>, }; diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/settings.test.tsx b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/settings.test.tsx index 34dacc79562536..28aa6ef90aedb2 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/settings.test.tsx +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__tests__/settings.test.tsx @@ -26,7 +26,7 @@ jest.mock('@elastic/eui/lib/services/accessibility', () => { }); jest.mock('@elastic/eui/lib/components/portal/portal', () => { // eslint-disable-next-line no-shadow - const React = require.requireActual('react'); + const React = jest.requireActual('react'); return { EuiPortal: (props: any) => <div>{props.children}</div>, }; diff --git a/x-pack/plugins/event_log/server/lib/ready_signal.test.ts b/x-pack/plugins/event_log/server/lib/ready_signal.test.ts index c216651ee94b19..d23d81861acc44 100644 --- a/x-pack/plugins/event_log/server/lib/ready_signal.test.ts +++ b/x-pack/plugins/event_log/server/lib/ready_signal.test.ts @@ -13,28 +13,9 @@ describe('ReadySignal', () => { readySignal = createReadySignal<number>(); }); - test('works as expected', async (done) => { - let value = 41; - - timeoutSet(100, async () => { - expect(value).toBe(41); - }); - - timeoutSet(250, async () => readySignal.signal(42)); - - timeoutSet(400, async () => { - expect(value).toBe(42); - - const innerValue = await readySignal.wait(); - expect(innerValue).toBe(42); - done(); - }); - - value = await readySignal.wait(); - expect(value).toBe(42); + test('works as expected', async () => { + readySignal.signal(42); + const ready = await readySignal.wait(); + expect(ready).toBe(42); }); }); - -function timeoutSet(ms: number, fn: () => Promise<unknown>): void { - setTimeout(fn, ms); -} diff --git a/x-pack/plugins/licensing/public/plugin.test.ts b/x-pack/plugins/licensing/public/plugin.test.ts index c20563dd159138..ce3fcdb0423269 100644 --- a/x-pack/plugins/licensing/public/plugin.test.ts +++ b/x-pack/plugins/licensing/public/plugin.test.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { take } from 'rxjs/operators'; +import { take, toArray } from 'rxjs/operators'; import { mountExpiredBannerMock } from './plugin.test.mocks'; import { LicenseType } from '../common/types'; @@ -92,7 +92,7 @@ describe('licensing plugin', () => { expect(sessionStorage.getItem).toHaveBeenCalledWith(licensingSessionStorageKey); }); - it('observable receives updated licenses', async (done) => { + it('observable receives updated licenses', async () => { const types: LicenseType[] = ['gold', 'platinum']; const sessionStorage = coreMock.createStorage(); @@ -104,27 +104,17 @@ describe('licensing plugin', () => { Promise.resolve(licenseMock.createLicense({ license: { type: types.shift() } })) ); - await plugin.setup(coreSetup); + plugin.setup(coreSetup); const { refresh, license$ } = await plugin.start(coreStart); + const promise = license$.pipe(take(3), toArray()).toPromise(); - let i = 0; - license$.subscribe((value) => { - i++; - if (i === 1) { - expect(value.type).toBe('basic'); - refresh(); - } else if (i === 2) { - expect(value.type).toBe('gold'); - // since this is a synchronous subscription, we need to give the exhaustMap a chance - // to mark the subscription as complete before emitting another value on the Subject - process.nextTick(() => refresh()); - } else if (i === 3) { - expect(value.type).toBe('platinum'); - done(); - } else { - throw new Error('unreachable'); - } - }); + await refresh(); + await refresh(); + + const licenses = await promise; + expect(licenses[0].type).toBe('basic'); + expect(licenses[1].type).toBe('gold'); + expect(licenses[2].type).toBe('platinum'); }); it('saved fetched license & signature in session storage', async () => { diff --git a/x-pack/plugins/ml/public/application/services/ml_server_info.test.ts b/x-pack/plugins/ml/public/application/services/ml_server_info.test.ts index cd0f10bb7f5771..e935d443f9857a 100644 --- a/x-pack/plugins/ml/public/application/services/ml_server_info.test.ts +++ b/x-pack/plugins/ml/public/application/services/ml_server_info.test.ts @@ -28,9 +28,8 @@ describe('ml_server_info initial state', () => { }); describe('ml_server_info', () => { - beforeEach(async (done) => { + beforeEach(async () => { await loadMlServerInfo(); - done(); }); describe('cloud information', () => { @@ -41,23 +40,21 @@ describe('ml_server_info', () => { }); describe('defaults', () => { - it('should get defaults', async (done) => { + it('should get defaults', async () => { const defaults = getNewJobDefaults(); expect(defaults.anomaly_detectors.model_memory_limit).toBe('128mb'); expect(defaults.anomaly_detectors.categorization_examples_limit).toBe(4); expect(defaults.anomaly_detectors.model_snapshot_retention_days).toBe(1); expect(defaults.datafeeds.scroll_size).toBe(1000); - done(); }); }); describe('limits', () => { - it('should get limits', async (done) => { + it('should get limits', async () => { const limits = getNewJobLimits(); expect(limits.max_model_memory_limit).toBe('128mb'); - done(); }); }); diff --git a/x-pack/plugins/ml/public/application/services/new_job_capabilities._service.test.ts b/x-pack/plugins/ml/public/application/services/new_job_capabilities._service.test.ts index 81f05065b51399..471a55061617b2 100644 --- a/x-pack/plugins/ml/public/application/services/new_job_capabilities._service.test.ts +++ b/x-pack/plugins/ml/public/application/services/new_job_capabilities._service.test.ts @@ -26,7 +26,7 @@ const indexPattern = ({ describe('new_job_capabilities_service', () => { describe('cloudwatch newJobCaps()', () => { - it('can construct job caps objects from endpoint json', async (done) => { + it('can construct job caps objects from endpoint json', async () => { await newJobCapsService.initializeFromIndexPattern(indexPattern); const { fields, aggs } = await newJobCapsService.newJobCaps; @@ -43,27 +43,22 @@ describe('new_job_capabilities_service', () => { expect(meanAgg.fields).toHaveLength(7); expect(distinctCountAgg.fields).toHaveLength(10); - done(); }); - it('job caps including text fields', async (done) => { + it('job caps including text fields', async () => { await newJobCapsService.initializeFromIndexPattern(indexPattern, true, false); const { fields, aggs } = await newJobCapsService.newJobCaps; expect(fields).toHaveLength(13); // one more field expect(aggs).toHaveLength(35); - - done(); }); - it('job caps excluding event rate', async (done) => { + it('job caps excluding event rate', async () => { await newJobCapsService.initializeFromIndexPattern(indexPattern, false, true); const { fields, aggs } = await newJobCapsService.newJobCaps; expect(fields).toHaveLength(11); // one less field expect(aggs).toHaveLength(35); - - done(); }); }); }); diff --git a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts index 4dd17f8cf48896..a436bcdebde4b0 100644 --- a/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts +++ b/x-pack/plugins/ml/server/lib/capabilities/check_capabilities.test.ts @@ -49,7 +49,7 @@ const mlClusterClientUpgrade = ({ describe('check_capabilities', () => { describe('getCapabilities() - right number of capabilities', () => { - test('kibana capabilities count', async (done) => { + test('kibana capabilities count', async () => { const { getCapabilities } = capabilitiesProvider( mlClusterClientNonUpgrade, getAdminCapabilities(), @@ -59,12 +59,11 @@ describe('check_capabilities', () => { const { capabilities } = await getCapabilities(); const count = Object.keys(capabilities).length; expect(count).toBe(28); - done(); }); }); describe('getCapabilities() with security', () => { - test('ml_user capabilities only', async (done) => { + test('ml_user capabilities only', async () => { const { getCapabilities } = capabilitiesProvider( mlClusterClientNonUpgrade, getUserCapabilities(), @@ -110,10 +109,9 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); }); - test('full capabilities', async (done) => { + test('full capabilities', async () => { const { getCapabilities } = capabilitiesProvider( mlClusterClientNonUpgrade, getAdminCapabilities(), @@ -159,10 +157,9 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(true); expect(capabilities.canCreateDataFrameAnalytics).toBe(true); expect(capabilities.canStartStopDataFrameAnalytics).toBe(true); - done(); }); - test('upgrade in progress with full capabilities', async (done) => { + test('upgrade in progress with full capabilities', async () => { const { getCapabilities } = capabilitiesProvider( mlClusterClientUpgrade, getAdminCapabilities(), @@ -208,10 +205,9 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); }); - test('upgrade in progress with partial capabilities', async (done) => { + test('upgrade in progress with partial capabilities', async () => { const { getCapabilities } = capabilitiesProvider( mlClusterClientUpgrade, getUserCapabilities(), @@ -257,10 +253,9 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); }); - test('full capabilities, ml disabled in space', async (done) => { + test('full capabilities, ml disabled in space', async () => { const { getCapabilities } = capabilitiesProvider( mlClusterClientNonUpgrade, getDefaultCapabilities(), @@ -306,11 +301,10 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); }); }); - test('full capabilities, basic license, ml disabled in space', async (done) => { + test('full capabilities, basic license, ml disabled in space', async () => { const { getCapabilities } = capabilitiesProvider( mlClusterClientNonUpgrade, getDefaultCapabilities(), @@ -357,6 +351,5 @@ describe('check_capabilities', () => { expect(capabilities.canDeleteDataFrameAnalytics).toBe(false); expect(capabilities.canCreateDataFrameAnalytics).toBe(false); expect(capabilities.canStartStopDataFrameAnalytics).toBe(false); - done(); }); }); 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 4c511b567615de..3d90cb09843910 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 @@ -35,7 +35,7 @@ describe('annotation_service', () => { }); describe('deleteAnnotation()', () => { - it('should delete annotation', async (done) => { + it('should delete annotation', async () => { const { deleteAnnotation } = annotationServiceProvider(mlClusterClientSpy); const mockFunct = mlClusterClientSpy; @@ -50,12 +50,11 @@ describe('annotation_service', () => { expect(mockFunct.asInternalUser.delete.mock.calls[0][0]).toStrictEqual(deleteParamsMock); expect(response).toBe(acknowledgedResponseMock); - done(); }); }); describe('getAnnotation()', () => { - it('should get annotations for specific job', async (done) => { + it('should get annotations for specific job', async () => { const { getAnnotations } = annotationServiceProvider(mlClusterClientSpy); const mockFunct = mlClusterClientSpy; @@ -74,7 +73,6 @@ describe('annotation_service', () => { expect(Object.keys(response.annotations)).toHaveLength(1); expect(response.annotations[jobIdMock]).toHaveLength(2); expect(isAnnotations(response.annotations[jobIdMock])).toBeTruthy(); - done(); }); it('should throw and catch an error', async () => { @@ -106,7 +104,7 @@ describe('annotation_service', () => { }); describe('indexAnnotation()', () => { - it('should index annotation', async (done) => { + it('should index annotation', async () => { const { indexAnnotation } = annotationServiceProvider(mlClusterClientSpy); const mockFunct = mlClusterClientSpy; @@ -129,10 +127,9 @@ describe('annotation_service', () => { expect(typeof annotation.modified_time).toBe('number'); expect(response).toBe(acknowledgedResponseMock); - done(); }); - it('should remove ._id and .key before updating annotation', async (done) => { + it('should remove ._id and .key before updating annotation', async () => { const { indexAnnotation } = annotationServiceProvider(mlClusterClientSpy); const mockFunct = mlClusterClientSpy; @@ -159,10 +156,9 @@ describe('annotation_service', () => { expect(typeof annotation.key).toBe('undefined'); expect(response).toBe(acknowledgedResponseMock); - done(); }); - it('should update annotation text and the username for modified_username', async (done) => { + it('should update annotation text and the username for modified_username', async () => { const { getAnnotations, indexAnnotation } = annotationServiceProvider(mlClusterClientSpy); const mockFunct = mlClusterClientSpy; @@ -196,7 +192,6 @@ describe('annotation_service', () => { expect(modifiedAnnotation.modified_username).toBe(modifiedUsernameMock); expect(typeof modifiedAnnotation.create_time).toBe('number'); expect(typeof modifiedAnnotation.modified_time).toBe('number'); - done(); }); }); }); diff --git a/x-pack/plugins/ml/server/models/job_service/new_job_caps/new_job_caps.test.ts b/x-pack/plugins/ml/server/models/job_service/new_job_caps/new_job_caps.test.ts index 891cb2e0d1e64b..1169123e93ea37 100644 --- a/x-pack/plugins/ml/server/models/job_service/new_job_caps/new_job_caps.test.ts +++ b/x-pack/plugins/ml/server/models/job_service/new_job_caps/new_job_caps.test.ts @@ -48,42 +48,38 @@ describe('job_service - job_caps', () => { }); describe('farequote newJobCaps()', () => { - it('can get job caps for index pattern', async (done) => { + it('can get job caps for index pattern', async () => { const indexPattern = 'farequote-*'; const isRollup = false; const { newJobCaps } = newJobCapsProvider(mlClusterClientNonRollupMock); const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock); expect(response).toEqual(farequoteJobCaps); - done(); }); - it('can get rollup job caps for non rollup index pattern', async (done) => { + it('can get rollup job caps for non rollup index pattern', async () => { const indexPattern = 'farequote-*'; const isRollup = true; const { newJobCaps } = newJobCapsProvider(mlClusterClientNonRollupMock); const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock); expect(response).toEqual(farequoteJobCapsEmpty); - done(); }); }); describe('cloudwatch newJobCaps()', () => { - it('can get rollup job caps for rollup index pattern', async (done) => { + it('can get rollup job caps for rollup index pattern', async () => { const indexPattern = 'cloud_roll_index'; const isRollup = true; const { newJobCaps } = newJobCapsProvider(mlClusterClientRollupMock); const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock); expect(response).toEqual(cloudwatchJobCaps); - done(); }); - it('can get non rollup job caps for rollup index pattern', async (done) => { + it('can get non rollup job caps for rollup index pattern', async () => { const indexPattern = 'cloud_roll_index'; const isRollup = false; const { newJobCaps } = newJobCapsProvider(mlClusterClientRollupMock); const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock); expect(response).not.toEqual(cloudwatchJobCaps); - done(); }); }); }); diff --git a/x-pack/plugins/monitoring/public/lib/ensure_minimum_time.test.js b/x-pack/plugins/monitoring/public/lib/ensure_minimum_time.test.js index f37dbe3283d29c..eda9c17db4e399 100644 --- a/x-pack/plugins/monitoring/public/lib/ensure_minimum_time.test.js +++ b/x-pack/plugins/monitoring/public/lib/ensure_minimum_time.test.js @@ -7,28 +7,25 @@ import { ensureMinimumTime } from './ensure_minimum_time'; describe('ensureMinimumTime', () => { - it('resolves single promise', async (done) => { + it('resolves single promise', async () => { const promiseA = new Promise((resolve) => resolve('a')); const a = await ensureMinimumTime(promiseA, 0); expect(a).toBe('a'); - done(); }); - it('resolves multiple promises', async (done) => { + it('resolves multiple promises', async () => { const promiseA = new Promise((resolve) => resolve('a')); const promiseB = new Promise((resolve) => resolve('b')); const [a, b] = await ensureMinimumTime([promiseA, promiseB], 0); expect(a).toBe('a'); expect(b).toBe('b'); - done(); }); - it('resolves in the amount of time provided, at minimum', async (done) => { + it('resolves in the amount of time provided, at minimum', async () => { const startTime = new Date().getTime(); const promise = new Promise((resolve) => resolve()); await ensureMinimumTime(promise, 100); const endTime = new Date().getTime(); expect(endTime - startTime).toBeGreaterThanOrEqual(100); - done(); }); }); 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 9aa5a6bdc04967..424da9d27ff0cb 100644 --- a/x-pack/plugins/monitoring/public/lib/setup_mode.test.js +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.test.js @@ -23,7 +23,13 @@ jest.mock('react-dom', () => ({ jest.mock('../legacy_shims', () => { return { Legacy: { - shims: { getAngularInjector: () => ({ get: () => ({ get: () => 'utc' }) }) }, + shims: { + getAngularInjector: () => ({ get: () => ({ get: () => 'utc' }) }), + toastNotifications: { + addDanger: jest.fn(), + }, + I18nContext: '<div>', + }, }, }; }); @@ -59,9 +65,6 @@ const angularStateMock = { // 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 waitForSetupModeData(action) { - process.nextTick(action); -} function setModulesAndMocks() { jest.clearAllMocks().resetModules(); @@ -75,6 +78,10 @@ function setModulesAndMocks() { setSetupModeMenuItem = setupMode.setSetupModeMenuItem; } +function waitForSetupModeData() { + return new Promise((resolve) => process.nextTick(resolve)); +} + describe('setup_mode', () => { beforeEach(async () => { setModulesAndMocks(); @@ -96,22 +103,22 @@ describe('setup_mode', () => { }); it('should enable toggle mode', async () => { - initSetupModeState(angularStateMock.scope, angularStateMock.injector); - await toggleSetupMode(true); + await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + toggleSetupMode(true); expect(injectorModulesMock.globalState.inSetupMode).toBe(true); }); it('should disable toggle mode', async () => { - initSetupModeState(angularStateMock.scope, angularStateMock.injector); - await toggleSetupMode(false); + await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + toggleSetupMode(false); expect(injectorModulesMock.globalState.inSetupMode).toBe(false); }); it('should set top nav config', async () => { const render = require('react-dom').render; - initSetupModeState(angularStateMock.scope, angularStateMock.injector); + await initSetupModeState(angularStateMock.scope, angularStateMock.injector); setSetupModeMenuItem(); - await toggleSetupMode(true); + toggleSetupMode(true); expect(render.mock.calls.length).toBe(2); }); }); @@ -121,7 +128,7 @@ describe('setup_mode', () => { data = {}; }); - it('should not fetch data if the user does not have sufficient permissions', async (done) => { + it('should not fetch data if the user does not have sufficient permissions', async () => { const addDanger = jest.fn(); jest.doMock('../legacy_shims', () => ({ Legacy: { @@ -129,6 +136,7 @@ describe('setup_mode', () => { toastNotifications: { addDanger, }, + I18nContext: '<div>', }, }, })); @@ -138,20 +146,19 @@ describe('setup_mode', () => { }, }; setModulesAndMocks(); - initSetupModeState(angularStateMock.scope, angularStateMock.injector); - await toggleSetupMode(true); - waitForSetupModeData(() => { - const state = getSetupModeState(); - expect(state.enabled).toBe(false); - expect(addDanger).toHaveBeenCalledWith({ - title: 'Setup mode is not available', - text: 'You do not have the necessary permissions to do this.', - }); - done(); + await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + toggleSetupMode(true); + await waitForSetupModeData(); + + const state = getSetupModeState(); + expect(state.enabled).toBe(false); + expect(addDanger).toHaveBeenCalledWith({ + title: 'Setup mode is not available', + text: 'You do not have the necessary permissions to do this.', }); }); - it('should set the newly discovered cluster uuid', async (done) => { + it('should set the newly discovered cluster uuid', async () => { const clusterUuid = '1ajy'; data = { _meta: { @@ -166,15 +173,14 @@ describe('setup_mode', () => { }, }, }; - initSetupModeState(angularStateMock.scope, angularStateMock.injector); - await toggleSetupMode(true); - waitForSetupModeData(() => { - expect(injectorModulesMock.globalState.cluster_uuid).toBe(clusterUuid); - done(); - }); + await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + toggleSetupMode(true); + await waitForSetupModeData(); + + expect(injectorModulesMock.globalState.cluster_uuid).toBe(clusterUuid); }); - it('should fetch data for a given cluster', async (done) => { + it('should fetch data for a given cluster', async () => { const clusterUuid = '1ajy'; data = { _meta: { @@ -190,22 +196,23 @@ describe('setup_mode', () => { }, }; - initSetupModeState(angularStateMock.scope, angularStateMock.injector); - await toggleSetupMode(true); - waitForSetupModeData(() => { - expect(injectorModulesMock.$http.post).toHaveBeenCalledWith( - `../api/monitoring/v1/setup/collection/cluster/${clusterUuid}`, - { - ccs: undefined, - } - ); - done(); - }); + await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + toggleSetupMode(true); + await waitForSetupModeData(); + + expect(injectorModulesMock.$http.post).toHaveBeenCalledWith( + `../api/monitoring/v1/setup/collection/cluster/${clusterUuid}`, + { + ccs: undefined, + } + ); }); it('should fetch data for a single node', async () => { - initSetupModeState(angularStateMock.scope, angularStateMock.injector); - await toggleSetupMode(true); + await initSetupModeState(angularStateMock.scope, angularStateMock.injector); + toggleSetupMode(true); + await waitForSetupModeData(); + injectorModulesMock.$http.post.mockClear(); await updateSetupModeData('45asd'); expect(injectorModulesMock.$http.post).toHaveBeenCalledWith( diff --git a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx index 3e555c843a0bb2..6b956ce71c0091 100644 --- a/x-pack/plugins/monitoring/public/lib/setup_mode.tsx +++ b/x-pack/plugins/monitoring/public/lib/setup_mode.tsx @@ -206,7 +206,7 @@ export const initSetupModeState = async ($scope: any, $injector: any, callback?: const globalState = $injector.get('globalState'); if (globalState.inSetupMode) { - await toggleSetupMode(true); + toggleSetupMode(true); } }; diff --git a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts b/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts index 932ebfdd22bbca..cee8a88000e298 100644 --- a/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts +++ b/x-pack/plugins/reporting/server/routes/lib/authorized_user_pre_routing.test.ts @@ -130,7 +130,7 @@ describe('authorized_user_pre_routing', function () { ).toMatchObject({ body: `Sorry, you don't have access to Reporting` }); }); - it('should return from handler when security is enabled and user has explicitly allowed role', async function (done) { + it('should return from handler when security is enabled and user has explicitly allowed role', function (done) { mockCore.getPluginSetupDeps = () => (({ // @ts-ignore diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js index f9f3cda6f2c209..3ac2ee5f5aadbb 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/detail_panel/detail_panel.test.js @@ -18,7 +18,7 @@ import { } from '../../components'; jest.mock('../../../../kibana_services', () => { - const services = require.requireActual('../../../../kibana_services'); + const services = jest.requireActual('../../../../kibana_services'); return { ...services, trackUiMetric: jest.fn(), diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js index e0fb73ef9c3bb4..ea70bdb3fbc414 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_list.test.js @@ -14,7 +14,7 @@ import { coreMock } from '../../../../../../../src/core/public/mocks'; const startMock = coreMock.createStart(); jest.mock('../../services', () => { - const services = require.requireActual('../../services'); + const services = jest.requireActual('../../services'); return { ...services, getRouterLinkProps: (link) => ({ href: link }), diff --git a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js index 481c419403754f..462742bee978f3 100644 --- a/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js +++ b/x-pack/plugins/rollup/public/crud_app/sections/job_list/job_table/job_table.test.js @@ -12,7 +12,7 @@ import { rollupJobsStore } from '../../../store'; import { JobTable } from './job_table'; jest.mock('../../../../kibana_services', () => { - const services = require.requireActual('../../../../kibana_services'); + const services = jest.requireActual('../../../../kibana_services'); return { ...services, trackUiMetric: jest.fn(), diff --git a/x-pack/plugins/rollup/public/test/client_integration/job_create_review.test.js b/x-pack/plugins/rollup/public/test/client_integration/job_create_review.test.js index 87fdabae182402..d50a718e5c5296 100644 --- a/x-pack/plugins/rollup/public/test/client_integration/job_create_review.test.js +++ b/x-pack/plugins/rollup/public/test/client_integration/job_create_review.test.js @@ -16,7 +16,7 @@ jest.mock('lodash', () => ({ })); jest.mock('../../kibana_services', () => { - const services = require.requireActual('../../kibana_services'); + const services = jest.requireActual('../../kibana_services'); return { ...services, getUiStatsReporter: jest.fn(() => () => {}), diff --git a/x-pack/plugins/rollup/public/test/client_integration/job_list.test.js b/x-pack/plugins/rollup/public/test/client_integration/job_list.test.js index f05e9fd1decd64..4091f79052b23a 100644 --- a/x-pack/plugins/rollup/public/test/client_integration/job_list.test.js +++ b/x-pack/plugins/rollup/public/test/client_integration/job_list.test.js @@ -10,7 +10,7 @@ import { JOBS } from './helpers/constants'; import { coreMock } from '../../../../../../src/core/public/mocks'; jest.mock('../../crud_app/services', () => { - const services = require.requireActual('../../crud_app/services'); + const services = jest.requireActual('../../crud_app/services'); return { ...services, getRouterLinkProps: (link) => ({ href: link }), @@ -18,7 +18,7 @@ jest.mock('../../crud_app/services', () => { }); jest.mock('../../kibana_services', () => { - const services = require.requireActual('../../kibana_services'); + const services = jest.requireActual('../../kibana_services'); return { ...services, getUiStatsReporter: jest.fn(() => () => {}), diff --git a/x-pack/plugins/rollup/public/test/client_integration/job_list_clone.test.js b/x-pack/plugins/rollup/public/test/client_integration/job_list_clone.test.js index 76be39a2c0e09f..7513e88a01fc84 100644 --- a/x-pack/plugins/rollup/public/test/client_integration/job_list_clone.test.js +++ b/x-pack/plugins/rollup/public/test/client_integration/job_list_clone.test.js @@ -16,7 +16,7 @@ jest.mock('lodash', () => ({ })); jest.mock('../../kibana_services', () => { - const services = require.requireActual('../../kibana_services'); + const services = jest.requireActual('../../kibana_services'); return { ...services, getUiStatsReporter: jest.fn(() => () => {}), diff --git a/x-pack/plugins/security_solution/public/network/components/flow_controls/__snapshots__/flow_target_select.test.tsx.snap b/x-pack/plugins/security_solution/public/network/components/flow_controls/__snapshots__/flow_target_select.test.tsx.snap index 6e38b5eeff5f63..efc4d4be9e9578 100644 --- a/x-pack/plugins/security_solution/public/network/components/flow_controls/__snapshots__/flow_target_select.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/network/components/flow_controls/__snapshots__/flow_target_select.test.tsx.snap @@ -7,21 +7,7 @@ exports[`FlowTargetSelect Component rendering it renders the FlowTargetSelect 1` hasDividers={false} isInvalid={false} isLoading={false} - onChange={ - [MockFunction] { - "calls": Array [ - Array [ - "destination", - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - } - } + onChange={[MockFunction]} options={ Array [ Object { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx index b1424848728135..b6e921ae9c0017 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/new_template_timeline.test.tsx @@ -38,6 +38,10 @@ describe('NewTemplateTimeline', () => { const mockTitle = 'NEW_TIMELINE'; let wrapper: ReactWrapper; + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('render if CRUD', () => { beforeAll(() => { (useKibana as jest.Mock).mockReturnValue({ @@ -52,10 +56,6 @@ describe('NewTemplateTimeline', () => { }, }); - afterAll(() => { - (useKibana as jest.Mock).mockReset(); - }); - wrapper = mount( <ReduxStoreProvider store={store}> <NewTemplateTimeline outline={true} closeGearMenu={mockClosePopover} title={mockTitle} /> @@ -100,10 +100,6 @@ describe('NewTemplateTimeline', () => { ); }); - afterAll(() => { - (useKibana as jest.Mock).mockReset(); - }); - test('no render', () => { expect( wrapper.find('[data-test-subj="template-timeline-new-with-border"]').exists() diff --git a/x-pack/plugins/task_manager/server/lib/bulk_operation_buffer.test.ts b/x-pack/plugins/task_manager/server/lib/bulk_operation_buffer.test.ts index 25abd92b32a26f..c007b323384965 100644 --- a/x-pack/plugins/task_manager/server/lib/bulk_operation_buffer.test.ts +++ b/x-pack/plugins/task_manager/server/lib/bulk_operation_buffer.test.ts @@ -161,7 +161,7 @@ describe('Bulk Operation Buffer', () => { }); }); - test('handles both resolutions and rejections at individual task level', async (done) => { + test('handles both resolutions and rejections at individual task level', async () => { const bulkUpdate: jest.Mocked<BulkOperation<TaskInstance, Error>> = jest.fn( ([task1, task2, task3]) => { return Promise.resolve([ @@ -178,7 +178,7 @@ describe('Bulk Operation Buffer', () => { const task2 = createTask(); const task3 = createTask(); - return Promise.all([ + await Promise.all([ expect(bufferedUpdate(task1)).resolves.toMatchObject(incrementAttempts(task1)), expect(bufferedUpdate(task2)).rejects.toMatchObject( mapErr( @@ -187,13 +187,12 @@ describe('Bulk Operation Buffer', () => { ) ), expect(bufferedUpdate(task3)).resolves.toMatchObject(incrementAttempts(task3)), - ]).then(() => { - expect(bulkUpdate).toHaveBeenCalledTimes(1); - done(); - }); + ]); + + expect(bulkUpdate).toHaveBeenCalledTimes(1); }); - test('handles bulkUpdate failure', async (done) => { + test('handles bulkUpdate failure', async () => { const bulkUpdate: jest.Mocked<BulkOperation<TaskInstance, Error>> = jest.fn(() => { return Promise.reject(new Error('bulkUpdate is an illusion')); }); @@ -204,7 +203,7 @@ describe('Bulk Operation Buffer', () => { const task2 = createTask(); const task3 = createTask(); - return Promise.all([ + await Promise.all([ expect(bufferedUpdate(task1)).rejects.toMatchInlineSnapshot(` Object { "error": [Error: bulkUpdate is an illusion], @@ -223,13 +222,12 @@ describe('Bulk Operation Buffer', () => { "tag": "err", } `), - ]).then(() => { - expect(bulkUpdate).toHaveBeenCalledTimes(1); - done(); - }); + ]); + + expect(bulkUpdate).toHaveBeenCalledTimes(1); }); - test('logs unknown bulk operation results', async (done) => { + test('logs unknown bulk operation results', async () => { const bulkUpdate: jest.Mocked<BulkOperation<TaskInstance, Error>> = jest.fn( ([task1, task2, task3]) => { return Promise.resolve([ @@ -248,7 +246,7 @@ describe('Bulk Operation Buffer', () => { const task2 = createTask(); const task3 = createTask(); - return Promise.all([ + await Promise.all([ expect(bufferedUpdate(task1)).resolves.toMatchObject(incrementAttempts(task1)), expect(bufferedUpdate(task2)).rejects.toMatchObject( asErr(new Error(`Unhandled buffered operation for entity: ${task2.id}`)) @@ -256,10 +254,9 @@ describe('Bulk Operation Buffer', () => { expect(bufferedUpdate(task3)).rejects.toMatchObject( asErr(new Error(`Unhandled buffered operation for entity: ${task3.id}`)) ), - ]).then(() => { - expect(logger.warn).toHaveBeenCalledTimes(2); - done(); - }); + ]); + + expect(logger.warn).toHaveBeenCalledTimes(2); }); }); }); diff --git a/x-pack/plugins/task_manager/server/task_store.test.ts b/x-pack/plugins/task_manager/server/task_store.test.ts index 45c41b4d1d69d2..f5fafe83748d96 100644 --- a/x-pack/plugins/task_manager/server/task_store.test.ts +++ b/x-pack/plugins/task_manager/server/task_store.test.ts @@ -7,7 +7,7 @@ import _ from 'lodash'; import sinon from 'sinon'; import uuid from 'uuid'; -import { filter } from 'rxjs/operators'; +import { filter, take } from 'rxjs/operators'; import { Option, some, none } from 'fp-ts/lib/Option'; import { @@ -1242,7 +1242,7 @@ if (doc['task.runAt'].size()!=0) { return { taskManagerId, runAt, tasks }; } - test('emits an event when a task is succesfully claimed by id', async (done) => { + test('emits an event when a task is succesfully claimed by id', async () => { const { taskManagerId, runAt, tasks } = generateTasks(); const callCluster = sinon.spy(async (name: string, params?: unknown) => name === 'updateByQuery' @@ -1262,49 +1262,47 @@ if (doc['task.runAt'].size()!=0) { index: '', }); - const sub = store.events + const promise = store.events .pipe( filter( (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => event.id === 'claimed-by-id' - ) + ), + take(1) ) - .subscribe({ - next: (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => { - expect(event).toMatchObject( - asTaskClaimEvent( - 'claimed-by-id', - asOk({ - id: 'claimed-by-id', - runAt, - taskType: 'foo', - schedule: undefined, - attempts: 0, - status: 'claiming' as TaskStatus, - params: { hello: 'world' }, - state: { baby: 'Henhen' }, - user: 'jimbo', - scope: ['reporting'], - ownerId: taskManagerId, - startedAt: null, - retryAt: null, - scheduledAt: new Date(), - }) - ) - ); - sub.unsubscribe(); - done(); - }, - }); + .toPromise(); await store.claimAvailableTasks({ claimTasksById: ['claimed-by-id'], claimOwnershipUntil: new Date(), size: 10, }); + + const event = await promise; + expect(event).toMatchObject( + asTaskClaimEvent( + 'claimed-by-id', + asOk({ + id: 'claimed-by-id', + runAt, + taskType: 'foo', + schedule: undefined, + attempts: 0, + status: 'claiming' as TaskStatus, + params: { hello: 'world' }, + state: { baby: 'Henhen' }, + user: 'jimbo', + scope: ['reporting'], + ownerId: taskManagerId, + startedAt: null, + retryAt: null, + scheduledAt: new Date(), + }) + ) + ); }); - test('emits an event when a task is succesfully by scheduling', async (done) => { + test('emits an event when a task is succesfully by scheduling', async () => { const { taskManagerId, runAt, tasks } = generateTasks(); const callCluster = sinon.spy(async (name: string, params?: unknown) => name === 'updateByQuery' @@ -1324,49 +1322,47 @@ if (doc['task.runAt'].size()!=0) { index: '', }); - const sub = store.events + const promise = store.events .pipe( filter( (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => event.id === 'claimed-by-schedule' - ) + ), + take(1) ) - .subscribe({ - next: (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => { - expect(event).toMatchObject( - asTaskClaimEvent( - 'claimed-by-schedule', - asOk({ - id: 'claimed-by-schedule', - runAt, - taskType: 'bar', - schedule: { interval: '5m' }, - attempts: 2, - status: 'claiming' as TaskStatus, - params: { shazm: 1 }, - state: { henry: 'The 8th' }, - user: 'dabo', - scope: ['reporting', 'ceo'], - ownerId: taskManagerId, - startedAt: null, - retryAt: null, - scheduledAt: new Date(), - }) - ) - ); - sub.unsubscribe(); - done(); - }, - }); + .toPromise(); await store.claimAvailableTasks({ claimTasksById: ['claimed-by-id'], claimOwnershipUntil: new Date(), size: 10, }); + + const event = await promise; + expect(event).toMatchObject( + asTaskClaimEvent( + 'claimed-by-schedule', + asOk({ + id: 'claimed-by-schedule', + runAt, + taskType: 'bar', + schedule: { interval: '5m' }, + attempts: 2, + status: 'claiming' as TaskStatus, + params: { shazm: 1 }, + state: { henry: 'The 8th' }, + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + startedAt: null, + retryAt: null, + scheduledAt: new Date(), + }) + ) + ); }); - test('emits an event when the store fails to claim a required task by id', async (done) => { + test('emits an event when the store fails to claim a required task by id', async () => { const { taskManagerId, runAt, tasks } = generateTasks(); const callCluster = sinon.spy(async (name: string, params?: unknown) => name === 'updateByQuery' @@ -1386,51 +1382,49 @@ if (doc['task.runAt'].size()!=0) { index: '', }); - const sub = store.events + const promise = store.events .pipe( filter( (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => event.id === 'already-running' - ) + ), + take(1) ) - .subscribe({ - next: (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => { - expect(event).toMatchObject( - asTaskClaimEvent( - 'already-running', - asErr( - some({ - id: 'already-running', - runAt, - taskType: 'bar', - schedule: { interval: '5m' }, - attempts: 2, - status: 'running' as TaskStatus, - params: { shazm: 1 }, - state: { henry: 'The 8th' }, - user: 'dabo', - scope: ['reporting', 'ceo'], - ownerId: taskManagerId, - startedAt: null, - retryAt: null, - scheduledAt: new Date(), - }) - ) - ) - ); - sub.unsubscribe(); - done(); - }, - }); + .toPromise(); await store.claimAvailableTasks({ claimTasksById: ['already-running'], claimOwnershipUntil: new Date(), size: 10, }); + + const event = await promise; + expect(event).toMatchObject( + asTaskClaimEvent( + 'already-running', + asErr( + some({ + id: 'already-running', + runAt, + taskType: 'bar', + schedule: { interval: '5m' }, + attempts: 2, + status: 'running' as TaskStatus, + params: { shazm: 1 }, + state: { henry: 'The 8th' }, + user: 'dabo', + scope: ['reporting', 'ceo'], + ownerId: taskManagerId, + startedAt: null, + retryAt: null, + scheduledAt: new Date(), + }) + ) + ) + ); }); - test('emits an event when the store fails to find a task which was required by id', async (done) => { + test('emits an event when the store fails to find a task which was required by id', async () => { const { taskManagerId, tasks } = generateTasks(); const callCluster = sinon.spy(async (name: string, params?: unknown) => name === 'updateByQuery' @@ -1450,26 +1444,24 @@ if (doc['task.runAt'].size()!=0) { index: '', }); - const sub = store.events + const promise = store.events .pipe( filter( (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => event.id === 'unknown-task' - ) + ), + take(1) ) - .subscribe({ - next: (event: TaskEvent<ConcreteTaskInstance, Option<ConcreteTaskInstance>>) => { - expect(event).toMatchObject(asTaskClaimEvent('unknown-task', asErr(none))); - sub.unsubscribe(); - done(); - }, - }); + .toPromise(); await store.claimAvailableTasks({ claimTasksById: ['unknown-task'], claimOwnershipUntil: new Date(), size: 10, }); + + const event = await promise; + expect(event).toMatchObject(asTaskClaimEvent('unknown-task', asErr(none))); }); }); }); diff --git a/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx b/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx index 4d752ee65fbfda..8b514b3e06519e 100644 --- a/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx +++ b/x-pack/plugins/transform/public/app/hooks/use_index_data.test.tsx @@ -6,7 +6,7 @@ import React, { FC } from 'react'; -import { render, wait } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import { CoreSetup } from 'src/core/public'; @@ -33,7 +33,7 @@ const query: SimpleQuery = { }; describe('Transform: useIndexData()', () => { - test('indexPattern set triggers loading', async (done) => { + test('indexPattern set triggers loading', async () => { const mlShared = await getMlSharedImports(); const wrapper: FC = ({ children }) => ( <MlSharedContext.Provider value={mlShared}>{children}</MlSharedContext.Provider> @@ -58,13 +58,11 @@ describe('Transform: useIndexData()', () => { expect(IndexObj.errorMessage).toBe(''); expect(IndexObj.status).toBe(1); expect(IndexObj.tableItems).toEqual([]); - done(); }); }); describe('Transform: <DataGrid /> with useIndexData()', () => { - // Using the async/await wait()/done() pattern to avoid act() errors. - test('Minimal initialization', async (done) => { + test('Minimal initialization', async () => { // Arrange const indexPattern = { title: 'the-index-pattern-title', @@ -97,7 +95,5 @@ describe('Transform: <DataGrid /> with useIndexData()', () => { // Act // Assert expect(getByText('the-index-preview-title')).toBeInTheDocument(); - await wait(); - done(); }); }); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx index cf1bfda6128ef7..a03ce841a09add 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { render, wait } from '@testing-library/react'; +import { render } from '@testing-library/react'; import { I18nProvider } from '@kbn/i18n/react'; @@ -51,8 +51,7 @@ const createMockStorage = () => ({ }); describe('Transform: <DefinePivotForm />', () => { - // Using the async/await wait()/done() pattern to avoid act() errors. - test('Minimal initialization', async (done) => { + test('Minimal initialization', async () => { // Arrange const mlSharedImports = await getMlSharedImports(); @@ -85,8 +84,6 @@ describe('Transform: <DefinePivotForm />', () => { // Assert expect(getByText('Index pattern')).toBeInTheDocument(); expect(getByText(searchItems.indexPattern.title)).toBeInTheDocument(); - await wait(); - done(); }); }); diff --git a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx index 7aed0568e6efc3..b3cd8a2937952d 100644 --- a/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx +++ b/x-pack/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx @@ -27,7 +27,7 @@ import { getMlSharedImports } from '../../../../../shared_imports'; describe('Transform: <DefinePivotSummary />', () => { // Using the async/await wait()/done() pattern to avoid act() errors. - test('Minimal initialization', async (done) => { + test('Minimal initialization', async () => { // Arrange const mlSharedImports = await getMlSharedImports(); @@ -72,6 +72,5 @@ describe('Transform: <DefinePivotSummary />', () => { expect(getByText('Group by')).toBeInTheDocument(); expect(getByText('Aggregations')).toBeInTheDocument(); await wait(); - done(); }); }); diff --git a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts index 9af79c9ac52597..99971c42e55123 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_action.test.ts @@ -5,7 +5,7 @@ */ import { findTestSubject } from '@elastic/eui/lib/test'; -import { skip } from 'rxjs/operators'; +import { skip, take } from 'rxjs/operators'; import * as Rx from 'rxjs'; import { mount } from 'enzyme'; @@ -27,7 +27,7 @@ const createOpenModalMock = () => { return mock; }; -test('Custom time range action prevents embeddable from using container time', async (done) => { +test('Custom time range action prevents embeddable from using container time', async () => { const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -79,19 +79,19 @@ test('Custom time range action prevents embeddable from using container time', a findTestSubject(wrapper, 'addPerPanelTimeRangeButton').simulate('click'); - const subscription = Rx.merge(container.getOutput$(), container.getInput$()) - .pipe(skip(2)) - .subscribe(() => { - expect(child1.getInput().timeRange).toEqual({ from: 'now-30days', to: 'now-29days' }); - expect(child2.getInput().timeRange).toEqual({ from: 'now-30m', to: 'now-1m' }); - subscription.unsubscribe(); - done(); - }); + const promise = Rx.merge(container.getOutput$(), container.getOutput$(), container.getInput$()) + .pipe(skip(2), take(1)) + .toPromise(); container.updateInput({ timeRange: { from: 'now-30m', to: 'now-1m' } }); + + await promise; + + expect(child1.getInput().timeRange).toEqual({ from: 'now-30days', to: 'now-29days' }); + expect(child2.getInput().timeRange).toEqual({ from: 'now-30m', to: 'now-1m' }); }); -test('Removing custom time range action resets embeddable back to container time', async (done) => { +test('Removing custom time range action resets embeddable back to container time', async () => { const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -153,19 +153,19 @@ test('Removing custom time range action resets embeddable back to container time const wrapper2 = mount(openModal2); findTestSubject(wrapper2, 'removePerPanelTimeRangeButton').simulate('click'); - const subscription = Rx.merge(container.getOutput$(), container.getInput$()) - .pipe(skip(2)) - .subscribe(() => { - expect(child1.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); - expect(child2.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); - subscription.unsubscribe(); - done(); - }); + const promise = Rx.merge(container.getOutput$(), container.getOutput$(), container.getInput$()) + .pipe(skip(2), take(1)) + .toPromise(); container.updateInput({ timeRange: { from: 'now-10m', to: 'now-5m' } }); + + await promise; + + expect(child1.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); + expect(child2.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); }); -test('Cancelling custom time range action leaves state alone', async (done) => { +test('Cancelling custom time range action leaves state alone', async () => { const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -212,16 +212,16 @@ test('Cancelling custom time range action leaves state alone', async (done) => { findTestSubject(wrapper, 'cancelPerPanelTimeRangeButton').simulate('click'); - const subscription = Rx.merge(container.getOutput$(), container.getInput$()) - .pipe(skip(2)) - .subscribe(() => { - expect(child1.getInput().timeRange).toEqual({ from: '1', to: '2' }); - expect(child2.getInput().timeRange).toEqual({ from: 'now-30m', to: 'now-1m' }); - subscription.unsubscribe(); - done(); - }); + const promise = Rx.merge(container.getOutput$(), container.getOutput$(), container.getInput$()) + .pipe(skip(2), take(1)) + .toPromise(); container.updateInput({ timeRange: { from: 'now-30m', to: 'now-1m' } }); + + await promise; + + expect(child1.getInput().timeRange).toEqual({ from: '1', to: '2' }); + expect(child2.getInput().timeRange).toEqual({ from: 'now-30m', to: 'now-1m' }); }); test(`badge is compatible with embeddable that inherits from parent`, async () => { diff --git a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_badge.test.ts b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_badge.test.ts index 03fb80c3ce75be..a8390791379052 100644 --- a/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_badge.test.ts +++ b/x-pack/plugins/ui_actions_enhanced/public/custom_time_range_badge.test.ts @@ -5,7 +5,7 @@ */ import { findTestSubject } from '@elastic/eui/lib/test'; -import { skip } from 'rxjs/operators'; +import { skip, take } from 'rxjs/operators'; import * as Rx from 'rxjs'; import { mount } from 'enzyme'; import { TimeRangeEmbeddable, TimeRangeContainer, TIME_RANGE_EMBEDDABLE } from './test_helpers'; @@ -13,7 +13,7 @@ import { CustomTimeRangeBadge } from './custom_time_range_badge'; import { ReactElement } from 'react'; import { nextTick } from 'test_utils/enzyme_helpers'; -test('Removing custom time range from badge resets embeddable back to container time', async (done) => { +test('Removing custom time range from badge resets embeddable back to container time', async () => { const container = new TimeRangeContainer( { timeRange: { from: 'now-15m', to: 'now' }, @@ -60,16 +60,16 @@ test('Removing custom time range from badge resets embeddable back to container const wrapper = mount(openModal); findTestSubject(wrapper, 'removePerPanelTimeRangeButton').simulate('click'); - const subscription = Rx.merge(child1.getInput$(), container.getOutput$(), container.getInput$()) - .pipe(skip(4)) - .subscribe(() => { - expect(child1.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); - expect(child2.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); - subscription.unsubscribe(); - done(); - }); + const promise = Rx.merge(child1.getInput$(), container.getOutput$(), container.getInput$()) + .pipe(skip(4), take(1)) + .toPromise(); container.updateInput({ timeRange: { from: 'now-10m', to: 'now-5m' } }); + + await promise; + + expect(child1.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); + expect(child2.getInput().timeRange).toEqual({ from: 'now-10m', to: 'now-5m' }); }); test(`badge is not compatible with embeddable that inherits from parent`, async () => { diff --git a/yarn.lock b/yarn.lock index 876f938cfce88a..77e3a399c43137 100644 --- a/yarn.lock +++ b/yarn.lock @@ -514,6 +514,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.10.4" +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" @@ -1650,59 +1657,61 @@ chalk "^2.0.1" slash "^2.0.0" -"@jest/console@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.5.0.tgz#770800799d510f37329c508a9edd0b7b447d9abb" - integrity sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw== +"@jest/console@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.3.0.tgz#ed04063efb280c88ba87388b6f16427c0a85c856" + integrity sha512-/5Pn6sJev0nPUcAdpJHMVIsA8sKizL2ZkcKPE5+dJrCccks7tcM7c9wbgHudBJbxXLoTbqsHkG1Dofoem4F09w== dependencies: - "@jest/types" "^25.5.0" - chalk "^3.0.0" - jest-message-util "^25.5.0" - jest-util "^25.5.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^26.3.0" + jest-util "^26.3.0" slash "^3.0.0" -"@jest/core@^25.5.4": - version "25.5.4" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.5.4.tgz#3ef7412f7339210f003cdf36646bbca786efe7b4" - integrity sha512-3uSo7laYxF00Dg/DMgbn4xMJKmDdWvZnf89n8Xj/5/AeQ2dOQmn6b6Hkj/MleyzZWXpwv+WSdYWl4cLsy2JsoA== +"@jest/core@^26.4.2": + version "26.4.2" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.4.2.tgz#85d0894f31ac29b5bab07aa86806d03dd3d33edc" + integrity sha512-sDva7YkeNprxJfepOctzS8cAk9TOekldh+5FhVuXS40+94SHbiicRO1VV2tSoRtgIo+POs/Cdyf8p76vPTd6dg== dependencies: - "@jest/console" "^25.5.0" - "@jest/reporters" "^25.5.1" - "@jest/test-result" "^25.5.0" - "@jest/transform" "^25.5.1" - "@jest/types" "^25.5.0" + "@jest/console" "^26.3.0" + "@jest/reporters" "^26.4.1" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" - jest-changed-files "^25.5.0" - jest-config "^25.5.4" - jest-haste-map "^25.5.1" - jest-message-util "^25.5.0" - jest-regex-util "^25.2.6" - jest-resolve "^25.5.1" - jest-resolve-dependencies "^25.5.4" - jest-runner "^25.5.4" - jest-runtime "^25.5.4" - jest-snapshot "^25.5.1" - jest-util "^25.5.0" - jest-validate "^25.5.0" - jest-watcher "^25.5.0" + jest-changed-files "^26.3.0" + jest-config "^26.4.2" + jest-haste-map "^26.3.0" + jest-message-util "^26.3.0" + jest-regex-util "^26.0.0" + jest-resolve "^26.4.0" + jest-resolve-dependencies "^26.4.2" + jest-runner "^26.4.2" + jest-runtime "^26.4.2" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" + jest-watcher "^26.3.0" micromatch "^4.0.2" p-each-series "^2.1.0" - realpath-native "^2.0.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.5.0.tgz#aa33b0c21a716c65686638e7ef816c0e3a0c7b37" - integrity sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA== +"@jest/environment@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.3.0.tgz#e6953ab711ae3e44754a025f838bde1a7fd236a0" + integrity sha512-EW+MFEo0DGHahf83RAaiqQx688qpXgl99wdb8Fy67ybyzHwR1a58LHcO376xQJHfmoXTu89M09dH3J509cx2AA== dependencies: - "@jest/fake-timers" "^25.5.0" - "@jest/types" "^25.5.0" - jest-mock "^25.5.0" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" "@jest/fake-timers@^24.9.0": version "24.9.0" @@ -1713,57 +1722,58 @@ jest-message-util "^24.9.0" jest-mock "^24.9.0" -"@jest/fake-timers@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.5.0.tgz#46352e00533c024c90c2bc2ad9f2959f7f114185" - integrity sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ== +"@jest/fake-timers@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.3.0.tgz#f515d4667a6770f60ae06ae050f4e001126c666a" + integrity sha512-ZL9ytUiRwVP8ujfRepffokBvD2KbxbqMhrXSBhSdAhISCw3gOkuntisiSFv+A6HN0n0fF4cxzICEKZENLmW+1A== dependencies: - "@jest/types" "^25.5.0" - jest-message-util "^25.5.0" - jest-mock "^25.5.0" - jest-util "^25.5.0" - lolex "^5.0.0" + "@jest/types" "^26.3.0" + "@sinonjs/fake-timers" "^6.0.1" + "@types/node" "*" + jest-message-util "^26.3.0" + jest-mock "^26.3.0" + jest-util "^26.3.0" -"@jest/globals@^25.5.2": - version "25.5.2" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-25.5.2.tgz#5e45e9de8d228716af3257eeb3991cc2e162ca88" - integrity sha512-AgAS/Ny7Q2RCIj5kZ+0MuKM1wbF0WMLxbCVl/GOMoCNbODRdJ541IxJ98xnZdVSZXivKpJlNPIWa3QmY0l4CXA== +"@jest/globals@^26.4.2": + version "26.4.2" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.4.2.tgz#73c2a862ac691d998889a241beb3dc9cada40d4a" + integrity sha512-Ot5ouAlehhHLRhc+sDz2/9bmNv9p5ZWZ9LE1pXGGTCXBasmi5jnYjlgYcYt03FBwLmZXCZ7GrL29c33/XRQiow== dependencies: - "@jest/environment" "^25.5.0" - "@jest/types" "^25.5.0" - expect "^25.5.0" + "@jest/environment" "^26.3.0" + "@jest/types" "^26.3.0" + expect "^26.4.2" -"@jest/reporters@^25.5.1": - version "25.5.1" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.5.1.tgz#cb686bcc680f664c2dbaf7ed873e93aa6811538b" - integrity sha512-3jbd8pPDTuhYJ7vqiHXbSwTJQNavczPs+f1kRprRDxETeE3u6srJ+f0NPuwvOmk+lmunZzPkYWIFZDLHQPkviw== +"@jest/reporters@^26.4.1": + version "26.4.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.4.1.tgz#3b4d6faf28650f3965f8b97bc3d114077fb71795" + integrity sha512-aROTkCLU8++yiRGVxLsuDmZsQEKO6LprlrxtAuzvtpbIFl3eIjgIf3EUxDKgomkS25R9ZzwGEdB5weCcBZlrpQ== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^25.5.0" - "@jest/test-result" "^25.5.0" - "@jest/transform" "^25.5.1" - "@jest/types" "^25.5.0" - chalk "^3.0.0" + "@jest/console" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" + chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" graceful-fs "^4.2.4" istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^4.0.0" + istanbul-lib-instrument "^4.0.3" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^25.5.1" - jest-resolve "^25.5.1" - jest-util "^25.5.0" - jest-worker "^25.5.0" + jest-haste-map "^26.3.0" + jest-resolve "^26.4.0" + jest-util "^26.3.0" + jest-worker "^26.3.0" slash "^3.0.0" source-map "^0.6.0" - string-length "^3.1.0" + string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^4.1.3" + v8-to-istanbul "^5.0.1" optionalDependencies: - node-notifier "^6.0.0" + node-notifier "^8.0.0" "@jest/source-map@^24.9.0": version "24.9.0" @@ -1774,10 +1784,10 @@ graceful-fs "^4.1.15" source-map "^0.6.0" -"@jest/source-map@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.5.0.tgz#df5c20d6050aa292c2c6d3f0d2c7606af315bd1b" - integrity sha512-eIGx0xN12yVpMcPaVpjXPnn3N30QGJCJQSkEDUt9x1fI1Gdvb07Ml6K5iN2hG7NmMP6FDmtPEssE3z6doOYUwQ== +"@jest/source-map@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.3.0.tgz#0e646e519883c14c551f7b5ae4ff5f1bfe4fc3d9" + integrity sha512-hWX5IHmMDWe1kyrKl7IhFwqOuAreIwHhbe44+XH2ZRHjrKIh0LO5eLQ/vxHFeAfRwJapmxuqlGAEYLadDq6ZGQ== dependencies: callsites "^3.0.0" graceful-fs "^4.2.4" @@ -1792,50 +1802,28 @@ "@jest/types" "^24.9.0" "@types/istanbul-lib-coverage" "^2.0.0" -"@jest/test-result@^25.5.0": - version "25.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.5.0.tgz#139a043230cdeffe9ba2d8341b27f2efc77ce87c" - integrity sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A== +"@jest/test-result@^26.3.0": + version "26.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.3.0.tgz#46cde01fa10c0aaeb7431bf71e4a20d885bc7fdb" + integrity sha512-a8rbLqzW/q7HWheFVMtghXV79Xk+GWwOK1FrtimpI5n1la2SY0qHri3/b0/1F0Ve0/yJmV8pEhxDfVwiUBGtgg== dependencies: - "@jest/console" "^25.5.0" - "@jest/types" "^25.5.0" + "@jest/console" "^26.3.0" + "@jest/types" "^26.3.0" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^25.5.4": - version "25.5.4" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.5.4.tgz#9b4e685b36954c38d0f052e596d28161bdc8b737" - integrity sha512-pTJGEkSeg1EkCO2YWq6hbFvKNXk8ejqlxiOg1jBNLnWrgXOkdY6UmqZpwGFXNnRt9B8nO1uWMzLLZ4eCmhkPNA== - dependencies: - "@jest/test-result" "^25.5.0" - graceful-fs "^4.2.4" - jest-haste-map "^25.5.1" - jest-runner "^25.5.4" - jest-runtime "^25.5.4" - -"@jest/transform@^25.5.1": - version "25.5.1" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.5.1.tgz#0469ddc17699dd2bf985db55fa0fb9309f5c2db3" - integrity sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg== +"@jest/test-sequencer@^26.4.2": + version "26.4.2" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.4.2.tgz#58a3760a61eec758a2ce6080201424580d97cbba" + integrity sha512-83DRD8N3M0tOhz9h0bn6Kl6dSp+US6DazuVF8J9m21WAp5x7CqSMaNycMP0aemC/SH/pDQQddbsfHRTBXVUgog== dependencies: - "@babel/core" "^7.1.0" - "@jest/types" "^25.5.0" - babel-plugin-istanbul "^6.0.0" - chalk "^3.0.0" - convert-source-map "^1.4.0" - fast-json-stable-stringify "^2.0.0" + "@jest/test-result" "^26.3.0" graceful-fs "^4.2.4" - jest-haste-map "^25.5.1" - jest-regex-util "^25.2.6" - jest-util "^25.5.0" - micromatch "^4.0.2" - pirates "^4.0.1" - realpath-native "^2.0.0" - slash "^3.0.0" - source-map "^0.6.1" - write-file-atomic "^3.0.0" + jest-haste-map "^26.3.0" + jest-runner "^26.4.2" + jest-runtime "^26.4.2" -"@jest/transform@^26.0.0": +"@jest/transform@^26.0.0", "@jest/transform@^26.3.0": version "26.3.0" resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.3.0.tgz#c393e0e01459da8a8bfc6d2a7c2ece1a13e8ba55" integrity sha512-Isj6NB68QorGoFWvcOjlUhpkT56PqNIsXKR7XfvoDlCANn/IANlh8DrKAA2l2JKC3yWSMH5wS0GwuQM20w3b2A== @@ -2841,6 +2829,13 @@ dependencies: type-detect "4.0.8" +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/formatio@^3.2.1": version "3.2.2" resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.2.2.tgz#771c60dfa75ea7f2d68e3b94c7e888a78781372c" @@ -3753,6 +3748,17 @@ resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.4.tgz#bfd5b0d0d1ba13e351dff65b6e52783b816826c8" integrity sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw== +"@types/babel__core@^7.0.0": + version "7.1.10" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" + integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + "@types/babel__core@^7.1.2": version "7.1.2" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" @@ -4374,7 +4380,7 @@ dependencies: "@types/jest" "*" -"@types/jest@*", "@types/jest@^25.1.1", "@types/jest@^25.2.3": +"@types/jest@*", "@types/jest@^25.1.1": version "25.2.3" resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf" integrity sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw== @@ -4382,6 +4388,14 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" +"@types/jest@^26.0.14": + version "26.0.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.14.tgz#078695f8f65cb55c5a98450d65083b2b73e5a3f3" + integrity sha512-Hz5q8Vu0D288x3iWXePSn53W7hAjP0H7EQ6QvDO9c7t46mR0lNOLlfuwQ+JkVxuhygHzlzPX+0jKdA3ZgSh+Vg== + dependencies: + jest-diff "^25.2.1" + pretty-format "^25.2.1" + "@types/joi@*", "@types/joi@^13.4.2": version "13.6.1" resolved "https://registry.yarnpkg.com/@types/joi/-/joi-13.6.1.tgz#325486a397504f8e22c8c551dc8b0e1d41d5d5ae" @@ -4768,11 +4782,6 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.16.1.tgz#328d1c9b54402e44119398bcb6a31b7bbd606d59" integrity sha512-db6pZL5QY3JrlCHBhYQzYDci0xnoDuxfseUuguLRr3JNk+bnCfpkK6p8quiUDyO8A0vbpBKkk59Fw125etrNeA== -"@types/prettier@^1.19.0": - version "1.19.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" - integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== - "@types/prettier@^2.0.0", "@types/prettier@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.2.tgz#5bb52ee68d0f8efa9cc0099920e56be6cc4e37f3" @@ -5166,13 +5175,20 @@ resolved "https://registry.yarnpkg.com/@types/tempy/-/tempy-0.2.0.tgz#8b7a93f6912aef25cc0b8d8a80ff974151478685" integrity sha512-YaX74QljqR45Xu7dd22wMvzTS+ItUiSyDl9XJl6WTgYNE09r2TF+mV2FDjWRM5Sdzf9C9dXRTUdz9J5SoEYxXg== -"@types/testing-library__jest-dom@^5.9.1", "@types/testing-library__jest-dom@^5.9.2": +"@types/testing-library__jest-dom@^5.9.1": version "5.9.2" resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.2.tgz#59e4771a1cf87d51e89a5cc8195cd3b647cba322" integrity sha512-K7nUSpH/5i8i0NagTJ+uFUDRueDlnMNhJtMjMwTGPPSqyImbWC/hgKPDCKt6Phu2iMJg2kWqlax+Ucj2DKMwpA== dependencies: "@types/jest" "*" +"@types/testing-library__jest-dom@^5.9.3": + version "5.9.3" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.3.tgz#574039e210140a536c6ec891063289fb742a75eb" + integrity sha512-5YxiCFA2vk0cxq2LIxYgHBpFlnJvMH9bkUIVNin+1GXT+LZgVOgXBeEyyo2ZrGXMO/KWe1ZV3p7Kb6LJAvJasw== + dependencies: + "@types/jest" "*" + "@types/testing-library__react-hooks@^3.3.0", "@types/testing-library__react-hooks@^3.4.0": version "3.4.0" resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.0.tgz#be148b7fa7d19cd3349c4ef9d9534486bc582fcc" @@ -5409,14 +5425,17 @@ eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/experimental-utils@^2.5.0": - version "2.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.15.0.tgz#41e35313bfaef91650ddb5380846d1c78a780070" - integrity sha512-Qkxu5zndY5hqlcQkmA88gfLvqQulMpX/TN91XC7OuXsRf4XG5xLGie0sbpX97o/oeccjeZYRMipIsjKk/tjDHA== +"@typescript-eslint/experimental-utils@^4.0.1": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.3.0.tgz#3f3c6c508e01b8050d51b016e7f7da0e3aefcb87" + integrity sha512-cmmIK8shn3mxmhpKfzMMywqiEheyfXLV/+yPDnOTvQX/ztngx7Lg/OD26J8gTZfkLKUmaEBxO2jYP3keV7h2OQ== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.15.0" + "@typescript-eslint/scope-manager" "4.3.0" + "@typescript-eslint/types" "4.3.0" + "@typescript-eslint/typescript-estree" "4.3.0" eslint-scope "^5.0.0" + eslint-utils "^2.0.0" "@typescript-eslint/parser@^3.10.0": version "3.10.0" @@ -5429,23 +5448,23 @@ "@typescript-eslint/typescript-estree" "3.10.0" eslint-visitor-keys "^1.1.0" +"@typescript-eslint/scope-manager@4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.3.0.tgz#c743227e087545968080d2362cfb1273842cb6a7" + integrity sha512-cTeyP5SCNE8QBRfc+Lgh4Xpzje46kNUhXYfc3pQWmJif92sjrFuHT9hH4rtOkDTo/si9Klw53yIr+djqGZS1ig== + dependencies: + "@typescript-eslint/types" "4.3.0" + "@typescript-eslint/visitor-keys" "4.3.0" + "@typescript-eslint/types@3.10.0": version "3.10.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.0.tgz#b81906674eca94a884345ba0bc1aaf6cd4da912a" integrity sha512-ktUWSa75heQNwH85GRM7qP/UUrXqx9d6yIdw0iLO9/uE1LILW+i+3+B64dUodUS2WFWLzKTlwfi9giqrODibWg== -"@typescript-eslint/typescript-estree@2.15.0": - version "2.15.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.15.0.tgz#79ae52eed8701b164d91e968a65d85a9105e76d3" - integrity sha512-L6Pog+w3VZzXkAdyqA0VlwybF8WcwZX+mufso86CMxSdWmcizJ38lgBdpqTbc9bo92iyi0rOvmATKiwl+amjxg== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash.unescape "4.0.1" - semver "^6.3.0" - tsutils "^3.17.1" +"@typescript-eslint/types@4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.3.0.tgz#1f0b2d5e140543e2614f06d48fb3ae95193c6ddf" + integrity sha512-Cx9TpRvlRjOppGsU6Y6KcJnUDOelja2NNCX6AZwtVHRzaJkdytJWMuYiqi8mS35MRNA3cJSwDzXePfmhU6TANw== "@typescript-eslint/typescript-estree@3.10.0": version "3.10.0" @@ -5461,6 +5480,20 @@ semver "^7.3.2" tsutils "^3.17.1" +"@typescript-eslint/typescript-estree@4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.3.0.tgz#0edc1068e6b2e4c7fdc54d61e329fce76241cee8" + integrity sha512-ZAI7xjkl+oFdLV/COEz2tAbQbR3XfgqHEGy0rlUXzfGQic6EBCR4s2+WS3cmTPG69aaZckEucBoTxW9PhzHxxw== + dependencies: + "@typescript-eslint/types" "4.3.0" + "@typescript-eslint/visitor-keys" "4.3.0" + debug "^4.1.1" + globby "^11.0.1" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@typescript-eslint/typescript-estree@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.9.0.tgz#5d6d49be936e96fb0f859673480f89b070a5dd9b" @@ -5476,6 +5509,14 @@ dependencies: eslint-visitor-keys "^1.1.0" +"@typescript-eslint/visitor-keys@4.3.0": + version "4.3.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.3.0.tgz#0e5ab0a09552903edeae205982e8521e17635ae0" + integrity sha512-xZxkuR7XLM6RhvLkgv9yYlTcBHnTULzfnw4i6+z2TGBLy9yljAypQaZl9c3zFvy7PNI7fYWyvKYtohyF8au3cw== + dependencies: + "@typescript-eslint/types" "4.3.0" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.8.5": version "1.8.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" @@ -5844,6 +5885,11 @@ abab@^2.0.0: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== +abab@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" + integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== + abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" @@ -5885,7 +5931,7 @@ accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^4.3.0, acorn-globals@^4.3.2: +acorn-globals@^4.3.0: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== @@ -5893,6 +5939,14 @@ acorn-globals@^4.3.0, acorn-globals@^4.3.2: acorn "^6.0.1" acorn-walk "^6.0.1" +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + acorn-jsx@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" @@ -5924,6 +5978,11 @@ acorn-walk@^7.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + acorn@5.X, acorn@^5.0.3, acorn@^5.5.0: version "5.7.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" @@ -5949,6 +6008,11 @@ acorn@^7.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== +acorn@^7.1.1: + version "7.4.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" + integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== + address@1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -7030,7 +7094,7 @@ async-foreach@^0.1.3: resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= -async-limiter@^1.0.0, async-limiter@~1.0.0: +async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== @@ -7270,17 +7334,17 @@ babel-helper-to-multiple-sequence-expressions@^0.5.0: resolved "https://registry.yarnpkg.com/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d" integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA== -babel-jest@^25.5.1: - version "25.5.1" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.5.1.tgz#bc2e6101f849d6f6aec09720ffc7bc5332e62853" - integrity sha512-9dA9+GmMjIzgPnYtkhBg73gOo/RHqPmLruP3BaGL4KEX3Dwz6pI8auSN8G8+iuEG90+GSswyKvslN+JYSaacaQ== +babel-jest@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.3.0.tgz#10d0ca4b529ca3e7d1417855ef7d7bd6fc0c3463" + integrity sha512-sxPnQGEyHAOPF8NcUsD0g7hDCnvLL2XyblRBcgrzTWBB/mAIpWow3n1bEL+VghnnZfreLhFSBsFluRoK2tRK4g== dependencies: - "@jest/transform" "^25.5.1" - "@jest/types" "^25.5.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" "@types/babel__core" "^7.1.7" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^25.5.0" - chalk "^3.0.0" + babel-preset-jest "^26.3.0" + chalk "^4.0.0" graceful-fs "^4.2.4" slash "^3.0.0" @@ -7378,13 +7442,14 @@ babel-plugin-istanbul@^6.0.0: istanbul-lib-instrument "^4.0.0" test-exclude "^6.0.0" -babel-plugin-jest-hoist@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.5.0.tgz#129c80ba5c7fc75baf3a45b93e2e372d57ca2677" - integrity sha512-u+/W+WAjMlvoocYGTwthAiQSxDcJAyHpQ6oWlHdFZaaN+Rlk8Q7iiwDPg2lN/FyJtAYnKjFxbn7xus4HCFkg5g== +babel-plugin-jest-hoist@^26.2.0: + version "26.2.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz#bdd0011df0d3d513e5e95f76bd53b51147aca2dd" + integrity sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA== dependencies: "@babel/template" "^7.3.3" "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" babel-plugin-macros@^2.0.0: @@ -7588,14 +7653,15 @@ babel-polyfill@^6.26.0: core-js "^2.5.0" regenerator-runtime "^0.10.5" -babel-preset-current-node-syntax@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" - integrity sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw== +babel-preset-current-node-syntax@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz#b4b547acddbf963cba555ba9f9cbbb70bfd044da" + integrity sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ== dependencies: "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-syntax-bigint" "^7.8.3" "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" "@babel/plugin-syntax-json-strings" "^7.8.3" "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" @@ -7604,13 +7670,13 @@ babel-preset-current-node-syntax@^0.1.2: "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -babel-preset-jest@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.5.0.tgz#c1d7f191829487a907764c65307faa0e66590b49" - integrity sha512-8ZczygctQkBU+63DtSOKGh7tFL0CeCuz+1ieud9lJ1WPQ9O6A1a/r+LGn6Y705PA6whHQ3T1XuB/PmpfNYf8Fw== +babel-preset-jest@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.3.0.tgz#ed6344506225c065fd8a0b53e191986f74890776" + integrity sha512-5WPdf7nyYi2/eRxCbVrE1kKCWxgWY4RsPEbdJWFm7QsesFGqjdkyLeu1zRkwM1cxK6EPIlNd6d2AxLk7J+t4pw== dependencies: - babel-plugin-jest-hoist "^25.5.0" - babel-preset-current-node-syntax "^0.1.2" + babel-plugin-jest-hoist "^26.2.0" + babel-preset-current-node-syntax "^0.1.3" "babel-preset-minify@^0.5.0 || 0.6.0-alpha.5": version "0.5.0" @@ -8143,6 +8209,11 @@ browser-process-hrtime@^0.1.2: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" integrity sha1-Ql1opY00R/AqBKqJQYf86K+Le44= +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + browser-resolve@^1.11.3, browser-resolve@^1.8.1: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" @@ -8851,6 +8922,11 @@ change-emitter@^0.1.2: resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU= +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + character-entities-html4@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.4.tgz#0e64b0a3753ddbf1fdc044c5fd01d0199a02e125" @@ -10451,7 +10527,7 @@ cssom@0.3.x, cssom@^0.3.4, cssom@~0.3.6: resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssom@^0.4.1: +cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== @@ -10463,10 +10539,10 @@ cssstyle@^1.1.1: dependencies: cssom "0.3.x" -cssstyle@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.2.0.tgz#e4c44debccd6b7911ed617a4395e5754bba59992" - integrity sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA== +cssstyle@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== dependencies: cssom "~0.3.6" @@ -10875,6 +10951,15 @@ data-urls@^1.1.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + date-fns@^1.27.2: version "1.29.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" @@ -10965,6 +11050,11 @@ decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, deca resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decimal.js@^10.2.0: + version "10.2.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" + integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -11641,6 +11731,13 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + domhandler@2.1: version "2.1.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.1.0.tgz#d2646f5e57f6c3bab11cf6cb05d3c0acf7412594" @@ -11992,6 +12089,11 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +emittery@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451" + integrity sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ== + "emoji-regex@>=6.0.0 <=6.1.1": version "6.1.1" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" @@ -12481,7 +12583,7 @@ escodegen@^1.11.1: optionalDependencies: source-map "~0.6.1" -escodegen@^1.12.0: +escodegen@^1.12.0, escodegen@^1.14.1: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== @@ -12644,12 +12746,12 @@ eslint-plugin-import@^2.19.1: read-pkg-up "^2.0.0" resolve "^1.12.0" -eslint-plugin-jest@^23.10.0: - version "23.10.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.10.0.tgz#4738c7ca9e6513da50f4e99d7b161c1f82fa8e8f" - integrity sha512-cHC//nesojSO1MLxVmFJR/bUaQQG7xvMHQD8YLbsQzevR41WKm8paKDUv2wMHlUy5XLZUmNcWuflOi4apS8D+Q== +eslint-plugin-jest@^24.0.2: + version "24.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.0.2.tgz#4bf0fcdc86289d702a7dacb430b4363482af773b" + integrity sha512-DSBLNpkKDOpUJQkTGSs5sVJWsu0nDyQ2rYxkr0Eh7nrkc5bMUr/dlDbtTj3l8y6UaCVsem6rryF1OZrKnz1S5g== dependencies: - "@typescript-eslint/experimental-utils" "^2.5.0" + "@typescript-eslint/experimental-utils" "^4.0.1" eslint-plugin-jsx-a11y@^6.2.3: version "6.2.3" @@ -12779,6 +12881,11 @@ eslint-visitor-keys@^1.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + eslint@^2.7.0: version "2.13.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-2.13.1.tgz#e4cc8fa0f009fb829aaae23855a29360be1f6c11" @@ -13055,10 +13162,10 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^3.2.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" - integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== +execa@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" + integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== dependencies: cross-spawn "^7.0.0" get-stream "^5.0.0" @@ -13067,7 +13174,6 @@ execa@^3.2.0: merge-stream "^2.0.0" npm-run-path "^4.0.0" onetime "^5.1.0" - p-finally "^2.0.0" signal-exit "^3.0.2" strip-final-newline "^2.0.0" @@ -13152,18 +13258,6 @@ expect@^24.8.0, expect@^24.9.0: jest-message-util "^24.9.0" jest-regex-util "^24.9.0" -expect@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-25.5.0.tgz#f07f848712a2813bb59167da3fb828ca21f58bba" - integrity sha512-w7KAXo0+6qqZZhovCaBVPSIqQp7/UTcx4M9uKt2m6pd2VB1voyC8JizLRqeEqud3AAVP02g+hbErDu5gu64tlA== - dependencies: - "@jest/types" "^25.5.0" - ansi-styles "^4.0.0" - jest-get-type "^25.2.6" - jest-matcher-utils "^25.5.0" - jest-message-util "^25.5.0" - jest-regex-util "^25.2.6" - expect@^26.4.0: version "26.4.0" resolved "https://registry.yarnpkg.com/expect/-/expect-26.4.0.tgz#34a0aae523343b0931ff1cf0aa972dfe40edfab4" @@ -13176,6 +13270,18 @@ expect@^26.4.0: jest-message-util "^26.3.0" jest-regex-util "^26.0.0" +expect@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.4.2.tgz#36db120928a5a2d7d9736643032de32f24e1b2a1" + integrity sha512-IlJ3X52Z0lDHm7gjEp+m76uX46ldH5VpqmU0006vqDju/285twh7zaWMRhs67VpQhBwjjMchk+p5aA0VkERCAA== + dependencies: + "@jest/types" "^26.3.0" + ansi-styles "^4.0.0" + jest-get-type "^26.3.0" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-regex-util "^26.0.0" + expiry-js@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/expiry-js/-/expiry-js-0.1.7.tgz#76be8c05e572bf936df40c1766448d0b3b2f555f" @@ -16085,6 +16191,13 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + html-entities@^1.2.0, html-entities@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" @@ -17499,6 +17612,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-potential-custom-element-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" + integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= + is-promise@^2.1, is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" @@ -17726,6 +17844,13 @@ is-wsl@^2.1.1: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -17860,6 +17985,16 @@ istanbul-lib-instrument@^4.0.0: istanbul-lib-coverage "^3.0.0" semver "^6.3.0" +istanbul-lib-instrument@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" + integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== + dependencies: + "@babel/core" "^7.7.5" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" + istanbul-lib-processinfo@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz#e1426514662244b2f25df728e8fd1ba35fe53b9c" @@ -17962,82 +18097,83 @@ jest-canvas-mock@^2.2.0: cssfontparser "^1.2.1" parse-color "^1.0.0" -jest-changed-files@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.5.0.tgz#141cc23567ceb3f534526f8614ba39421383634c" - integrity sha512-EOw9QEqapsDT7mKF162m8HFzRPbmP8qJQny6ldVOdOVBz3ACgPm/1nAn5fPQ/NDaYhX/AHkrGwwkCncpAVSXcw== +jest-changed-files@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.3.0.tgz#68fb2a7eb125f50839dab1f5a17db3607fe195b1" + integrity sha512-1C4R4nijgPltX6fugKxM4oQ18zimS7LqQ+zTTY8lMCMFPrxqBFb7KJH0Z2fRQJvw2Slbaipsqq7s1mgX5Iot+g== dependencies: - "@jest/types" "^25.5.0" - execa "^3.2.0" + "@jest/types" "^26.3.0" + execa "^4.0.0" throat "^5.0.0" -jest-circus@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-25.5.4.tgz#d5578346f53d5604b7b1d1e5e56be48b1c73c61d" - integrity sha512-nOLJXZjWuV2i8yQ2w+MZ8NhFuqrbgpPAa4mh+DWPYmNI377YYpDKvDUrLMW8U1sa2iGt5mjKQbmJz2SK9AYjWg== +jest-circus@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-26.4.2.tgz#f84487d2ea635cadf1feb269b14ad0602135ad17" + integrity sha512-gzxoteivskdUTNxT7Jx6hrANsEm+x1wh8jaXmQCtzC7zoNWirk9chYdSosHFC4tJlfDZa0EsPreVAxLicLsV0w== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^25.5.0" - "@jest/test-result" "^25.5.0" - "@jest/types" "^25.5.0" - chalk "^3.0.0" + "@jest/environment" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" co "^4.6.0" - expect "^25.5.0" + dedent "^0.7.0" + expect "^26.4.2" is-generator-fn "^2.0.0" - jest-each "^25.5.0" - jest-matcher-utils "^25.5.0" - jest-message-util "^25.5.0" - jest-runtime "^25.5.4" - jest-snapshot "^25.5.1" - jest-util "^25.5.0" - pretty-format "^25.5.0" - stack-utils "^1.0.1" + jest-each "^26.4.2" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-runner "^26.4.2" + jest-runtime "^26.4.2" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + pretty-format "^26.4.2" + stack-utils "^2.0.2" throat "^5.0.0" -jest-cli@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.5.4.tgz#b9f1a84d1301a92c5c217684cb79840831db9f0d" - integrity sha512-rG8uJkIiOUpnREh1768/N3n27Cm+xPFkSNFO91tgg+8o2rXeVLStz+vkXkGr4UtzH6t1SNbjwoiswd7p4AhHTw== +jest-cli@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.4.2.tgz#24afc6e4dfc25cde4c7ec4226fb7db5f157c21da" + integrity sha512-zb+lGd/SfrPvoRSC/0LWdaWCnscXc1mGYW//NP4/tmBvRPT3VntZ2jtKUONsRi59zc5JqmsSajA9ewJKFYp8Cw== dependencies: - "@jest/core" "^25.5.4" - "@jest/test-result" "^25.5.0" - "@jest/types" "^25.5.0" - chalk "^3.0.0" + "@jest/core" "^26.4.2" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.4" import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^25.5.4" - jest-util "^25.5.0" - jest-validate "^25.5.0" + jest-config "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" prompts "^2.0.1" - realpath-native "^2.0.0" yargs "^15.3.1" -jest-config@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.5.4.tgz#38e2057b3f976ef7309b2b2c8dcd2a708a67f02c" - integrity sha512-SZwR91SwcdK6bz7Gco8qL7YY2sx8tFJYzvg216DLihTWf+LKY/DoJXpM9nTzYakSyfblbqeU48p/p7Jzy05Atg== +jest-config@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.4.2.tgz#da0cbb7dc2c131ffe831f0f7f2a36256e6086558" + integrity sha512-QBf7YGLuToiM8PmTnJEdRxyYy3mHWLh24LJZKVdXZ2PNdizSe1B/E8bVm+HYcjbEzGuVXDv/di+EzdO/6Gq80A== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^25.5.4" - "@jest/types" "^25.5.0" - babel-jest "^25.5.1" - chalk "^3.0.0" + "@jest/test-sequencer" "^26.4.2" + "@jest/types" "^26.3.0" + babel-jest "^26.3.0" + chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" graceful-fs "^4.2.4" - jest-environment-jsdom "^25.5.0" - jest-environment-node "^25.5.0" - jest-get-type "^25.2.6" - jest-jasmine2 "^25.5.4" - jest-regex-util "^25.2.6" - jest-resolve "^25.5.1" - jest-util "^25.5.0" - jest-validate "^25.5.0" + jest-environment-jsdom "^26.3.0" + jest-environment-node "^26.3.0" + jest-get-type "^26.3.0" + jest-jasmine2 "^26.4.2" + jest-regex-util "^26.0.0" + jest-resolve "^26.4.0" + jest-util "^26.3.0" + jest-validate "^26.4.2" micromatch "^4.0.2" - pretty-format "^25.5.0" - realpath-native "^2.0.0" + pretty-format "^26.4.2" jest-diff@^24.9.0: version "24.9.0" @@ -18049,7 +18185,7 @@ jest-diff@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-diff@^25.2.1, jest-diff@^25.5.0: +jest-diff@^25.2.1: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A== @@ -18069,23 +18205,33 @@ jest-diff@^26.4.0: jest-get-type "^26.3.0" pretty-format "^26.4.0" -jest-docblock@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.3.0.tgz#8b777a27e3477cd77a168c05290c471a575623ef" - integrity sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg== +jest-diff@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.4.2.tgz#a1b7b303bcc534aabdb3bd4a7caf594ac059f5aa" + integrity sha512-6T1XQY8U28WH0Z5rGpQ+VqZSZz8EN8rZcBtfvXaOkbwxIEeRre6qnuZQlbY1AJ4MKDxQF8EkrCvK+hL/VkyYLQ== + dependencies: + chalk "^4.0.0" + diff-sequences "^26.3.0" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" + +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== dependencies: detect-newline "^3.0.0" -jest-each@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.5.0.tgz#0c3c2797e8225cb7bec7e4d249dcd96b934be516" - integrity sha512-QBogUxna3D8vtiItvn54xXde7+vuzqRrEeaw8r1s+1TG9eZLVJE5ZkKoSUlqFwRjnlaA4hyKGiu9OlkFIuKnjA== +jest-each@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.4.2.tgz#bb14f7f4304f2bb2e2b81f783f989449b8b6ffae" + integrity sha512-p15rt8r8cUcRY0Mvo1fpkOGYm7iI8S6ySxgIdfh3oOIv+gHwrHTy5VWCGOecWUhDsit4Nz8avJWdT07WLpbwDA== dependencies: - "@jest/types" "^25.5.0" - chalk "^3.0.0" - jest-get-type "^25.2.6" - jest-util "^25.5.0" - pretty-format "^25.5.0" + "@jest/types" "^26.3.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" + jest-util "^26.3.0" + pretty-format "^26.4.2" jest-environment-jsdom-thirteen@^1.0.1: version "1.0.1" @@ -18096,29 +18242,30 @@ jest-environment-jsdom-thirteen@^1.0.1: jest-util "^24.0.0" jsdom "^13.0.0" -jest-environment-jsdom@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.5.0.tgz#dcbe4da2ea997707997040ecf6e2560aec4e9834" - integrity sha512-7Jr02ydaq4jaWMZLY+Skn8wL5nVIYpWvmeatOHL3tOcV3Zw8sjnPpx+ZdeBfc457p8jCR9J6YCc+Lga0oIy62A== +jest-environment-jsdom@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.3.0.tgz#3b749ba0f3a78e92ba2c9ce519e16e5dd515220c" + integrity sha512-zra8He2btIMJkAzvLaiZ9QwEPGEetbxqmjEBQwhH3CA+Hhhu0jSiEJxnJMbX28TGUvPLxBt/zyaTLrOPF4yMJA== dependencies: - "@jest/environment" "^25.5.0" - "@jest/fake-timers" "^25.5.0" - "@jest/types" "^25.5.0" - jest-mock "^25.5.0" - jest-util "^25.5.0" - jsdom "^15.2.1" + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" + jest-util "^26.3.0" + jsdom "^16.2.2" -jest-environment-node@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.5.0.tgz#0f55270d94804902988e64adca37c6ce0f7d07a1" - integrity sha512-iuxK6rQR2En9EID+2k+IBs5fCFd919gVVK5BeND82fYeLWPqvRcFNPKu9+gxTwfB5XwBGBvZ0HFQa+cHtIoslA== +jest-environment-node@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.3.0.tgz#56c6cfb506d1597f94ee8d717072bda7228df849" + integrity sha512-c9BvYoo+FGcMj5FunbBgtBnbR5qk3uky8PKyRVpSfe2/8+LrNQMiXX53z6q2kY+j15SkjQCOSL/6LHnCPLVHNw== dependencies: - "@jest/environment" "^25.5.0" - "@jest/fake-timers" "^25.5.0" - "@jest/types" "^25.5.0" - jest-mock "^25.5.0" - jest-util "^25.5.0" - semver "^6.3.0" + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + jest-mock "^26.3.0" + jest-util "^26.3.0" jest-get-type@^24.9.0: version "24.9.0" @@ -18135,26 +18282,6 @@ jest-get-type@^26.3.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.3.0.tgz#e97dc3c3f53c2b406ca7afaed4493b1d099199e0" integrity sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig== -jest-haste-map@^25.5.1: - version "25.5.1" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.5.1.tgz#1df10f716c1d94e60a1ebf7798c9fb3da2620943" - integrity sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ== - dependencies: - "@jest/types" "^25.5.0" - "@types/graceful-fs" "^4.1.2" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.4" - jest-serializer "^25.5.0" - jest-util "^25.5.0" - jest-worker "^25.5.0" - micromatch "^4.0.2" - sane "^4.0.3" - walker "^1.0.7" - which "^2.0.2" - optionalDependencies: - fsevents "^2.1.2" - jest-haste-map@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.3.0.tgz#c51a3b40100d53ab777bfdad382d2e7a00e5c726" @@ -18176,36 +18303,37 @@ jest-haste-map@^26.3.0: optionalDependencies: fsevents "^2.1.2" -jest-jasmine2@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.5.4.tgz#66ca8b328fb1a3c5364816f8958f6970a8526968" - integrity sha512-9acbWEfbmS8UpdcfqnDO+uBUgKa/9hcRh983IHdM+pKmJPL77G0sWAAK0V0kr5LK3a8cSBfkFSoncXwQlRZfkQ== +jest-jasmine2@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.4.2.tgz#18a9d5bec30904267ac5e9797570932aec1e2257" + integrity sha512-z7H4EpCldHN1J8fNgsja58QftxBSL+JcwZmaXIvV9WKIM+x49F4GLHu/+BQh2kzRKHAgaN/E82od+8rTOBPyPA== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^25.5.0" - "@jest/source-map" "^25.5.0" - "@jest/test-result" "^25.5.0" - "@jest/types" "^25.5.0" - chalk "^3.0.0" + "@jest/environment" "^26.3.0" + "@jest/source-map" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" co "^4.6.0" - expect "^25.5.0" + expect "^26.4.2" is-generator-fn "^2.0.0" - jest-each "^25.5.0" - jest-matcher-utils "^25.5.0" - jest-message-util "^25.5.0" - jest-runtime "^25.5.4" - jest-snapshot "^25.5.1" - jest-util "^25.5.0" - pretty-format "^25.5.0" + jest-each "^26.4.2" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-runtime "^26.4.2" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + pretty-format "^26.4.2" throat "^5.0.0" -jest-leak-detector@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.5.0.tgz#2291c6294b0ce404241bb56fe60e2d0c3e34f0bb" - integrity sha512-rV7JdLsanS8OkdDpZtgBf61L5xZ4NnYLBq72r6ldxahJWWczZjXawRsoHyXzibM5ed7C2QRjpp6ypgwGdKyoVA== +jest-leak-detector@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.4.2.tgz#c73e2fa8757bf905f6f66fb9e0070b70fa0f573f" + integrity sha512-akzGcxwxtE+9ZJZRW+M2o+nTNnmQZxrHJxX/HjgDaU5+PLmY1qnQPnMjgADPGCRPhB+Yawe1iij0REe+k/aHoA== dependencies: - jest-get-type "^25.2.6" - pretty-format "^25.5.0" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" jest-matcher-utils@^24.9.0: version "24.9.0" @@ -18217,16 +18345,6 @@ jest-matcher-utils@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-matcher-utils@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867" - integrity sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw== - dependencies: - chalk "^3.0.0" - jest-diff "^25.5.0" - jest-get-type "^25.2.6" - pretty-format "^25.5.0" - jest-matcher-utils@^26.4.0: version "26.4.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.4.0.tgz#2bce9a939e008b894faf1bd4b5bb58facd00c252" @@ -18237,6 +18355,16 @@ jest-matcher-utils@^26.4.0: jest-get-type "^26.3.0" pretty-format "^26.4.0" +jest-matcher-utils@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz#fa81f3693f7cb67e5fc1537317525ef3b85f4b06" + integrity sha512-KcbNqWfWUG24R7tu9WcAOKKdiXiXCbMvQYT6iodZ9k1f7065k0keUOW6XpJMMvah+hTfqkhJhRXmA3r3zMAg0Q== + dependencies: + chalk "^4.0.0" + jest-diff "^26.4.2" + jest-get-type "^26.3.0" + pretty-format "^26.4.2" + jest-message-util@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" @@ -18251,20 +18379,6 @@ jest-message-util@^24.9.0: slash "^2.0.0" stack-utils "^1.0.1" -jest-message-util@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea" - integrity sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA== - dependencies: - "@babel/code-frame" "^7.0.0" - "@jest/types" "^25.5.0" - "@types/stack-utils" "^1.0.1" - chalk "^3.0.0" - graceful-fs "^4.2.4" - micromatch "^4.0.2" - slash "^3.0.0" - stack-utils "^1.0.1" - jest-message-util@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.3.0.tgz#3bdb538af27bb417f2d4d16557606fd082d5841a" @@ -18286,12 +18400,13 @@ jest-mock@^24.0.0, jest-mock@^24.9.0: dependencies: "@jest/types" "^24.9.0" -jest-mock@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a" - integrity sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA== +jest-mock@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.3.0.tgz#ee62207c3c5ebe5f35b760e1267fee19a1cfdeba" + integrity sha512-PeaRrg8Dc6mnS35gOo/CbZovoDPKAeB1FICZiuagAgGvbWdNNyjQjkOaGUa/3N3JtpQ/Mh9P4A2D4Fv51NnP8Q== dependencies: - "@jest/types" "^25.5.0" + "@jest/types" "^26.3.0" + "@types/node" "*" jest-pnp-resolver@^1.2.1: version "1.2.1" @@ -18313,24 +18428,19 @@ jest-regex-util@^24.9.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== -jest-regex-util@^25.2.6: - version "25.2.6" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" - integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw== - jest-regex-util@^26.0.0: version "26.0.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-resolve-dependencies@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.5.4.tgz#85501f53957c8e3be446e863a74777b5a17397a7" - integrity sha512-yFmbPd+DAQjJQg88HveObcGBA32nqNZ02fjYmtL16t1xw9bAttSn5UGRRhzMHIQbsep7znWvAvnD4kDqOFM0Uw== +jest-resolve-dependencies@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.4.2.tgz#739bdb027c14befb2fe5aabbd03f7bab355f1dc5" + integrity sha512-ADHaOwqEcVc71uTfySzSowA/RdxUpCxhxa2FNLiin9vWLB1uLPad3we+JSSROq5+SrL9iYPdZZF8bdKM7XABTQ== dependencies: - "@jest/types" "^25.5.0" - jest-regex-util "^25.2.6" - jest-snapshot "^25.5.1" + "@jest/types" "^26.3.0" + jest-regex-util "^26.0.0" + jest-snapshot "^26.4.2" jest-resolve@^24.9.0: version "24.9.0" @@ -18343,21 +18453,6 @@ jest-resolve@^24.9.0: jest-pnp-resolver "^1.2.1" realpath-native "^1.1.0" -jest-resolve@^25.5.1: - version "25.5.1" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.5.1.tgz#0e6fbcfa7c26d2a5fe8f456088dc332a79266829" - integrity sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ== - dependencies: - "@jest/types" "^25.5.0" - browser-resolve "^1.11.3" - chalk "^3.0.0" - graceful-fs "^4.2.4" - jest-pnp-resolver "^1.2.1" - read-pkg-up "^7.0.1" - realpath-native "^2.0.0" - resolve "^1.17.0" - slash "^3.0.0" - jest-resolve@^26.4.0: version "26.4.0" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.4.0.tgz#6dc0af7fb93e65b73fec0368ca2b76f3eb59a6d7" @@ -18372,70 +18467,64 @@ jest-resolve@^26.4.0: resolve "^1.17.0" slash "^3.0.0" -jest-runner@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.5.4.tgz#ffec5df3875da5f5c878ae6d0a17b8e4ecd7c71d" - integrity sha512-V/2R7fKZo6blP8E9BL9vJ8aTU4TH2beuqGNxHbxi6t14XzTb+x90B3FRgdvuHm41GY8ch4xxvf0ATH4hdpjTqg== +jest-runner@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.4.2.tgz#c3ec5482c8edd31973bd3935df5a449a45b5b853" + integrity sha512-FgjDHeVknDjw1gRAYaoUoShe1K3XUuFMkIaXbdhEys+1O4bEJS8Avmn4lBwoMfL8O5oFTdWYKcf3tEJyyYyk8g== dependencies: - "@jest/console" "^25.5.0" - "@jest/environment" "^25.5.0" - "@jest/test-result" "^25.5.0" - "@jest/types" "^25.5.0" - chalk "^3.0.0" + "@jest/console" "^26.3.0" + "@jest/environment" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.7.1" exit "^0.1.2" graceful-fs "^4.2.4" - jest-config "^25.5.4" - jest-docblock "^25.3.0" - jest-haste-map "^25.5.1" - jest-jasmine2 "^25.5.4" - jest-leak-detector "^25.5.0" - jest-message-util "^25.5.0" - jest-resolve "^25.5.1" - jest-runtime "^25.5.4" - jest-util "^25.5.0" - jest-worker "^25.5.0" + jest-config "^26.4.2" + jest-docblock "^26.0.0" + jest-haste-map "^26.3.0" + jest-leak-detector "^26.4.2" + jest-message-util "^26.3.0" + jest-resolve "^26.4.0" + jest-runtime "^26.4.2" + jest-util "^26.3.0" + jest-worker "^26.3.0" source-map-support "^0.5.6" throat "^5.0.0" -jest-runtime@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.5.4.tgz#dc981fe2cb2137abcd319e74ccae7f7eeffbfaab" - integrity sha512-RWTt8LeWh3GvjYtASH2eezkc8AehVoWKK20udV6n3/gC87wlTbE1kIA+opCvNWyyPeBs6ptYsc6nyHUb1GlUVQ== - dependencies: - "@jest/console" "^25.5.0" - "@jest/environment" "^25.5.0" - "@jest/globals" "^25.5.2" - "@jest/source-map" "^25.5.0" - "@jest/test-result" "^25.5.0" - "@jest/transform" "^25.5.1" - "@jest/types" "^25.5.0" +jest-runtime@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.4.2.tgz#94ce17890353c92e4206580c73a8f0c024c33c42" + integrity sha512-4Pe7Uk5a80FnbHwSOk7ojNCJvz3Ks2CNQWT5Z7MJo4tX0jb3V/LThKvD9tKPNVNyeMH98J/nzGlcwc00R2dSHQ== + dependencies: + "@jest/console" "^26.3.0" + "@jest/environment" "^26.3.0" + "@jest/fake-timers" "^26.3.0" + "@jest/globals" "^26.4.2" + "@jest/source-map" "^26.3.0" + "@jest/test-result" "^26.3.0" + "@jest/transform" "^26.3.0" + "@jest/types" "^26.3.0" "@types/yargs" "^15.0.0" - chalk "^3.0.0" + chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.2.4" - jest-config "^25.5.4" - jest-haste-map "^25.5.1" - jest-message-util "^25.5.0" - jest-mock "^25.5.0" - jest-regex-util "^25.2.6" - jest-resolve "^25.5.1" - jest-snapshot "^25.5.1" - jest-util "^25.5.0" - jest-validate "^25.5.0" - realpath-native "^2.0.0" + jest-config "^26.4.2" + jest-haste-map "^26.3.0" + jest-message-util "^26.3.0" + jest-mock "^26.3.0" + jest-regex-util "^26.0.0" + jest-resolve "^26.4.0" + jest-snapshot "^26.4.2" + jest-util "^26.3.0" + jest-validate "^26.4.2" slash "^3.0.0" strip-bom "^4.0.0" yargs "^15.3.1" -jest-serializer@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.5.0.tgz#a993f484e769b4ed54e70e0efdb74007f503072b" - integrity sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA== - dependencies: - graceful-fs "^4.2.4" - jest-serializer@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.3.0.tgz#1c9d5e1b74d6e5f7e7f9627080fa205d976c33ef" @@ -18463,27 +18552,6 @@ jest-snapshot@^24.1.0: pretty-format "^24.9.0" semver "^6.2.0" -jest-snapshot@^25.5.1: - version "25.5.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.5.1.tgz#1a2a576491f9961eb8d00c2e5fd479bc28e5ff7f" - integrity sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^25.5.0" - "@types/prettier" "^1.19.0" - chalk "^3.0.0" - expect "^25.5.0" - graceful-fs "^4.2.4" - jest-diff "^25.5.0" - jest-get-type "^25.2.6" - jest-matcher-utils "^25.5.0" - jest-message-util "^25.5.0" - jest-resolve "^25.5.1" - make-dir "^3.0.0" - natural-compare "^1.4.0" - pretty-format "^25.5.0" - semver "^6.3.0" - jest-snapshot@^26.3.0: version "26.4.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.4.0.tgz#efd42eef09bcb33e9a3eb98e229f2368c73c9235" @@ -18505,6 +18573,27 @@ jest-snapshot@^26.3.0: pretty-format "^26.4.0" semver "^7.3.2" +jest-snapshot@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.4.2.tgz#87d3ac2f2bd87ea8003602fbebd8fcb9e94104f6" + integrity sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg== + dependencies: + "@babel/types" "^7.0.0" + "@jest/types" "^26.3.0" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.4.2" + graceful-fs "^4.2.4" + jest-diff "^26.4.2" + jest-get-type "^26.3.0" + jest-haste-map "^26.3.0" + jest-matcher-utils "^26.4.2" + jest-message-util "^26.3.0" + jest-resolve "^26.4.0" + natural-compare "^1.4.0" + pretty-format "^26.4.2" + semver "^7.3.2" + jest-specific-snapshot@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/jest-specific-snapshot/-/jest-specific-snapshot-2.0.0.tgz#425fe524b25df154aa39f97fa6fe9726faaac273" @@ -18544,17 +18633,6 @@ jest-util@^24.0.0: slash "^2.0.0" source-map "^0.6.0" -jest-util@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.5.0.tgz#31c63b5d6e901274d264a4fec849230aa3fa35b0" - integrity sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA== - dependencies: - "@jest/types" "^25.5.0" - chalk "^3.0.0" - graceful-fs "^4.2.4" - is-ci "^2.0.0" - make-dir "^3.0.0" - jest-util@^26.3.0: version "26.3.0" resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e" @@ -18567,29 +18645,30 @@ jest-util@^26.3.0: is-ci "^2.0.0" micromatch "^4.0.2" -jest-validate@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.5.0.tgz#fb4c93f332c2e4cf70151a628e58a35e459a413a" - integrity sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ== +jest-validate@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.4.2.tgz#e871b0dfe97747133014dcf6445ee8018398f39c" + integrity sha512-blft+xDX7XXghfhY0mrsBCYhX365n8K5wNDC4XAcNKqqjEzsRUSXP44m6PL0QJEW2crxQFLLztVnJ4j7oPlQrQ== dependencies: - "@jest/types" "^25.5.0" - camelcase "^5.3.1" - chalk "^3.0.0" - jest-get-type "^25.2.6" + "@jest/types" "^26.3.0" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.3.0" leven "^3.1.0" - pretty-format "^25.5.0" + pretty-format "^26.4.2" -jest-watcher@^25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.5.0.tgz#d6110d101df98badebe435003956fd4a465e8456" - integrity sha512-XrSfJnVASEl+5+bb51V0Q7WQx65dTSk7NL4yDdVjPnRNpM0hG+ncFmDYJo9O8jaSRcAitVbuVawyXCRoxGrT5Q== +jest-watcher@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.3.0.tgz#f8ef3068ddb8af160ef868400318dc4a898eed08" + integrity sha512-XnLdKmyCGJ3VoF6G/p5ohbJ04q/vv5aH9ENI+i6BL0uu9WWB6Z7Z2lhQQk0d2AVZcRGp1yW+/TsoToMhBFPRdQ== dependencies: - "@jest/test-result" "^25.5.0" - "@jest/types" "^25.5.0" + "@jest/test-result" "^26.3.0" + "@jest/types" "^26.3.0" + "@types/node" "*" ansi-escapes "^4.2.1" - chalk "^3.0.0" - jest-util "^25.5.0" - string-length "^3.1.0" + chalk "^4.0.0" + jest-util "^26.3.0" + string-length "^4.0.1" jest-when@^2.7.2: version "2.7.2" @@ -18599,7 +18678,7 @@ jest-when@^2.7.2: bunyan "^1.8.12" expect "^24.8.0" -jest-worker@^25.4.0, jest-worker@^25.5.0: +jest-worker@^25.4.0: version "25.5.0" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz#2611d071b79cea0f43ee57a3d118593ac1547db1" integrity sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw== @@ -18616,14 +18695,14 @@ jest-worker@^26.2.1, jest-worker@^26.3.0: merge-stream "^2.0.0" supports-color "^7.0.0" -jest@^25.5.4: - version "25.5.4" - resolved "https://registry.yarnpkg.com/jest/-/jest-25.5.4.tgz#f21107b6489cfe32b076ce2adcadee3587acb9db" - integrity sha512-hHFJROBTqZahnO+X+PMtT6G2/ztqAZJveGqz//FnWWHurizkD05PQGzRZOhF3XP6z7SJmL+5tCfW8qV06JypwQ== +jest@^26.4.2: + version "26.4.2" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.4.2.tgz#7e8bfb348ec33f5459adeaffc1a25d5752d9d312" + integrity sha512-LLCjPrUh98Ik8CzW8LLVnSCfLaiY+wbK53U7VxnFSX7Q+kWC4noVeDvGWIFw0Amfq1lq2VfGm7YHWSLBV62MJw== dependencies: - "@jest/core" "^25.5.4" + "@jest/core" "^26.4.2" import-local "^3.0.2" - jest-cli "^25.5.4" + jest-cli "^26.4.2" jimp@^0.14.0: version "0.14.0" @@ -18750,36 +18829,36 @@ jsdom@13.1.0, jsdom@^13.0.0: ws "^6.1.2" xml-name-validator "^3.0.0" -jsdom@^15.2.1: - version "15.2.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" - integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== - dependencies: - abab "^2.0.0" - acorn "^7.1.0" - acorn-globals "^4.3.2" - array-equal "^1.0.0" - cssom "^0.4.1" - cssstyle "^2.0.0" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.1" - html-encoding-sniffer "^1.0.2" +jsdom@^16.2.2: + version "16.4.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.4.0.tgz#36005bde2d136f73eee1a830c6d45e55408edddb" + integrity sha512-lYMm3wYdgPhrl7pDcRmvzPhhrGVBeVhPIqeHjzeiHN3DFmD1RBpbExbi8vU7BJdH8VAZYovR8DMt0PNNDM7k8w== + dependencies: + abab "^2.0.3" + acorn "^7.1.1" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.2.0" + data-urls "^2.0.0" + decimal.js "^10.2.0" + domexception "^2.0.1" + escodegen "^1.14.1" + html-encoding-sniffer "^2.0.1" + is-potential-custom-element-name "^1.0.0" nwsapi "^2.2.0" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.7" - saxes "^3.1.9" - symbol-tree "^3.2.2" + parse5 "5.1.1" + request "^2.88.2" + request-promise-native "^1.0.8" + saxes "^5.0.0" + symbol-tree "^3.2.4" tough-cookie "^3.0.1" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.1.2" - webidl-conversions "^4.0.2" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" whatwg-encoding "^1.0.5" whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^7.0.0" + whatwg-url "^8.0.0" + ws "^7.2.3" xml-name-validator "^3.0.0" jsesc@^1.3.0: @@ -20011,7 +20090,7 @@ lolex@^4.2.0: resolved "https://registry.yarnpkg.com/lolex/-/lolex-4.2.0.tgz#ddbd7f6213ca1ea5826901ab1222b65d714b3cd7" integrity sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg== -lolex@^5.0.0, lolex@^5.0.1: +lolex@^5.0.1: version "5.1.2" resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== @@ -21653,16 +21732,17 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-6.0.0.tgz#cea319e06baa16deec8ce5cd7f133c4a46b68e12" - integrity sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw== +node-notifier@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.0.tgz#a7eee2d51da6d0f7ff5094bc7108c911240c1620" + integrity sha512-46z7DUmcjoYdaWyXouuFNNfUo6eFa94t23c53c+lG/9Cvauk4a98rAUp9672X5dxGdQmLpPzTxzu8f/OeEPaFA== dependencies: growly "^1.3.0" - is-wsl "^2.1.1" - semver "^6.3.0" + is-wsl "^2.2.0" + semver "^7.3.2" shellwords "^0.1.1" - which "^1.3.1" + uuid "^8.3.0" + which "^2.0.2" node-pre-gyp@^0.11.0: version "0.11.0" @@ -22583,11 +22663,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-finally@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" - integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== - p-is-promise@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e" @@ -22977,6 +23052,11 @@ parse5@5.1.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== +parse5@5.1.1, parse5@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== + parse5@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" @@ -22984,11 +23064,6 @@ parse5@^3.0.1: dependencies: "@types/node" "*" -parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - parse5@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" @@ -25249,11 +25324,6 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" -realpath-native@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" - integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== - recast@^0.14.7: version "0.14.7" resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" @@ -25874,7 +25944,14 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" -request-promise-native@^1.0.5, request-promise-native@^1.0.7: +request-promise-core@1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" + integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw== + dependencies: + lodash "^4.17.19" + +request-promise-native@^1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== @@ -25883,6 +25960,15 @@ request-promise-native@^1.0.5, request-promise-native@^1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" +request-promise-native@^1.0.8: + version "1.0.9" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" + integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== + dependencies: + request-promise-core "1.1.4" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" + request-promise@^4.2.2: version "4.2.4" resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.4.tgz#1c5ed0d71441e38ad58c7ce4ea4ea5b06d54b310" @@ -26531,13 +26617,20 @@ sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^3.1.4, saxes@^3.1.9: +saxes@^3.1.4: version "3.1.11" resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== dependencies: xmlchars "^2.1.1" +saxes@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4" @@ -27693,13 +27786,13 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" -string-length@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" - integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== +string-length@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" + integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== dependencies: - astral-regex "^1.0.0" - strip-ansi "^5.2.0" + char-regex "^1.0.2" + strip-ansi "^6.0.0" string-replace-loader@^2.2.0: version "2.2.0" @@ -28270,6 +28363,11 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + symbol.prototype.description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/symbol.prototype.description/-/symbol.prototype.description-1.0.0.tgz#6e355660eb1e44ca8ad53a68fdb72ef131ca4b12" @@ -29029,6 +29127,13 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tr46@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" + integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== + dependencies: + punycode "^2.1.1" + traceparent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/traceparent/-/traceparent-1.0.0.tgz#9b14445cdfe5c19f023f1c04d249c3d8e003a5ce" @@ -30143,10 +30248,10 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== -v8-to-istanbul@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz#22fe35709a64955f49a08a7c7c959f6520ad6f20" - integrity sha512-sAjOC+Kki6aJVbUOXJbcR0MnbfjvBzwKZazEJymA2IX49uoOdEdk+4fBq5cXgYgiyKtAyrrJNtBZdOeDIF+Fng== +v8-to-istanbul@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz#0608f5b49a481458625edb058488607f25498ba5" + integrity sha512-mbDNjuDajqYe3TXFk5qxcQy8L1msXNE37WTlLoqqpBfRsimbNcrlhQlDPntmECEcUvdC+AQ8CyMMf6EUx1r74Q== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -30823,7 +30928,14 @@ w3c-hr-time@^1.0.1: dependencies: browser-process-hrtime "^0.1.2" -w3c-xmlserializer@^1.0.1, w3c-xmlserializer@^1.1.2: +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== @@ -30832,6 +30944,13 @@ w3c-xmlserializer@^1.0.1, w3c-xmlserializer@^1.1.2: webidl-conversions "^4.0.2" xml-name-validator "^3.0.0" +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + walk@2.3.x: version "2.3.9" resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.9.tgz#31b4db6678f2ae01c39ea9fb8725a9031e558a7b" @@ -30937,6 +31056,16 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" + integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== + webpack-cli@^3.3.10: version "3.3.10" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13" @@ -31193,6 +31322,15 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" +whatwg-url@^8.0.0: + version "8.2.2" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.2.2.tgz#85e7f9795108b53d554cec640b2e8aee2a0d4bfd" + integrity sha512-PcVnO6NiewhkmzV0qn7A+UZ9Xx4maNTI+O+TShmfE4pqjoCMwUMjkvoNhNHPTvgR7QH9Xt3R13iHuWy2sToFxQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^2.0.2" + webidl-conversions "^6.1.0" + which-boxed-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" @@ -31535,12 +31673,10 @@ ws@^6.1.2, ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.0.tgz#422eda8c02a4b5dba7744ba66eebbd84bcef0ec7" - integrity sha512-+SqNqFbwTm/0DC18KYzIsMTnEWpLwJsiasW/O17la4iDRRIO9uaHbvKiAS3AHgTiuuWerK/brj4O6MYZkei9xg== - dependencies: - async-limiter "^1.0.0" +ws@^7.2.3: + version "7.3.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" + integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== x-is-function@^1.0.4: version "1.0.4" @@ -31634,7 +31770,7 @@ xmlbuilder@~9.0.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f" integrity sha1-UZy0ymhtAFqEINNJbz8MruzKWA8= -xmlchars@^2.1.1: +xmlchars@^2.1.1, xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== From 5f187307c2e20ea1c8057a0e65c7b5801a4467f6 Mon Sep 17 00:00:00 2001 From: Jen Huang <its.jenetic@gmail.com> Date: Thu, 1 Oct 2020 14:50:03 -0700 Subject: [PATCH 059/128] Fix condition for filtering to installed packages (#79205) --- .../ingest_manager/server/services/epm/packages/get.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts index c4232247cc4bd5..2d11b6157804f4 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/packages/get.ts @@ -51,14 +51,14 @@ export async function getPackages( } // Get package names for packages which cannot have more than one package policy on an agent policy -// Assume packages only export one config template for now +// Assume packages only export one policy template for now export async function getLimitedPackages(options: { savedObjectsClient: SavedObjectsClientContract; }): Promise<string[]> { const { savedObjectsClient } = options; const allPackages = await getPackages({ savedObjectsClient, experimental: true }); const installedPackages = allPackages.filter( - (pkg) => (pkg.status = InstallationStatus.installed) + (pkg) => pkg.status === InstallationStatus.installed ); const installedPackagesInfo = await Promise.all( installedPackages.map((pkgInstall) => { From 117b5771dcee1b3ee00e56d9c7bb025e459fec14 Mon Sep 17 00:00:00 2001 From: Patrick Mueller <pmuellr@gmail.com> Date: Thu, 1 Oct 2020 18:06:26 -0400 Subject: [PATCH 060/128] [Alerting] formalize alert status and add status fields to alert saved object (#75553) resolves https://github.com/elastic/kibana/issues/51099 This formalizes the concept of "alert status", in terms of it's execution, with some new fields in the alert saved object and types used with the alert client and http APIs. These fields are read-only from the client point-of-view; they are provided in the alert structures, but are only updated by the alerting framework itself. The values will be updated after each run of the alert type executor. The data is added to the alert as the `executionStatus` field, with the following shape: ```ts interface AlertExecutionStatus { status: 'ok' | 'active' | 'error' | 'pending' | 'unknown'; lastExecutionDate: Date; error?: { reason: 'read' | 'decrypt' | 'execute' | 'unknown'; message: string; }; } ``` --- x-pack/plugins/alerts/common/alert.ts | 23 ++ .../alerts/server/alerts_client.test.ts | 20 ++ x-pack/plugins/alerts/server/alerts_client.ts | 21 +- .../server/lib/alert_execution_status.test.ts | 185 ++++++++++ .../server/lib/alert_execution_status.ts | 66 ++++ ...rt_instance_summary_from_event_log.test.ts | 4 + .../server/lib/error_with_reason.test.ts | 28 ++ .../alerts/server/lib/error_with_reason.ts | 29 ++ x-pack/plugins/alerts/server/lib/index.ts | 7 + .../lib/is_alert_not_found_error.test.ts | 22 +- .../server/lib/is_alert_not_found_error.ts | 8 +- x-pack/plugins/alerts/server/plugin.ts | 1 + .../alerts/server/routes/create.test.ts | 7 +- .../plugins/alerts/server/routes/get.test.ts | 7 +- .../alerts/server/saved_objects/index.ts | 13 +- .../alerts/server/saved_objects/mappings.json | 20 ++ .../server/saved_objects/migrations.test.ts | 30 +- .../alerts/server/saved_objects/migrations.ts | 23 +- .../saved_objects/partially_update_alert.ts | 4 +- .../task_runner/alert_task_instance.test.ts | 4 + .../server/task_runner/task_runner.test.ts | 15 +- .../alerts/server/task_runner/task_runner.ts | 59 +++- .../task_runner/task_runner_factory.test.ts | 6 +- .../server/task_runner/task_runner_factory.ts | 3 +- x-pack/plugins/alerts/server/types.ts | 15 + .../routes/__mocks__/request_responses.ts | 8 + .../rules/patch_rules.mock.ts | 4 + .../public/application/lib/alert_api.test.ts | 8 + .../public/application/lib/alert_api.ts | 5 +- .../components/alert_details.test.tsx | 4 + .../components/alert_details_route.test.tsx | 4 + .../components/alert_instances.test.tsx | 4 + .../components/alert_instances_route.test.tsx | 4 + .../components/view_in_app.test.tsx | 4 + .../sections/alert_form/alert_edit.test.tsx | 9 +- .../with_bulk_alert_api_operations.test.tsx | 4 + .../common/lib/test_assertions.ts | 18 + .../tests/alerting/create.ts | 1 + .../tests/alerting/execution_status.ts | 87 +++++ .../tests/alerting/find.ts | 81 +++++ .../security_and_spaces/tests/alerting/get.ts | 1 + .../tests/alerting/index.ts | 1 + .../tests/alerting/update.ts | 5 + .../spaces_only/tests/alerting/create.ts | 1 + .../tests/alerting/execution_status.ts | 333 ++++++++++++++++++ .../spaces_only/tests/alerting/find.ts | 1 + .../spaces_only/tests/alerting/get.ts | 1 + .../spaces_only/tests/alerting/index.ts | 1 + .../spaces_only/tests/alerting/update.ts | 1 + .../detection_engine_api_integration/utils.ts | 13 +- 50 files changed, 1176 insertions(+), 47 deletions(-) create mode 100644 x-pack/plugins/alerts/server/lib/alert_execution_status.test.ts create mode 100644 x-pack/plugins/alerts/server/lib/alert_execution_status.ts create mode 100644 x-pack/plugins/alerts/server/lib/error_with_reason.test.ts create mode 100644 x-pack/plugins/alerts/server/lib/error_with_reason.ts create mode 100644 x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts create mode 100644 x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts diff --git a/x-pack/plugins/alerts/common/alert.ts b/x-pack/plugins/alerts/common/alert.ts index 3ff7ed742e810f..4ebe66f7b7c9f2 100644 --- a/x-pack/plugins/alerts/common/alert.ts +++ b/x-pack/plugins/alerts/common/alert.ts @@ -15,6 +15,28 @@ export interface IntervalSchedule extends SavedObjectAttributes { interval: string; } +// for the `typeof ThingValues[number]` types below, become string types that +// only accept the values in the associated string arrays +export const AlertExecutionStatusValues = ['ok', 'active', 'error', 'pending', 'unknown'] as const; +export type AlertExecutionStatuses = typeof AlertExecutionStatusValues[number]; + +export const AlertExecutionStatusErrorReasonValues = [ + 'read', + 'decrypt', + 'execute', + 'unknown', +] as const; +export type AlertExecutionStatusErrorReasons = typeof AlertExecutionStatusErrorReasonValues[number]; + +export interface AlertExecutionStatus { + status: AlertExecutionStatuses; + lastExecutionDate: Date; + error?: { + reason: AlertExecutionStatusErrorReasons; + message: string; + }; +} + export type AlertActionParams = SavedObjectAttributes; export interface AlertAction { @@ -44,6 +66,7 @@ export interface Alert { throttle: string | null; muteAll: boolean; mutedInstanceIds: string[]; + executionStatus: AlertExecutionStatus; } export type SanitizedAlert = Omit<Alert, 'apiKey'>; diff --git a/x-pack/plugins/alerts/server/alerts_client.test.ts b/x-pack/plugins/alerts/server/alerts_client.test.ts index 088390c3cb6e77..b20018fcc26f78 100644 --- a/x-pack/plugins/alerts/server/alerts_client.test.ts +++ b/x-pack/plugins/alerts/server/alerts_client.test.ts @@ -393,6 +393,11 @@ describe('create()', () => { "createdAt": "2019-02-12T21:01:22.479Z", "createdBy": "elastic", "enabled": true, + "executionStatus": Object { + "error": null, + "lastExecutionDate": "2019-02-12T21:01:22.479Z", + "status": "pending", + }, "meta": Object { "versionApiKeyLastmodified": "v7.10.0", }, @@ -1034,6 +1039,11 @@ describe('create()', () => { muteAll: false, mutedInstanceIds: [], tags: ['foo'], + executionStatus: { + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending', + error: null, + }, }, { references: [ @@ -1150,6 +1160,11 @@ describe('create()', () => { muteAll: false, mutedInstanceIds: [], tags: ['foo'], + executionStatus: { + lastExecutionDate: '2019-02-12T21:01:22.479Z', + status: 'pending', + error: null, + }, }, { references: [ @@ -2506,6 +2521,11 @@ const BaseAlertInstanceSummarySavedObject: SavedObject<RawAlert> = { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: '2020-08-20T19:23:38Z', + error: null, + }, }, references: [], }; diff --git a/x-pack/plugins/alerts/server/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client.ts index bcc3c9bcf7e555..bd278d39c62294 100644 --- a/x-pack/plugins/alerts/server/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client.ts @@ -28,7 +28,7 @@ import { AlertTaskState, AlertInstanceSummary, } from './types'; -import { validateAlertTypeParams } from './lib'; +import { validateAlertTypeParams, alertExecutionStatusFromRaw } from './lib'; import { InvalidateAPIKeyParams, GrantAPIKeyResult as SecurityPluginGrantAPIKeyResult, @@ -122,6 +122,7 @@ export interface CreateOptions { | 'muteAll' | 'mutedInstanceIds' | 'actions' + | 'executionStatus' > & { actions: NormalizedAlertAction[] }; options?: { migrationVersion?: Record<string, string>; @@ -228,6 +229,11 @@ export class AlertsClient { params: validatedAlertTypeParams as RawAlert['params'], muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'pending', + lastExecutionDate: new Date().toISOString(), + error: null, + }, }; let createdAlert: SavedObject<RawAlert>; try { @@ -978,9 +984,19 @@ export class AlertsClient { updatedAt: SavedObject['updated_at'] = createdAt, references: SavedObjectReference[] | undefined ): PartialAlert { + // Not the prettiest code here, but if we want to use most of the + // alert fields from the rawAlert using `...rawAlert` kind of access, we + // need to specifically delete the executionStatus as it's a different type + // in RawAlert and Alert. Probably next time we need to do something similar + // here, we should look at redesigning the implementation of this method. + const rawAlertWithoutExecutionStatus: Partial<Omit<RawAlert, 'executionStatus'>> = { + ...rawAlert, + }; + delete rawAlertWithoutExecutionStatus.executionStatus; + const executionStatus = alertExecutionStatusFromRaw(this.logger, id, rawAlert.executionStatus); return { id, - ...rawAlert, + ...rawAlertWithoutExecutionStatus, // we currently only support the Interval Schedule type // Once we support additional types, this type signature will likely change schedule: rawAlert.schedule as IntervalSchedule, @@ -990,6 +1006,7 @@ export class AlertsClient { ...(updatedAt ? { updatedAt: new Date(updatedAt) } : {}), ...(createdAt ? { createdAt: new Date(createdAt) } : {}), ...(scheduledTaskId ? { scheduledTaskId } : {}), + ...(executionStatus ? { executionStatus } : {}), }; } diff --git a/x-pack/plugins/alerts/server/lib/alert_execution_status.test.ts b/x-pack/plugins/alerts/server/lib/alert_execution_status.test.ts new file mode 100644 index 00000000000000..3372d19cd40901 --- /dev/null +++ b/x-pack/plugins/alerts/server/lib/alert_execution_status.test.ts @@ -0,0 +1,185 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { loggingSystemMock } from '../../../../../src/core/server/mocks'; +import { AlertExecutionStatusErrorReasons } from '../types'; +import { + executionStatusFromState, + executionStatusFromError, + alertExecutionStatusToRaw, + alertExecutionStatusFromRaw, +} from './alert_execution_status'; +import { ErrorWithReason } from './error_with_reason'; + +const MockLogger = loggingSystemMock.create().get(); + +describe('AlertExecutionStatus', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + describe('executionStatusFromState()', () => { + test('empty task state', () => { + const status = executionStatusFromState({}); + checkDateIsNearNow(status.lastExecutionDate); + expect(status.status).toBe('ok'); + expect(status.error).toBe(undefined); + }); + + test('task state with no instances', () => { + const status = executionStatusFromState({ alertInstances: {} }); + checkDateIsNearNow(status.lastExecutionDate); + expect(status.status).toBe('ok'); + expect(status.error).toBe(undefined); + }); + + test('task state with one instance', () => { + const status = executionStatusFromState({ alertInstances: { a: {} } }); + checkDateIsNearNow(status.lastExecutionDate); + expect(status.status).toBe('active'); + expect(status.error).toBe(undefined); + }); + }); + + describe('executionStatusFromError()', () => { + test('error with no reason', () => { + const status = executionStatusFromError(new Error('boo!')); + expect(status.status).toBe('error'); + expect(status.error).toMatchInlineSnapshot(` + Object { + "message": "boo!", + "reason": "unknown", + } + `); + }); + + test('error with a reason', () => { + const status = executionStatusFromError(new ErrorWithReason('execute', new Error('hoo!'))); + expect(status.status).toBe('error'); + expect(status.error).toMatchInlineSnapshot(` + Object { + "message": "hoo!", + "reason": "execute", + } + `); + }); + }); + + describe('alertExecutionStatusToRaw()', () => { + const date = new Date('2020-09-03T16:26:58Z'); + const status = 'ok'; + const reason: AlertExecutionStatusErrorReasons = 'decrypt'; + const error = { reason, message: 'wops' }; + + test('status without an error', () => { + expect(alertExecutionStatusToRaw({ lastExecutionDate: date, status })).toMatchInlineSnapshot(` + Object { + "error": null, + "lastExecutionDate": "2020-09-03T16:26:58.000Z", + "status": "ok", + } + `); + }); + + test('status with an error', () => { + expect(alertExecutionStatusToRaw({ lastExecutionDate: date, status, error })) + .toMatchInlineSnapshot(` + Object { + "error": Object { + "message": "wops", + "reason": "decrypt", + }, + "lastExecutionDate": "2020-09-03T16:26:58.000Z", + "status": "ok", + } + `); + }); + }); + + describe('alertExecutionStatusFromRaw()', () => { + const date = new Date('2020-09-03T16:26:58Z').toISOString(); + const status = 'active'; + const reason: AlertExecutionStatusErrorReasons = 'execute'; + const error = { reason, message: 'wops' }; + + test('no input', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id'); + expect(result).toBe(undefined); + }); + + test('undefined input', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', undefined); + expect(result).toBe(undefined); + }); + + test('null input', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', null); + expect(result).toBe(undefined); + }); + + test('invalid date', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', { + lastExecutionDate: 'an invalid date', + })!; + checkDateIsNearNow(result.lastExecutionDate); + expect(result.status).toBe('unknown'); + expect(result.error).toBe(undefined); + expect(MockLogger.debug).toBeCalledWith( + 'invalid alertExecutionStatus lastExecutionDate "an invalid date" in raw alert alert-id' + ); + }); + + test('valid date', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', { + lastExecutionDate: date, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "lastExecutionDate": 2020-09-03T16:26:58.000Z, + "status": "unknown", + } + `); + }); + + test('valid status and date', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', { + status, + lastExecutionDate: date, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "lastExecutionDate": 2020-09-03T16:26:58.000Z, + "status": "active", + } + `); + }); + + test('valid status, date and error', () => { + const result = alertExecutionStatusFromRaw(MockLogger, 'alert-id', { + status, + lastExecutionDate: date, + error, + }); + expect(result).toMatchInlineSnapshot(` + Object { + "error": Object { + "message": "wops", + "reason": "execute", + }, + "lastExecutionDate": 2020-09-03T16:26:58.000Z, + "status": "active", + } + `); + }); + }); +}); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function checkDateIsNearNow(date: any) { + expect(date instanceof Date).toBe(true); + // allow for lots of slop in the time difference + expect(Date.now() - date.valueOf()).toBeLessThanOrEqual(10000); +} diff --git a/x-pack/plugins/alerts/server/lib/alert_execution_status.ts b/x-pack/plugins/alerts/server/lib/alert_execution_status.ts new file mode 100644 index 00000000000000..9eb0c8817f28c6 --- /dev/null +++ b/x-pack/plugins/alerts/server/lib/alert_execution_status.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Logger } from 'src/core/server'; +import { AlertTaskState, AlertExecutionStatus, RawAlertExecutionStatus } from '../types'; +import { getReasonFromError } from './error_with_reason'; + +export function executionStatusFromState(state: AlertTaskState): AlertExecutionStatus { + const instanceIds = Object.keys(state.alertInstances ?? {}); + return { + lastExecutionDate: new Date(), + status: instanceIds.length === 0 ? 'ok' : 'active', + }; +} + +export function executionStatusFromError(error: Error): AlertExecutionStatus { + return { + lastExecutionDate: new Date(), + status: 'error', + error: { + reason: getReasonFromError(error), + message: error.message, + }, + }; +} + +export function alertExecutionStatusToRaw({ + lastExecutionDate, + status, + error, +}: AlertExecutionStatus): RawAlertExecutionStatus { + return { + lastExecutionDate: lastExecutionDate.toISOString(), + status, + // explicitly setting to null (in case undefined) due to partial update concerns + error: error ?? null, + }; +} + +export function alertExecutionStatusFromRaw( + logger: Logger, + alertId: string, + rawAlertExecutionStatus?: Partial<RawAlertExecutionStatus> | null | undefined +): AlertExecutionStatus | undefined { + if (!rawAlertExecutionStatus) return undefined; + + const { lastExecutionDate, status = 'unknown', error } = rawAlertExecutionStatus; + + let parsedDateMillis = lastExecutionDate ? Date.parse(lastExecutionDate) : Date.now(); + if (isNaN(parsedDateMillis)) { + logger.debug( + `invalid alertExecutionStatus lastExecutionDate "${lastExecutionDate}" in raw alert ${alertId}` + ); + parsedDateMillis = Date.now(); + } + + const parsedDate = new Date(parsedDateMillis); + if (error) { + return { lastExecutionDate: parsedDate, status, error }; + } else { + return { lastExecutionDate: parsedDate, status }; + } +} diff --git a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts index b5936cf3577b36..566a1770c0658e 100644 --- a/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts +++ b/x-pack/plugins/alerts/server/lib/alert_instance_summary_from_event_log.test.ts @@ -511,4 +511,8 @@ const BaseAlert: SanitizedAlert = { createdAt: new Date(), updatedAt: new Date(), apiKeyOwner: null, + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; diff --git a/x-pack/plugins/alerts/server/lib/error_with_reason.test.ts b/x-pack/plugins/alerts/server/lib/error_with_reason.test.ts new file mode 100644 index 00000000000000..f31f5844003086 --- /dev/null +++ b/x-pack/plugins/alerts/server/lib/error_with_reason.test.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ErrorWithReason, getReasonFromError, isErrorWithReason } from './error_with_reason'; + +describe('ErrorWithReason', () => { + const plainError = new Error('well, actually'); + const errorWithReason = new ErrorWithReason('decrypt', plainError); + + test('ErrorWithReason class', () => { + expect(errorWithReason.message).toBe(plainError.message); + expect(errorWithReason.error).toBe(plainError); + expect(errorWithReason.reason).toBe('decrypt'); + }); + + test('getReasonFromError()', () => { + expect(getReasonFromError(plainError)).toBe('unknown'); + expect(getReasonFromError(errorWithReason)).toBe('decrypt'); + }); + + test('isErrorWithReason()', () => { + expect(isErrorWithReason(plainError)).toBe(false); + expect(isErrorWithReason(errorWithReason)).toBe(true); + }); +}); diff --git a/x-pack/plugins/alerts/server/lib/error_with_reason.ts b/x-pack/plugins/alerts/server/lib/error_with_reason.ts new file mode 100644 index 00000000000000..29eb666e644272 --- /dev/null +++ b/x-pack/plugins/alerts/server/lib/error_with_reason.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { AlertExecutionStatusErrorReasons } from '../types'; + +export class ErrorWithReason extends Error { + public readonly reason: AlertExecutionStatusErrorReasons; + public readonly error: Error; + + constructor(reason: AlertExecutionStatusErrorReasons, error: Error) { + super(error.message); + this.error = error; + this.reason = reason; + } +} + +export function getReasonFromError(error: Error): AlertExecutionStatusErrorReasons { + if (isErrorWithReason(error)) { + return error.reason; + } + return 'unknown'; +} + +export function isErrorWithReason(error: Error | ErrorWithReason): error is ErrorWithReason { + return error instanceof ErrorWithReason; +} diff --git a/x-pack/plugins/alerts/server/lib/index.ts b/x-pack/plugins/alerts/server/lib/index.ts index 2f610aafd8c31f..32047ae5cbfa83 100644 --- a/x-pack/plugins/alerts/server/lib/index.ts +++ b/x-pack/plugins/alerts/server/lib/index.ts @@ -7,3 +7,10 @@ export { parseDuration, validateDurationSchema } from '../../common/parse_duration'; export { LicenseState } from './license_state'; export { validateAlertTypeParams } from './validate_alert_type_params'; +export { ErrorWithReason, getReasonFromError, isErrorWithReason } from './error_with_reason'; +export { + executionStatusFromState, + executionStatusFromError, + alertExecutionStatusToRaw, + alertExecutionStatusFromRaw, +} from './alert_execution_status'; diff --git a/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.test.ts b/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.test.ts index 46ceee3ce420ba..b570957d82de4a 100644 --- a/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.test.ts +++ b/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.test.ts @@ -5,27 +5,27 @@ */ import { isAlertSavedObjectNotFoundError } from './is_alert_not_found_error'; +import { ErrorWithReason } from './error_with_reason'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import uuid from 'uuid'; describe('isAlertSavedObjectNotFoundError', () => { + const id = uuid.v4(); + const errorSONF = SavedObjectsErrorHelpers.createGenericNotFoundError('alert', id); + test('identifies SavedObjects Not Found errors', () => { - const id = uuid.v4(); // ensure the error created by SO parses as a string with the format we expect - expect( - `${SavedObjectsErrorHelpers.createGenericNotFoundError('alert', id)}`.includes(`alert/${id}`) - ).toBe(true); - - const errorBySavedObjectsHelper = SavedObjectsErrorHelpers.createGenericNotFoundError( - 'alert', - id - ); + expect(`${errorSONF}`.includes(`alert/${id}`)).toBe(true); - expect(isAlertSavedObjectNotFoundError(errorBySavedObjectsHelper, id)).toBe(true); + expect(isAlertSavedObjectNotFoundError(errorSONF, id)).toBe(true); }); test('identifies generic errors', () => { - const id = uuid.v4(); expect(isAlertSavedObjectNotFoundError(new Error(`not found`), id)).toBe(false); }); + + test('identifies SavedObjects Not Found errors wrapped in an ErrorWithReason', () => { + const error = new ErrorWithReason('read', errorSONF); + expect(isAlertSavedObjectNotFoundError(error, id)).toBe(true); + }); }); diff --git a/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.ts b/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.ts index 0aa83ad0e883ce..038e234586688a 100644 --- a/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.ts +++ b/x-pack/plugins/alerts/server/lib/is_alert_not_found_error.ts @@ -5,7 +5,13 @@ */ import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; +import { isErrorWithReason } from './error_with_reason'; export function isAlertSavedObjectNotFoundError(err: Error, alertId: string) { - return SavedObjectsErrorHelpers.isNotFoundError(err) && `${err}`.includes(alertId); + // if this is an error with a reason, the actual error needs to be extracted + const actualError = isErrorWithReason(err) ? err.error : err; + + return ( + SavedObjectsErrorHelpers.isNotFoundError(actualError) && `${actualError}`.includes(alertId) + ); } diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts index e9caf4b78e6279..4cdcac4c9e889f 100644 --- a/x-pack/plugins/alerts/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -264,6 +264,7 @@ export class AlertingPlugin { encryptedSavedObjectsClient, getBasePath: this.getBasePath, eventLogger: this.eventLogger!, + internalSavedObjectsRepository: core.savedObjects.createInternalRepository(['alert']), }); this.eventLogService!.registerSavedObjectProvider('alert', (request) => { diff --git a/x-pack/plugins/alerts/server/routes/create.test.ts b/x-pack/plugins/alerts/server/routes/create.test.ts index 274acaf01c4751..51c5d2525631d6 100644 --- a/x-pack/plugins/alerts/server/routes/create.test.ts +++ b/x-pack/plugins/alerts/server/routes/create.test.ts @@ -10,6 +10,7 @@ import { mockLicenseState } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { alertsClientMock } from '../alerts_client.mock'; +import { Alert } from '../../common/alert'; const alertsClient = alertsClientMock.create(); @@ -46,7 +47,7 @@ describe('createAlertRoute', () => { ], }; - const createResult = { + const createResult: Alert = { ...mockedAlert, enabled: true, muteAll: false, @@ -64,6 +65,10 @@ describe('createAlertRoute', () => { actionTypeId: 'test', }, ], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; it('creates an alert with proper parameters', async () => { diff --git a/x-pack/plugins/alerts/server/routes/get.test.ts b/x-pack/plugins/alerts/server/routes/get.test.ts index 8c4b06adf70f76..c60177e90b79d8 100644 --- a/x-pack/plugins/alerts/server/routes/get.test.ts +++ b/x-pack/plugins/alerts/server/routes/get.test.ts @@ -10,6 +10,7 @@ import { mockLicenseState } from '../lib/license_state.mock'; import { verifyApiAccess } from '../lib/license_api_access'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { alertsClientMock } from '../alerts_client.mock'; +import { Alert } from '../../common'; const alertsClient = alertsClientMock.create(); jest.mock('../lib/license_api_access.ts', () => ({ @@ -21,7 +22,7 @@ beforeEach(() => { }); describe('getAlertRoute', () => { - const mockedAlert = { + const mockedAlert: Alert = { id: '1', alertTypeId: '1', schedule: { interval: '10s' }, @@ -51,6 +52,10 @@ describe('getAlertRoute', () => { apiKeyOwner: '', throttle: '30s', mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; it('gets an alert with proper parameters', async () => { diff --git a/x-pack/plugins/alerts/server/saved_objects/index.ts b/x-pack/plugins/alerts/server/saved_objects/index.ts index 51ac68b5899776..9aa1f86676eaa6 100644 --- a/x-pack/plugins/alerts/server/saved_objects/index.ts +++ b/x-pack/plugins/alerts/server/saved_objects/index.ts @@ -16,15 +16,19 @@ export const AlertAttributesExcludedFromAAD = [ 'muteAll', 'mutedInstanceIds', 'updatedBy', + 'executionStatus', ]; // useful for Pick<RawAlert, AlertAttributesExcludedFromAADType> which is a // type which is a subset of RawAlert with just attributes excluded from AAD + +// useful for Pick<RawAlert, AlertAttributesExcludedFromAADType> export type AlertAttributesExcludedFromAADType = | 'scheduledTaskId' | 'muteAll' | 'mutedInstanceIds' - | 'updatedBy'; + | 'updatedBy' + | 'executionStatus'; export function setupSavedObjects( savedObjects: SavedObjectsServiceSetup, @@ -42,11 +46,6 @@ export function setupSavedObjects( encryptedSavedObjects.registerType({ type: 'alert', attributesToEncrypt: new Set(['apiKey']), - attributesToExcludeFromAAD: new Set([ - 'scheduledTaskId', - 'muteAll', - 'mutedInstanceIds', - 'updatedBy', - ]), + attributesToExcludeFromAAD: new Set(AlertAttributesExcludedFromAAD), }); } diff --git a/x-pack/plugins/alerts/server/saved_objects/mappings.json b/x-pack/plugins/alerts/server/saved_objects/mappings.json index 8440b963975ffc..a6c92080f18be5 100644 --- a/x-pack/plugins/alerts/server/saved_objects/mappings.json +++ b/x-pack/plugins/alerts/server/saved_objects/mappings.json @@ -83,6 +83,26 @@ "type": "keyword" } } + }, + "executionStatus": { + "properties": { + "status": { + "type": "keyword" + }, + "lastExecutionDate": { + "type": "date" + }, + "error": { + "properties": { + "reason": { + "type": "keyword" + }, + "message": { + "type": "keyword" + } + } + } + } } } } diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts index 10e1a9ae421b7d..8c9d10769b18a1 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.test.ts @@ -177,7 +177,7 @@ describe('7.10.0', () => { }, ], }); - expect(migration710(alert, { log })).toEqual({ + expect(migration710(alert, { log })).toMatchObject({ ...alert, attributes: { ...alert.attributes, @@ -199,6 +199,32 @@ describe('7.10.0', () => { }, }); }); + + test('creates execution status', () => { + const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0']; + const alert = getMockData(); + const dateStart = Date.now(); + const migratedAlert = migration710(alert, { log }); + const dateStop = Date.now(); + const dateExecutionStatus = Date.parse( + migratedAlert.attributes.executionStatus.lastExecutionDate + ); + + expect(dateStart).toBeLessThanOrEqual(dateExecutionStatus); + expect(dateStop).toBeGreaterThanOrEqual(dateExecutionStatus); + + expect(migratedAlert).toMatchObject({ + ...alert, + attributes: { + ...alert.attributes, + executionStatus: { + lastExecutionDate: migratedAlert.attributes.executionStatus.lastExecutionDate, + status: 'pending', + error: null, + }, + }, + }); + }); }); describe('7.10.0 migrates with failure', () => { @@ -237,7 +263,7 @@ describe('7.10.0 migrates with failure', () => { function getMockData( overwrites: Record<string, unknown> = {} -): SavedObjectUnsanitizedDoc<RawAlert> { +): SavedObjectUnsanitizedDoc<Partial<RawAlert>> { return { attributes: { enabled: true, diff --git a/x-pack/plugins/alerts/server/saved_objects/migrations.ts b/x-pack/plugins/alerts/server/saved_objects/migrations.ts index 537c21e85c0bd7..0b2c86b84f67b3 100644 --- a/x-pack/plugins/alerts/server/saved_objects/migrations.ts +++ b/x-pack/plugins/alerts/server/saved_objects/migrations.ts @@ -30,7 +30,11 @@ export function getMigrations( // migrate all documents in 7.10 in order to add the "meta" RBAC field return true; }, - pipeMigrations(markAsLegacyAndChangeConsumer, setAlertIdAsDefaultDedupkeyOnPagerDutyActions) + pipeMigrations( + markAsLegacyAndChangeConsumer, + setAlertIdAsDefaultDedupkeyOnPagerDutyActions, + initializeExecutionStatus + ) ); return { @@ -110,6 +114,23 @@ function setAlertIdAsDefaultDedupkeyOnPagerDutyActions( }; } +function initializeExecutionStatus( + doc: SavedObjectUnsanitizedDoc<RawAlert> +): SavedObjectUnsanitizedDoc<RawAlert> { + const { attributes } = doc; + return { + ...doc, + attributes: { + ...attributes, + executionStatus: { + status: 'pending', + lastExecutionDate: new Date().toISOString(), + error: null, + }, + }, + }; +} + function pipeMigrations(...migrations: AlertMigration[]): AlertMigration { return (doc: SavedObjectUnsanitizedDoc<RawAlert>) => migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc); diff --git a/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.ts b/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.ts index cc25aaba357981..b829a6788a3dd3 100644 --- a/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.ts +++ b/x-pack/plugins/alerts/server/saved_objects/partially_update_alert.ts @@ -15,7 +15,9 @@ import { import { AlertAttributesExcludedFromAAD, AlertAttributesExcludedFromAADType } from './index'; -export type PartiallyUpdateableAlertAttributes = Pick<RawAlert, AlertAttributesExcludedFromAADType>; +export type PartiallyUpdateableAlertAttributes = Partial< + Pick<RawAlert, AlertAttributesExcludedFromAADType> +>; export interface PartiallyUpdateAlertSavedObjectOptions { version?: string; diff --git a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts index efac4c5dcdc01f..cf0dd9d135e275 100644 --- a/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/alert_task_instance.test.ts @@ -29,6 +29,10 @@ const alert: SanitizedAlert = { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; describe('Alert Task Instance', () => { diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts index 801d30b6406ee5..d9af3d0ae6d5bd 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.test.ts @@ -11,14 +11,17 @@ import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server'; import { TaskRunnerContext } from './task_runner_factory'; import { TaskRunner } from './task_runner'; import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; -import { loggingSystemMock } from '../../../../../src/core/server/mocks'; +import { + loggingSystemMock, + savedObjectsRepositoryMock, +} from '../../../../../src/core/server/mocks'; import { PluginStartContract as ActionsPluginStart } from '../../../actions/server'; import { actionsMock, actionsClientMock } from '../../../actions/server/mocks'; import { alertsMock, alertsClientMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; import { IEventLogger } from '../../../event_log/server'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; - +import { Alert } from '../../common'; const alertType = { id: 'test', name: 'My test alert', @@ -71,9 +74,10 @@ describe('Task Runner', () => { spaceIdToNamespace: jest.fn().mockReturnValue(undefined), getBasePath: jest.fn().mockReturnValue(undefined), eventLogger: eventLoggerMock.create(), + internalSavedObjectsRepository: savedObjectsRepositoryMock.create(), }; - const mockedAlertTypeSavedObject = { + const mockedAlertTypeSavedObject: Alert = { id: '1', consumer: 'bar', createdAt: new Date('2019-02-12T21:01:22.479Z'), @@ -82,6 +86,7 @@ describe('Task Runner', () => { muteAll: false, enabled: true, alertTypeId: '123', + apiKey: '', apiKeyOwner: 'elastic', schedule: { interval: '10s' }, name: 'alert-name', @@ -102,6 +107,10 @@ describe('Task Runner', () => { }, }, ], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; beforeEach(() => { diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner.ts b/x-pack/plugins/alerts/server/task_runner/task_runner.ts index 7ea3f83d747c0c..1ccf14a3a53340 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner.ts @@ -11,7 +11,13 @@ import { ConcreteTaskInstance } from '../../../task_manager/server'; import { createExecutionHandler } from './create_execution_handler'; import { AlertInstance, createAlertInstanceFactory } from '../alert_instance'; import { getNextRunAt } from './get_next_run_at'; -import { validateAlertTypeParams } from '../lib'; +import { + validateAlertTypeParams, + executionStatusFromState, + executionStatusFromError, + alertExecutionStatusToRaw, + ErrorWithReason, +} from '../lib'; import { AlertType, RawAlert, @@ -22,6 +28,7 @@ import { Alert, AlertExecutorOptions, SanitizedAlert, + AlertExecutionStatus, } from '../types'; import { promiseResult, map, Resultable, asOk, asErr, resolveErr } from '../lib/result_type'; import { taskInstanceToAlertTaskInstance } from './alert_task_instance'; @@ -29,6 +36,7 @@ import { EVENT_LOG_ACTIONS } from '../plugin'; import { IEvent, IEventLogger, SAVED_OBJECT_REL_PRIMARY } from '../../../event_log/server'; import { isAlertSavedObjectNotFoundError } from '../lib/is_alert_not_found_error'; import { AlertsClient } from '../alerts_client'; +import { partiallyUpdateAlert } from '../saved_objects'; const FALLBACK_RETRY_INTERVAL: IntervalSchedule = { interval: '5m' }; @@ -204,7 +212,7 @@ export class TaskRunner { event.event = event.event || {}; event.event.outcome = 'failure'; eventLogger.logEvent(event); - throw err; + throw new ErrorWithReason('execute', err); } eventLogger.stopTiming(event); @@ -278,15 +286,22 @@ export class TaskRunner { const { params: { alertId, spaceId }, } = this.taskInstance; + let apiKey: string | null; + try { + apiKey = await this.getApiKeyForAlertPermissions(alertId, spaceId); + } catch (err) { + throw new ErrorWithReason('decrypt', err); + } + const [services, alertsClient] = this.getServicesWithSpaceLevelPermissions(spaceId, apiKey); - const apiKey = await this.getApiKeyForAlertPermissions(alertId, spaceId); - const [services, alertsClient] = await this.getServicesWithSpaceLevelPermissions( - spaceId, - apiKey - ); + let alert: SanitizedAlert; // Ensure API key is still valid and user has access - const alert = await alertsClient.get({ id: alertId }); + try { + alert = await alertsClient.get({ id: alertId }); + } catch (err) { + throw new ErrorWithReason('read', err); + } return { state: await promiseResult<AlertTaskState, Error>( @@ -306,12 +321,38 @@ export class TaskRunner { async run(): Promise<AlertTaskRunResult> { const { - params: { alertId }, + params: { alertId, spaceId }, startedAt: previousStartedAt, state: originalState, } = this.taskInstance; const { state, runAt } = await errorAsAlertTaskRunResult(this.loadAlertAttributesAndRun()); + const namespace = spaceId === 'default' ? undefined : spaceId; + + const executionStatus: AlertExecutionStatus = map( + state, + (alertTaskState: AlertTaskState) => executionStatusFromState(alertTaskState), + (err: Error) => executionStatusFromError(err) + ); + this.logger.debug( + `alertExecutionStatus for ${this.alertType.id}:${alertId}: ${JSON.stringify(executionStatus)}` + ); + + const client = this.context.internalSavedObjectsRepository; + const attributes = { + executionStatus: alertExecutionStatusToRaw(executionStatus), + }; + + try { + await partiallyUpdateAlert(client, alertId, attributes, { + ignore404: true, + namespace, + }); + } catch (err) { + this.logger.error( + `error updating alert execution status for ${this.alertType.id}:${alertId} ${err.message}` + ); + } return { state: map<AlertTaskState, Error, AlertTaskState>( diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts index 9af7d9ddc44eb3..5da8e4296f4dd1 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.test.ts @@ -8,7 +8,10 @@ import sinon from 'sinon'; import { ConcreteTaskInstance, TaskStatus } from '../../../task_manager/server'; import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory'; import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; -import { loggingSystemMock } from '../../../../../src/core/server/mocks'; +import { + loggingSystemMock, + savedObjectsRepositoryMock, +} from '../../../../../src/core/server/mocks'; import { actionsMock } from '../../../actions/server/mocks'; import { alertsMock, alertsClientMock } from '../mocks'; import { eventLoggerMock } from '../../../event_log/server/event_logger.mock'; @@ -63,6 +66,7 @@ describe('Task Runner Factory', () => { spaceIdToNamespace: jest.fn().mockReturnValue(undefined), getBasePath: jest.fn().mockReturnValue(undefined), eventLogger: eventLoggerMock.create(), + internalSavedObjectsRepository: savedObjectsRepositoryMock.create(), }; beforeEach(() => { diff --git a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts index 6f83e34cdbe031..944c4dc64ce7a0 100644 --- a/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts +++ b/x-pack/plugins/alerts/server/task_runner/task_runner_factory.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { Logger, KibanaRequest } from '../../../../../src/core/server'; +import { Logger, KibanaRequest, ISavedObjectsRepository } from '../../../../../src/core/server'; import { RunContext } from '../../../task_manager/server'; import { EncryptedSavedObjectsClient } from '../../../encrypted_saved_objects/server'; import { PluginStartContract as ActionsPluginStartContract } from '../../../actions/server'; @@ -26,6 +26,7 @@ export interface TaskRunnerContext { encryptedSavedObjectsClient: EncryptedSavedObjectsClient; spaceIdToNamespace: SpaceIdToNamespaceFunction; getBasePath: GetBasePathFunction; + internalSavedObjectsRepository: ISavedObjectsRepository; } export class TaskRunnerFactory { diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 8d568e8b7ecd1b..03d41724213ce6 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -24,6 +24,8 @@ import { AlertTypeState, AlertInstanceContext, AlertInstanceState, + AlertExecutionStatuses, + AlertExecutionStatusErrorReasons, } from '../common'; export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>; @@ -115,6 +117,18 @@ export interface AlertMeta extends SavedObjectAttributes { versionApiKeyLastmodified?: string; } +// note that the `error` property is "null-able", as we're doing a partial +// update on the alert when we update this data, but need to ensure we +// delete any previous error if the current status has no error +export interface RawAlertExecutionStatus extends SavedObjectAttributes { + status: AlertExecutionStatuses; + lastExecutionDate: string; + error: null | { + reason: AlertExecutionStatusErrorReasons; + message: string; + }; +} + export type PartialAlert = Pick<Alert, 'id'> & Partial<Omit<Alert, 'id'>>; export interface RawAlert extends SavedObjectAttributes { @@ -136,6 +150,7 @@ export interface RawAlert extends SavedObjectAttributes { muteAll: boolean; mutedInstanceIds: string[]; meta?: AlertMeta; + executionStatus: RawAlertExecutionStatus; } export type AlertInfoParams = Pick< 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 5d9cfb4bb44928..9081831c454978 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 @@ -419,6 +419,10 @@ export const getResult = (): RuleAlertType => ({ muteAll: false, mutedInstanceIds: [], scheduledTaskId: '2dabe330-0702-11ea-8b50-773b89126888', + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }); export const getMlResult = (): RuleAlertType => { @@ -630,6 +634,10 @@ export const getNotificationResult = (): RuleNotificationAlertType => ({ mutedInstanceIds: [], scheduledTaskId: '62b3a130-6b70-11ea-9ce9-6b9818c4cbd7', updatedAt: new Date('2020-03-21T12:37:08.730Z'), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }); export const getFindNotificationsResultWithSingleHit = (): FindHit<RuleNotificationAlertType> => ({ 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 aeb136a969aa18..8672c85f984269 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 @@ -110,6 +110,10 @@ const rule: SanitizedAlert = { muteAll: false, mutedInstanceIds: [], scheduledTaskId: '2dabe330-0702-11ea-8b50-773b89126888', + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; export const getPatchRulesOptionsMock = (): PatchRulesOptions => ({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts index fc5d301cb7cd03..f6cefb77a240e8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts @@ -398,6 +398,10 @@ describe('createAlert', () => { updatedBy: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; http.post.mockResolvedValueOnce(resolvedValue); @@ -440,6 +444,10 @@ describe('updateAlert', () => { updatedBy: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; http.put.mockResolvedValueOnce(resolvedValue); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts index 97feea6ba8a0f8..d5711a3e8c919a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts @@ -136,7 +136,10 @@ export async function createAlert({ alert, }: { http: HttpSetup; - alert: Omit<AlertWithoutId, 'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds'>; + alert: Omit< + AlertWithoutId, + 'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds' | 'executionStatus' + >; }): Promise<Alert> { return await http.post(`${BASE_ALERT_API_PATH}/alert`, { body: JSON.stringify(alert), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx index 16d1a5c7c9c652..5c9969221cfc35 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx @@ -757,6 +757,10 @@ function mockAlert(overloads: Partial<Alert> = {}): Alert { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, ...overloads, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx index 7a40104e97d9fd..5ed924c37fe7a5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details_route.test.tsx @@ -404,6 +404,10 @@ function mockAlert(overloads: Partial<Alert> = {}): Alert { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, ...overloads, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.test.tsx index f59b836a7936e0..2c1020ff1d5b36 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances.test.tsx @@ -254,6 +254,10 @@ function mockAlert(overloads: Partial<Alert> = {}): Alert { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, ...overloads, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx index d92148a8fea531..603f06d0bbae44 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_instances_route.test.tsx @@ -132,6 +132,10 @@ function mockAlert(overloads: Partial<Alert> = {}): Alert { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, ...overloads, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx index 54d335aaba5aa9..7e43fd22ff8c8e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/view_in_app.test.tsx @@ -105,6 +105,10 @@ function mockAlert(overloads: Partial<Alert> = {}): Alert { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, ...overloads, }; } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx index e408c7fcb81441..24eb7aabb95493 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_edit.test.tsx @@ -8,7 +8,7 @@ import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { act } from 'react-dom/test-utils'; import { coreMock } from '../../../../../../../src/core/public/mocks'; import { actionTypeRegistryMock } from '../../action_type_registry.mock'; -import { ValidationResult } from '../../../types'; +import { ValidationResult, Alert } from '../../../types'; import { AlertsContextProvider } from '../../context/alerts_context'; import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; import { ReactWrapper } from 'enzyme'; @@ -73,7 +73,7 @@ describe('alert_edit', () => { actionParamsFields: null, }; - const alert = { + const alert: Alert = { id: 'ab5661e0-197e-45ee-b477-302d89193b5e', params: { aggType: 'average', @@ -93,7 +93,6 @@ describe('alert_edit', () => { actionTypeId: 'my-action-type', group: 'threshold met', params: { message: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold' }, - message: 'Alert [{{ctx.metadata.name}}] has exceeded the threshold', id: '917f5d41-fbc4-4056-a8ad-ac592f7dcee2', }, ], @@ -107,6 +106,10 @@ describe('alert_edit', () => { muteAll: false, mutedInstanceIds: [], updatedAt: new Date(), + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, }; actionTypeRegistry.get.mockReturnValueOnce(actionTypeModel); actionTypeRegistry.has.mockReturnValue(true); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx index 074e2d5147b5ec..72d4f8857a610a 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/common/components/with_bulk_alert_api_operations.test.tsx @@ -264,6 +264,10 @@ function mockAlert(overloads: Partial<Alert> = {}): Alert { throttle: null, muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'unknown', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + }, ...overloads, }; } diff --git a/x-pack/test/alerting_api_integration/common/lib/test_assertions.ts b/x-pack/test/alerting_api_integration/common/lib/test_assertions.ts index 9495dd4cfae82a..6124a5fb7c358e 100644 --- a/x-pack/test/alerting_api_integration/common/lib/test_assertions.ts +++ b/x-pack/test/alerting_api_integration/common/lib/test_assertions.ts @@ -15,3 +15,21 @@ export function ensureDatetimeIsWithinRange( expect(diff).to.be.greaterThan(expectedDiff - buffer); expect(diff).to.be.lessThan(expectedDiff + buffer); } + +export function ensureDatetimesAreOrdered(dates: Array<Date | string | number>) { + const dateStrings = dates.map(normalizeDate); + const sortedDateStrings = dateStrings.slice().sort(); + expect(dateStrings).to.eql(sortedDateStrings); +} + +function normalizeDate(date: Date | string | number): string { + if (typeof date === 'number') return new Date(date).toISOString(); + if (date instanceof Date) return date.toISOString(); + + const dateString = `${date}`; + const dateNumber = Date.parse(dateString); + if (isNaN(dateNumber)) { + throw new Error(`invalid date string: "${dateString}"`); + } + return new Date(dateNumber).toISOString(); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts index 983f87405a1a64..19d90378e8b7a8 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/create.ts @@ -119,6 +119,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { apiKeyOwner: user.username, muteAll: false, mutedInstanceIds: [], + executionStatus: response.body.executionStatus, }); expect(typeof response.body.scheduledTaskId).to.be('string'); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts new file mode 100644 index 00000000000000..8fb89042e4a903 --- /dev/null +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/execution_status.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { Spaces } from '../../scenarios'; +import { getUrlPrefix, getTestAlertData, ObjectRemover } from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function executionStatusAlertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const spaceId = Spaces[0].id; + + // the only tests here are those that can't be run in spaces_only + describe('executionStatus', () => { + const objectRemover = new ObjectRemover(supertest); + + after(async () => await objectRemover.removeAll()); + + it('should eventually have error reason "decrypt" when appropriate', async () => { + const response = await supertest + .post(`${getUrlPrefix(spaceId)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.noop', + schedule: { interval: '1s' }, + }) + ); + expect(response.status).to.eql(200); + const alertId = response.body.id; + objectRemover.add(spaceId, alertId, 'alert', 'alerts'); + + let executionStatus = await waitForStatus(alertId, new Set(['ok']), 10000); + + // break AAD + await supertest + .put(`${getUrlPrefix(spaceId)}/api/alerts_fixture/saved_object/alert/${alertId}`) + .set('kbn-xsrf', 'foo') + .send({ + attributes: { + name: 'bar', + }, + }) + .expect(200); + + executionStatus = await waitForStatus(alertId, new Set(['error'])); + expect(executionStatus.error).to.be.ok(); + expect(executionStatus.error.reason).to.be('decrypt'); + expect(executionStatus.error.message).to.be('Unable to decrypt attribute "apiKey"'); + }); + }); + + const WaitForStatusIncrement = 500; + + async function waitForStatus( + id: string, + statuses: Set<string>, + waitMillis: number = 10000 + ): Promise<Record<string, any>> { + if (waitMillis < 0) { + expect().fail(`waiting for alert ${id} statuses ${Array.from(statuses)} timed out`); + } + + const response = await supertest.get(`${getUrlPrefix(spaceId)}/api/alerts/alert/${id}`); + expect(response.status).to.eql(200); + const { status } = response.body.executionStatus; + if (statuses.has(status)) return response.body.executionStatus; + + // eslint-disable-next-line no-console + console.log( + `waitForStatus(${Array.from(statuses)}): got ${JSON.stringify( + response.body.executionStatus + )}, retrying` + ); + + await delay(WaitForStatusIncrement); + return await waitForStatus(id, statuses, waitMillis - WaitForStatusIncrement); + } +} + +async function delay(millis: number): Promise<void> { + await new Promise((resolve) => setTimeout(resolve, millis)); +} diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts index 268212d4294d0c..adfe5cd27b33a1 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/find.ts @@ -79,6 +79,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { apiKeyOwner: 'elastic', muteAll: false, mutedInstanceIds: [], + executionStatus: match.executionStatus, }); expect(Date.parse(match.createdAt)).to.be.greaterThan(0); expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); @@ -273,6 +274,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { mutedInstanceIds: [], createdAt: match.createdAt, updatedAt: match.updatedAt, + executionStatus: match.executionStatus, }); expect(Date.parse(match.createdAt)).to.be.greaterThan(0); expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); @@ -359,6 +361,85 @@ export default function createFindTests({ getService }: FtrProviderContext) { } }); + it('should handle find alert request with executionStatus field appropriately', async () => { + const myTag = uuid.v4(); + const { body: createdAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + enabled: false, + tags: [myTag], + alertTypeId: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdAlert.id, 'alert', 'alerts'); + + // create another type with same tag + const { body: createdSecondAlert } = await supertest + .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + tags: [myTag], + alertTypeId: 'test.restricted-noop', + consumer: 'alertsRestrictedFixture', + }) + ) + .expect(200); + objectRemover.add(space.id, createdSecondAlert.id, 'alert', 'alerts'); + + const response = await supertestWithoutAuth + .get( + `${getUrlPrefix( + space.id + )}/api/alerts/_find?filter=alert.attributes.alertTypeId:test.restricted-noop&fields=["tags","executionStatus"]&sort_field=createdAt` + ) + .auth(user.username, user.password); + + switch (scenario.id) { + case 'no_kibana_privileges at space1': + case 'space_1_all at space2': + expect(response.statusCode).to.eql(403); + expect(response.body).to.eql({ + error: 'Forbidden', + message: `Unauthorized to find any alert types`, + statusCode: 403, + }); + break; + case 'space_1_all at space1': + case 'space_1_all_alerts_none_actions at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.data).to.eql([]); + break; + case 'global_read at space1': + case 'superuser at space1': + case 'space_1_all_with_restricted_fixture at space1': + expect(response.statusCode).to.eql(200); + expect(response.body.page).to.equal(1); + expect(response.body.perPage).to.be.greaterThan(0); + expect(response.body.total).to.be.greaterThan(0); + const [matchFirst, matchSecond] = response.body.data; + expect(omit(matchFirst, 'updatedAt')).to.eql({ + id: createdAlert.id, + actions: [], + tags: [myTag], + executionStatus: matchFirst.executionStatus, + }); + expect(omit(matchSecond, 'updatedAt')).to.eql({ + id: createdSecondAlert.id, + actions: [], + tags: [myTag], + executionStatus: matchSecond.executionStatus, + }); + break; + default: + throw new Error(`Scenario untested: ${JSON.stringify(scenario)}`); + } + }); + it(`shouldn't find alert from another space`, async () => { const { body: createdAlert } = await supertest .post(`${getUrlPrefix(space.id)}/api/alerts/alert`) diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts index 1043ece08a2ac0..93e9be771ab5c6 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/get.ts @@ -75,6 +75,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { apiKeyOwner: 'elastic', muteAll: false, mutedInstanceIds: [], + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts index fa0130780cb692..1fbee9e18fdaa2 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/index.ts @@ -14,6 +14,7 @@ export default function alertingTests({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./delete')); loadTestFile(require.resolve('./disable')); loadTestFile(require.resolve('./enable')); + loadTestFile(require.resolve('./execution_status')); loadTestFile(require.resolve('./get')); loadTestFile(require.resolve('./get_alert_state')); loadTestFile(require.resolve('./get_alert_instance_summary')); diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts index 48269cc1c4498f..d75aa868253deb 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/alerting/update.ts @@ -129,6 +129,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { scheduledTaskId: createdAlert.scheduledTaskId, createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); @@ -211,6 +212,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { scheduledTaskId: createdAlert.scheduledTaskId, createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); @@ -304,6 +306,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { scheduledTaskId: createdAlert.scheduledTaskId, createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); @@ -397,6 +400,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { scheduledTaskId: createdAlert.scheduledTaskId, createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); @@ -486,6 +490,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { scheduledTaskId: createdAlert.scheduledTaskId, createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts index 86775f77a7671c..41f6b66c30aafa 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/create.ts @@ -87,6 +87,7 @@ export default function createAlertTests({ getService }: FtrProviderContext) { mutedInstanceIds: [], createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts new file mode 100644 index 00000000000000..ac63fe8faadc7c --- /dev/null +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts @@ -0,0 +1,333 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { Spaces } from '../../scenarios'; +import { + checkAAD, + getUrlPrefix, + getTestAlertData, + ObjectRemover, + ensureDatetimesAreOrdered, +} from '../../../common/lib'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function executionStatusAlertTests({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + + describe('executionStatus', () => { + const objectRemover = new ObjectRemover(supertest); + + after(async () => await objectRemover.removeAll()); + + it('should be "pending" for newly created alert', async () => { + const dateStart = Date.now(); + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()); + const dateEnd = Date.now(); + expect(response.status).to.eql(200); + objectRemover.add(Spaces.space1.id, response.body.id, 'alert', 'alerts'); + + expect(response.body.executionStatus).to.be.ok(); + const { status, lastExecutionDate, error } = response.body.executionStatus; + expect(status).to.be('pending'); + ensureDatetimesAreOrdered([dateStart, lastExecutionDate, dateEnd]); + expect(error).not.to.be.ok(); + + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: Spaces.space1.id, + type: 'alert', + id: response.body.id, + }); + }); + + it('should eventually be "ok" for no-op alert', async () => { + const dates = []; + dates.push(Date.now()); + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.noop', + schedule: { interval: '1s' }, + }) + ); + expect(response.status).to.eql(200); + const alertId = response.body.id; + dates.push(response.body.executionStatus.lastExecutionDate); + dates.push(Date.now()); + objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + + const executionStatus = await waitForStatus(alertId, new Set(['ok'])); + dates.push(executionStatus.lastExecutionDate); + dates.push(Date.now()); + ensureDatetimesAreOrdered(dates); + + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: Spaces.space1.id, + type: 'alert', + id: response.body.id, + }); + }); + + it('should eventually be "active" for firing alert', async () => { + const dates = []; + dates.push(Date.now()); + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.patternFiring', + schedule: { interval: '1s' }, + params: { + pattern: { instance: trues(100) }, + }, + }) + ); + expect(response.status).to.eql(200); + const alertId = response.body.id; + dates.push(response.body.executionStatus.lastExecutionDate); + dates.push(Date.now()); + objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + + const executionStatus = await waitForStatus(alertId, new Set(['active'])); + dates.push(executionStatus.lastExecutionDate); + dates.push(Date.now()); + ensureDatetimesAreOrdered(dates); + + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: Spaces.space1.id, + type: 'alert', + id: response.body.id, + }); + }); + + it('should eventually be "error" for an error alert', async () => { + const dates = []; + dates.push(Date.now()); + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.throw', + schedule: { interval: '1s' }, + }) + ); + expect(response.status).to.eql(200); + const alertId = response.body.id; + dates.push(response.body.executionStatus.lastExecutionDate); + dates.push(Date.now()); + objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + + const executionStatus = await waitForStatus(alertId, new Set(['error'])); + dates.push(executionStatus.lastExecutionDate); + dates.push(Date.now()); + ensureDatetimesAreOrdered(dates); + + // Ensure AAD isn't broken + await checkAAD({ + supertest, + spaceId: Spaces.space1.id, + type: 'alert', + id: response.body.id, + }); + }); + + // not sure how to test the read error reason! + + // note the decrypt error reason is tested in security_and_spaces, can't be tested + // without security on + + it('should eventually have error reason "execute" when appropriate', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.throw', + schedule: { interval: '1s' }, + }) + ); + expect(response.status).to.eql(200); + const alertId = response.body.id; + objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + + const executionStatus = await waitForStatus(alertId, new Set(['error'])); + expect(executionStatus.error).to.be.ok(); + expect(executionStatus.error.reason).to.be('execute'); + expect(executionStatus.error.message).to.be('this alert is intended to fail'); + }); + + it('should eventually have error reason "unknown" when appropriate', async () => { + const response = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.validation', + schedule: { interval: '1s' }, + params: { param1: 'valid now, but will change to a number soon!' }, + }) + ); + expect(response.status).to.eql(200); + const alertId = response.body.id; + objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + + let executionStatus = await waitForStatus(alertId, new Set(['ok'])); + + // break the validation of the params + await supertest + .put(`${getUrlPrefix(Spaces.space1.id)}/api/alerts_fixture/saved_object/alert/${alertId}`) + .set('kbn-xsrf', 'foo') + .send({ + attributes: { + params: { param1: 42 }, + }, + }) + .expect(200); + + executionStatus = await waitForStatus(alertId, new Set(['error'])); + expect(executionStatus.error).to.be.ok(); + expect(executionStatus.error.reason).to.be('unknown'); + + const message = 'params invalid: [param1]: expected value of type [string] but got [number]'; + expect(executionStatus.error.message).to.be(message); + }); + + it('should be able to find over all the fields', async () => { + const startDate = Date.now(); + const createResponse = await supertest + .post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.throw', + schedule: { interval: '1s' }, + }) + ); + expect(createResponse.status).to.eql(200); + const alertId = createResponse.body.id; + objectRemover.add(Spaces.space1.id, alertId, 'alert', 'alerts'); + + await waitForStatus(alertId, new Set(['error'])); + + let filter = `lastExecutionDate>${startDate}`; + let executionStatus = await waitForFindStatus(alertId, new Set(['error']), filter); + expectErrorExecutionStatus(executionStatus, startDate); + + filter = `status:error`; + executionStatus = await waitForFindStatus(alertId, new Set(['error']), filter); + expectErrorExecutionStatus(executionStatus, startDate); + + filter = `error.message:*intended*`; + executionStatus = await waitForFindStatus(alertId, new Set(['error']), filter); + expectErrorExecutionStatus(executionStatus, startDate); + + filter = `error.reason:execute`; + executionStatus = await waitForFindStatus(alertId, new Set(['error']), filter); + expectErrorExecutionStatus(executionStatus, startDate); + }); + }); + + const WaitForStatusIncrement = 500; + + async function waitForStatus( + id: string, + statuses: Set<string>, + waitMillis: number = 10000 + ): Promise<Record<string, any>> { + if (waitMillis < 0) { + expect().fail(`waiting for alert ${id} statuses ${Array.from(statuses)} timed out`); + } + + const response = await supertest.get( + `${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert/${id}` + ); + expect(response.status).to.eql(200); + const { status } = response.body.executionStatus; + + const message = `waitForStatus(${Array.from(statuses)}): got ${JSON.stringify( + response.body.executionStatus + )}`; + + if (statuses.has(status)) { + return response.body.executionStatus; + } + + // eslint-disable-next-line no-console + console.log(`${message}, retrying`); + + await delay(WaitForStatusIncrement); + return await waitForStatus(id, statuses, waitMillis - WaitForStatusIncrement); + } + + async function waitForFindStatus( + id: string, + statuses: Set<string>, + filter: string, + waitMillis: number = 10000 + ): Promise<Record<string, any>> { + if (waitMillis < 0) { + expect().fail(`waiting for find alert ${id} statuses ${Array.from(statuses)} timed out`); + } + + const findUri = getFindUri(filter); + const response = await supertest.get(`${getUrlPrefix(Spaces.space1.id)}/${findUri}`); + + expect(response.status).to.eql(200); + const { executionStatus } = response.body.data.find((obj: any) => obj.id === id); + + const message = `waitForFindStatus(${Array.from(statuses)}): got ${JSON.stringify( + executionStatus + )}`; + + if (statuses.has(executionStatus.status)) { + return executionStatus; + } + + // eslint-disable-next-line no-console + console.log(`${message}, retrying`); + + await delay(WaitForStatusIncrement); + return await waitForStatus(id, statuses, waitMillis - WaitForStatusIncrement); + } +} + +function expectErrorExecutionStatus(executionStatus: Record<string, any>, startDate: number) { + expect(executionStatus.status).to.equal('error'); + + const statusDate = Date.parse(executionStatus.lastExecutionDate); + const stopDate = Date.now(); + expect(startDate).to.be.lessThan(statusDate); + expect(stopDate).to.be.greaterThan(statusDate); + + expect(executionStatus.error.message).to.equal('this alert is intended to fail'); + expect(executionStatus.error.reason).to.equal('execute'); +} + +function getFindUri(filter: string) { + return `api/alerts/_find?filter=alert.attributes.executionStatus.${filter}`; +} + +function trues(length: number): boolean[] { + return new Array(length).fill(true); +} + +async function delay(millis: number): Promise<void> { + await new Promise((resolve) => setTimeout(resolve, millis)); +} diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts index b28ce89b304724..850ec24789f5b1 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/find.ts @@ -56,6 +56,7 @@ export default function createFindTests({ getService }: FtrProviderContext) { mutedInstanceIds: [], createdAt: match.createdAt, updatedAt: match.updatedAt, + executionStatus: match.executionStatus, }); expect(Date.parse(match.createdAt)).to.be.greaterThan(0); expect(Date.parse(match.updatedAt)).to.be.greaterThan(0); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts index 165eaa09126a81..14a57f57c9237d 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/get.ts @@ -50,6 +50,7 @@ export default function createGetTests({ getService }: FtrProviderContext) { mutedInstanceIds: [], createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts index 1907eed728053e..a80970788e517d 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/index.ts @@ -23,6 +23,7 @@ export default function alertingTests({ loadTestFile, getService }: FtrProviderC loadTestFile(require.resolve('./get_alert_instance_summary')); loadTestFile(require.resolve('./list_alert_types')); loadTestFile(require.resolve('./event_log')); + loadTestFile(require.resolve('./execution_status')); loadTestFile(require.resolve('./mute_all')); loadTestFile(require.resolve('./mute_instance')); loadTestFile(require.resolve('./unmute_all')); diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts index 9c8e6f6b8d94c8..f44a7d71318799 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/update.ts @@ -57,6 +57,7 @@ export default function createUpdateTests({ getService }: FtrProviderContext) { scheduledTaskId: createdAlert.scheduledTaskId, createdAt: response.body.createdAt, updatedAt: response.body.updatedAt, + executionStatus: response.body.executionStatus, }); expect(Date.parse(response.body.createdAt)).to.be.greaterThan(0); expect(Date.parse(response.body.updatedAt)).to.be.greaterThan(0); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 1dba1a154373b3..5d82eed41d3c5a 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -248,16 +248,25 @@ export const getSimpleMlRuleOutput = (ruleId = 'rule-1'): Partial<RulesSchema> = export const deleteAllAlerts = async (es: Client, retryCount = 20): Promise<void> => { if (retryCount > 0) { try { - await es.deleteByQuery({ + const result = await es.deleteByQuery({ index: '.kibana', q: 'type:alert', wait_for_completion: true, refresh: true, + conflicts: 'proceed', body: {}, }); + // deleteByQuery will cause version conflicts as alerts are being updated + // by background processes; the code below accounts for that + if (result.body.version_conflicts !== 0) { + throw new Error(`Version conflicts for ${result.body.version_conflicts} alerts`); + } } catch (err) { // eslint-disable-next-line no-console - console.log(`Failure trying to deleteAllAlerts, retries left are: ${retryCount - 1}`, err); + console.log(`Error in deleteAllAlerts(), retries left: ${retryCount - 1}`, err); + + // retry, counting down, and delay a bit before + await new Promise((resolve) => setTimeout(resolve, 250)); await deleteAllAlerts(es, retryCount - 1); } } else { From d6c712842d86482d23534298315d3f849384496d Mon Sep 17 00:00:00 2001 From: Frank Hassanabad <frank.hassanabad@elastic.co> Date: Thu, 1 Oct 2020 16:31:00 -0600 Subject: [PATCH 061/128] [Security Solution][Detection Engine] Adds threat matching to the rule creator (#78955) ## Summary This adds threat matching rule type to the rule creator. Screen shot of creating a threat match <img width="1023" alt="Screen Shot 2020-09-30 at 3 31 09 PM" src="https://user-images.githubusercontent.com/1151048/94742158-791b1c00-0332-11eb-9d79-78ab431322f0.png"> --- Screen shot of the description after creating one <img width="1128" alt="Screen Shot 2020-09-30 at 3 29 32 PM" src="https://user-images.githubusercontent.com/1151048/94742203-8b955580-0332-11eb-837f-5b4383044a13.png"> --- Screen shot of first creating a threat match without values filled out <img width="1017" alt="Screen Shot 2020-09-30 at 3 27 29 PM" src="https://user-images.githubusercontent.com/1151048/94742222-95b75400-0332-11eb-9872-e7670e917941.png"> Additions and bug fixes: * Changes the threat index to be an array * Adds a threat_language to the REST schema so that we can use KQL, Lucene, (others in the future) * Adds plumbing for threat_list to work with the other REST endpoints such as PUT, PATCH, etc... * Adds the AND, OR dialog and user interface **Usage** If you are a team member using the team servers you can skip this usage section of creating threat index. Otherwise if you want to know how to create a mock threat index, instructions are below. Go to the folder: ```ts /kibana/x-pack/plugins/security_solution/server/lib/detection_engine/scripts ``` And post a small ECS threat mapping to the index called `mock-threat-list`: ```ts ./create_threat_mapping.sh ``` Then to post a small number of threats that represent simple port numbers you can run: ```ts ./create_threat_data.sh ``` However, feel free to also manually create them directly in your dev tools like so: ```ts # Posts a threat list item called some-name with an IP but change these out for valid data in your system PUT mock-threat-list-1/_doc/9999 { "@timestamp": "2020-09-09T20:30:45.725Z", "host": { "name": "some-name", "ip": "127.0.0.1" } } ``` ```ts # Posts a destination port number to watch PUT mock-threat-list-1/_doc/10000 { "@timestamp": "2020-09-08T20:30:45.725Z", "destination": { "port": "443" } } ``` ```ts # Posts a source port number to watch PUT mock-threat-list-1/_doc/10001 { "@timestamp": "2020-09-08T20:30:45.725Z", "source": { "port": "443" } } ``` ### Checklist - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md) - [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 - [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) --- .../add_prepackaged_rules_schema.mock.ts | 4 +- .../request/add_prepackaged_rules_schema.ts | 2 + .../request/create_rules_schema.mock.ts | 4 +- .../schemas/request/create_rules_schema.ts | 2 + .../request/import_rules_schema.mock.ts | 4 +- .../schemas/request/import_rules_schema.ts | 2 + .../schemas/request/patch_rules_schema.ts | 12 + .../schemas/request/update_rules_schema.ts | 12 + .../schemas/response/rules_schema.mocks.ts | 2 +- .../schemas/response/rules_schema.test.ts | 4 +- .../schemas/response/rules_schema.ts | 3 + .../detection_engine/schemas/types/index.ts | 1 + .../schemas/types/threat_mapping.ts | 40 +- .../threat_match/and_badge.test.tsx | 46 +++ .../components/threat_match/and_badge.tsx | 50 +++ .../threat_match/entry_delete_button.test.tsx | 123 ++++++ .../threat_match/entry_delete_button.tsx | 67 +++ .../threat_match/entry_item.test.tsx | 130 ++++++ .../components/threat_match/entry_item.tsx | 131 ++++++ .../components/threat_match/helpers.test.tsx | 225 +++++++++++ .../components/threat_match/helpers.tsx | 171 ++++++++ .../components/threat_match/index.test.tsx | 304 ++++++++++++++ .../common/components/threat_match/index.tsx | 220 ++++++++++ .../threat_match/list_item.test.tsx | 382 ++++++++++++++++++ .../components/threat_match/list_item.tsx | 120 ++++++ .../threat_match/logic_buttons.stories.tsx | 49 +++ .../threat_match/logic_buttons.test.tsx | 90 +++++ .../components/threat_match/logic_buttons.tsx | 55 +++ .../components/threat_match/reducer.test.ts | 101 +++++ .../common/components/threat_match/reducer.ts | 51 +++ .../components/threat_match/translations.ts | 37 ++ .../common/components/threat_match/types.ts | 26 ++ .../rules/description_step/helpers.tsx | 42 +- .../rules/description_step/index.tsx | 22 +- .../rules/description_step/translations.tsx | 7 + .../rules/description_step/types.ts | 1 + .../rules/select_rule_type/index.tsx | 23 ++ .../rules/select_rule_type/translations.ts | 14 + .../rules/step_define_rule/index.tsx | 63 ++- .../rules/step_define_rule/schema.tsx | 136 ++++++- .../rules/step_define_rule/translations.tsx | 21 + .../rules/threatmatch_input/index.tsx | 114 ++++++ .../rules/threatmatch_input/translations.ts | 14 + .../detection_engine/rules/types.ts | 14 +- .../rules/all/__mocks__/mock.ts | 3 + .../detection_engine/rules/create/helpers.ts | 80 +++- .../detection_engine/rules/create/index.tsx | 8 +- .../detection_engine/rules/helpers.test.tsx | 30 ++ .../pages/detection_engine/rules/helpers.tsx | 8 +- .../pages/detection_engine/rules/types.ts | 9 +- .../routes/__mocks__/request_responses.ts | 1 + .../routes/rules/create_rules_bulk_route.ts | 2 + .../routes/rules/create_rules_route.ts | 2 + .../routes/rules/import_rules_route.ts | 9 +- .../routes/rules/patch_rules_bulk_route.ts | 10 + .../routes/rules/patch_rules_route.ts | 10 + .../routes/rules/update_rules_bulk_route.ts | 10 + .../routes/rules/update_rules_route.ts | 10 + .../routes/rules/utils.test.ts | 4 +- .../detection_engine/routes/rules/utils.ts | 1 + .../rules/create_rules.mock.ts | 2 + .../detection_engine/rules/create_rules.ts | 2 + .../rules/install_prepacked_rules.ts | 2 + .../rules/patch_rules.mock.ts | 10 + .../lib/detection_engine/rules/patch_rules.ts | 15 + .../lib/detection_engine/rules/types.ts | 12 + .../rules/update_prepacked_rules.ts | 10 + .../rules/update_rules.mock.ts | 10 + .../detection_engine/rules/update_rules.ts | 15 + .../lib/detection_engine/rules/utils.test.ts | 15 + .../lib/detection_engine/rules/utils.ts | 14 +- .../queries/query_with_threat_mapping.json | 2 +- .../signals/__mocks__/es_results.ts | 1 + .../detection_engine/signals/build_rule.ts | 1 + .../signals/signal_params_schema.ts | 3 +- .../signals/signal_rule_alert_type.ts | 2 + .../threat_mapping/create_threat_signal.ts | 4 +- .../threat_mapping/create_threat_signals.ts | 5 +- .../signals/threat_mapping/get_threat_list.ts | 9 +- .../signals/threat_mapping/types.ts | 9 +- .../server/lib/detection_engine/types.ts | 2 + 81 files changed, 3224 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/helpers.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/index.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.stories.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.tsx create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/reducer.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/reducer.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/translations.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/threat_match/types.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/index.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/translations.ts diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock.ts index 777256ff961f90..c033e0adccf0f8 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.mock.ts @@ -60,7 +60,7 @@ export const getAddPrepackagedThreatMatchRulesSchemaMock = (): AddPrepackagedRul rule_id: 'rule-1', version: 1, threat_query: '*:*', - threat_index: 'list-index', + threat_index: ['list-index'], threat_mapping: [ { entries: [ @@ -118,7 +118,7 @@ export const getAddPrepackagedThreatMatchRulesSchemaDecodedMock = (): AddPrepack exceptions_list: [], rule_id: 'rule-1', threat_query: '*:*', - threat_index: 'list-index', + threat_index: ['list-index'], threat_mapping: [ { entries: [ diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts index 3f338c57dd930c..6ffbf4e4c8d4ca 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/add_prepackaged_rules_schema.ts @@ -51,6 +51,7 @@ import { threat_query, threat_filters, threat_mapping, + threat_language, } from '../types/threat_mapping'; import { @@ -128,6 +129,7 @@ export const addPrepackagedRulesSchema = t.intersection([ threat_mapping, // defaults to "undefined" if not set during decode threat_query, // defaults to "undefined" if not set during decode threat_index, // defaults to "undefined" if not set during decode + threat_language, // defaults "undefined" if not set during decode }) ), ]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.mock.ts index 32299be500b457..94dd1215d8026e 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.mock.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.mock.ts @@ -66,7 +66,7 @@ export const getCreateThreatMatchRulesSchemaMock = (ruleId = 'rule-1'): CreateRu language: 'kuery', rule_id: ruleId, threat_query: '*:*', - threat_index: 'list-index', + threat_index: ['list-index'], threat_mapping: [ { entries: [ @@ -124,7 +124,7 @@ export const getCreateThreatMatchRulesSchemaDecodedMock = (): CreateRulesSchemaD exceptions_list: [], rule_id: 'rule-1', threat_query: '*:*', - threat_index: 'list-index', + threat_index: ['list-index'], threat_mapping: [ { entries: [ diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts index 2489210a26c8f3..d8e7614fcb8401 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/create_rules_schema.ts @@ -52,6 +52,7 @@ import { threat_query, threat_filters, threat_mapping, + threat_language, } from '../types/threat_mapping'; import { @@ -124,6 +125,7 @@ export const createRulesSchema = t.intersection([ threat_query, // defaults to "undefined" if not set during decode threat_filters, // defaults to "undefined" if not set during decode threat_index, // defaults to "undefined" if not set during decode + threat_language, // defaults "undefined" if not set during decode }) ), ]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.mock.ts index 160dbb92b74cd2..2eea9ac0f30c78 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.mock.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.mock.ts @@ -86,7 +86,7 @@ export const getImportThreatMatchRulesSchemaMock = (ruleId = 'rule-1'): ImportRu risk_score: 55, language: 'kuery', rule_id: ruleId, - threat_index: 'index-123', + threat_index: ['index-123'], threat_mapping: [{ entries: [{ field: 'host.name', type: 'mapping', value: 'host.name' }] }], threat_query: '*:*', threat_filters: [ @@ -136,7 +136,7 @@ export const getImportThreatMatchRulesSchemaDecodedMock = (): ImportRulesSchemaD rule_id: 'rule-1', immutable: false, threat_query: '*:*', - threat_index: 'index-123', + threat_index: ['index-123'], threat_mapping: [ { entries: [ diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts index a411b3d439a1f6..852394b74767b7 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/import_rules_schema.ts @@ -58,6 +58,7 @@ import { threat_query, threat_filters, threat_mapping, + threat_language, } from '../types/threat_mapping'; import { @@ -147,6 +148,7 @@ export const importRulesSchema = t.intersection([ threat_mapping, // defaults to "undefined" if not set during decode threat_query, // defaults to "undefined" if not set during decode threat_index, // defaults to "undefined" if not set during decode + threat_language, // defaults "undefined" if not set during decode }) ), ]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts index 40e79d96a9e6b9..f4dce5c7ac05f8 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/patch_rules_schema.ts @@ -48,6 +48,13 @@ import { severity_mapping, event_category_override, } from '../common/schemas'; +import { + threat_index, + threat_query, + threat_filters, + threat_mapping, + threat_language, +} from '../types/threat_mapping'; import { listArrayOrUndefined } from '../types/lists'; /** @@ -97,6 +104,11 @@ export const patchRulesSchema = t.exact( note, version, exceptions_list: listArrayOrUndefined, + threat_index, + threat_query, + threat_filters, + threat_mapping, + threat_language, }) ); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts index 8a13dd2f4e9087..b0cd8b1c53688a 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/update_rules_schema.ts @@ -49,6 +49,13 @@ import { SeverityMapping, event_category_override, } from '../common/schemas'; +import { + threat_index, + threat_query, + threat_filters, + threat_mapping, + threat_language, +} from '../types/threat_mapping'; import { DefaultStringArray, @@ -122,6 +129,11 @@ export const updateRulesSchema = t.intersection([ note, // defaults to "undefined" if not set during decode version, // defaults to "undefined" if not set during decode exceptions_list: DefaultListArray, // defaults to empty array if not set during decode + threat_mapping, // defaults to "undefined" if not set during decode + threat_query, // defaults to "undefined" if not set during decode + threat_filters, // defaults to "undefined" if not set during decode + threat_index, // defaults to "undefined" if not set during decode + threat_language, // defaults "undefined" if not set during decode }) ), ]); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts index aaa246c82d9d77..340f93150ce5cf 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.mocks.ts @@ -87,7 +87,7 @@ export const getThreatMatchingSchemaMock = (anchorDate: string = ANCHOR_DATE): R return { ...getRulesSchemaMock(anchorDate), type: 'threat_match', - threat_index: 'index-123', + threat_index: ['index-123'], threat_mapping: [{ entries: [{ field: 'host.name', type: 'mapping', value: 'host.name' }] }], threat_query: '*:*', threat_filters: [ diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts index c5bad3c55066b5..82675768a11b7c 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.test.ts @@ -626,7 +626,7 @@ describe('rules_schema', () => { const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'invalid keys "threat_index,threat_mapping,[{"entries":[{"field":"host.name","type":"mapping","value":"host.name"}]}],threat_query,threat_filters,[{"bool":{"must":[{"query_string":{"query":"host.name: linux","analyze_wildcard":true,"time_zone":"Zulu"}}],"filter":[],"should":[],"must_not":[]}}]"', + 'invalid keys "threat_index,["index-123"],threat_mapping,[{"entries":[{"field":"host.name","type":"mapping","value":"host.name"}]}],threat_query,threat_filters,[{"bool":{"must":[{"query_string":{"query":"host.name: linux","analyze_wildcard":true,"time_zone":"Zulu"}}],"filter":[],"should":[],"must_not":[]}}]"', ]); expect(message.schema).toEqual({}); }); @@ -764,7 +764,7 @@ describe('rules_schema', () => { test('should return 5 fields for a rule of type "threat_match"', () => { const fields = addThreatMatchFields({ type: 'threat_match' }); - expect(fields.length).toEqual(5); + expect(fields.length).toEqual(6); }); }); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts index 908425a7496d07..e85beddf0e51e9 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/rules_schema.ts @@ -66,6 +66,7 @@ import { threat_query, threat_filters, threat_mapping, + threat_language, } from '../types/threat_mapping'; import { DefaultListArray } from '../types/lists_default_array'; @@ -144,6 +145,7 @@ export const dependentRulesSchema = t.partial({ threat_index, threat_query, threat_mapping, + threat_language, }); /** @@ -277,6 +279,7 @@ export const addThreatMatchFields = (typeAndTimelineOnly: TypeAndTimelineOnly): t.exact(t.type({ threat_query: dependentRulesSchema.props.threat_query })), t.exact(t.type({ threat_index: dependentRulesSchema.props.threat_index })), t.exact(t.type({ threat_mapping: dependentRulesSchema.props.threat_mapping })), + t.exact(t.partial({ threat_language: dependentRulesSchema.props.threat_language })), t.exact(t.partial({ threat_filters: dependentRulesSchema.props.threat_filters })), t.exact(t.partial({ saved_id: dependentRulesSchema.props.saved_id })), ]; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts index aab9a550d25e73..28a66d2948a92e 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts @@ -33,4 +33,5 @@ export * from './positive_integer'; export * from './positive_integer_greater_than_zero'; export * from './references_default_array'; export * from './risk_score'; +export * from './threat_mapping'; export * from './uuid'; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts index f2b4754c2d113c..a1be6485f596b7 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/threat_mapping.ts @@ -7,6 +7,7 @@ /* eslint-disable @typescript-eslint/naming-convention */ import * as t from 'io-ts'; +import { language } from '../common/schemas'; import { NonEmptyString } from './non_empty_string'; export const threat_query = t.string; @@ -19,29 +20,38 @@ export type ThreatFilters = t.TypeOf<typeof threat_filters>; export const threatFiltersOrUndefined = t.union([threat_filters, t.undefined]); export type ThreatFiltersOrUndefined = t.TypeOf<typeof threatFiltersOrUndefined>; -export const threatMappingEntries = t.array( - t.exact( - t.type({ - field: NonEmptyString, - type: t.keyof({ mapping: null }), - value: NonEmptyString, - }) - ) +export const threatMapEntry = t.exact( + t.type({ + field: NonEmptyString, + type: t.keyof({ mapping: null }), + value: NonEmptyString, + }) ); + +export type ThreatMapEntry = t.TypeOf<typeof threatMapEntry>; + +export const threatMappingEntries = t.array(threatMapEntry); export type ThreatMappingEntries = t.TypeOf<typeof threatMappingEntries>; -export const threat_mapping = t.array( - t.exact( - t.type({ - entries: threatMappingEntries, - }) - ) +export const threatMap = t.exact( + t.type({ + entries: threatMappingEntries, + }) ); +export type ThreatMap = t.TypeOf<typeof threatMap>; + +export const threat_mapping = t.array(threatMap); export type ThreatMapping = t.TypeOf<typeof threat_mapping>; export const threatMappingOrUndefined = t.union([threat_mapping, t.undefined]); export type ThreatMappingOrUndefined = t.TypeOf<typeof threatMappingOrUndefined>; -export const threat_index = t.string; +export const threat_index = t.array(t.string); +export type ThreatIndex = t.TypeOf<typeof threat_index>; export const threatIndexOrUndefined = t.union([threat_index, t.undefined]); export type ThreatIndexOrUndefined = t.TypeOf<typeof threatIndexOrUndefined>; + +export const threat_language = t.union([language, t.undefined]); +export type ThreatLanguage = t.TypeOf<typeof threat_language>; +export const threatLanguageOrUndefined = t.union([threat_language, t.undefined]); +export type ThreatLanguageOrUndefined = t.TypeOf<typeof threatLanguageOrUndefined>; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.test.tsx new file mode 100644 index 00000000000000..87d2b5ede7d674 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.test.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { mount } from 'enzyme'; +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { AndBadgeComponent } from './and_badge'; + +describe('AndBadgeComponent', () => { + test('it renders entryItemIndexItemEntryFirstRowAndBadge for very first item', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <AndBadgeComponent entriesLength={2} entryItemIndex={0} /> + </ThemeProvider> + ); + + expect(wrapper.find('[data-test-subj="entryItemEntryFirstRowAndBadge"]').exists()).toBeTruthy(); + }); + + test('it renders entryItemEntryInvisibleAndBadge if "entriesLength" is 1 or less', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <AndBadgeComponent entriesLength={1} entryItemIndex={0} /> + </ThemeProvider> + ); + + expect( + wrapper.find('[data-test-subj="entryItemEntryInvisibleAndBadge"]').exists() + ).toBeTruthy(); + }); + + test('it renders regular "and" badge if item is not the first one and includes more than one entry', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <AndBadgeComponent entriesLength={2} entryItemIndex={1} /> + </ThemeProvider> + ); + + expect(wrapper.find('[data-test-subj="entryItemEntryAndBadge"]').exists()).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.tsx new file mode 100644 index 00000000000000..fd8d3f08e5de39 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/and_badge.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; + +import { AndOrBadge } from '../and_or_badge'; + +const MyInvisibleAndBadge = styled(EuiFlexItem)` + visibility: hidden; +`; + +const MyFirstRowContainer = styled(EuiFlexItem)` + padding-top: 20px; +`; + +interface AndBadgeProps { + entriesLength: number; + entryItemIndex: number; +} + +export const AndBadgeComponent = React.memo<AndBadgeProps>(({ entriesLength, entryItemIndex }) => { + const badge = <AndOrBadge includeAntennas type="and" />; + + if (entriesLength > 1 && entryItemIndex === 0) { + return ( + <MyFirstRowContainer grow={false} data-test-subj="entryItemEntryFirstRowAndBadge"> + {badge} + </MyFirstRowContainer> + ); + } else if (entriesLength <= 1) { + return ( + <MyInvisibleAndBadge grow={false} data-test-subj="entryItemEntryInvisibleAndBadge"> + {badge} + </MyInvisibleAndBadge> + ); + } else { + return ( + <EuiFlexItem grow={false} data-test-subj="entryItemEntryAndBadge"> + {badge} + </EuiFlexItem> + ); + } +}); + +AndBadgeComponent.displayName = 'AndBadge'; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.test.tsx new file mode 100644 index 00000000000000..063499902094c4 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mount } from 'enzyme'; +import React from 'react'; +import { ThreatMappingEntries } from '../../../../common/detection_engine/schemas/types'; + +import { EntryDeleteButtonComponent } from './entry_delete_button'; + +const entries: ThreatMappingEntries = [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, +]; + +describe('EntryDeleteButtonComponent', () => { + test('it renders firstRowDeleteButton for very first entry', () => { + const wrapper = mount( + <EntryDeleteButtonComponent + entryIndex={0} + itemIndex={0} + isOnlyItem={false} + entries={entries} + onDelete={jest.fn()} + /> + ); + + expect(wrapper.find('[data-test-subj="firstRowDeleteButton"] button')).toHaveLength(1); + }); + + test('it does not render firstRowDeleteButton if entryIndex is not 0', () => { + const wrapper = mount( + <EntryDeleteButtonComponent + entryIndex={1} + itemIndex={0} + isOnlyItem={false} + entries={entries} + onDelete={jest.fn()} + /> + ); + + expect(wrapper.find('[data-test-subj="firstRowDeleteButton"]')).toHaveLength(0); + expect(wrapper.find('[data-test-subj="deleteButton"] button')).toHaveLength(1); + }); + + test('it does not render firstRowDeleteButton if itemIndex is not 0', () => { + const wrapper = mount( + <EntryDeleteButtonComponent + entryIndex={0} + itemIndex={1} + isOnlyItem={false} + entries={entries} + onDelete={jest.fn()} + /> + ); + + expect(wrapper.find('[data-test-subj="firstRowDeleteButton"]')).toHaveLength(0); + expect(wrapper.find('[data-test-subj="deleteButton"] button')).toHaveLength(1); + }); + + test('it invokes "onDelete" when button is clicked', () => { + const onDelete = jest.fn(); + + const wrapper = mount( + <EntryDeleteButtonComponent + entryIndex={0} + itemIndex={1} + isOnlyItem={false} + entries={entries} + onDelete={onDelete} + /> + ); + wrapper.find('[data-test-subj="deleteButton"] button').simulate('click'); + + expect(onDelete).toHaveBeenCalledTimes(1); + expect(onDelete).toHaveBeenCalledWith(0); + }); + + test('it disables button if it is the only entry left and no field has been selected', () => { + const emptyEntries: ThreatMappingEntries = [ + { + field: '', + type: 'mapping', + value: 'field.one', + }, + ]; + + const wrapper = mount( + <EntryDeleteButtonComponent + entryIndex={0} + itemIndex={0} + isOnlyItem + entries={emptyEntries} + onDelete={jest.fn()} + /> + ); + + const button = wrapper.find('[data-test-subj="firstRowDeleteButton"] button').at(0); + + expect(button.prop('disabled')).toBeTruthy(); + }); + + test('it does not disable button if it is the only entry left and field has been selected', () => { + const wrapper = mount( + <EntryDeleteButtonComponent + entryIndex={1} + itemIndex={0} + isOnlyItem + entries={entries} + onDelete={jest.fn()} + /> + ); + + const button = wrapper.find('[data-test-subj="deleteButton"] button').at(0); + + expect(button.prop('disabled')).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.tsx new file mode 100644 index 00000000000000..10a82855bb0a3a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_delete_button.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback } from 'react'; +import { EuiButtonIcon, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; + +import { Entry } from './types'; + +const MyFirstRowContainer = styled(EuiFlexItem)` + padding-top: 20px; +`; + +interface EntryDeleteButtonProps { + entries: Entry[]; + isOnlyItem: boolean; + entryIndex: number; + itemIndex: number; + onDelete: (item: number) => void; +} + +export const EntryDeleteButtonComponent = React.memo<EntryDeleteButtonProps>( + ({ entries, isOnlyItem, entryIndex, itemIndex, onDelete }) => { + const isDisabled: boolean = + isOnlyItem && + entries.length === 1 && + itemIndex === 0 && + (entries[0].field == null || entries[0].field === ''); + + const handleDelete = useCallback((): void => { + onDelete(entryIndex); + }, [onDelete, entryIndex]); + + const button = ( + <EuiButtonIcon + color="danger" + iconType="trash" + onClick={handleDelete} + isDisabled={isDisabled} + aria-label="entryDeleteButton" + className="itemEntryDeleteButton" + data-test-subj="itemEntryDeleteButton" + /> + ); + + if (entryIndex === 0 && itemIndex === 0) { + // This logic was added to work around it including the field + // labels in centering the delete icon for the first row + return ( + <MyFirstRowContainer grow={false} data-test-subj="firstRowDeleteButton"> + {button} + </MyFirstRowContainer> + ); + } else { + return ( + <EuiFlexItem grow={false} data-test-subj="deleteButton"> + {button} + </EuiFlexItem> + ); + } + } +); + +EntryDeleteButtonComponent.displayName = 'EntryDeleteButton'; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.test.tsx new file mode 100644 index 00000000000000..36033c358766df --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.test.tsx @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mount } from 'enzyme'; +import React from 'react'; +import { EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; + +import { EntryItem } from './entry_item'; +import { + fields, + getField, +} from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; +import { IndexPattern } from 'src/plugins/data/public'; + +jest.mock('../../../common/lib/kibana'); + +describe('EntryItem', () => { + test('it renders field labels if "showLabel" is "true"', () => { + const wrapper = mount( + <EntryItem + entry={{ + field: undefined, + value: undefined, + type: 'mapping', + entryIndex: 0, + }} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + showLabel={true} + onChange={jest.fn()} + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + /> + ); + + expect(wrapper.find('[data-test-subj="threatFieldInputFormRow"]')).not.toEqual(0); + }); + + test('it invokes "onChange" when new field is selected and resets value fields', () => { + const mockOnChange = jest.fn(); + const wrapper = mount( + <EntryItem + entry={{ + field: getField('ip'), + type: 'mapping', + value: getField('ip'), + entryIndex: 0, + }} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + showLabel={false} + onChange={mockOnChange} + /> + ); + + ((wrapper.find(EuiComboBox).at(0).props() as unknown) as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + }).onChange([{ label: 'machine.os' }]); + + expect(mockOnChange).toHaveBeenCalledWith( + { + field: 'machine.os', + type: 'mapping', + value: 'ip', + }, + 0 + ); + }); + + test('it invokes "onChange" when new value is selected', () => { + const mockOnChange = jest.fn(); + const wrapper = mount( + <EntryItem + entry={{ + field: getField('ip'), + type: 'mapping', + value: getField('ip'), + entryIndex: 0, + }} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + showLabel={false} + onChange={mockOnChange} + /> + ); + + ((wrapper.find(EuiComboBox).at(1).props() as unknown) as { + onChange: (a: EuiComboBoxOptionOption[]) => void; + }).onChange([{ label: 'is not' }]); + + expect(mockOnChange).toHaveBeenCalledWith({ field: 'ip', type: 'mapping', value: '' }, 0); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx new file mode 100644 index 00000000000000..c99e63ff4eda08 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/entry_item.tsx @@ -0,0 +1,131 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useCallback, useMemo } from 'react'; +import { EuiFormRow, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; + +import { IFieldType, IndexPattern } from '../../../../../../../src/plugins/data/common'; +import { FieldComponent } from '../autocomplete/field'; +import { FormattedEntry, Entry } from './types'; +import * as i18n from './translations'; +import { getEntryOnFieldChange, getEntryOnThreatFieldChange } from './helpers'; + +interface EntryItemProps { + entry: FormattedEntry; + indexPattern: IndexPattern; + threatIndexPatterns: IndexPattern; + showLabel: boolean; + onChange: (arg: Entry, i: number) => void; +} + +const FlexItemWithLabel = styled(EuiFlexItem)` + padding-top: 20px; + text-align: center; +`; + +const FlexItemWithoutLabel = styled(EuiFlexItem)` + text-align: center; +`; + +export const EntryItem: React.FC<EntryItemProps> = ({ + entry, + indexPattern, + threatIndexPatterns, + showLabel, + onChange, +}): JSX.Element => { + const handleFieldChange = useCallback( + ([newField]: IFieldType[]): void => { + const { updatedEntry, index } = getEntryOnFieldChange(entry, newField); + onChange(updatedEntry, index); + }, + [onChange, entry] + ); + + const handleThreatFieldChange = useCallback( + ([newField]: IFieldType[]): void => { + const { updatedEntry, index } = getEntryOnThreatFieldChange(entry, newField); + onChange(updatedEntry, index); + }, + [onChange, entry] + ); + + const renderFieldInput = useMemo(() => { + const comboBox = ( + <FieldComponent + placeholder={i18n.FIELD_PLACEHOLDER} + indexPattern={indexPattern} + selectedField={entry.field} + isClearable={false} + isLoading={false} + isDisabled={indexPattern == null} + onChange={handleFieldChange} + data-test-subj="entryField" + fieldInputWidth={360} + /> + ); + + if (showLabel) { + return ( + <EuiFormRow label={i18n.FIELD} data-test-subj="entryItemFieldInputFormRow"> + {comboBox} + </EuiFormRow> + ); + } else { + return comboBox; + } + }, [handleFieldChange, indexPattern, entry, showLabel]); + + const renderThreatFieldInput = useMemo(() => { + const comboBox = ( + <FieldComponent + placeholder={i18n.FIELD_PLACEHOLDER} + indexPattern={threatIndexPatterns} + selectedField={entry.value} + isClearable={false} + isLoading={false} + isDisabled={threatIndexPatterns == null} + onChange={handleThreatFieldChange} + data-test-subj="threatEntryField" + fieldInputWidth={360} + /> + ); + + if (showLabel) { + return ( + <EuiFormRow label={i18n.THREAT_FIELD} data-test-subj="threatFieldInputFormRow"> + {comboBox} + </EuiFormRow> + ); + } else { + return comboBox; + } + }, [handleThreatFieldChange, threatIndexPatterns, entry, showLabel]); + + return ( + <EuiFlexGroup + direction="row" + gutterSize="s" + alignItems="center" + justifyContent="spaceAround" + data-test-subj="itemEntryContainer" + > + <EuiFlexItem grow={false}>{renderFieldInput}</EuiFlexItem> + <EuiFlexItem grow={true}> + <EuiFlexGroup justifyContent="spaceAround" alignItems="center"> + {showLabel ? ( + <FlexItemWithLabel grow={true}>{i18n.MATCHES}</FlexItemWithLabel> + ) : ( + <FlexItemWithoutLabel grow={true}>{i18n.MATCHES}</FlexItemWithoutLabel> + )} + </EuiFlexGroup> + </EuiFlexItem> + <EuiFlexItem grow={false}>{renderThreatFieldInput}</EuiFlexItem> + </EuiFlexGroup> + ); +}; + +EntryItem.displayName = 'EntryItem'; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.test.tsx new file mode 100644 index 00000000000000..7bab8e93ea9dbe --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.test.tsx @@ -0,0 +1,225 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + fields, + getField, +} from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; +import { Entry, EmptyEntry, ThreatMapEntries, FormattedEntry } from './types'; +import { IndexPattern } from '../../../../../../../src/plugins/data/common'; +import moment from 'moment-timezone'; + +import { + filterItems, + getEntryOnFieldChange, + getFormattedEntries, + getFormattedEntry, + getUpdatedEntriesOnDelete, +} from './helpers'; +import { ThreatMapEntry } from '../../../../common/detection_engine/schemas/types'; + +const getMockIndexPattern = (): IndexPattern => + ({ + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern); + +const getMockEntry = (): FormattedEntry => ({ + field: getField('ip'), + value: getField('ip'), + type: 'mapping', + entryIndex: 0, +}); + +describe('Helpers', () => { + beforeEach(() => { + moment.tz.setDefault('UTC'); + }); + + afterEach(() => { + moment.tz.setDefault('Browser'); + }); + + describe('#getFormattedEntry', () => { + test('it returns entry with a value when "item.field" is of type "text" and matching keyword field exists', () => { + const payloadIndexPattern: IndexPattern = { + ...getMockIndexPattern(), + fields: [ + ...fields, + { + name: 'machine.os.raw.text', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: false, + readFromDocValues: true, + }, + ], + } as IndexPattern; + const payloadItem: Entry = { + field: 'machine.os.raw.text', + type: 'mapping', + value: 'some os', + }; + const output = getFormattedEntry(payloadIndexPattern, payloadItem, 0); + const expected: FormattedEntry = { + entryIndex: 0, + field: { + name: 'machine.os.raw.text', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: false, + aggregatable: false, + readFromDocValues: true, + }, + type: 'mapping', + value: undefined, + }; + expect(output).toEqual(expected); + }); + }); + + describe('#getFormattedEntries', () => { + test('it returns formatted entry with fields undefined if it unable to find a matching index pattern field', () => { + const payloadIndexPattern: IndexPattern = getMockIndexPattern(); + const payloadItems: Entry[] = [{ field: 'field.one', type: 'mapping', value: 'field.one' }]; + const output = getFormattedEntries(payloadIndexPattern, payloadItems); + const expected: FormattedEntry[] = [ + { + entryIndex: 0, + field: undefined, + value: undefined, + type: 'mapping', + }, + ]; + expect(output).toEqual(expected); + }); + + test('it returns formatted entries', () => { + const payloadIndexPattern: IndexPattern = getMockIndexPattern(); + const payloadItems: Entry[] = [ + { field: 'machine.os', type: 'mapping', value: 'machine.os' }, + { field: 'ip', type: 'mapping', value: 'ip' }, + ]; + const output = getFormattedEntries(payloadIndexPattern, payloadItems); + const expected: FormattedEntry[] = [ + { + field: { + name: 'machine.os', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + type: 'mapping', + value: { + name: 'machine.os', + type: 'string', + esTypes: ['text'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: false, + }, + entryIndex: 0, + }, + { + field: { + name: 'ip', + type: 'ip', + esTypes: ['ip'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + type: 'mapping', + value: { + name: 'ip', + type: 'ip', + esTypes: ['ip'], + count: 0, + scripted: false, + searchable: true, + aggregatable: true, + readFromDocValues: true, + }, + entryIndex: 1, + }, + ]; + expect(output).toEqual(expected); + }); + }); + + describe('#getUpdatedEntriesOnDelete', () => { + test('it removes entry corresponding to "entryIndex"', () => { + const payloadItem: ThreatMapEntries = { + entries: [ + { field: 'field.one', type: 'mapping', value: 'field.one' }, + { field: 'field.two', type: 'mapping', value: 'field.two' }, + ], + }; + const output = getUpdatedEntriesOnDelete(payloadItem, 0); + const expected: ThreatMapEntries = { + entries: [ + { + field: 'field.two', + type: 'mapping', + value: 'field.two', + }, + ], + }; + expect(output).toEqual(expected); + }); + }); + + describe('#getEntryOnFieldChange', () => { + test('it returns field of type "match" with updated field', () => { + const payloadItem = getMockEntry(); + const payloadIFieldType = getField('ip'); + const output = getEntryOnFieldChange(payloadItem, payloadIFieldType); + const expected: { updatedEntry: Entry; index: number } = { + index: 0, + updatedEntry: { + field: 'ip', + type: 'mapping', + value: 'ip', + }, + }; + expect(output).toEqual(expected); + }); + }); + + describe('#filterItems', () => { + test('it removes entry items with "value" of "undefined"', () => { + const entry: ThreatMapEntry = { field: 'host.name', type: 'mapping', value: 'host.name' }; + const mockEmpty: EmptyEntry = { + field: 'host.name', + type: 'mapping', + value: undefined, + }; + const items = filterItems([ + { + entries: [entry], + }, + { + entries: [mockEmpty], + }, + ]); + expect(items).toEqual([{ entries: [entry] }]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx new file mode 100644 index 00000000000000..9b155e1d568a84 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/helpers.tsx @@ -0,0 +1,171 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ThreatMap, + threatMap, + ThreatMapping, +} from '../../../../common/detection_engine/schemas/types'; + +import { IndexPattern, IFieldType } from '../../../../../../../src/plugins/data/common'; +import { Entry, FormattedEntry, ThreatMapEntries, EmptyEntry } from './types'; + +/** + * Formats the entry into one that is easily usable for the UI. + * + * @param patterns IndexPattern containing available fields on rule index + * @param item item entry + * @param itemIndex entry index + */ +export const getFormattedEntry = ( + indexPattern: IndexPattern, + item: Entry, + itemIndex: number +): FormattedEntry => { + const { fields } = indexPattern; + const field = item.field; + const threatField = item.value; + const [foundField] = fields.filter(({ name }) => field != null && field === name); + const [threatFoundField] = fields.filter( + ({ name }) => threatField != null && threatField === name + ); + return { + field: foundField, + type: 'mapping', + value: threatFoundField, + entryIndex: itemIndex, + }; +}; + +/** + * Formats the entries to be easily usable for the UI + * + * @param patterns IndexPattern containing available fields on rule index + * @param entries item entries + */ +export const getFormattedEntries = ( + indexPattern: IndexPattern, + entries: Entry[] +): FormattedEntry[] => { + return entries.reduce<FormattedEntry[]>((acc, item, index) => { + const newItemEntry = getFormattedEntry(indexPattern, item, index); + return [...acc, newItemEntry]; + }, []); +}; + +/** + * Determines whether an entire entry or item need to be removed + * + * @param item + * @param entryIndex index of given entry + * + */ +export const getUpdatedEntriesOnDelete = ( + item: ThreatMapEntries, + entryIndex: number +): ThreatMapEntries => { + return { + ...item, + entries: [...item.entries.slice(0, entryIndex), ...item.entries.slice(entryIndex + 1)], + }; +}; + +/** + * Determines proper entry update when user selects new field + * + * @param item - current item entry values + * @param newField - newly selected field + * + */ +export const getEntryOnFieldChange = ( + item: FormattedEntry, + newField: IFieldType +): { updatedEntry: Entry; index: number } => { + const { entryIndex } = item; + return { + updatedEntry: { + field: newField != null ? newField.name : '', + type: 'mapping', + value: item.value != null ? item.value.name : '', + }, + index: entryIndex, + }; +}; + +/** + * Determines proper entry update when user selects new field + * + * @param item - current item entry values + * @param newField - newly selected field + * + */ +export const getEntryOnThreatFieldChange = ( + item: FormattedEntry, + newField: IFieldType +): { updatedEntry: Entry; index: number } => { + const { entryIndex } = item; + return { + updatedEntry: { + field: item.field != null ? item.field.name : '', + type: 'mapping', + value: newField != null ? newField.name : '', + }, + index: entryIndex, + }; +}; + +export const getDefaultEmptyEntry = (): EmptyEntry => ({ + field: '', + type: 'mapping', + value: '', +}); + +export const getNewItem = (): ThreatMap => { + return { + entries: [ + { + field: '', + type: 'mapping', + value: '', + }, + ], + }; +}; + +export const filterItems = (items: ThreatMapEntries[]): ThreatMapping => { + return items.reduce<ThreatMapping>((acc, item) => { + const newItem = { ...item, entries: item.entries }; + if (threatMap.is(newItem)) { + return [...acc, newItem]; + } else { + return acc; + } + }, []); +}; + +/** + * Given a list of items checks each one to see if any of them have an empty field + * or an empty value. + * @param items The items to check if we have an empty entries. + */ +export const containsInvalidItems = (items: ThreatMapEntries[]): boolean => { + return items.some((item) => + item.entries.some((subEntry) => subEntry.field === '' || subEntry.value === '') + ); +}; + +/** + * Given a list of items checks if we have a single empty entry and if we do returns true. + * @param items The items to check if we have a single empty entry. + */ +export const singleEntryThreat = (items: ThreatMapEntries[]): boolean => { + return ( + items.length === 1 && + items[0].entries.length === 1 && + items[0].entries[0].field === '' && + items[0].entries[0].value === '' + ); +}; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx new file mode 100644 index 00000000000000..14bc64c90a6618 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx @@ -0,0 +1,304 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { mount } from 'enzyme'; +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; +import { waitFor } from '@testing-library/react'; + +import { fields } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; + +import { useKibana } from '../../../common/lib/kibana'; + +import { ThreatMatchComponent } from './'; +import { ThreatMapEntries } from './types'; +import { IndexPattern } from 'src/plugins/data/public'; + +jest.mock('../../../common/lib/kibana'); + +const getPayLoad = (): ThreatMapEntries[] => [ + { entries: [{ field: 'host.name', type: 'mapping', value: 'host.name' }] }, +]; + +const getDoublePayLoad = (): ThreatMapEntries[] => [ + { entries: [{ field: 'host.name', type: 'mapping', value: 'host.name' }] }, + { entries: [{ field: 'host.name', type: 'mapping', value: 'host.name' }] }, +]; + +describe('ThreatMatchComponent', () => { + const getValueSuggestionsMock = jest.fn().mockResolvedValue(['value 1', 'value 2']); + + beforeEach(() => { + (useKibana as jest.Mock).mockReturnValue({ + services: { + data: { + autocomplete: { + getValueSuggestions: getValueSuggestionsMock, + }, + }, + }, + }); + }); + + afterEach(() => { + getValueSuggestionsMock.mockClear(); + }); + + test('it displays empty entry if no "listItems" are passed in', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={[]} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + + expect(wrapper.find('EuiFlexGroup[data-test-subj="itemEntryContainer"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="entryField"]').text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="threatEntryField"]').text()).toEqual('Search'); + }); + + test('it displays "Search" for "listItems" that are passed in', async () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={getPayLoad()} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + expect(wrapper.find('EuiFlexGroup[data-test-subj="itemEntryContainer"]')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="entryField"]').at(0).text()).toEqual('Search'); + + wrapper.unmount(); + }); + + test('it displays "or", "and" enabled', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={[]} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + + expect(wrapper.find('[data-test-subj="andButton"] button').prop('disabled')).toBeFalsy(); + expect(wrapper.find('[data-test-subj="orButton"] button').prop('disabled')).toBeFalsy(); + }); + + test('it adds an entry when "and" clicked', async () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={[]} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + + expect(wrapper.find('EuiFlexGroup[data-test-subj="itemEntryContainer"]')).toHaveLength(1); + + wrapper.find('[data-test-subj="andButton"] button').simulate('click'); + + await waitFor(() => { + expect(wrapper.find('EuiFlexGroup[data-test-subj="itemEntryContainer"]')).toHaveLength(2); + expect(wrapper.find('[data-test-subj="entryField"]').at(0).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="threatEntryField"]').at(0).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"]').at(1).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="threatEntryField"]').at(1).text()).toEqual('Search'); + }); + }); + + test('it adds an item when "or" clicked', async () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={[]} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + + expect(wrapper.find('EuiFlexGroup[data-test-subj="entriesContainer"]')).toHaveLength(1); + + wrapper.find('[data-test-subj="orButton"] button').simulate('click'); + + await waitFor(() => { + expect(wrapper.find('EuiFlexGroup[data-test-subj="entriesContainer"]')).toHaveLength(2); + expect(wrapper.find('[data-test-subj="entryField"]').at(0).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="threatEntryField"]').at(0).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"]').at(1).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="threatEntryField"]').at(1).text()).toEqual('Search'); + }); + }); + + test('it removes one row if user deletes a row', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={getDoublePayLoad()} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + + expect(wrapper.find('[data-test-subj="entriesContainer"]').length).toEqual(4); + wrapper.find('[data-test-subj="firstRowDeleteButton"] button').simulate('click'); + expect(wrapper.find('[data-test-subj="entriesContainer"]').length).toEqual(2); + wrapper.unmount(); + }); + + test('it displays "and" badge if at least one item includes more than one entry', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={[]} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + + expect(wrapper.find('[data-test-subj="entryItemEntryFirstRowAndBadge"]').exists()).toBeFalsy(); + + wrapper.find('[data-test-subj="andButton"] button').simulate('click'); + + expect(wrapper.find('[data-test-subj="entryItemEntryFirstRowAndBadge"]').exists()).toBeTruthy(); + }); + + test('it does not display "and" badge if none of the items include more than one entry', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ThreatMatchComponent + listItems={[]} + indexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + onChange={jest.fn()} + /> + </ThemeProvider> + ); + + wrapper.find('[data-test-subj="orButton"] button').simulate('click'); + + expect(wrapper.find('[data-test-subj="entryItemEntryFirstRowAndBadge"]').exists()).toBeFalsy(); + + wrapper.find('[data-test-subj="orButton"] button').simulate('click'); + + expect(wrapper.find('[data-test-subj="entryItemEntryFirstRowAndBadge"]').exists()).toBeFalsy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/index.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/index.tsx new file mode 100644 index 00000000000000..d3936e10bd877a --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/index.tsx @@ -0,0 +1,220 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React, { useCallback, useEffect, useReducer } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; + +import { ThreatMapping } from '../../../../common/detection_engine/schemas/types'; +import { ListItemComponent } from './list_item'; +import { IndexPattern } from '../../../../../../../src/plugins/data/common'; +import { AndOrBadge } from '../and_or_badge'; +import { LogicButtons } from './logic_buttons'; +import { ThreatMapEntries } from './types'; +import { State, reducer } from './reducer'; +import { getDefaultEmptyEntry, getNewItem, filterItems } from './helpers'; + +const MyInvisibleAndBadge = styled(EuiFlexItem)` + visibility: hidden; +`; + +const MyAndBadge = styled(AndOrBadge)` + & > .euiFlexItem { + margin: 0; + } +`; + +const MyButtonsContainer = styled(EuiFlexItem)` + margin: 16px 0; +`; + +const initialState: State = { + andLogicIncluded: false, + entries: [], + entriesToDelete: [], +}; + +interface OnChangeProps { + entryItems: ThreatMapping; + entriesToDelete: ThreatMapEntries[]; +} + +interface ThreatMatchComponentProps { + listItems: ThreatMapEntries[]; + indexPatterns: IndexPattern; + threatIndexPatterns: IndexPattern; + onChange: (arg: OnChangeProps) => void; +} + +export const ThreatMatchComponent = ({ + listItems, + indexPatterns, + threatIndexPatterns, + onChange, +}: ThreatMatchComponentProps) => { + const [{ entries, entriesToDelete, andLogicIncluded }, dispatch] = useReducer(reducer(), { + ...initialState, + }); + + const setUpdateEntries = useCallback( + (items: ThreatMapEntries[]): void => { + dispatch({ + type: 'setEntries', + entries: items, + }); + }, + [dispatch] + ); + + const setDefaultEntries = useCallback( + (item: ThreatMapEntries): void => { + dispatch({ + type: 'setDefault', + initialState, + lastEntry: item, + }); + }, + [dispatch] + ); + + const handleEntryItemChange = useCallback( + (item: ThreatMapEntries, index: number): void => { + const updatedEntries = [ + ...entries.slice(0, index), + { + ...item, + }, + ...entries.slice(index + 1), + ]; + + setUpdateEntries(updatedEntries); + }, + [setUpdateEntries, entries] + ); + + const handleDeleteEntryItem = useCallback( + (item: ThreatMapEntries, itemIndex: number): void => { + if (item.entries.length === 0) { + const updatedEntries = [...entries.slice(0, itemIndex), ...entries.slice(itemIndex + 1)]; + // if it's the only item left, don't delete it just add a default entry to it + if (updatedEntries.length === 0) { + setDefaultEntries(item); + } else { + setUpdateEntries([...entries.slice(0, itemIndex), ...entries.slice(itemIndex + 1)]); + } + } else { + handleEntryItemChange(item, itemIndex); + } + }, + [handleEntryItemChange, setUpdateEntries, entries, setDefaultEntries] + ); + + const handleAddNewEntryItemEntry = useCallback((): void => { + const lastEntry = entries[entries.length - 1]; + const { entries: innerEntries } = lastEntry; + + const updatedEntry: ThreatMapEntries = { + ...lastEntry, + entries: [...innerEntries, getDefaultEmptyEntry()], + }; + + setUpdateEntries([...entries.slice(0, entries.length - 1), { ...updatedEntry }]); + }, [setUpdateEntries, entries]); + + const handleAddNewEntryItem = useCallback((): void => { + // There is a case where there are numerous list items, all with + // empty `entries` array. + const newItem = getNewItem(); + setUpdateEntries([...entries, { ...newItem }]); + }, [setUpdateEntries, entries]); + + const handleAddClick = useCallback((): void => { + handleAddNewEntryItemEntry(); + }, [handleAddNewEntryItemEntry]); + + // Bubble up changes to parent + useEffect(() => { + onChange({ entryItems: filterItems(entries), entriesToDelete }); + }, [onChange, entriesToDelete, entries]); + + // Defaults to never be sans entry, instead + // always falls back to an empty entry if user deletes all + useEffect(() => { + if ( + entries.length === 0 || + (entries.length === 1 && entries[0].entries != null && entries[0].entries.length === 0) + ) { + handleAddNewEntryItem(); + } + }, [entries, handleAddNewEntryItem]); + + useEffect(() => { + if (listItems.length > 0) { + setUpdateEntries(listItems); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + return ( + <EuiFlexGroup gutterSize="s" direction="column"> + {entries.map((entryListItem, index) => ( + <EuiFlexItem grow={1} key={`${index}`}> + <EuiFlexGroup gutterSize="s" direction="column"> + {index !== 0 && + (andLogicIncluded ? ( + <EuiFlexItem grow={false}> + <EuiFlexGroup gutterSize="none" direction="row"> + <MyInvisibleAndBadge grow={false}> + <MyAndBadge includeAntennas type="and" /> + </MyInvisibleAndBadge> + <EuiFlexItem grow={false}> + <MyAndBadge type="or" /> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + ) : ( + <EuiFlexItem grow={false}> + <MyAndBadge type="or" /> + </EuiFlexItem> + ))} + <EuiFlexItem grow={false}> + <ListItemComponent + key={`${index}`} + listItem={entryListItem} + listId={`${index}`} + indexPattern={indexPatterns} + threatIndexPatterns={threatIndexPatterns} + listItemIndex={index} + andLogicIncluded={andLogicIncluded} + isOnlyItem={entries.length === 1} + onDeleteEntryItem={handleDeleteEntryItem} + onChangeEntryItem={handleEntryItemChange} + /> + </EuiFlexItem> + </EuiFlexGroup> + </EuiFlexItem> + ))} + + <MyButtonsContainer data-test-subj={'andOrOperatorButtons'}> + <EuiFlexGroup gutterSize="s"> + {andLogicIncluded && ( + <MyInvisibleAndBadge grow={false}> + <AndOrBadge includeAntennas type="and" /> + </MyInvisibleAndBadge> + )} + <EuiFlexItem grow={1}> + <LogicButtons + isOrDisabled={false} + isAndDisabled={false} + onOrClicked={handleAddNewEntryItem} + onAndClicked={handleAddClick} + /> + </EuiFlexItem> + </EuiFlexGroup> + </MyButtonsContainer> + </EuiFlexGroup> + ); +}; + +ThreatMatchComponent.displayName = 'ThreatMatch'; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx new file mode 100644 index 00000000000000..90492bc46e2b0b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.test.tsx @@ -0,0 +1,382 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import { mount } from 'enzyme'; +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { useKibana } from '../../../common/lib/kibana'; +import { fields } from '../../../../../../../src/plugins/data/common/index_patterns/fields/fields.mocks'; + +import { ListItemComponent } from './list_item'; +import { ThreatMapEntries } from './types'; +import { IndexPattern } from 'src/plugins/data/public'; + +jest.mock('../../../common/lib/kibana'); + +const singlePayload = (): ThreatMapEntries => ({ + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + ], +}); + +const doublePayload = (): ThreatMapEntries => ({ + entries: [ + { + field: 'field.one', + type: 'mapping', + value: 'field.one', + }, + { + field: 'field.two', + type: 'mapping', + value: 'field.two', + }, + ], +}); + +describe('ListItemComponent', () => { + const getValueSuggestionsMock = jest.fn().mockResolvedValue(['field.one', 'field.two']); + + beforeAll(() => { + (useKibana as jest.Mock).mockReturnValue({ + services: { + data: { + autocomplete: { + getValueSuggestions: getValueSuggestionsMock, + }, + }, + }, + }); + }); + + afterEach(() => { + getValueSuggestionsMock.mockClear(); + }); + + describe('and badge logic', () => { + test('it renders "and" badge with extra top padding for the first item when "andLogicIncluded" is "true"', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ListItemComponent + listItem={doublePayload()} + listId={'123'} + listItemIndex={0} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={true} + isOnlyItem={false} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + /> + </ThemeProvider> + ); + + expect( + wrapper.find('[data-test-subj="entryItemEntryFirstRowAndBadge"]').exists() + ).toBeTruthy(); + }); + + test('it renders "and" badge when more than one item entry exists and it is not the first item', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ListItemComponent + listItem={doublePayload()} + listId={'123'} + listItemIndex={1} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={true} + isOnlyItem={false} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + /> + </ThemeProvider> + ); + + expect(wrapper.find('[data-test-subj="entryItemEntryAndBadge"]').exists()).toBeTruthy(); + }); + + test('it renders indented "and" badge when "andLogicIncluded" is "true" and only one entry exists', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ListItemComponent + listItem={singlePayload()} + listId={'123'} + listItemIndex={1} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={true} + isOnlyItem={false} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + /> + </ThemeProvider> + ); + + expect( + wrapper.find('[data-test-subj="entryItemEntryInvisibleAndBadge"]').exists() + ).toBeTruthy(); + }); + + test('it renders no "and" badge when "andLogicIncluded" is "false"', () => { + const wrapper = mount( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}> + <ListItemComponent + listItem={singlePayload()} + listId={'123'} + listItemIndex={1} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={false} + isOnlyItem={false} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + /> + </ThemeProvider> + ); + + expect( + wrapper.find('[data-test-subj="entryItemEntryInvisibleAndBadge"]').exists() + ).toBeFalsy(); + expect(wrapper.find('[data-test-subj="entryItemEntryAndBadge"]').exists()).toBeFalsy(); + expect( + wrapper.find('[data-test-subj="entryItemEntryFirstRowAndBadge"]').exists() + ).toBeFalsy(); + }); + }); + + describe('delete button logic', () => { + test('it renders delete button disabled when it is only entry left', () => { + const item: ThreatMapEntries = { + entries: [{ ...singlePayload(), field: '', type: 'mapping', value: '' }], + }; + const wrapper = mount( + <ListItemComponent + listItem={item} + listId={'123'} + listItemIndex={0} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={false} + isOnlyItem={true} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + /> + ); + + expect( + wrapper.find('[data-test-subj="itemEntryDeleteButton"] button').props().disabled + ).toBeTruthy(); + }); + + test('it does not render delete button disabled when it is not the only entry left', () => { + const wrapper = mount( + <ListItemComponent + listItem={singlePayload()} + listId={'123'} + listItemIndex={0} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={false} + isOnlyItem={false} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + /> + ); + + expect( + wrapper.find('[data-test-subj="itemEntryDeleteButton"] button').props().disabled + ).toBeFalsy(); + }); + + test('it does not render delete button disabled when "entryItemIndex" is not "0"', () => { + const wrapper = mount( + <ListItemComponent + listItem={singlePayload()} + listId={'123'} + listItemIndex={1} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={false} + // if entryItemIndex is not 0, wouldn't make sense for + // this to be true, but done for testing purposes + isOnlyItem={true} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + /> + ); + + expect( + wrapper.find('[data-test-subj="itemEntryDeleteButton"] button').props().disabled + ).toBeFalsy(); + }); + + test('it does not render delete button disabled when more than one entry exists', () => { + const wrapper = mount( + <ListItemComponent + listItem={doublePayload()} + listId={'123'} + listItemIndex={0} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={false} + isOnlyItem={true} + onDeleteEntryItem={jest.fn()} + onChangeEntryItem={jest.fn()} + /> + ); + + expect( + wrapper.find('[data-test-subj="itemEntryDeleteButton"] button').at(0).props().disabled + ).toBeFalsy(); + }); + + test('it invokes "onChangeEntryItem" when delete button clicked', () => { + const mockOnDeleteEntryItem = jest.fn(); + const wrapper = mount( + <ListItemComponent + listItem={doublePayload()} + listId={'123'} + listItemIndex={0} + indexPattern={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + threatIndexPatterns={ + { + id: '1234', + title: 'logstash-*', + fields, + } as IndexPattern + } + andLogicIncluded={false} + isOnlyItem={true} + onDeleteEntryItem={mockOnDeleteEntryItem} + onChangeEntryItem={jest.fn()} + /> + ); + + wrapper.find('[data-test-subj="itemEntryDeleteButton"] button').at(0).simulate('click'); + + const expected: ThreatMapEntries = { + entries: [ + { + field: 'field.two', + type: 'mapping', + value: 'field.two', + }, + ], + }; + + expect(mockOnDeleteEntryItem).toHaveBeenCalledWith(expected, 0); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx new file mode 100644 index 00000000000000..578986ccf17e4b --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/list_item.tsx @@ -0,0 +1,120 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useMemo, useCallback } from 'react'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; + +import { IndexPattern } from '../../../../../../../src/plugins/data/common'; +import { getFormattedEntries, getUpdatedEntriesOnDelete } from './helpers'; +import { FormattedEntry, ThreatMapEntries, Entry } from './types'; +import { EntryItem } from './entry_item'; +import { EntryDeleteButtonComponent } from './entry_delete_button'; +import { AndBadgeComponent } from './and_badge'; + +const MyOverflowContainer = styled(EuiFlexItem)` + overflow: hidden; + width: 100%; +`; + +interface ListItemProps { + listItem: ThreatMapEntries; + listId: string; + listItemIndex: number; + indexPattern: IndexPattern; + threatIndexPatterns: IndexPattern; + andLogicIncluded: boolean; + isOnlyItem: boolean; + onDeleteEntryItem: (item: ThreatMapEntries, index: number) => void; + onChangeEntryItem: (item: ThreatMapEntries, index: number) => void; +} + +export const ListItemComponent = React.memo<ListItemProps>( + ({ + listItem, + listId, + listItemIndex, + indexPattern, + threatIndexPatterns, + isOnlyItem, + andLogicIncluded, + onDeleteEntryItem, + onChangeEntryItem, + }) => { + const handleEntryChange = useCallback( + (entry: Entry, entryIndex: number): void => { + const updatedEntries: Entry[] = [ + ...listItem.entries.slice(0, entryIndex), + { ...entry }, + ...listItem.entries.slice(entryIndex + 1), + ]; + const updatedEntryItem: ThreatMapEntries = { + ...listItem, + entries: updatedEntries, + }; + onChangeEntryItem(updatedEntryItem, listItemIndex); + }, + [onChangeEntryItem, listItem, listItemIndex] + ); + + const handleDeleteEntry = useCallback( + (entryIndex: number): void => { + const updatedEntryItem = getUpdatedEntriesOnDelete(listItem, entryIndex); + + onDeleteEntryItem(updatedEntryItem, listItemIndex); + }, + [listItem, onDeleteEntryItem, listItemIndex] + ); + + const entries = useMemo( + (): FormattedEntry[] => + indexPattern != null && listItem.entries.length > 0 + ? getFormattedEntries(indexPattern, listItem.entries) + : [], + [listItem.entries, indexPattern] + ); + return ( + <EuiFlexItem> + <EuiFlexGroup gutterSize="s" data-test-subj="entriesContainer"> + {andLogicIncluded && ( + <AndBadgeComponent + entriesLength={listItem.entries.length} + entryItemIndex={listItemIndex} + /> + )} + <MyOverflowContainer grow={6}> + <EuiFlexGroup gutterSize="s" direction="column"> + {entries.map((item, index) => ( + <EuiFlexItem key={`${listId}-${index}`} grow={1}> + <EuiFlexGroup gutterSize="xs" alignItems="center" direction="row"> + <MyOverflowContainer grow={1}> + <EntryItem + entry={item} + threatIndexPatterns={threatIndexPatterns} + indexPattern={indexPattern} + showLabel={listItemIndex === 0 && index === 0} + onChange={handleEntryChange} + /> + </MyOverflowContainer> + <EntryDeleteButtonComponent + entries={listItem.entries} + isOnlyItem={isOnlyItem} + entryIndex={item.entryIndex} + itemIndex={listItemIndex} + onDelete={handleDeleteEntry} + /> + </EuiFlexGroup> + </EuiFlexItem> + ))} + </EuiFlexGroup> + </MyOverflowContainer> + </EuiFlexGroup> + </EuiFlexItem> + ); + } +); + +ListItemComponent.displayName = 'ListItem'; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.stories.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.stories.tsx new file mode 100644 index 00000000000000..dc2fa79a7b8c17 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.stories.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { storiesOf, addDecorator } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import React from 'react'; +import { ThemeProvider } from 'styled-components'; +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; + +import { LogicButtons } from './logic_buttons'; + +addDecorator((storyFn) => ( + <ThemeProvider theme={() => ({ eui: euiLightVars, darkMode: false })}>{storyFn()}</ThemeProvider> +)); + +storiesOf('ThreatMatching|LogicButtons', module) + .add('and/or buttons', () => { + return ( + <LogicButtons + isAndDisabled={false} + isOrDisabled={false} + onOrClicked={action('onClick')} + onAndClicked={action('onClick')} + /> + ); + }) + .add('and disabled', () => { + return ( + <LogicButtons + isAndDisabled + isOrDisabled={false} + onOrClicked={action('onClick')} + onAndClicked={action('onClick')} + /> + ); + }) + .add('or disabled', () => { + return ( + <LogicButtons + isAndDisabled={false} + isOrDisabled + onOrClicked={action('onClick')} + onAndClicked={action('onClick')} + /> + ); + }); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.test.tsx new file mode 100644 index 00000000000000..cd2fe3dc8f5500 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.test.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mount } from 'enzyme'; +import React from 'react'; + +import { LogicButtons } from './logic_buttons'; + +describe('LogicButtons', () => { + test('it renders "and" and "or" buttons', () => { + const wrapper = mount( + <LogicButtons + isAndDisabled={false} + isOrDisabled={false} + onOrClicked={jest.fn()} + onAndClicked={jest.fn()} + /> + ); + + expect(wrapper.find('[data-test-subj="andButton"] button')).toHaveLength(1); + expect(wrapper.find('[data-test-subj="orButton"] button')).toHaveLength(1); + }); + + test('it invokes "onOrClicked" when "or" button is clicked', () => { + const onOrClicked = jest.fn(); + + const wrapper = mount( + <LogicButtons + isAndDisabled={false} + isOrDisabled={false} + onOrClicked={onOrClicked} + onAndClicked={jest.fn()} + /> + ); + + wrapper.find('[data-test-subj="orButton"] button').simulate('click'); + + expect(onOrClicked).toHaveBeenCalledTimes(1); + }); + + test('it invokes "onAndClicked" when "and" button is clicked', () => { + const onAndClicked = jest.fn(); + + const wrapper = mount( + <LogicButtons + isAndDisabled={false} + isOrDisabled={false} + onOrClicked={jest.fn()} + onAndClicked={onAndClicked} + /> + ); + + wrapper.find('[data-test-subj="andButton"] button').simulate('click'); + + expect(onAndClicked).toHaveBeenCalledTimes(1); + }); + + test('it disables "and" button if "isAndDisabled" is true', () => { + const wrapper = mount( + <LogicButtons + isOrDisabled={false} + isAndDisabled + onOrClicked={jest.fn()} + onAndClicked={jest.fn()} + /> + ); + + const andButton = wrapper.find('[data-test-subj="andButton"] button').at(0); + + expect(andButton.prop('disabled')).toBeTruthy(); + }); + + test('it disables "or" button if "isOrDisabled" is "true"', () => { + const wrapper = mount( + <LogicButtons + isOrDisabled + isAndDisabled={false} + onOrClicked={jest.fn()} + onAndClicked={jest.fn()} + /> + ); + + const orButton = wrapper.find('[data-test-subj="orButton"] button').at(0); + + expect(orButton.prop('disabled')).toBeTruthy(); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.tsx new file mode 100644 index 00000000000000..abfbfecdb1baaf --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/logic_buttons.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import React from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui'; +import styled from 'styled-components'; + +import * as i18n from './translations'; + +const MyEuiButton = styled(EuiButton)` + min-width: 95px; +`; + +interface LogicButtonsProps { + isOrDisabled: boolean; + isAndDisabled: boolean; + onAndClicked: () => void; + onOrClicked: () => void; +} + +export const LogicButtons: React.FC<LogicButtonsProps> = ({ + isOrDisabled = false, + isAndDisabled = false, + onAndClicked, + onOrClicked, +}) => ( + <EuiFlexGroup gutterSize="s" alignItems="center"> + <EuiFlexItem grow={false}> + <MyEuiButton + fill + size="s" + iconType="plusInCircle" + onClick={onAndClicked} + data-test-subj="andButton" + isDisabled={isAndDisabled} + > + {i18n.AND} + </MyEuiButton> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <MyEuiButton + fill + size="s" + iconType="plusInCircle" + onClick={onOrClicked} + isDisabled={isOrDisabled} + data-test-subj="orButton" + > + {i18n.OR} + </MyEuiButton> + </EuiFlexItem> + </EuiFlexGroup> +); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/reducer.test.ts b/x-pack/plugins/security_solution/public/common/components/threat_match/reducer.test.ts new file mode 100644 index 00000000000000..6b2a443ec45a5d --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/reducer.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ThreatMapEntries } from './types'; +import { State, reducer } from './reducer'; +import { getDefaultEmptyEntry } from './helpers'; +import { ThreatMapEntry } from '../../../../common/detection_engine/schemas/types'; + +const initialState: State = { + andLogicIncluded: false, + entries: [], + entriesToDelete: [], +}; + +const getEntry = (): ThreatMapEntry => ({ + field: 'host.name', + type: 'mapping', + value: 'host.name', +}); + +describe('reducer', () => { + describe('#setEntries', () => { + test('should return "andLogicIncluded" ', () => { + const update = reducer()(initialState, { + type: 'setEntries', + entries: [], + }); + const expected: State = { + andLogicIncluded: false, + entries: [], + entriesToDelete: [], + }; + expect(update).toEqual(expected); + }); + + test('should set "andLogicIncluded" to true if any of the entries include entries with length greater than 1 ', () => { + const entries: ThreatMapEntries[] = [ + { + entries: [getEntry(), getEntry()], + }, + ]; + const { andLogicIncluded } = reducer()(initialState, { + type: 'setEntries', + entries, + }); + + expect(andLogicIncluded).toBeTruthy(); + }); + + test('should set "andLogicIncluded" to false if any of the entries include entries with length greater than 1 ', () => { + const entries: ThreatMapEntries[] = [ + { + entries: [getEntry()], + }, + ]; + const { andLogicIncluded } = reducer()(initialState, { + type: 'setEntries', + entries, + }); + + expect(andLogicIncluded).toBeFalsy(); + }); + }); + + describe('#setDefault', () => { + test('should restore initial state and add default empty entry to item" ', () => { + const entries: ThreatMapEntries[] = [ + { + entries: [getEntry()], + }, + ]; + + const update = reducer()( + { + andLogicIncluded: true, + entries, + entriesToDelete: [], + }, + { + type: 'setDefault', + initialState, + lastEntry: { + entries: [], + }, + } + ); + + expect(update).toEqual({ + ...initialState, + entries: [ + { + entries: [getDefaultEmptyEntry()], + }, + ], + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/reducer.ts b/x-pack/plugins/security_solution/public/common/components/threat_match/reducer.ts new file mode 100644 index 00000000000000..3fd19d40afa531 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/reducer.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { ThreatMapEntries } from './types'; +import { getDefaultEmptyEntry } from './helpers'; + +export type ViewerModalName = 'addModal' | 'editModal' | null; + +export interface State { + andLogicIncluded: boolean; + entries: ThreatMapEntries[]; + entriesToDelete: ThreatMapEntries[]; +} + +export type Action = + | { + type: 'setEntries'; + entries: ThreatMapEntries[]; + } + | { + type: 'setDefault'; + initialState: State; + lastEntry: ThreatMapEntries; + }; + +export const reducer = () => (state: State, action: Action): State => { + switch (action.type) { + case 'setEntries': { + const isAndLogicIncluded = + action.entries.filter(({ entries }) => entries.length > 1).length > 0; + + const returnState = { + ...state, + andLogicIncluded: isAndLogicIncluded, + entries: action.entries, + }; + return returnState; + } + case 'setDefault': { + return { + ...state, + ...action.initialState, + entries: [{ ...action.lastEntry, entries: [getDefaultEmptyEntry()] }], + }; + } + default: + return state; + } +}; diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/translations.ts b/x-pack/plugins/security_solution/public/common/components/threat_match/translations.ts new file mode 100644 index 00000000000000..ca9f6a13856cf7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/translations.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const FIELD = i18n.translate('xpack.securitySolution.threatMatch.fieldDescription', { + defaultMessage: 'Field', +}); + +export const THREAT_FIELD = i18n.translate( + 'xpack.securitySolution.threatMatch.threatFieldDescription', + { + defaultMessage: 'Threat index field', + } +); + +export const FIELD_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.threatMatch.fieldPlaceholderDescription', + { + defaultMessage: 'Search', + } +); + +export const MATCHES = i18n.translate('xpack.securitySolution.threatMatch.matchesLabel', { + defaultMessage: 'MATCHES', +}); + +export const AND = i18n.translate('xpack.securitySolution.threatMatch.andDescription', { + defaultMessage: 'AND', +}); + +export const OR = i18n.translate('xpack.securitySolution.threatMatch.orDescription', { + defaultMessage: 'OR', +}); diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/types.ts b/x-pack/plugins/security_solution/public/common/components/threat_match/types.ts new file mode 100644 index 00000000000000..0cbd885db2d546 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/types.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { ThreatMap, ThreatMapEntry } from '../../../../common/detection_engine/schemas/types'; +import { IFieldType } from '../../../../../../../src/plugins/data/common'; + +export interface FormattedEntry { + field: IFieldType | undefined; + type: 'mapping'; + value: IFieldType | undefined; + entryIndex: number; +} + +export interface EmptyEntry { + field: string | undefined; + type: 'mapping'; + value: string | undefined; +} + +export type Entry = ThreatMapEntry | EmptyEntry; + +export type ThreatMapEntries = Omit<ThreatMap, 'entries'> & { + entries: Entry[]; +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx index 4d46d4dc868467..9ef1dd2bcb204e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/helpers.tsx @@ -21,6 +21,8 @@ import { isEmpty } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; +import { MATCHES, AND, OR } from '../../../../common/components/threat_match/translations'; +import { ThreatMapping } from '../../../../../common/detection_engine/schemas/types'; import { assertUnreachable } from '../../../../../common/utility_types'; import * as i18nSeverity from '../severity_mapping/translations'; import * as i18nRiskScore from '../risk_score_mapping/translations'; @@ -56,6 +58,7 @@ export const buildQueryBarDescription = ({ query, savedId, indexPatterns, + queryLabel, }: BuildQueryBarDescription): ListItems[] => { let items: ListItems[] = []; if (!isEmpty(filters)) { @@ -89,7 +92,7 @@ export const buildQueryBarDescription = ({ items = [ ...items, { - title: <>{i18n.QUERY_LABEL} </>, + title: <>{queryLabel ?? i18n.QUERY_LABEL} </>, description: <>{query} </>, }, ]; @@ -416,3 +419,40 @@ export const buildThresholdDescription = (label: string, threshold: Threshold): ), }, ]; + +export const buildThreatMappingDescription = ( + title: string, + threatMapping: ThreatMapping +): ListItems[] => { + const description = threatMapping.reduce<string>( + (accumThreatMaps, threatMap, threatMapIndex, { length: threatMappingLength }) => { + const matches = threatMap.entries.reduce<string>( + (accumItems, item, itemsIndex, { length: threatMapLength }) => { + if (threatMapLength === 1) { + return `${item.field} ${MATCHES} ${item.value}`; + } else if (itemsIndex === 0) { + return `(${item.field} ${MATCHES} ${item.value})`; + } else { + return `${accumItems} ${AND} (${item.field} ${MATCHES} ${item.value})`; + } + }, + '' + ); + + if (threatMappingLength === 1) { + return `${matches}`; + } else if (threatMapIndex === 0) { + return `(${matches})`; + } else { + return `${accumThreatMaps} ${OR} (${matches})`; + } + }, + '' + ); + return [ + { + title, + description, + }, + ]; +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx index 99e36669f78bb6..83d087e60bc7d2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx @@ -9,6 +9,7 @@ import { isEmpty, chunk, get, pick, isNumber } from 'lodash/fp'; import React, { memo, useState } from 'react'; import styled from 'styled-components'; +import { ThreatMapping } from '../../../../../common/detection_engine/schemas/types'; import { IIndexPattern, Filter, @@ -36,11 +37,13 @@ import { buildRiskScoreDescription, buildRuleTypeDescription, buildThresholdDescription, + buildThreatMappingDescription, } from './helpers'; import { buildMlJobDescription } from './ml_job_description'; import { buildActionsDescription } from './actions_description'; import { buildThrottleDescription } from './throttle_description'; import { Type } from '../../../../../common/detection_engine/schemas/common/schemas'; +import { THREAT_QUERY_LABEL } from './translations'; const DescriptionListContainer = styled(EuiDescriptionList)` &.euiDescriptionList--column .euiDescriptionList__title { @@ -156,6 +159,7 @@ export const addFilterStateIfNotThere = (filters: Filter[]): Filter[] => { }); }; +/* eslint complexity: ["error", 21]*/ export const getDescriptionItem = ( field: string, label: string, @@ -189,7 +193,7 @@ export const getDescriptionItem = ( } else if (field === 'falsePositives') { const values: string[] = get(field, data); return buildUnorderedListArrayDescription(label, field, values); - } else if (Array.isArray(get(field, data))) { + } else if (Array.isArray(get(field, data)) && field !== 'threatMapping') { const values: string[] = get(field, data); return buildStringArrayDescription(label, field, values); } else if (field === 'riskScore') { @@ -214,6 +218,22 @@ export const getDescriptionItem = ( return buildRuleTypeDescription(label, ruleType); } else if (field === 'kibanaSiemAppUrl') { return []; + } else if (field === 'threatQueryBar') { + const filters = addFilterStateIfNotThere(get('threatQueryBar.filters', data) ?? []); + const query = get('threatQueryBar.query.query', data); + const savedId = get('threatQueryBar.saved_id', data); + return buildQueryBarDescription({ + field, + filters, + filterManager, + query, + savedId, + indexPatterns, + queryLabel: THREAT_QUERY_LABEL, + }); + } else if (field === 'threatMapping') { + const threatMap: ThreatMapping = get(field, data); + return buildThreatMappingDescription(label, threatMap); } const description: string = get(field, data); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx index d714f04f519d4e..d9186c2da72256 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/translations.tsx @@ -20,6 +20,13 @@ export const QUERY_LABEL = i18n.translate( } ); +export const THREAT_QUERY_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.threatQueryLabel', + { + defaultMessage: 'Threat query', + } +); + export const SAVED_ID_LABEL = i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.savedIdLabel', { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/types.ts b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/types.ts index bcda5ff67a9a62..719c38689b7223 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/types.ts @@ -24,6 +24,7 @@ export interface BuildQueryBarDescription { query: string; savedId: string; indexPatterns?: IIndexPattern; + queryLabel?: string; } export interface BuildThreatDescription { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx index 169e4f81d34986..9a1d11a2dfe42c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx @@ -12,6 +12,7 @@ import { isThresholdRule, isEqlRule, isQueryRule, + isThreatMatchRule, } from '../../../../../common/detection_engine/utils'; import { FieldHook } from '../../../../shared_imports'; import { useKibana } from '../../../../common/lib/kibana'; @@ -45,6 +46,7 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({ const setMl = useCallback(() => setType('machine_learning'), [setType]); const setQuery = useCallback(() => setType('query'), [setType]); const setThreshold = useCallback(() => setType('threshold'), [setType]); + const setThreatMatch = useCallback(() => setType('threat_match'), [setType]); const mlCardDisabled = isReadOnly || !hasValidLicense || !isMlAdmin; const licensingUrl = useKibana().services.application.getUrlForApp('kibana', { path: '#/management/stack/license_management', @@ -86,6 +88,15 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({ [isReadOnly, ruleType, setThreshold] ); + const threatMatchSelectableConfig = useMemo( + () => ({ + isDisabled: isReadOnly, + onClick: setThreatMatch, + isSelected: isThreatMatchRule(ruleType), + }), + [isReadOnly, ruleType, setThreatMatch] + ); + return ( <EuiFormRow fullWidth @@ -138,6 +149,18 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({ selectable={eqlSelectableConfig} /> </EuiFlexItem> + <EuiFlexItem> + <EuiCard + data-test-subj="threatMatchRuleType" + title={i18n.THREAT_MATCH_TYPE_TITLE} + description={i18n.THREAT_MATCH_TYPE_DESCRIPTION} + icon={<EuiIcon size="l" type="list" />} + isDisabled={ + threatMatchSelectableConfig.isDisabled && !threatMatchSelectableConfig.isSelected + } + selectable={threatMatchSelectableConfig} + /> + </EuiFlexItem> </EuiFlexGrid> </EuiFormRow> ); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/translations.ts index e7b231ca74958e..7043aa2d2f956d 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/translations.ts @@ -62,3 +62,17 @@ export const THRESHOLD_TYPE_DESCRIPTION = i18n.translate( defaultMessage: 'Aggregate query results to detect when number of matches exceeds threshold.', } ); + +export const THREAT_MATCH_TYPE_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ruleTypeField.threatMatchTitle', + { + defaultMessage: 'Threat Match', + } +); + +export const THREAT_MATCH_TYPE_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.ruleTypeField.threatMatchDescription', + { + defaultMessage: 'Upload value lists to write rules around a list of known bad attributes', + } +); 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 dc31db76c39112..f728a508fef86b 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 @@ -11,6 +11,7 @@ import styled from 'styled-components'; // eslint-disable-next-line no-restricted-imports import isEqual from 'lodash/isEqual'; +import { IndexPattern } from 'src/plugins/data/public'; import { DEFAULT_INDEX_KEY } from '../../../../../common/constants'; import { DEFAULT_TIMELINE_TITLE } from '../../../../timelines/components/timeline/translations'; import { isMlRule } from '../../../../../common/machine_learning/helpers'; @@ -47,8 +48,13 @@ import { } from '../../../../shared_imports'; import { schema } from './schema'; import * as i18n from './translations'; -import { isEqlRule, isThresholdRule } from '../../../../../common/detection_engine/utils'; +import { + isEqlRule, + isThreatMatchRule, + isThresholdRule, +} from '../../../../../common/detection_engine/utils'; import { EqlQueryBar } from '../eql_query_bar'; +import { ThreatMatchInput } from '../threatmatch_input'; import { useFetchIndex } from '../../../../common/containers/source'; const CommonUseField = getUseField({ component: Field }); @@ -62,11 +68,18 @@ const stepDefineDefaultValue: DefineStepRule = { index: [], machineLearningJobId: '', ruleType: 'query', + threatIndex: [], queryBar: { query: { query: '', language: 'kuery' }, filters: [], saved_id: undefined, }, + threatQueryBar: { + query: { query: '*:*', language: 'kuery' }, + filters: [], + saved_id: undefined, + }, + threatMapping: [], threshold: { field: [], value: '200', @@ -121,14 +134,22 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({ schema, }); const { getFields, getFormData, reset, submit } = form; - const [{ index: formIndex, ruleType: formRuleType }] = (useFormData({ - form, - watch: ['index', 'ruleType'], - }) as unknown) as [Partial<DefineStepRule>]; + const [{ index: formIndex, ruleType: formRuleType, threatIndex: formThreatIndex }] = (useFormData( + { + form, + watch: ['index', 'ruleType', 'threatIndex'], + } + ) as unknown) as [Partial<DefineStepRule>]; const index = formIndex || initialState.index; + const threatIndex = formThreatIndex || initialState.threatIndex; const ruleType = formRuleType || initialState.ruleType; const [indexPatternsLoading, { browserFields, indexPatterns }] = useFetchIndex(index); + const [ + threatIndexPatternsLoading, + { browserFields: threatBrowserFields, indexPatterns: threatIndexPatterns }, + ] = useFetchIndex(threatIndex); + // reset form when rule type changes useEffect(() => { reset({ resetValues: false }); @@ -146,7 +167,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({ const getData = useCallback(async () => { const result = await submit(); - return result?.isValid + return result.isValid ? result : { isValid: false, @@ -184,6 +205,19 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({ [browserFields] ); + const ThreatMatchInputChildren = useCallback( + ({ threatMapping }) => ( + <ThreatMatchInput + threatBrowserFields={threatBrowserFields} + indexPatterns={indexPatterns as IndexPattern} + threatIndexPatterns={threatIndexPatterns as IndexPattern} + threatMapping={threatMapping} + threatIndexPatternsLoading={threatIndexPatternsLoading} + /> + ), + [threatBrowserFields, threatIndexPatternsLoading, threatIndexPatterns, indexPatterns] + ); + return isReadOnlyView ? ( <StepContentWrapper data-test-subj="definitionRule" addPadding={addPadding}> <StepRuleDescription @@ -309,6 +343,23 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({ </UseMultiFields> </> </RuleTypeEuiFormRow> + <RuleTypeEuiFormRow + $isVisible={isThreatMatchRule(ruleType)} + data-test-subj="threatMatchInput" + fullWidth + > + <> + <UseMultiFields + fields={{ + threatMapping: { + path: 'threatMapping', + }, + }} + > + {ThreatMatchInputChildren} + </UseMultiFields> + </> + </RuleTypeEuiFormRow> <UseField path="timeline" component={PickTimeline} diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx index 07eff94bbbef4b..9c2f8fd80f66e8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx @@ -9,6 +9,11 @@ import { EuiText } from '@elastic/eui'; import { isEmpty } from 'lodash/fp'; import React from 'react'; +import { + singleEntryThreat, + containsInvalidItems, +} from '../../../../common/components/threat_match/helpers'; +import { isThreatMatchRule } from '../../../../../common/detection_engine/utils'; import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { esKuery } from '../../../../../../../../src/plugins/data/public'; import { FieldValueQueryBar } from '../query_bar'; @@ -20,7 +25,14 @@ import { ValidationFunc, } from '../../../../shared_imports'; import { DefineStepRule } from '../../../pages/detection_engine/rules/types'; -import { CUSTOM_QUERY_REQUIRED, INVALID_CUSTOM_QUERY, INDEX_HELPER_TEXT } from './translations'; +import { + CUSTOM_QUERY_REQUIRED, + INVALID_CUSTOM_QUERY, + INDEX_HELPER_TEXT, + THREAT_MATCH_INDEX_HELPER_TEXT, + THREAT_MATCH_REQUIRED, + THREAT_MATCH_EMPTIES, +} from './translations'; export const schema: FormSchema<DefineStepRule> = { index: { @@ -219,4 +231,126 @@ export const schema: FormSchema<DefineStepRule> = { ], }, }, + threatIndex: { + type: FIELD_TYPES.COMBO_BOX, + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThreatIndexPatternsLabel', + { + defaultMessage: 'Threat index patterns', + } + ), + helpText: <EuiText size="xs">{THREAT_MATCH_INDEX_HELPER_TEXT}</EuiText>, + validations: [ + { + validator: ( + ...args: Parameters<ValidationFunc> + ): ReturnType<ValidationFunc<{}, ERROR_CODE>> | undefined => { + const [{ formData }] = args; + const needsValidation = isThreatMatchRule(formData.ruleType); + if (!needsValidation) { + return; + } + return fieldValidators.emptyField( + i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchoutputIndiceNameFieldRequiredError', + { + defaultMessage: 'A minimum of one index pattern is required.', + } + ) + )(...args); + }, + }, + ], + }, + threatMapping: { + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThreatMappingLabel', + { + defaultMessage: 'Threat Mapping', + } + ), + validations: [ + { + validator: ( + ...args: Parameters<ValidationFunc> + ): ReturnType<ValidationFunc<{}, ERROR_CODE>> | undefined => { + const [{ path, formData }] = args; + const needsValidation = isThreatMatchRule(formData.ruleType); + if (!needsValidation) { + return; + } + if (singleEntryThreat(formData.threatMapping)) { + return { + code: 'ERR_FIELD_MISSING', + path, + message: THREAT_MATCH_REQUIRED, + }; + } else if (containsInvalidItems(formData.threatMapping)) { + return { + code: 'ERR_FIELD_MISSING', + path, + message: THREAT_MATCH_EMPTIES, + }; + } else { + return undefined; + } + }, + }, + ], + }, + threatQueryBar: { + label: i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldThreatQueryBarLabel', + { + defaultMessage: 'Threat index query', + } + ), + validations: [ + { + validator: ( + ...args: Parameters<ValidationFunc> + ): ReturnType<ValidationFunc<{}, ERROR_CODE>> | undefined => { + const [{ value, path, formData }] = args; + const needsValidation = isThreatMatchRule(formData.ruleType); + if (!needsValidation) { + return; + } + + const { query, filters } = value as FieldValueQueryBar; + + return isEmpty(query.query as string) && isEmpty(filters) + ? { + code: 'ERR_FIELD_MISSING', + path, + message: CUSTOM_QUERY_REQUIRED, + } + : undefined; + }, + }, + { + validator: ( + ...args: Parameters<ValidationFunc> + ): ReturnType<ValidationFunc<{}, ERROR_CODE>> | undefined => { + const [{ value, path, formData }] = args; + const needsValidation = isThreatMatchRule(formData.ruleType); + if (!needsValidation) { + return; + } + const { query } = value as FieldValueQueryBar; + + if (!isEmpty(query.query as string) && query.language === 'kuery') { + try { + esKuery.fromKueryExpression(query.query); + } catch (err) { + return { + code: 'ERR_FIELD_FORMAT', + path, + message: INVALID_CUSTOM_QUERY, + }; + } + } + }, + }, + ], + }, }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx index 860ed1831fdc6a..8e0a3f9b8659e4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx @@ -70,3 +70,24 @@ export const ENABLE_ML_JOB_WARNING = i18n.translate( 'This ML job is not currently running. Please set this job to run via "ML job settings" before activating this rule.', } ); + +export const THREAT_MATCH_INDEX_HELPER_TEXT = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription', + { + defaultMessage: 'Select threat indices', + } +); + +export const THREAT_MATCH_REQUIRED = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.customThreatQueryFieldRequiredError', + { + defaultMessage: 'At least one threat match is required.', + } +); + +export const THREAT_MATCH_EMPTIES = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.customThreatQueryFieldRequiredEmptyError', + { + defaultMessage: 'All matches require both a field and threat index field.', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/index.tsx new file mode 100644 index 00000000000000..2a4609a2f5e9e3 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/index.tsx @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useCallback } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiFormRow } from '@elastic/eui'; + +import { ThreatMapEntries } from '../../../../common/components/threat_match/types'; +import { ThreatMatchComponent } from '../../../../common/components/threat_match'; +import { BrowserField } from '../../../../common/containers/source'; +import { + FieldHook, + Field, + getUseField, + UseField, + getFieldValidityAndErrorMessage, +} from '../../../../shared_imports'; +import { schema } from '../step_define_rule/schema'; +import { QueryBarDefineRule } from '../query_bar'; +import { IndexPattern } from '../../../../../../../../src/plugins/data/public'; + +const CommonUseField = getUseField({ component: Field }); + +interface ThreatMatchInputProps { + threatMapping: FieldHook; + threatBrowserFields: Readonly<Record<string, Partial<BrowserField>>>; + threatIndexPatterns: IndexPattern; + indexPatterns: IndexPattern; + threatIndexPatternsLoading: boolean; +} + +const ThreatMatchInputComponent: React.FC<ThreatMatchInputProps> = ({ + threatMapping, + indexPatterns, + threatIndexPatterns, + threatIndexPatternsLoading, + threatBrowserFields, +}: ThreatMatchInputProps) => { + const { setValue, value: threatItems } = threatMapping; + const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(threatMapping); + const handleBuilderOnChange = useCallback( + ({ entryItems }: { entryItems: ThreatMapEntries[] }): void => { + setValue(entryItems); + }, + [setValue] + ); + return ( + <> + <EuiSpacer size="m" /> + <EuiFlexGroup direction="column"> + <EuiFlexItem grow={true}> + <CommonUseField + path="threatIndex" + config={{ + ...schema.threatIndex, + labelAppend: null, + }} + componentProps={{ + idAria: 'detectionEngineStepDefineRuleThreatMatchIndices', + 'data-test-subj': 'detectionEngineStepDefineRuleThreatMatchIndices', + euiFieldProps: { + fullWidth: true, + isDisabled: false, + placeholder: '', + }, + }} + /> + </EuiFlexItem> + <EuiFlexItem grow={true}> + <UseField + path="threatQueryBar" + config={{ + ...schema.threatQueryBar, + labelAppend: null, + }} + component={QueryBarDefineRule} + componentProps={{ + browserFields: threatBrowserFields, + idAria: 'detectionEngineStepDefineThreatRuleQueryBar', + indexPattern: threatIndexPatterns, + isDisabled: false, + isLoading: threatIndexPatternsLoading, + dataTestSubj: 'detectionEngineStepDefineThreatRuleQueryBar', + openTimelineSearch: false, + }} + /> + </EuiFlexItem> + </EuiFlexGroup> + <EuiSpacer size="m" /> + <EuiFormRow + label={threatMapping.label} + labelAppend={threatMapping.labelAppend} + helpText={threatMapping.helpText} + error={errorMessage} + isInvalid={isInvalid} + fullWidth + > + <ThreatMatchComponent + listItems={threatItems as ThreatMapEntries[]} + indexPatterns={indexPatterns} + threatIndexPatterns={threatIndexPatterns} + data-test-subj="threatmatch-builder" + id-aria="threatmatch-builder" + onChange={handleBuilderOnChange} + /> + </EuiFormRow> + <EuiSpacer size="m" /> + </> + ); +}; + +export const ThreatMatchInput = React.memo(ThreatMatchInputComponent); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/translations.ts new file mode 100644 index 00000000000000..0aa268ae24ae12 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/threatmatch_input/translations.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const THREAT_MATCH_FIELD_PLACEHOLDER = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchField.threatMatchFieldPlaceholderText', + { + defaultMessage: 'All results', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts index 49579e893029b6..e9c89130736c05 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/types.ts @@ -18,7 +18,14 @@ import { threshold, type, } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { listArray } from '../../../../../common/detection_engine/schemas/types'; +import { + listArray, + threat_query, + threat_index, + threat_mapping, + threat_language, + threat_filters, +} from '../../../../../common/detection_engine/schemas/types'; import { CreateRulesSchema, PatchRulesSchema, @@ -110,6 +117,11 @@ export const RuleSchema = t.intersection([ status: t.string, status_date: t.string, threshold, + threat_query, + threat_filters, + threat_index, + threat_mapping, + threat_language, timeline_id: t.string, timeline_title: t.string, timestamp_override, diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts index 5a626ce0ff00a2..5851177a4e4ab4 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/__mocks__/mock.ts @@ -212,10 +212,13 @@ export const mockDefineStepRule = (): DefineStepRule => ({ machineLearningJobId: '', index: ['filebeat-'], queryBar: mockQueryBar, + threatQueryBar: mockQueryBar, + threatMapping: [], timeline: { id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', title: 'Titled timeline', }, + threatIndex: [], threshold: { field: [''], value: '100', diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts index 65a5c6aca00508..160809a2ba3cd4 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/create/helpers.ts @@ -73,28 +73,77 @@ export interface RuleFields { index: unknown; ruleType: unknown; threshold?: unknown; + threatIndex?: unknown; + threatQueryBar?: unknown; + threatMapping?: unknown; + threatLanguage?: unknown; } -type QueryRuleFields<T> = Omit<T, 'anomalyThreshold' | 'machineLearningJobId' | 'threshold'>; -type ThresholdRuleFields<T> = Omit<T, 'anomalyThreshold' | 'machineLearningJobId'>; -type MlRuleFields<T> = Omit<T, 'queryBar' | 'index' | 'threshold'>; + +type QueryRuleFields<T> = Omit< + T, + | 'anomalyThreshold' + | 'machineLearningJobId' + | 'threshold' + | 'threatIndex' + | 'threatQueryBar' + | 'threatMapping' +>; +type ThresholdRuleFields<T> = Omit< + T, + 'anomalyThreshold' | 'machineLearningJobId' | 'threatIndex' | 'threatQueryBar' | 'threatMapping' +>; +type MlRuleFields<T> = Omit< + T, + 'queryBar' | 'index' | 'threshold' | 'threatIndex' | 'threatQueryBar' | 'threatMapping' +>; +type ThreatMatchRuleFields<T> = Omit<T, 'anomalyThreshold' | 'machineLearningJobId' | 'threshold'>; const isMlFields = <T>( - fields: QueryRuleFields<T> | MlRuleFields<T> | ThresholdRuleFields<T> + fields: QueryRuleFields<T> | MlRuleFields<T> | ThresholdRuleFields<T> | ThreatMatchRuleFields<T> ): fields is MlRuleFields<T> => has('anomalyThreshold', fields); const isThresholdFields = <T>( - fields: QueryRuleFields<T> | MlRuleFields<T> | ThresholdRuleFields<T> + fields: QueryRuleFields<T> | MlRuleFields<T> | ThresholdRuleFields<T> | ThreatMatchRuleFields<T> ): fields is ThresholdRuleFields<T> => has('threshold', fields); -export const filterRuleFieldsForType = <T extends RuleFields>(fields: T, type: Type) => { +const isThreatMatchFields = <T>( + fields: QueryRuleFields<T> | MlRuleFields<T> | ThresholdRuleFields<T> | ThreatMatchRuleFields<T> +): fields is ThreatMatchRuleFields<T> => has('threatIndex', fields); + +export const filterRuleFieldsForType = <T extends RuleFields>( + fields: T, + type: Type +): QueryRuleFields<T> | MlRuleFields<T> | ThresholdRuleFields<T> | ThreatMatchRuleFields<T> => { switch (type) { case 'machine_learning': - const { index, queryBar, threshold, ...mlRuleFields } = fields; + const { + index, + queryBar, + threshold, + threatIndex, + threatQueryBar, + threatMapping, + ...mlRuleFields + } = fields; return mlRuleFields; case 'threshold': - const { anomalyThreshold, machineLearningJobId, ...thresholdRuleFields } = fields; + const { + anomalyThreshold, + machineLearningJobId, + threatIndex: _removedThreatIndex, + threatQueryBar: _removedThreatQueryBar, + threatMapping: _removedThreatMapping, + ...thresholdRuleFields + } = fields; return thresholdRuleFields; case 'threat_match': + const { + anomalyThreshold: _removedAnomalyThreshold, + machineLearningJobId: _removedMachineLearningJobId, + threshold: _removedThreshold, + ...threatMatchRuleFields + } = fields; + return threatMatchRuleFields; case 'query': case 'saved_query': case 'eql': @@ -102,6 +151,9 @@ export const filterRuleFieldsForType = <T extends RuleFields>(fields: T, type: T anomalyThreshold: _a, machineLearningJobId: _m, threshold: _t, + threatIndex: __removedThreatIndex, + threatQueryBar: __removedThreatQueryBar, + threatMapping: __removedThreatMapping, ...queryRuleFields } = fields; return queryRuleFields; @@ -140,6 +192,18 @@ export const formatDefineStepData = (defineStepData: DefineStepRule): DefineStep }, }), } + : isThreatMatchFields(ruleFields) + ? { + index: ruleFields.index, + filters: ruleFields.queryBar?.filters, + language: ruleFields.queryBar?.query?.language, + query: ruleFields.queryBar?.query?.query as string, + saved_id: ruleFields.queryBar?.saved_id, + threat_index: ruleFields.threatIndex, + threat_query: ruleFields.threatQueryBar?.query?.query as string, + threat_mapping: ruleFields.threatMapping, + threat_language: ruleFields.threatQueryBar?.query?.language, + } : { index: ruleFields.index, filters: ruleFields.queryBar?.filters, 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 48247392dfe7f6..542b7b1b84c3cb 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 @@ -183,10 +183,10 @@ const CreateRulePageComponent: React.FC = () => { if (nextStep != null) { goToStep(nextStep); } else { - const defineStep = await stepsData.current[RuleStep.defineRule]; - const aboutStep = await stepsData.current[RuleStep.aboutRule]; - const scheduleStep = await stepsData.current[RuleStep.scheduleRule]; - const actionsStep = await stepsData.current[RuleStep.ruleActions]; + const defineStep = stepsData.current[RuleStep.defineRule]; + const aboutStep = stepsData.current[RuleStep.aboutRule]; + const scheduleStep = stepsData.current[RuleStep.scheduleRule]; + const actionsStep = stepsData.current[RuleStep.ruleActions]; if ( stepIsValid(defineStep) && diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx index 8545e5da512bbd..0cd75506fa9f50 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.test.tsx @@ -82,6 +82,16 @@ describe('rule helpers', () => { field: ['host.name'], value: '50', }, + threatIndex: [], + threatMapping: [], + threatQueryBar: { + query: { + query: '', + language: '', + }, + filters: [], + saved_id: undefined, + }, timeline: { id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', title: 'Titled timeline', @@ -217,6 +227,16 @@ describe('rule helpers', () => { field: [], value: '100', }, + threatIndex: [], + threatMapping: [], + threatQueryBar: { + query: { + query: '', + language: '', + }, + filters: [], + saved_id: undefined, + }, timeline: { id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', title: 'Untitled timeline', @@ -249,6 +269,16 @@ describe('rule helpers', () => { field: [], value: '100', }, + threatIndex: [], + threatMapping: [], + threatQueryBar: { + query: { + query: '', + language: '', + }, + filters: [], + saved_id: undefined, + }, timeline: { id: '86aa74d0-2136-11ea-9864-ebc8cc1cb8c2', title: 'Untitled timeline', diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 42fbe40d690ea7..456bf8419a1f71 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -79,6 +79,13 @@ export const getDefineStepsData = (rule: Rule): DefineStepRule => ({ anomalyThreshold: rule.anomaly_threshold ?? 50, machineLearningJobId: rule.machine_learning_job_id ?? '', index: rule.index ?? [], + threatIndex: rule.threat_index ?? [], + threatQueryBar: { + query: { query: rule.threat_query ?? '', language: rule.threat_language ?? '' }, + filters: (rule.threat_filters ?? []) as Filter[], + saved_id: undefined, + }, + threatMapping: rule.threat_mapping ?? [], queryBar: { query: { query: rule.query ?? '', language: rule.language ?? '' }, filters: (rule.filters ?? []) as Filter[], @@ -341,7 +348,6 @@ export const getActionMessageRuleParams = (ruleType: Type): string[] => { 'threat', 'type', 'version', - // 'lists', ]; const ruleParamsKeys = [ diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts index e3d0ea123872a4..f2afe32b1e12ce 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/types.ts @@ -22,7 +22,11 @@ import { Type, Severity, } from '../../../../../common/detection_engine/schemas/common/schemas'; -import { List } from '../../../../../common/detection_engine/schemas/types'; +import { + List, + ThreatIndex, + ThreatMapping, +} from '../../../../../common/detection_engine/schemas/types'; export interface EuiBasicTableSortTypes { field: string; @@ -124,6 +128,9 @@ export interface DefineStepRule { ruleType: Type; timeline: FieldValueTimeline; threshold: FieldValueThreshold; + threatIndex: ThreatIndex; + threatQueryBar: FieldValueQueryBar; + threatMapping: ThreatMapping; } export interface ScheduleStepRule { 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 9081831c454978..894ac2e0bb703a 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 @@ -399,6 +399,7 @@ export const getResult = (): RuleAlertType => ({ timestampOverride: undefined, threatFilters: undefined, threatMapping: undefined, + threatLanguage: undefined, threatIndex: undefined, threatQuery: undefined, references: ['http://www.example.com', 'https://ww.example.com'], diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts index 067a4352e10809..09e161166dddf1 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_bulk_route.ts @@ -96,6 +96,7 @@ export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => threat_index: threatIndex, threat_mapping: threatMapping, threat_query: threatQuery, + threat_language: threatLanguage, threshold, throttle, timestamp_override: timestampOverride, @@ -186,6 +187,7 @@ export const createRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => threatMapping, threatQuery, threatIndex, + threatLanguage, threshold, timestampOverride, references, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts index 54df87ca17787d..9940a56988c770 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/rules/create_rules_route.ts @@ -84,6 +84,7 @@ export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void threat_index: threatIndex, threat_query: threatQuery, threat_mapping: threatMapping, + threat_language: threatLanguage, throttle, timestamp_override: timestampOverride, to, @@ -175,6 +176,7 @@ export const createRulesRoute = (router: IRouter, ml: SetupPlugins['ml']): void threatIndex, threatQuery, threatMapping, + threatLanguage, timestampOverride, references, note, 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 4dbca5df0041cf..fb9d9c4ea72cf5 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 @@ -163,6 +163,7 @@ export const importRulesRoute = (router: IRouter, config: ConfigType, ml: SetupP threat_index: threatIndex, threat_query: threatQuery, threat_mapping: threatMapping, + threat_language: threatLanguage, threshold, timestamp_override: timestampOverride, to, @@ -223,11 +224,12 @@ export const importRulesRoute = (router: IRouter, config: ConfigType, ml: SetupP to, type, threat, + threshold, threatFilters, threatIndex, threatQuery, - threshold, threatMapping, + threatLanguage, timestampOverride, references, note, @@ -272,6 +274,11 @@ export const importRulesRoute = (router: IRouter, config: ConfigType, ml: SetupP type, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, references, note, version, 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 39bbe9ee686a49..081e804cf73568 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 @@ -87,6 +87,11 @@ export const patchRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => type, threat, threshold, + threat_filters: threatFilters, + threat_index: threatIndex, + threat_query: threatQuery, + threat_mapping: threatMapping, + threat_language: threatLanguage, timestamp_override: timestampOverride, throttle, references, @@ -147,6 +152,11 @@ export const patchRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => type, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, references, note, 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 879bd8d5b8a1d1..baa5468f862c37 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 @@ -78,6 +78,11 @@ export const patchRulesRoute = (router: IRouter, ml: SetupPlugins['ml']) => { type, threat, threshold, + threat_filters: threatFilters, + threat_index: threatIndex, + threat_query: threatQuery, + threat_mapping: threatMapping, + threat_language: threatLanguage, timestamp_override: timestampOverride, throttle, references, @@ -146,6 +151,11 @@ export const patchRulesRoute = (router: IRouter, ml: SetupPlugins['ml']) => { type, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, references, note, 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 4df0773f86317a..8828bbe6c98264 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 @@ -91,6 +91,11 @@ export const updateRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => type, threat, threshold, + threat_filters: threatFilters, + threat_index: threatIndex, + threat_query: threatQuery, + threat_mapping: threatMapping, + threat_language: threatLanguage, throttle, timestamp_override: timestampOverride, references, @@ -158,6 +163,11 @@ export const updateRulesBulkRoute = (router: IRouter, ml: SetupPlugins['ml']) => type, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, references, note, 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 ef698db008d804..1fa3bb8c9bc827 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 @@ -81,6 +81,11 @@ export const updateRulesRoute = (router: IRouter, ml: SetupPlugins['ml']) => { type, threat, threshold, + threat_filters: threatFilters, + threat_index: threatIndex, + threat_query: threatQuery, + threat_mapping: threatMapping, + threat_language: threatLanguage, throttle, timestamp_override: timestampOverride, references, @@ -148,6 +153,11 @@ export const updateRulesRoute = (router: IRouter, ml: SetupPlugins['ml']) => { type, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, references, note, 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 2159245f2f7350..13eb7495a898aa 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 @@ -156,7 +156,7 @@ describe('utils', () => { ], }, ]; - threatRule.params.threatIndex = 'index-123'; + threatRule.params.threatIndex = ['index-123']; threatRule.params.threatFilters = threatFilters; threatRule.params.threatMapping = threatMapping; threatRule.params.threatQuery = '*:*'; @@ -164,7 +164,7 @@ describe('utils', () => { const rule = transformAlertToRule(threatRule); expect(rule).toEqual( expect.objectContaining({ - threat_index: 'index-123', + threat_index: ['index-123'], threat_filters: threatFilters, threat_mapping: threatMapping, threat_query: '*:*', 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 c75b32b614e078..fb4ba855f65369 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 @@ -150,6 +150,7 @@ export const transformAlertToRule = ( threat_index: alert.params.threatIndex, threat_query: alert.params.threatQuery, threat_mapping: alert.params.threatMapping, + threat_language: alert.params.threatLanguage, throttle: ruleActions?.ruleThrottle || 'no_actions', timestamp_override: alert.params.timestampOverride, note: alert.params.note, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts index a6034f3d7b7b37..271b1043ea5683 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.mock.ts @@ -42,6 +42,7 @@ export const getCreateRulesOptionsMock = (): CreateRulesOptions => ({ threat: [], threatFilters: undefined, threatMapping: undefined, + threatLanguage: undefined, threatQuery: undefined, threatIndex: undefined, threshold: undefined, @@ -92,6 +93,7 @@ export const getCreateMlRulesOptionsMock = (): CreateRulesOptions => ({ threatIndex: undefined, threatMapping: undefined, threatQuery: undefined, + threatLanguage: undefined, threshold: undefined, timestampOverride: undefined, to: 'now', diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts index 3a311d03e3c89f..776882d0f84941 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/create_rules.ts @@ -45,6 +45,7 @@ export const createRules = async ({ threat, threatFilters, threatIndex, + threatLanguage, threatQuery, threatMapping, threshold, @@ -96,6 +97,7 @@ export const createRules = async ({ threatIndex, threatQuery, threatMapping, + threatLanguage, timestampOverride, to, type, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts index 38adc03c00d502..0a43c652234d09 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/install_prepacked_rules.ts @@ -50,6 +50,7 @@ export const installPrepackagedRules = ( threat, threat_filters: threatFilters, threat_mapping: threatMapping, + threat_language: threatLanguage, threat_query: threatQuery, threat_index: threatIndex, threshold, @@ -101,6 +102,7 @@ export const installPrepackagedRules = ( threat, threatFilters, threatMapping, + threatLanguage, threatQuery, threatIndex, threshold, 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 8672c85f984269..ef7cd35f28f1bd 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 @@ -149,6 +149,11 @@ export const getPatchRulesOptionsMock = (): PatchRulesOptions => ({ tags: [], threat: [], threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, timestampOverride: undefined, to: 'now', type: 'query', @@ -193,6 +198,11 @@ export const getPatchMlRulesOptionsMock = (): PatchRulesOptions => ({ tags: [], threat: [], threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, timestampOverride: undefined, to: 'now', type: 'machine_learning', 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 852ff06bdc736c..1982dcf9dd9b67 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 @@ -44,6 +44,11 @@ export const patchRules = async ({ tags, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, to, type, @@ -87,6 +92,11 @@ export const patchRules = async ({ tags, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, to, type, @@ -126,6 +136,11 @@ export const patchRules = async ({ severityMapping, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, to, type, 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 d688e1b338e21b..8af622e6a128b9 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 @@ -91,6 +91,7 @@ import { ThreatQueryOrUndefined, ThreatMappingOrUndefined, ThreatFiltersOrUndefined, + ThreatLanguageOrUndefined, } from '../../../../common/detection_engine/schemas/types/threat_mapping'; import { AlertsClient, PartialAlert } from '../../../../../alerts/server'; @@ -219,6 +220,7 @@ export interface CreateRulesOptions { threatIndex: ThreatIndexOrUndefined; threatQuery: ThreatQueryOrUndefined; threatMapping: ThreatMappingOrUndefined; + threatLanguage: ThreatLanguageOrUndefined; timestampOverride: TimestampOverrideOrUndefined; to: To; type: Type; @@ -264,6 +266,11 @@ export interface UpdateRulesOptions { tags: Tags; threat: Threat; threshold: ThresholdOrUndefined; + threatFilters: ThreatFiltersOrUndefined; + threatIndex: ThreatIndexOrUndefined; + threatQuery: ThreatQueryOrUndefined; + threatMapping: ThreatMappingOrUndefined; + threatLanguage: ThreatLanguageOrUndefined; timestampOverride: TimestampOverrideOrUndefined; to: To; type: Type; @@ -307,6 +314,11 @@ export interface PatchRulesOptions { tags: TagsOrUndefined; threat: ThreatOrUndefined; threshold: ThresholdOrUndefined; + threatFilters: ThreatFiltersOrUndefined; + threatIndex: ThreatIndexOrUndefined; + threatQuery: ThreatQueryOrUndefined; + threatMapping: ThreatMappingOrUndefined; + threatLanguage: ThreatLanguageOrUndefined; timestampOverride: TimestampOverrideOrUndefined; to: ToOrUndefined; type: TypeOrUndefined; 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 01a481ed7b2d9b..c685c4198c1193 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 @@ -47,6 +47,11 @@ export const updatePrepackagedRules = async ( type, threat, threshold, + threat_filters: threatFilters, + threat_index: threatIndex, + threat_query: threatQuery, + threat_mapping: threatMapping, + threat_language: threatLanguage, timestamp_override: timestampOverride, references, version, @@ -97,6 +102,11 @@ export const updatePrepackagedRules = async ( type, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, references, version, note, 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 8cdc904a861c7a..a33651580ef221 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 @@ -43,6 +43,11 @@ export const getUpdateRulesOptionsMock = (): UpdateRulesOptions => ({ tags: [], threat: [], threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, timestampOverride: undefined, to: 'now', type: 'query', @@ -88,6 +93,11 @@ export const getUpdateMlRulesOptionsMock = (): UpdateRulesOptions => ({ tags: [], threat: [], threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, timestampOverride: undefined, to: 'now', type: 'machine_learning', 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 08df785884b76b..3da921ed47f26e 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 @@ -45,6 +45,11 @@ export const updateRules = async ({ tags, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, to, type, @@ -89,6 +94,11 @@ export const updateRules = async ({ tags, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, to, type, @@ -134,6 +144,11 @@ export const updateRules = async ({ severityMapping, threat, threshold, + threatFilters, + threatIndex, + threatQuery, + threatMapping, + threatLanguage, timestampOverride, to, type, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts index 227f574bc4e4b4..654383ff97c7ad 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rules/utils.test.ts @@ -55,6 +55,11 @@ describe('utils', () => { tags: undefined, threat: undefined, threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, to: undefined, timestampOverride: undefined, type: undefined, @@ -98,6 +103,11 @@ describe('utils', () => { tags: undefined, threat: undefined, threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, to: undefined, timestampOverride: undefined, type: undefined, @@ -141,6 +151,11 @@ describe('utils', () => { tags: undefined, threat: undefined, threshold: undefined, + threatFilters: undefined, + threatIndex: undefined, + threatQuery: undefined, + threatMapping: undefined, + threatLanguage: undefined, to: undefined, timestampOverride: undefined, type: undefined, 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 d9f953f2803a61..a9a100543b528a 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 @@ -42,7 +42,14 @@ import { EventCategoryOverrideOrUndefined, } from '../../../../common/detection_engine/schemas/common/schemas'; import { PartialFilter } from '../types'; -import { ListArrayOrUndefined } from '../../../../common/detection_engine/schemas/types'; +import { + ListArrayOrUndefined, + ThreatFiltersOrUndefined, + ThreatIndexOrUndefined, + ThreatLanguageOrUndefined, + ThreatMappingOrUndefined, + ThreatQueryOrUndefined, +} from '../../../../common/detection_engine/schemas/types'; export const calculateInterval = ( interval: string | undefined, @@ -86,6 +93,11 @@ export interface UpdateProperties { tags: TagsOrUndefined; threat: ThreatOrUndefined; threshold: ThresholdOrUndefined; + threatFilters: ThreatFiltersOrUndefined; + threatIndex: ThreatIndexOrUndefined; + threatQuery: ThreatQueryOrUndefined; + threatMapping: ThreatMappingOrUndefined; + threatLanguage: ThreatLanguageOrUndefined; timestampOverride: TimestampOverrideOrUndefined; to: ToOrUndefined; type: TypeOrUndefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_threat_mapping.json b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_threat_mapping.json index c914e568048a17..1e2f217751e963 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_threat_mapping.json +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/scripts/rules/queries/query_with_threat_mapping.json @@ -7,7 +7,7 @@ "type": "threat_match", "query": "*:*", "tags": ["tag_1", "tag_2"], - "threat_index": "mock-threat-list", + "threat_index": ["mock-threat-list"], "threat_query": "*:*", "threat_mapping": [ { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts index 501cd1fa6ecfb2..cbf70f3119b315 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/__mocks__/es_results.ts @@ -60,6 +60,7 @@ export const sampleRuleAlertParams = ( threatQuery: undefined, threatMapping: undefined, threatIndex: undefined, + threatLanguage: undefined, timelineId: undefined, timelineTitle: undefined, timestampOverride: undefined, 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 344f705c4af24e..6a76c7842e4517 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 @@ -168,6 +168,7 @@ export const buildRuleWithoutOverrides = ( threat_index: ruleParams.threatIndex, threat_query: ruleParams.threatQuery, threat_mapping: ruleParams.threatMapping, + threat_language: ruleParams.threatLanguage, }; return removeInternalTagsFromRule(rule); }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts index 4006345b243856..cfe71f66395b04 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/signal_params_schema.ts @@ -50,9 +50,10 @@ const signalSchema = schema.object({ exceptions_list: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), // For backwards compatibility with customers that had a data bug in 7.8. Once we use a migration script please remove this. exceptionsList: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), threatFilters: schema.nullable(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), - threatIndex: schema.maybe(schema.string()), + threatIndex: schema.maybe(schema.arrayOf(schema.string())), threatQuery: schema.maybe(schema.string()), threatMapping: schema.maybe(schema.arrayOf(schema.object({}, { unknowns: 'allow' }))), + threatLanguage: schema.maybe(schema.string()), }); /** 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 95348808bb58fd..9436dc9cf8a8ee 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 @@ -112,6 +112,7 @@ export const signalRulesAlertType = ({ threatQuery, threatIndex, threatMapping, + threatLanguage, type, exceptionsList, } = params; @@ -389,6 +390,7 @@ export const signalRulesAlertType = ({ throttle, threatFilters: threatFilters ?? [], threatQuery, + threatLanguage, buildRuleMessage, threatIndex, }); 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 560e7ad7fe2cb3..09ddfb342496d0 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 @@ -45,6 +45,7 @@ export const createThreatSignal = async ({ throttle, threatFilters, threatQuery, + threatLanguage, buildRuleMessage, threatIndex, name, @@ -105,8 +106,9 @@ export const createThreatSignal = async ({ callCluster: services.callCluster, exceptionItems, query: threatQuery, + language: threatLanguage, threatFilters, - index: [threatIndex], + index: threatIndex, searchAfter, sortField: undefined, sortOrder: undefined, 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 f44c7a96844578..eeace508c9bfe9 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 @@ -41,6 +41,7 @@ export const createThreatSignals = async ({ throttle, threatFilters, threatQuery, + threatLanguage, buildRuleMessage, threatIndex, name, @@ -59,7 +60,8 @@ export const createThreatSignals = async ({ exceptionItems, threatFilters, query: threatQuery, - index: [threatIndex], + language: threatLanguage, + index: threatIndex, searchAfter: undefined, sortField: undefined, sortOrder: undefined, @@ -99,6 +101,7 @@ export const createThreatSignals = async ({ threatQuery, buildRuleMessage, threatIndex, + threatLanguage, name, currentThreatList: threatList, currentResult: results, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.ts index 8b381ca0d96dcc..3c3f5b544bb177 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/signals/threat_mapping/get_threat_list.ts @@ -21,6 +21,7 @@ export const MAX_PER_PAGE = 9000; export const getThreatList = async ({ callCluster, query, + language, index, perPage, searchAfter, @@ -33,7 +34,13 @@ export const getThreatList = async ({ if (calculatedPerPage > 10000) { throw new TypeError('perPage cannot exceed the size of 10000'); } - const queryFilter = getQueryFilter(query, 'kuery', threatFilters, index, exceptionItems); + const queryFilter = getQueryFilter( + query, + language ?? 'kuery', + threatFilters, + index, + exceptionItems + ); const response: SearchResponse<ThreatListItem> = await callCluster('search', { body: { query: queryFilter, 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 7cd6e5196ea681..06c9c4c13c5f3c 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 @@ -15,6 +15,8 @@ import { ThreatQuery, ThreatMapping, ThreatMappingEntries, + ThreatIndex, + ThreatLanguageOrUndefined, } from '../../../../../common/detection_engine/schemas/types/threat_mapping'; import { PartialFilter, RuleTypeParams } from '../../types'; import { AlertServices } from '../../../../../../alerts/server'; @@ -57,7 +59,8 @@ export interface CreateThreatSignalsOptions { threatFilters: PartialFilter[]; threatQuery: ThreatQuery; buildRuleMessage: BuildRuleMessage; - threatIndex: string; + threatIndex: ThreatIndex; + threatLanguage: ThreatLanguageOrUndefined; name: string; } @@ -93,7 +96,8 @@ export interface CreateThreatSignalOptions { threatFilters: PartialFilter[]; threatQuery: ThreatQuery; buildRuleMessage: BuildRuleMessage; - threatIndex: string; + threatIndex: ThreatIndex; + threatLanguage: ThreatLanguageOrUndefined; name: string; currentThreatList: SearchResponse<ThreatListItem>; currentResult: SearchAfterAndBulkCreateReturnType; @@ -138,6 +142,7 @@ export interface BooleanFilter { export interface GetThreatListOptions { callCluster: ILegacyScopedClusterClient['callAsCurrentUser']; query: string; + language: ThreatLanguageOrUndefined; index: string[]; perPage?: number; searchAfter: string[] | undefined; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts index 728f5b1dd867fd..cf4d989c1f4c84 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/types.ts @@ -43,6 +43,7 @@ import { ThreatIndexOrUndefined, ThreatQueryOrUndefined, ThreatMappingOrUndefined, + ThreatLanguageOrUndefined, } from '../../../common/detection_engine/schemas/types/threat_mapping'; import { LegacyCallAPIOptions } from '../../../../../../src/core/server'; @@ -85,6 +86,7 @@ export interface RuleTypeParams { threatIndex: ThreatIndexOrUndefined; threatQuery: ThreatQueryOrUndefined; threatMapping: ThreatMappingOrUndefined; + threatLanguage: ThreatLanguageOrUndefined; timestampOverride: TimestampOverrideOrUndefined; to: To; type: Type; From 0ee253e9e016b776e45387e3e30c0cec0721d372 Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Thu, 1 Oct 2020 16:43:13 -0700 Subject: [PATCH 062/128] [optimizer] strip proptypes from plugin bundles in production (#79221) Co-authored-by: spalger <spalger@users.noreply.github.com> --- packages/kbn-babel-preset/package.json | 1 + packages/kbn-babel-preset/webpack_preset.js | 13 +++++++++++++ packages/kbn-optimizer/src/worker/webpack.config.ts | 1 + yarn.lock | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index d6d1a78dd4a23a..bc4e0ec338f94e 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -15,6 +15,7 @@ "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-styled-components": "^1.10.7", "babel-plugin-transform-define": "^1.3.1", + "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "react-is": "^16.8.0", "styled-components": "^5.1.0" } diff --git a/packages/kbn-babel-preset/webpack_preset.js b/packages/kbn-babel-preset/webpack_preset.js index a43d607edb17c2..503b99d3c3e8ad 100644 --- a/packages/kbn-babel-preset/webpack_preset.js +++ b/packages/kbn-babel-preset/webpack_preset.js @@ -40,5 +40,18 @@ module.exports = () => { }, ], ], + env: { + production: { + plugins: [ + [ + require.resolve('babel-plugin-transform-react-remove-prop-types'), + { + mode: 'remove', + removeImport: true, + }, + ], + ], + }, + }, }; }; diff --git a/packages/kbn-optimizer/src/worker/webpack.config.ts b/packages/kbn-optimizer/src/worker/webpack.config.ts index 2edf1c999888ee..9678dd5de868b7 100644 --- a/packages/kbn-optimizer/src/worker/webpack.config.ts +++ b/packages/kbn-optimizer/src/worker/webpack.config.ts @@ -200,6 +200,7 @@ export function getWebpackConfig(bundle: Bundle, bundleRefs: BundleRefs, worker: loader: 'babel-loader', options: { babelrc: false, + envName: worker.dist ? 'production' : 'development', presets: IS_CODE_COVERAGE ? [ISTANBUL_PRESET_PATH, BABEL_PRESET_PATH] : [BABEL_PRESET_PATH], diff --git a/yarn.lock b/yarn.lock index 77e3a399c43137..d795a174cfaa0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7612,6 +7612,11 @@ babel-plugin-transform-property-literals@^6.9.4: dependencies: esutils "^2.0.2" +babel-plugin-transform-react-remove-prop-types@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" + integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA== + babel-plugin-transform-regexp-constructors@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz#58b7775b63afcf33328fae9a5f88fbd4fb0b4965" From e5d8d49164a8d993bdd314dd6c800f9186092148 Mon Sep 17 00:00:00 2001 From: Tiago Costa <tiagoffcc@hotmail.com> Date: Fri, 2 Oct 2020 00:46:00 +0100 Subject: [PATCH 063/128] chore(NA): assures a single version for the same dependency across the entire project (#78825) * chore(NA): script to check for multiple version of same dependency * chore(NA): remove multiple versions for the same dependency * chore(NA): hook single_version_dependencies script into the CI * chore(NA): remove grunt from the CI hook integration * chore(NA): update kbn pm dist * chore(NA): fix typechecking * chore(NA): update code to run under last extract-zip version * fix(NA): multiple versions of the same type dependency * move validation to bootstrap (#13) Co-authored-by: spalger <spalger@users.noreply.github.com> * chore(NA): todo to remove logic to validate single version deps once we move into a single package.json * chore(NA): remove verify dependency versions jenkins task * chore(NA): update kbn pm dist file * chore(NA): remove last mention to verify_dependency_versions.sh fom tasks.groovy Co-authored-by: Spencer <email@spalger.com> Co-authored-by: spalger <spalger@users.noreply.github.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- package.json | 10 +- packages/kbn-apm-config-loader/package.json | 2 +- packages/kbn-es/package.json | 2 +- packages/kbn-plugin-helpers/package.json | 2 +- packages/kbn-pm/dist/index.js | 4764 +++++++++-------- packages/kbn-pm/package.json | 6 +- .../kbn-pm/src/utils/validate_yarn_lock.ts | 62 + packages/kbn-release-notes/package.json | 2 +- packages/kbn-storybook/package.json | 4 +- packages/kbn-storybook/webpack.config.ts | 4 - packages/kbn-test/package.json | 2 +- packages/kbn-ui-framework/package.json | 6 +- tasks/config/run.js | 4 - tasks/jenkins.js | 1 - tasks/verify_dependency_versions.js | 53 - .../checks/verify_dependency_versions.sh | 5 - vars/tasks.groovy | 1 - x-pack/package.json | 12 +- x-pack/plugins/apm/e2e/package.json | 2 +- x-pack/plugins/apm/scripts/package.json | 2 +- .../server/browsers/extract/unzip.js | 16 +- .../public/common/mock/timeline_results.ts | 2 +- .../scripts/beat_docs/build.js | 38 +- yarn.lock | 363 +- 24 files changed, 2667 insertions(+), 2698 deletions(-) delete mode 100644 tasks/verify_dependency_versions.js delete mode 100755 test/scripts/checks/verify_dependency_versions.sh diff --git a/package.json b/package.json index 0eda8dd9f41143..ff98d7f85dceff 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "angular-sanitize": "^1.8.0", "bluebird": "3.5.5", "boom": "^7.2.0", - "chalk": "^2.4.2", + "chalk": "^4.1.0", "check-disk-space": "^2.1.0", "chokidar": "^3.4.2", "color": "1.0.3", @@ -331,7 +331,7 @@ "@types/selenium-webdriver": "^4.0.9", "@types/semver": "^5.5.0", "@types/sinon": "^7.0.13", - "@types/strip-ansi": "^3.0.0", + "@types/strip-ansi": "^5.2.1", "@types/styled-components": "^5.1.0", "@types/supertest": "^2.0.5", "@types/supertest-as-promised": "^2.0.38", @@ -343,7 +343,7 @@ "@types/uuid": "^3.4.4", "@types/vinyl": "^2.0.4", "@types/vinyl-fs": "^2.4.11", - "@types/webpack": "^4.41.21", + "@types/webpack": "^4.41.3", "@types/webpack-env": "^1.15.2", "@types/zen-observable": "^0.8.0", "@typescript-eslint/eslint-plugin": "^3.10.0", @@ -470,10 +470,10 @@ "selenium-webdriver": "^4.0.0-alpha.7", "simple-git": "1.116.0", "sinon": "^7.4.2", - "strip-ansi": "^3.0.1", + "strip-ansi": "^6.0.0", "supertest": "^3.1.0", "supertest-as-promised": "^4.0.2", - "tape": "^4.13.0", + "tape": "^5.0.1", "topojson-client": "3.0.0", "tree-kill": "^1.2.2", "typescript": "4.0.2", diff --git a/packages/kbn-apm-config-loader/package.json b/packages/kbn-apm-config-loader/package.json index 1982ccdeda0ff9..c570fdc0218b98 100644 --- a/packages/kbn-apm-config-loader/package.json +++ b/packages/kbn-apm-config-loader/package.json @@ -18,6 +18,6 @@ }, "devDependencies": { "typescript": "4.0.2", - "tsd": "^0.7.4" + "tsd": "^0.13.1" } } diff --git a/packages/kbn-es/package.json b/packages/kbn-es/package.json index 40d34c5d710bb2..c3733094350bec 100644 --- a/packages/kbn-es/package.json +++ b/packages/kbn-es/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --watch" }, "dependencies": { - "@elastic/elasticsearch": "7.9.0-rc.1", + "@elastic/elasticsearch": "7.9.1", "@kbn/dev-utils": "1.0.0", "abort-controller": "^3.0.0", "chalk": "^4.1.0", diff --git a/packages/kbn-plugin-helpers/package.json b/packages/kbn-plugin-helpers/package.json index 65b44b6965048d..a2c4e1e2134e77 100644 --- a/packages/kbn-plugin-helpers/package.json +++ b/packages/kbn-plugin-helpers/package.json @@ -18,7 +18,7 @@ "del": "^5.1.0", "execa": "^4.0.2", "gulp-zip": "^5.0.2", - "inquirer": "^1.2.2", + "inquirer": "^7.3.3", "load-json-file": "^6.2.0", "vinyl-fs": "^3.0.3" }, diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 7dcfc1d778ea8b..2882a72b3ac9d8 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,7 +94,7 @@ __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__(496); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(500); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); @@ -103,10 +103,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(163); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; }); -/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(275); +/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(270); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__["copyWorkspacePackages"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(276); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(271); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -150,7 +150,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__(127); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(489); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(493); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(143); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -8763,9 +8763,9 @@ 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__(128); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(284); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(388); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(389); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(279); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(392); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(393); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8806,10 +8806,10 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(277); -/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(282); -/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(279); -/* harmony import */ var _utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(283); +/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(272); +/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(277); +/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(274); +/* harmony import */ var _utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(278); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -10831,7 +10831,7 @@ __webpack_require__.r(__webpack_exports__); /* 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__(162); /* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(163); -/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(275); +/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(270); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -23648,11 +23648,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "spawnStreaming", function() { return spawnStreaming; }); /* harmony import */ var stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(137); /* harmony import */ var stream__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(stream__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(227); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(112); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(232); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(227); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(267); +/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(262); /* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__); /* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(143); 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; } @@ -23743,602 +23743,18 @@ function spawnStreaming(command, args, opts, { "use strict"; -const ansiStyles = __webpack_require__(113); -const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(228); -const { - stringReplaceAll, - stringEncaseCRLFWithFirstIndex -} = __webpack_require__(230); - -const {isArray} = Array; - -// `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = [ - 'ansi', - 'ansi', - 'ansi256', - 'ansi16m' -]; - -const styles = Object.create(null); - -const applyOptions = (object, options = {}) => { - if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { - 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; -}; - -class ChalkClass { - constructor(options) { - // eslint-disable-next-line no-constructor-return - 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.'); - }; - - chalk.template.Instance = ChalkClass; - - return chalk.template; -}; - -function Chalk(options) { - return chalkFactory(options); -} - -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; - } - }; -} - -styles.visible = { - get() { - const builder = createBuilder(this, this._styler, true); - Object.defineProperty(this, 'visible', {value: builder}); - return builder; - } -}; - -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); - }; - } - }; -} - -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); - }; - } - }; -} - -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_) => { - if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) { - // Called as a template literal, for example: chalk.red`2 + 3 = {bold ${2+3}}` - return applyStyle(builder, chalkTag(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(' ')); - }; - - // We alter the prototype because we must return a function, but there is - // no way to create a function with a different prototype - Object.setPrototypeOf(builder, 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; - } - - 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; - } - } - - // 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); - } - - return openAll + string + closeAll; -}; - -let template; -const chalkTag = (chalk, ...strings) => { - const [firstString] = strings; - - if (!isArray(firstString) || !isArray(firstString.raw)) { - // If chalk() was called by itself or with a string, - // return the string itself as a string. - return strings.join(' '); - } - - 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__(231); - } - - return template(chalk, parts.join('')); -}; - -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; - -module.exports = chalk; - - -/***/ }), -/* 228 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -const os = __webpack_require__(120); -const tty = __webpack_require__(121); -const hasFlag = __webpack_require__(229); - -const {env} = process; - -let forceColor; -if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false') || - hasFlag('color=never')) { - forceColor = 0; -} else if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - forceColor = 1; -} - -if ('FORCE_COLOR' in env) { - if (env.FORCE_COLOR === 'true') { - forceColor = 1; - } else if (env.FORCE_COLOR === 'false') { - forceColor = 0; - } else { - forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); - } -} - -function translateLevel(level) { - if (level === 0) { - return false; - } - - return { - level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; -} - -function supportsColor(haveStream, streamIsTTY) { - if (forceColor === 0) { - return 0; - } - - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; - } - - if (hasFlag('color=256')) { - return 2; - } - - if (haveStream && !streamIsTTY && forceColor === undefined) { - return 0; - } - - const min = forceColor || 0; - - if (env.TERM === 'dumb') { - return min; - } - - if (process.platform === 'win32') { - // 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(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 ('GITHUB_ACTIONS' in env) { - return 1; - } - - 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; - } - - return min; -} - -function getSupportLevel(stream) { - const level = supportsColor(stream, stream && stream.isTTY); - return translateLevel(level); -} - -module.exports = { - supportsColor: getSupportLevel, - stdout: translateLevel(supportsColor(true, tty.isatty(1))), - stderr: translateLevel(supportsColor(true, tty.isatty(2))) -}; - - -/***/ }), -/* 229 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -module.exports = (flag, argv = process.argv) => { - const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const position = argv.indexOf(prefix + flag); - const terminatorPosition = argv.indexOf('--'); - return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); -}; - - -/***/ }), -/* 230 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -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 -}; - - -/***/ }), -/* 231 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -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] === '{'; - - 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; -} - -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}')`); - } - } - - 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, 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; -} - -module.exports = (chalk, temporary) => { - const styles = []; - const chunks = []; - let chunk = []; - - // 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'); - } - - chunks.push(buildStyle(chalk, styles)(chunk.join(''))); - chunk = []; - styles.pop(); - } else { - chunk.push(character); - } - }); - - chunks.push(chunk.join('')); - - if (styles.length > 0) { - const errMessage = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; - throw new Error(errMessage); - } - - return chunks.join(''); -}; - - -/***/ }), -/* 232 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - const path = __webpack_require__(4); -const childProcess = __webpack_require__(233); -const crossSpawn = __webpack_require__(234); -const stripFinalNewline = __webpack_require__(247); -const npmRunPath = __webpack_require__(248); -const onetime = __webpack_require__(249); -const makeError = __webpack_require__(251); -const normalizeStdio = __webpack_require__(256); -const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(257); -const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(258); -const {mergePromise, getSpawnedPromise} = __webpack_require__(265); -const {joinCommand, parseCommand} = __webpack_require__(266); +const childProcess = __webpack_require__(228); +const crossSpawn = __webpack_require__(229); +const stripFinalNewline = __webpack_require__(242); +const npmRunPath = __webpack_require__(243); +const onetime = __webpack_require__(244); +const makeError = __webpack_require__(246); +const normalizeStdio = __webpack_require__(251); +const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(252); +const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(253); +const {mergePromise, getSpawnedPromise} = __webpack_require__(260); +const {joinCommand, parseCommand} = __webpack_require__(261); const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; @@ -24585,21 +24001,21 @@ module.exports.node = (scriptPath, args, options = {}) => { /***/ }), -/* 233 */ +/* 228 */ /***/ (function(module, exports) { module.exports = require("child_process"); /***/ }), -/* 234 */ +/* 229 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const cp = __webpack_require__(233); -const parse = __webpack_require__(235); -const enoent = __webpack_require__(246); +const cp = __webpack_require__(228); +const parse = __webpack_require__(230); +const enoent = __webpack_require__(241); function spawn(command, args, options) { // Parse the arguments @@ -24637,16 +24053,16 @@ module.exports._enoent = enoent; /***/ }), -/* 235 */ +/* 230 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const resolveCommand = __webpack_require__(236); -const escape = __webpack_require__(242); -const readShebang = __webpack_require__(243); +const resolveCommand = __webpack_require__(231); +const escape = __webpack_require__(237); +const readShebang = __webpack_require__(238); const isWin = process.platform === 'win32'; const isExecutableRegExp = /\.(?:com|exe)$/i; @@ -24735,15 +24151,15 @@ module.exports = parse; /***/ }), -/* 236 */ +/* 231 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const which = __webpack_require__(237); -const pathKey = __webpack_require__(241)(); +const which = __webpack_require__(232); +const pathKey = __webpack_require__(236)(); function resolveCommandAttempt(parsed, withoutPathExt) { const cwd = process.cwd(); @@ -24793,7 +24209,7 @@ module.exports = resolveCommand; /***/ }), -/* 237 */ +/* 232 */ /***/ (function(module, exports, __webpack_require__) { const isWindows = process.platform === 'win32' || @@ -24802,7 +24218,7 @@ const isWindows = process.platform === 'win32' || const path = __webpack_require__(4) const COLON = isWindows ? ';' : ':' -const isexe = __webpack_require__(238) +const isexe = __webpack_require__(233) const getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' }) @@ -24924,15 +24340,15 @@ which.sync = whichSync /***/ }), -/* 238 */ +/* 233 */ /***/ (function(module, exports, __webpack_require__) { var fs = __webpack_require__(133) var core if (process.platform === 'win32' || global.TESTING_WINDOWS) { - core = __webpack_require__(239) + core = __webpack_require__(234) } else { - core = __webpack_require__(240) + core = __webpack_require__(235) } module.exports = isexe @@ -24987,7 +24403,7 @@ function sync (path, options) { /***/ }), -/* 239 */ +/* 234 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe @@ -25035,7 +24451,7 @@ function sync (path, options) { /***/ }), -/* 240 */ +/* 235 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe @@ -25082,7 +24498,7 @@ function checkMode (stat, options) { /***/ }), -/* 241 */ +/* 236 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25105,7 +24521,7 @@ module.exports.default = pathKey; /***/ }), -/* 242 */ +/* 237 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25157,14 +24573,14 @@ module.exports.argument = escapeArgument; /***/ }), -/* 243 */ +/* 238 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const shebangCommand = __webpack_require__(244); +const shebangCommand = __webpack_require__(239); function readShebang(command) { // Read the first 150 bytes from the file @@ -25187,12 +24603,12 @@ module.exports = readShebang; /***/ }), -/* 244 */ +/* 239 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const shebangRegex = __webpack_require__(245); +const shebangRegex = __webpack_require__(240); module.exports = (string = '') => { const match = string.match(shebangRegex); @@ -25213,7 +24629,7 @@ module.exports = (string = '') => { /***/ }), -/* 245 */ +/* 240 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25222,7 +24638,7 @@ module.exports = /^#!(.*)/; /***/ }), -/* 246 */ +/* 241 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25288,7 +24704,7 @@ module.exports = { /***/ }), -/* 247 */ +/* 242 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25311,13 +24727,13 @@ module.exports = input => { /***/ }), -/* 248 */ +/* 243 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathKey = __webpack_require__(241); +const pathKey = __webpack_require__(236); const npmRunPath = options => { options = { @@ -25365,12 +24781,12 @@ module.exports.env = options => { /***/ }), -/* 249 */ +/* 244 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(250); +const mimicFn = __webpack_require__(245); const calledFunctions = new WeakMap(); @@ -25422,7 +24838,7 @@ module.exports.callCount = fn => { /***/ }), -/* 250 */ +/* 245 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25442,12 +24858,12 @@ module.exports.default = mimicFn; /***/ }), -/* 251 */ +/* 246 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {signalsByName} = __webpack_require__(252); +const {signalsByName} = __webpack_require__(247); const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { if (timedOut) { @@ -25535,14 +24951,14 @@ module.exports = makeError; /***/ }), -/* 252 */ +/* 247 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports,"__esModule",{value:true});exports.signalsByNumber=exports.signalsByName=void 0;var _os=__webpack_require__(120); -var _signals=__webpack_require__(253); -var _realtime=__webpack_require__(255); +var _signals=__webpack_require__(248); +var _realtime=__webpack_require__(250); @@ -25612,14 +25028,14 @@ const signalsByNumber=getSignalsByNumber();exports.signalsByNumber=signalsByNumb //# sourceMappingURL=main.js.map /***/ }), -/* 253 */ +/* 248 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports,"__esModule",{value:true});exports.getSignals=void 0;var _os=__webpack_require__(120); -var _core=__webpack_require__(254); -var _realtime=__webpack_require__(255); +var _core=__webpack_require__(249); +var _realtime=__webpack_require__(250); @@ -25653,7 +25069,7 @@ return{name,number,description,supported,action,forced,standard}; //# sourceMappingURL=signals.js.map /***/ }), -/* 254 */ +/* 249 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25932,7 +25348,7 @@ standard:"other"}];exports.SIGNALS=SIGNALS; //# sourceMappingURL=core.js.map /***/ }), -/* 255 */ +/* 250 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25957,7 +25373,7 @@ const SIGRTMAX=64;exports.SIGRTMAX=SIGRTMAX; //# sourceMappingURL=realtime.js.map /***/ }), -/* 256 */ +/* 251 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26016,7 +25432,7 @@ module.exports.node = opts => { /***/ }), -/* 257 */ +/* 252 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26135,14 +25551,14 @@ module.exports = { /***/ }), -/* 258 */ +/* 253 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isStream = __webpack_require__(259); -const getStream = __webpack_require__(260); -const mergeStream = __webpack_require__(264); +const isStream = __webpack_require__(254); +const getStream = __webpack_require__(255); +const mergeStream = __webpack_require__(259); // `input` option const handleInput = (spawned, input) => { @@ -26239,7 +25655,7 @@ module.exports = { /***/ }), -/* 259 */ +/* 254 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26275,13 +25691,13 @@ module.exports = isStream; /***/ }), -/* 260 */ +/* 255 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pump = __webpack_require__(261); -const bufferStream = __webpack_require__(263); +const pump = __webpack_require__(256); +const bufferStream = __webpack_require__(258); class MaxBufferError extends Error { constructor() { @@ -26340,11 +25756,11 @@ module.exports.MaxBufferError = MaxBufferError; /***/ }), -/* 261 */ +/* 256 */ /***/ (function(module, exports, __webpack_require__) { var once = __webpack_require__(161) -var eos = __webpack_require__(262) +var eos = __webpack_require__(257) var fs = __webpack_require__(133) // we only need fs to get the ReadStream and WriteStream prototypes var noop = function () {} @@ -26428,7 +25844,7 @@ module.exports = pump /***/ }), -/* 262 */ +/* 257 */ /***/ (function(module, exports, __webpack_require__) { var once = __webpack_require__(161); @@ -26528,7 +25944,7 @@ module.exports = eos; /***/ }), -/* 263 */ +/* 258 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26587,7 +26003,7 @@ module.exports = options => { /***/ }), -/* 264 */ +/* 259 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26635,7 +26051,7 @@ module.exports = function (/*streams...*/) { /***/ }), -/* 265 */ +/* 260 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26688,7 +26104,7 @@ module.exports = { /***/ }), -/* 266 */ +/* 261 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26733,7 +26149,7 @@ module.exports = { /***/ }), -/* 267 */ +/* 262 */ /***/ (function(module, exports, __webpack_require__) { // Copyright IBM Corp. 2014,2018. All Rights Reserved. @@ -26741,12 +26157,12 @@ module.exports = { // This file is licensed under the Apache License 2.0. // License text available at https://opensource.org/licenses/Apache-2.0 -module.exports = __webpack_require__(268); -module.exports.cli = __webpack_require__(272); +module.exports = __webpack_require__(263); +module.exports.cli = __webpack_require__(267); /***/ }), -/* 268 */ +/* 263 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26761,9 +26177,9 @@ var stream = __webpack_require__(137); var util = __webpack_require__(111); var fs = __webpack_require__(133); -var through = __webpack_require__(269); -var duplexer = __webpack_require__(270); -var StringDecoder = __webpack_require__(271).StringDecoder; +var through = __webpack_require__(264); +var duplexer = __webpack_require__(265); +var StringDecoder = __webpack_require__(266).StringDecoder; module.exports = Logger; @@ -26952,7 +26368,7 @@ function lineMerger(host) { /***/ }), -/* 269 */ +/* 264 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(137) @@ -27066,7 +26482,7 @@ function through (write, end, opts) { /***/ }), -/* 270 */ +/* 265 */ /***/ (function(module, exports, __webpack_require__) { var Stream = __webpack_require__(137) @@ -27159,13 +26575,13 @@ function duplex(writer, reader) { /***/ }), -/* 271 */ +/* 266 */ /***/ (function(module, exports) { module.exports = require("string_decoder"); /***/ }), -/* 272 */ +/* 267 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -27176,11 +26592,11 @@ module.exports = require("string_decoder"); -var minimist = __webpack_require__(273); +var minimist = __webpack_require__(268); var path = __webpack_require__(4); -var Logger = __webpack_require__(268); -var pkg = __webpack_require__(274); +var Logger = __webpack_require__(263); +var pkg = __webpack_require__(269); module.exports = cli; @@ -27234,7 +26650,7 @@ function usage($0, p) { /***/ }), -/* 273 */ +/* 268 */ /***/ (function(module, exports) { module.exports = function (args, opts) { @@ -27485,13 +26901,13 @@ function isNumber (x) { /***/ }), -/* 274 */ +/* 269 */ /***/ (function(module) { module.exports = JSON.parse("{\"name\":\"strong-log-transformer\",\"version\":\"2.1.0\",\"description\":\"Stream transformer that prefixes lines with timestamps and other things.\",\"author\":\"Ryan Graham <ryan@strongloop.com>\",\"license\":\"Apache-2.0\",\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/strongloop/strong-log-transformer\"},\"keywords\":[\"logging\",\"streams\"],\"bugs\":{\"url\":\"https://github.com/strongloop/strong-log-transformer/issues\"},\"homepage\":\"https://github.com/strongloop/strong-log-transformer\",\"directories\":{\"test\":\"test\"},\"bin\":{\"sl-log-transformer\":\"bin/sl-log-transformer.js\"},\"main\":\"index.js\",\"scripts\":{\"test\":\"tap --100 test/test-*\"},\"dependencies\":{\"duplexer\":\"^0.1.1\",\"minimist\":\"^1.2.0\",\"through\":\"^2.3.4\"},\"devDependencies\":{\"tap\":\"^12.0.1\"},\"engines\":{\"node\":\">=4\"}}"); /***/ }), -/* 275 */ +/* 270 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -27504,7 +26920,7 @@ __webpack_require__.r(__webpack_exports__); /* 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__(111); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(276); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(271); /* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(130); /* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(164); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(145); @@ -27599,7 +27015,7 @@ function packagesFromGlobPattern({ } /***/ }), -/* 276 */ +/* 271 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -27669,7 +27085,7 @@ function getProjectPaths({ } /***/ }), -/* 277 */ +/* 272 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -27677,13 +27093,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAllChecksums", function() { return getAllChecksums; }); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(278); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(273); /* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(232); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(227); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(279); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(274); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -27882,20 +27298,20 @@ async function getAllChecksums(kbn, log, yarnLock) { } /***/ }), -/* 278 */ +/* 273 */ /***/ (function(module, exports) { module.exports = require("crypto"); /***/ }), -/* 279 */ +/* 274 */ /***/ (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__(280); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(275); /* 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__(130); /* @@ -28008,7 +27424,7 @@ function resolveDepsForProject({ } /***/ }), -/* 280 */ +/* 275 */ /***/ (function(module, exports, __webpack_require__) { module.exports = @@ -29567,7 +28983,7 @@ module.exports = invariant; /* 9 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(278); +module.exports = __webpack_require__(273); /***/ }), /* 10 */, @@ -31891,7 +31307,7 @@ function onceStrict (fn) { /* 63 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(281); +module.exports = __webpack_require__(276); /***/ }), /* 64 */, @@ -38286,13 +37702,13 @@ module.exports = process && support(supportLevel); /******/ ]); /***/ }), -/* 281 */ +/* 276 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 282 */ +/* 277 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -38389,13 +37805,13 @@ class BootstrapCacheFile { } /***/ }), -/* 283 */ +/* 278 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateYarnLock", function() { return validateYarnLock; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(280); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(275); /* 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__); @@ -38490,21 +37906,73 @@ async function validateYarnLock(kbn, yarnLock) { `); 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()) { + 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); + } + } + + const duplicateRanges = Array.from(depRanges.entries()).filter(([, ranges]) => ranges.length > 1).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_3__["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); } _log__WEBPACK_IMPORTED_MODULE_3__["log"].success('yarn.lock analysis completed without any issues'); } /***/ }), -/* 284 */ +/* 279 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(285); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(280); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(372); +/* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(367); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(ora__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__); @@ -38604,21 +38072,21 @@ const CleanCommand = { }; /***/ }), -/* 285 */ +/* 280 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(111); const path = __webpack_require__(4); -const globby = __webpack_require__(286); -const isGlob = __webpack_require__(364); -const slash = __webpack_require__(362); +const globby = __webpack_require__(281); +const isGlob = __webpack_require__(359); +const slash = __webpack_require__(357); const gracefulFs = __webpack_require__(132); -const isPathCwd = __webpack_require__(365); -const isPathInside = __webpack_require__(366); -const rimraf = __webpack_require__(367); -const pMap = __webpack_require__(368); +const isPathCwd = __webpack_require__(360); +const isPathInside = __webpack_require__(361); +const rimraf = __webpack_require__(362); +const pMap = __webpack_require__(363); const rimrafP = promisify(rimraf); @@ -38732,19 +38200,19 @@ module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options /***/ }), -/* 286 */ +/* 281 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const arrayUnion = __webpack_require__(287); -const merge2 = __webpack_require__(288); +const arrayUnion = __webpack_require__(282); +const merge2 = __webpack_require__(283); const glob = __webpack_require__(146); -const fastGlob = __webpack_require__(289); -const dirGlob = __webpack_require__(358); -const gitignore = __webpack_require__(360); -const {FilterStream, UniqueStream} = __webpack_require__(363); +const fastGlob = __webpack_require__(284); +const dirGlob = __webpack_require__(353); +const gitignore = __webpack_require__(355); +const {FilterStream, UniqueStream} = __webpack_require__(358); const DEFAULT_FILTER = () => false; @@ -38917,7 +38385,7 @@ module.exports.gitignore = gitignore; /***/ }), -/* 287 */ +/* 282 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38929,7 +38397,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 288 */ +/* 283 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39080,17 +38548,17 @@ function pauseStreams (streams, options) { /***/ }), -/* 289 */ +/* 284 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(290); -const async_1 = __webpack_require__(319); -const stream_1 = __webpack_require__(354); -const sync_1 = __webpack_require__(355); -const settings_1 = __webpack_require__(357); -const utils = __webpack_require__(291); +const taskManager = __webpack_require__(285); +const async_1 = __webpack_require__(314); +const stream_1 = __webpack_require__(349); +const sync_1 = __webpack_require__(350); +const settings_1 = __webpack_require__(352); +const utils = __webpack_require__(286); async function FastGlob(source, options) { assertPatternsInput(source); const works = getWorks(source, async_1.default, options); @@ -39154,13 +38622,13 @@ module.exports = FastGlob; /***/ }), -/* 290 */ +/* 285 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(286); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -39225,30 +38693,30 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 291 */ +/* 286 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const array = __webpack_require__(292); +const array = __webpack_require__(287); exports.array = array; -const errno = __webpack_require__(293); +const errno = __webpack_require__(288); exports.errno = errno; -const fs = __webpack_require__(294); +const fs = __webpack_require__(289); exports.fs = fs; -const path = __webpack_require__(295); +const path = __webpack_require__(290); exports.path = path; -const pattern = __webpack_require__(296); +const pattern = __webpack_require__(291); exports.pattern = pattern; -const stream = __webpack_require__(317); +const stream = __webpack_require__(312); exports.stream = stream; -const string = __webpack_require__(318); +const string = __webpack_require__(313); exports.string = string; /***/ }), -/* 292 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39276,7 +38744,7 @@ exports.splitWhen = splitWhen; /***/ }), -/* 293 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39289,7 +38757,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 294 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39314,7 +38782,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 295 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39353,16 +38821,16 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 296 */ +/* 291 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const globParent = __webpack_require__(297); -const micromatch = __webpack_require__(300); -const picomatch = __webpack_require__(311); +const globParent = __webpack_require__(292); +const micromatch = __webpack_require__(295); +const picomatch = __webpack_require__(306); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -39472,13 +38940,13 @@ exports.matchAny = matchAny; /***/ }), -/* 297 */ +/* 292 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isGlob = __webpack_require__(298); +var isGlob = __webpack_require__(293); var pathPosixDirname = __webpack_require__(4).posix.dirname; var isWin32 = __webpack_require__(120).platform() === 'win32'; @@ -39520,7 +38988,7 @@ module.exports = function globParent(str, opts) { /***/ }), -/* 298 */ +/* 293 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -39530,7 +38998,7 @@ module.exports = function globParent(str, opts) { * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(294); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -39574,7 +39042,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 299 */ +/* 294 */ /***/ (function(module, exports) { /*! @@ -39600,16 +39068,16 @@ module.exports = function isExtglob(str) { /***/ }), -/* 300 */ +/* 295 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const util = __webpack_require__(111); -const braces = __webpack_require__(301); -const picomatch = __webpack_require__(311); -const utils = __webpack_require__(314); +const braces = __webpack_require__(296); +const picomatch = __webpack_require__(306); +const utils = __webpack_require__(309); const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); /** @@ -40074,16 +39542,16 @@ module.exports = micromatch; /***/ }), -/* 301 */ +/* 296 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(302); -const compile = __webpack_require__(304); -const expand = __webpack_require__(308); -const parse = __webpack_require__(309); +const stringify = __webpack_require__(297); +const compile = __webpack_require__(299); +const expand = __webpack_require__(303); +const parse = __webpack_require__(304); /** * Expand the given pattern or create a regex-compatible string. @@ -40251,13 +39719,13 @@ module.exports = braces; /***/ }), -/* 302 */ +/* 297 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(303); +const utils = __webpack_require__(298); module.exports = (ast, options = {}) => { let stringify = (node, parent = {}) => { @@ -40290,7 +39758,7 @@ module.exports = (ast, options = {}) => { /***/ }), -/* 303 */ +/* 298 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40409,14 +39877,14 @@ exports.flatten = (...args) => { /***/ }), -/* 304 */ +/* 299 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(305); -const utils = __webpack_require__(303); +const fill = __webpack_require__(300); +const utils = __webpack_require__(298); const compile = (ast, options = {}) => { let walk = (node, parent = {}) => { @@ -40473,7 +39941,7 @@ module.exports = compile; /***/ }), -/* 305 */ +/* 300 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40487,7 +39955,7 @@ module.exports = compile; const util = __webpack_require__(111); -const toRegexRange = __webpack_require__(306); +const toRegexRange = __webpack_require__(301); const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); @@ -40729,7 +40197,7 @@ module.exports = fill; /***/ }), -/* 306 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40742,7 +40210,7 @@ module.exports = fill; -const isNumber = __webpack_require__(307); +const isNumber = __webpack_require__(302); const toRegexRange = (min, max, options) => { if (isNumber(min) === false) { @@ -41024,7 +40492,7 @@ module.exports = toRegexRange; /***/ }), -/* 307 */ +/* 302 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41049,15 +40517,15 @@ module.exports = function(num) { /***/ }), -/* 308 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(305); -const stringify = __webpack_require__(302); -const utils = __webpack_require__(303); +const fill = __webpack_require__(300); +const stringify = __webpack_require__(297); +const utils = __webpack_require__(298); const append = (queue = '', stash = '', enclose = false) => { let result = []; @@ -41169,13 +40637,13 @@ module.exports = expand; /***/ }), -/* 309 */ +/* 304 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(302); +const stringify = __webpack_require__(297); /** * Constants @@ -41197,7 +40665,7 @@ const { CHAR_SINGLE_QUOTE, /* ' */ CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(310); +} = __webpack_require__(305); /** * parse @@ -41509,7 +40977,7 @@ module.exports = parse; /***/ }), -/* 310 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41573,27 +41041,27 @@ module.exports = { /***/ }), -/* 311 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(312); +module.exports = __webpack_require__(307); /***/ }), -/* 312 */ +/* 307 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const scan = __webpack_require__(313); -const parse = __webpack_require__(316); -const utils = __webpack_require__(314); -const constants = __webpack_require__(315); +const scan = __webpack_require__(308); +const parse = __webpack_require__(311); +const utils = __webpack_require__(309); +const constants = __webpack_require__(310); const isObject = val => val && typeof val === 'object' && !Array.isArray(val); /** @@ -41929,13 +41397,13 @@ module.exports = picomatch; /***/ }), -/* 313 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(314); +const utils = __webpack_require__(309); const { CHAR_ASTERISK, /* * */ CHAR_AT, /* @ */ @@ -41952,7 +41420,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(315); +} = __webpack_require__(310); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -42319,7 +41787,7 @@ module.exports = scan; /***/ }), -/* 314 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42332,7 +41800,7 @@ const { REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(315); +} = __webpack_require__(310); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -42390,7 +41858,7 @@ exports.wrapOutput = (input, state = {}, options = {}) => { /***/ }), -/* 315 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42576,14 +42044,14 @@ module.exports = { /***/ }), -/* 316 */ +/* 311 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const constants = __webpack_require__(315); -const utils = __webpack_require__(314); +const constants = __webpack_require__(310); +const utils = __webpack_require__(309); /** * Constants @@ -43661,13 +43129,13 @@ module.exports = parse; /***/ }), -/* 317 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const merge2 = __webpack_require__(288); +const merge2 = __webpack_require__(283); function merge(streams) { const mergedStream = merge2(streams); streams.forEach((stream) => { @@ -43684,7 +43152,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 318 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43701,14 +43169,14 @@ exports.isEmpty = isEmpty; /***/ }), -/* 319 */ +/* 314 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(320); -const provider_1 = __webpack_require__(347); +const stream_1 = __webpack_require__(315); +const provider_1 = __webpack_require__(342); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -43736,16 +43204,16 @@ exports.default = ProviderAsync; /***/ }), -/* 320 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(137); -const fsStat = __webpack_require__(321); -const fsWalk = __webpack_require__(326); -const reader_1 = __webpack_require__(346); +const fsStat = __webpack_require__(316); +const fsWalk = __webpack_require__(321); +const reader_1 = __webpack_require__(341); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -43798,15 +43266,15 @@ exports.default = ReaderStream; /***/ }), -/* 321 */ +/* 316 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(322); -const sync = __webpack_require__(323); -const settings_1 = __webpack_require__(324); +const async = __webpack_require__(317); +const sync = __webpack_require__(318); +const settings_1 = __webpack_require__(319); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43829,7 +43297,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 322 */ +/* 317 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43867,7 +43335,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 323 */ +/* 318 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43896,13 +43364,13 @@ exports.read = read; /***/ }), -/* 324 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(325); +const fs = __webpack_require__(320); class Settings { constructor(_options = {}) { this._options = _options; @@ -43919,7 +43387,7 @@ exports.default = Settings; /***/ }), -/* 325 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43942,16 +43410,16 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 326 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(327); -const stream_1 = __webpack_require__(342); -const sync_1 = __webpack_require__(343); -const settings_1 = __webpack_require__(345); +const async_1 = __webpack_require__(322); +const stream_1 = __webpack_require__(337); +const sync_1 = __webpack_require__(338); +const settings_1 = __webpack_require__(340); exports.Settings = settings_1.default; function walk(directory, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43981,13 +43449,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 327 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(328); +const async_1 = __webpack_require__(323); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -44018,17 +43486,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 328 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = __webpack_require__(155); -const fsScandir = __webpack_require__(329); -const fastq = __webpack_require__(338); -const common = __webpack_require__(340); -const reader_1 = __webpack_require__(341); +const fsScandir = __webpack_require__(324); +const fastq = __webpack_require__(333); +const common = __webpack_require__(335); +const reader_1 = __webpack_require__(336); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -44118,15 +43586,15 @@ exports.default = AsyncReader; /***/ }), -/* 329 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(330); -const sync = __webpack_require__(335); -const settings_1 = __webpack_require__(336); +const async = __webpack_require__(325); +const sync = __webpack_require__(330); +const settings_1 = __webpack_require__(331); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -44149,16 +43617,16 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 330 */ +/* 325 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(321); -const rpl = __webpack_require__(331); -const constants_1 = __webpack_require__(332); -const utils = __webpack_require__(333); +const fsStat = __webpack_require__(316); +const rpl = __webpack_require__(326); +const constants_1 = __webpack_require__(327); +const utils = __webpack_require__(328); function read(directory, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings, callback); @@ -44246,7 +43714,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 331 */ +/* 326 */ /***/ (function(module, exports) { module.exports = runParallel @@ -44300,7 +43768,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 332 */ +/* 327 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44320,18 +43788,18 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_B /***/ }), -/* 333 */ +/* 328 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(334); +const fs = __webpack_require__(329); exports.fs = fs; /***/ }), -/* 334 */ +/* 329 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44356,15 +43824,15 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 335 */ +/* 330 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(321); -const constants_1 = __webpack_require__(332); -const utils = __webpack_require__(333); +const fsStat = __webpack_require__(316); +const constants_1 = __webpack_require__(327); +const utils = __webpack_require__(328); function read(directory, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings); @@ -44415,15 +43883,15 @@ exports.readdir = readdir; /***/ }), -/* 336 */ +/* 331 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(321); -const fs = __webpack_require__(337); +const fsStat = __webpack_require__(316); +const fs = __webpack_require__(332); class Settings { constructor(_options = {}) { this._options = _options; @@ -44446,7 +43914,7 @@ exports.default = Settings; /***/ }), -/* 337 */ +/* 332 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44471,13 +43939,13 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 338 */ +/* 333 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var reusify = __webpack_require__(339) +var reusify = __webpack_require__(334) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -44651,7 +44119,7 @@ module.exports = fastqueue /***/ }), -/* 339 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44691,7 +44159,7 @@ module.exports = reusify /***/ }), -/* 340 */ +/* 335 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44722,13 +44190,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 341 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(340); +const common = __webpack_require__(335); class Reader { constructor(_root, _settings) { this._root = _root; @@ -44740,14 +44208,14 @@ exports.default = Reader; /***/ }), -/* 342 */ +/* 337 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(137); -const async_1 = __webpack_require__(328); +const async_1 = __webpack_require__(323); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -44777,13 +44245,13 @@ exports.default = StreamProvider; /***/ }), -/* 343 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(344); +const sync_1 = __webpack_require__(339); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -44798,15 +44266,15 @@ exports.default = SyncProvider; /***/ }), -/* 344 */ +/* 339 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(329); -const common = __webpack_require__(340); -const reader_1 = __webpack_require__(341); +const fsScandir = __webpack_require__(324); +const common = __webpack_require__(335); +const reader_1 = __webpack_require__(336); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -44864,14 +44332,14 @@ exports.default = SyncReader; /***/ }), -/* 345 */ +/* 340 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsScandir = __webpack_require__(329); +const fsScandir = __webpack_require__(324); class Settings { constructor(_options = {}) { this._options = _options; @@ -44897,15 +44365,15 @@ exports.default = Settings; /***/ }), -/* 346 */ +/* 341 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(321); -const utils = __webpack_require__(291); +const fsStat = __webpack_require__(316); +const utils = __webpack_require__(286); class Reader { constructor(_settings) { this._settings = _settings; @@ -44937,17 +44405,17 @@ exports.default = Reader; /***/ }), -/* 347 */ +/* 342 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(348); -const entry_1 = __webpack_require__(351); -const error_1 = __webpack_require__(352); -const entry_2 = __webpack_require__(353); +const deep_1 = __webpack_require__(343); +const entry_1 = __webpack_require__(346); +const error_1 = __webpack_require__(347); +const entry_2 = __webpack_require__(348); class Provider { constructor(_settings) { this._settings = _settings; @@ -44992,14 +44460,14 @@ exports.default = Provider; /***/ }), -/* 348 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); -const partial_1 = __webpack_require__(349); +const utils = __webpack_require__(286); +const partial_1 = __webpack_require__(344); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -45053,13 +44521,13 @@ exports.default = DeepFilter; /***/ }), -/* 349 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(350); +const matcher_1 = __webpack_require__(345); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -45098,13 +44566,13 @@ exports.default = PartialMatcher; /***/ }), -/* 350 */ +/* 345 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(286); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -45155,13 +44623,13 @@ exports.default = Matcher; /***/ }), -/* 351 */ +/* 346 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(286); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -45217,13 +44685,13 @@ exports.default = EntryFilter; /***/ }), -/* 352 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(286); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -45239,13 +44707,13 @@ exports.default = ErrorFilter; /***/ }), -/* 353 */ +/* 348 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(291); +const utils = __webpack_require__(286); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -45272,15 +44740,15 @@ exports.default = EntryTransformer; /***/ }), -/* 354 */ +/* 349 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const stream_1 = __webpack_require__(137); -const stream_2 = __webpack_require__(320); -const provider_1 = __webpack_require__(347); +const stream_2 = __webpack_require__(315); +const provider_1 = __webpack_require__(342); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -45310,14 +44778,14 @@ exports.default = ProviderStream; /***/ }), -/* 355 */ +/* 350 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(356); -const provider_1 = __webpack_require__(347); +const sync_1 = __webpack_require__(351); +const provider_1 = __webpack_require__(342); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -45340,15 +44808,15 @@ exports.default = ProviderSync; /***/ }), -/* 356 */ +/* 351 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(321); -const fsWalk = __webpack_require__(326); -const reader_1 = __webpack_require__(346); +const fsStat = __webpack_require__(316); +const fsWalk = __webpack_require__(321); +const reader_1 = __webpack_require__(341); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -45390,7 +44858,7 @@ exports.default = ReaderSync; /***/ }), -/* 357 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45449,13 +44917,13 @@ exports.default = Settings; /***/ }), -/* 358 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(359); +const pathType = __webpack_require__(354); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -45531,7 +44999,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 359 */ +/* 354 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45581,7 +45049,7 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 360 */ +/* 355 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45589,9 +45057,9 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); const {promisify} = __webpack_require__(111); const fs = __webpack_require__(133); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(289); -const gitIgnore = __webpack_require__(361); -const slash = __webpack_require__(362); +const fastGlob = __webpack_require__(284); +const gitIgnore = __webpack_require__(356); +const slash = __webpack_require__(357); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -45705,7 +45173,7 @@ module.exports.sync = options => { /***/ }), -/* 361 */ +/* 356 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -46308,7 +45776,7 @@ if ( /***/ }), -/* 362 */ +/* 357 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46326,7 +45794,7 @@ module.exports = path => { /***/ }), -/* 363 */ +/* 358 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46379,7 +45847,7 @@ module.exports = { /***/ }), -/* 364 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -46389,7 +45857,7 @@ module.exports = { * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(294); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -46433,7 +45901,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 365 */ +/* 360 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46455,7 +45923,7 @@ module.exports = path_ => { /***/ }), -/* 366 */ +/* 361 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -46483,7 +45951,7 @@ module.exports = (childPath, parentPath) => { /***/ }), -/* 367 */ +/* 362 */ /***/ (function(module, exports, __webpack_require__) { const assert = __webpack_require__(139) @@ -46849,12 +46317,12 @@ rimraf.sync = rimrafSync /***/ }), -/* 368 */ +/* 363 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const AggregateError = __webpack_require__(369); +const AggregateError = __webpack_require__(364); module.exports = async ( iterable, @@ -46937,13 +46405,13 @@ module.exports = async ( /***/ }), -/* 369 */ +/* 364 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const indentString = __webpack_require__(370); -const cleanStack = __webpack_require__(371); +const indentString = __webpack_require__(365); +const cleanStack = __webpack_require__(366); const cleanInternalStack = stack => stack.replace(/\s+at .*aggregate-error\/index.js:\d+:\d+\)?/g, ''); @@ -46991,7 +46459,7 @@ module.exports = AggregateError; /***/ }), -/* 370 */ +/* 365 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47033,7 +46501,7 @@ module.exports = (string, count = 1, options) => { /***/ }), -/* 371 */ +/* 366 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -47080,47 +46548,221 @@ module.exports = (stack, options) => { /***/ }), -/* 372 */ +/* 367 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(373); -const cliCursor = __webpack_require__(378); -const cliSpinners = __webpack_require__(382); -const logSymbols = __webpack_require__(384); +const readline = __webpack_require__(368); +const chalk = __webpack_require__(369); +const cliCursor = __webpack_require__(374); +const cliSpinners = __webpack_require__(376); +const logSymbols = __webpack_require__(378); +const stripAnsi = __webpack_require__(384); +const wcwidth = __webpack_require__(386); +const isInteractive = __webpack_require__(390); +const MuteStream = __webpack_require__(391); + +const TEXT = Symbol('text'); +const PREFIX_TEXT = Symbol('prefixText'); + +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; + } + + this.rl.close(); + this.rl = undefined; + } +} + +let stdinDiscarder; class Ora { constructor(options) { + if (!stdinDiscarder) { + stdinDiscarder = new StdinDiscarder(); + } + if (typeof options === 'string') { options = { text: options }; } - this.options = Object.assign({ + this.options = { text: '', color: 'cyan', - stream: process.stderr - }, options); - - const sp = this.options.spinner; - this.spinner = typeof sp === 'object' ? sp : (process.platform === 'win32' ? cliSpinners.line : (cliSpinners[sp] || cliSpinners.dots)); // eslint-disable-line no-nested-ternary + stream: process.stderr, + discardStdin: true, + ...options + }; - if (this.spinner.frames === undefined) { - throw new Error('Spinner must define `frames`'); - } + this.spinner = this.options.spinner; - this.text = this.options.text; 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 = null; + 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; + } + + get indent() { + return this._indent; + } + + set indent(indent = 0) { + if (!(indent >= 0 && Number.isInteger(indent))) { + throw new Error('The `indent` option must be an integer from 0 and up'); + } + + this._indent = indent; + } + + _updateInterval(interval) { + if (interval !== undefined) { + this.interval = interval; + } + } + + get spinner() { + return this._spinner; + } + + set spinner(spinner) { this.frameIndex = 0; - this.enabled = typeof this.options.enabled === 'boolean' ? this.options.enabled : ((this.stream && this.stream.isTTY) && !process.env.CI); + + 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.`); + } + + this._updateInterval(this._spinner.interval); + } + + get text() { + return this[TEXT]; + } + + get prefixText() { + return this[PREFIX_TEXT]; + } + + 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); + } + + set text(value) { + this[TEXT] = value; + this.updateLineCount(); + } + + set prefixText(value) { + this[PREFIX_TEXT] = value; + this.updateLineCount(); + } + frame() { - const frames = this.spinner.frames; + const {frames} = this.spinner; let frame = frames[this.frameIndex]; if (this.color) { @@ -47128,92 +46770,129 @@ class Ora { } 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 frame + ' ' + this.text; + return fullPrefixText + frame + fullText; } + clear() { - if (!this.enabled) { + if (!this.isEnabled || !this.stream.isTTY) { return this; } - this.stream.clearLine(); - this.stream.cursorTo(0); + for (let i = 0; i < this.linesToClear; i++) { + if (i > 0) { + this.stream.moveCursor(0, -1); + } + + this.stream.clearLine(); + this.stream.cursorTo(this.indent); + } + + this.linesToClear = 0; return this; } + render() { this.clear(); this.stream.write(this.frame()); + this.linesToClear = this.lineCount; return this; } + start(text) { if (text) { this.text = text; } - if (!this.enabled || this.id) { + if (!this.isEnabled) { + if (this.text) { + this.stream.write(`- ${this.text}\n`); + } + + return this; + } + + if (this.isSpinning) { return this; } - cliCursor.hide(this.stream); + if (this.hideCursor) { + cliCursor.hide(this.stream); + } + + if (this.discardStdin && process.stdin.isTTY) { + this.isDiscardingStdin = true; + stdinDiscarder.start(); + } + this.render(); this.id = setInterval(this.render.bind(this), this.interval); return this; } + stop() { - if (!this.enabled) { + if (!this.isEnabled) { return this; } clearInterval(this.id); - this.id = null; + this.id = undefined; this.frameIndex = 0; this.clear(); - cliCursor.show(this.stream); + if (this.hideCursor) { + cliCursor.show(this.stream); + } + + if (this.discardStdin && process.stdin.isTTY && this.isDiscardingStdin) { + stdinDiscarder.stop(); + this.isDiscardingStdin = false; + } return this; } + succeed(text) { return this.stopAndPersist({symbol: logSymbols.success, text}); } + fail(text) { return this.stopAndPersist({symbol: logSymbols.error, text}); } + warn(text) { return this.stopAndPersist({symbol: logSymbols.warning, text}); } - info(text) { - return this.stopAndPersist({symbol: logSymbols.info, text}); - } - stopAndPersist(options) { - if (!this.enabled) { - return this; - } - // Legacy argument - // TODO: Deprecate sometime in the future - if (typeof options === 'string') { - options = { - symbol: options - }; - } + info(text) { + return this.stopAndPersist({symbol: logSymbols.info, text}); + } - options = options || {}; + 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(`${options.symbol || ' '} ${options.text || this.text}\n`); + this.stream.write(`${fullPrefixText}${options.symbol || ' '}${fullText}\n`); return this; } } -module.exports = function (opts) { - return new Ora(opts); +const oraFactory = function (options) { + return new Ora(options); }; +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'); } @@ -47221,451 +46900,298 @@ module.exports.promise = (action, options) => { const spinner = new Ora(options); spinner.start(); - action.then( - () => { + (async () => { + try { + await action; spinner.succeed(); - }, - () => { + } catch (_) { spinner.fail(); } - ); + })(); return spinner; }; /***/ }), -/* 373 */ -/***/ (function(module, exports, __webpack_require__) { +/* 368 */ +/***/ (function(module, exports) { -"use strict"; +module.exports = require("readline"); -const escapeStringRegexp = __webpack_require__(178); -const ansiStyles = __webpack_require__(374); -const stdoutColor = __webpack_require__(375).stdout; +/***/ }), +/* 369 */ +/***/ (function(module, exports, __webpack_require__) { -const template = __webpack_require__(377); +"use strict"; -const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); +const ansiStyles = __webpack_require__(113); +const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(370); +const { + stringReplaceAll, + stringEncaseCRLFWithFirstIndex +} = __webpack_require__(372); // `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; - -// `color-convert` models to exclude from the Chalk API due to conflicts and such -const skipModels = new Set(['gray']); +const levelMapping = [ + 'ansi', + 'ansi', + 'ansi256', + 'ansi16m' +]; const styles = Object.create(null); -function applyOptions(obj, options) { - options = options || {}; +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 scLevel = stdoutColor ? stdoutColor.level : 0; - obj.level = options.level === undefined ? scLevel : options.level; - obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0; + const colorLevel = stdoutColor ? stdoutColor.level : 0; + object.level = options.level === undefined ? colorLevel : options.level; +}; + +class ChalkClass { + constructor(options) { + return chalkFactory(options); + } } -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); +const chalkFactory = options => { + const chalk = {}; + applyOptions(chalk, options); - chalk.template = function () { - const args = [].slice.call(arguments); - return chalkTag.apply(null, [chalk.template].concat(args)); - }; + chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_); - Object.setPrototypeOf(chalk, Chalk.prototype); - Object.setPrototypeOf(chalk.template, chalk); + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); - chalk.template.constructor = Chalk; + chalk.template.constructor = () => { + throw new Error('`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.'); + }; - return chalk.template; - } + chalk.template.Instance = ChalkClass; - applyOptions(this, options); -} + return chalk.template; +}; -// Use bright blue on Windows as the normal blue color is illegible -if (isSimpleWindowsTerm) { - ansiStyles.blue.open = '\u001B[94m'; +function Chalk(options) { + return chalkFactory(options); } -for (const key of Object.keys(ansiStyles)) { - ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); - - styles[key] = { +for (const [styleName, style] of Object.entries(ansiStyles)) { + styles[styleName] = { get() { - const codes = ansiStyles[key]; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); + const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty); + Object.defineProperty(this, styleName, {value: builder}); + return builder; } }; } styles.visible = { get() { - return build.call(this, this._styles || [], true, 'visible'); + const builder = createBuilder(this, this._styler, true); + Object.defineProperty(this, 'visible', {value: builder}); + return builder; } }; -ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); -for (const model of Object.keys(ansiStyles.color.ansi)) { - if (skipModels.has(model)) { - continue; - } +const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256']; +for (const model of usedModels) { 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); + 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); }; } }; } -ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); -for (const model of Object.keys(ansiStyles.bgColor.ansi)) { - if (skipModels.has(model)) { - continue; - } - +for (const model of usedModels) { 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 {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); }; } }; } -const proto = Object.defineProperties(() => {}, styles); - -function build(_styles, _empty, key) { - const builder = function () { - return applyStyle.apply(builder, arguments); - }; - - builder._styles = _styles; - builder._empty = _empty; - - const self = this; - - Object.defineProperty(builder, 'level', { +const proto = Object.defineProperties(() => {}, { + ...styles, + level: { enumerable: true, get() { - return self.level; + return this._generator.level; }, set(level) { - self.level = level; + this._generator.level = level; } - }); + } +}); - Object.defineProperty(builder, 'enabled', { - enumerable: true, - get() { - return self.enabled; - }, - set(enabled) { - self.enabled = enabled; - } - }); +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; + } - // See below for fix regarding invisible grey/dim combination on Windows - builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + 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 - return builder; -} + builder._generator = self; + builder._styler = _styler; + builder._isEmpty = _isEmpty; -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]); + return builder; +}; - if (argsLen === 0) { - return ''; +const applyStyle = (self, string) => { + if (self.level <= 0 || !string) { + return self._isEmpty ? '' : string; } - if (argsLen > 1) { - // Don't slice `arguments`, it prevents V8 optimizations - for (let a = 1; a < argsLen; a++) { - str += ' ' + args[a]; - } - } + let styler = self._styler; - if (!this.enabled || this.level <= 0 || !str) { - return this._empty ? '' : str; + if (styler === undefined) { + return string; } - // 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 = ''; - } + 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); - 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; + styler = styler.parent; + } + } - // 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}`); + // 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); } - // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue - ansiStyles.dim.open = originalDim; + return openAll + string + closeAll; +}; - return str; -} +let template; +const chalkTag = (chalk, ...strings) => { + const [firstString] = strings; -function chalkTag(chalk, strings) { - if (!Array.isArray(strings)) { + if (!Array.isArray(firstString)) { // If chalk() was called by itself or with a string, // return the string itself as a string. - return [].slice.call(arguments, 1).join(' '); + return strings.join(' '); } - const args = [].slice.call(arguments, 2); - const parts = [strings.raw[0]]; + const arguments_ = strings.slice(1); + const parts = [firstString.raw[0]]; - for (let i = 1; i < strings.length; i++) { - parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); - parts.push(String(strings.raw[i])); + 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__(373); } 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 - - -/***/ }), -/* 374 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(180); - -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 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; -const wrapAnsi16m = (fn, offset) => function () { - const rgb = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +// For TypeScript +chalk.Level = { + None: 0, + Basic: 1, + Ansi256: 2, + TrueColor: 3, + 0: 'None', + 1: 'Basic', + 2: 'Ansi256', + 3: 'TrueColor' }; -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], - - // 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], - - // 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] - } - }; - - // 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` - }; - - group[styleName] = styles[styleName]; - - codes.set(style[0], style[1]); - } - - Object.defineProperty(styles, groupName, { - value: group, - enumerable: false - }); - - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); - } - - const ansi2ansi = n => n; - const rgb2rgb = (r, g, b) => [r, g, b]; - - styles.color.close = '\u001B[39m'; - styles.bgColor.close = '\u001B[49m'; - - 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) - }; - - for (let key of Object.keys(colorConvert)) { - if (typeof colorConvert[key] !== 'object') { - continue; - } - - const suite = colorConvert[key]; - - if (key === 'ansi16') { - key = 'ansi'; - } - - if ('ansi16' in suite) { - styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); - styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); - } - - 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 -}); +module.exports = chalk; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) /***/ }), -/* 375 */ +/* 370 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const os = __webpack_require__(120); -const hasFlag = __webpack_require__(376); +const tty = __webpack_require__(121); +const hasFlag = __webpack_require__(371); -const env = process.env; +const {env} = process; let forceColor; if (hasFlag('no-color') || hasFlag('no-colors') || - hasFlag('color=false')) { - forceColor = false; + hasFlag('color=false') || + hasFlag('color=never')) { + forceColor = 0; } else if (hasFlag('color') || hasFlag('colors') || hasFlag('color=true') || hasFlag('color=always')) { - forceColor = true; + forceColor = 1; } + if ('FORCE_COLOR' in env) { - forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; + if (env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } } function translateLevel(level) { @@ -47681,8 +47207,8 @@ function translateLevel(level) { }; } -function supportsColor(stream) { - if (forceColor === false) { +function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { return 0; } @@ -47696,22 +47222,21 @@ function supportsColor(stream) { return 2; } - if (stream && !stream.isTTY && forceColor !== true) { + if (haveStream && !streamIsTTY && forceColor === undefined) { return 0; } - const min = forceColor ? 1 : 0; + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } 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. + // 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 ) { @@ -47733,6 +47258,10 @@ function supportsColor(stream) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; } + if ('GITHUB_ACTIONS' in env) { + return 1; + } + if (env.COLORTERM === 'truecolor') { return 3; } @@ -47761,50 +47290,92 @@ function supportsColor(stream) { return 1; } - if (env.TERM === 'dumb') { - return min; - } - return min; } function getSupportLevel(stream) { - const level = supportsColor(stream); + const level = supportsColor(stream, stream && stream.isTTY); return translateLevel(level); } module.exports = { supportsColor: getSupportLevel, - stdout: getSupportLevel(process.stdout), - stderr: getSupportLevel(process.stderr) + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))) }; /***/ }), -/* 376 */ +/* 371 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = (flag, argv) => { - argv = argv || process.argv; + +module.exports = (flag, 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 position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); }; /***/ }), -/* 377 */ +/* 372 */ /***/ (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 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 +}; + + +/***/ }), +/* 373 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +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}|x[a-f\d]{2}|.)|([^\\])/gi; +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'], @@ -47820,23 +47391,31 @@ const ESCAPES = new Map([ ]); function unescape(c) { - if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + const u = c[0] === 'u'; + const bracket = c[1] === '{'; + + 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; } -function parseArguments(name, args) { +function parseArguments(name, arguments_) { const results = []; - const chunks = args.trim().split(/\s*,\s*/g); + const chunks = arguments_.trim().split(/\s*,\s*/g); let matches; for (const chunk of chunks) { - if (!isNaN(chunk)) { - results.push(Number(chunk)); + 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, chr) => escape ? unescape(escape) : chr)); + 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}')`); } @@ -47875,36 +47454,34 @@ function buildStyle(chalk, styles) { } 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}`); - } + for (const [styleName, styles] of Object.entries(enabled)) { + if (!Array.isArray(styles)) { + continue; + } - if (enabled[styleName].length > 0) { - current = current[styleName].apply(current, enabled[styleName]); - } else { - current = current[styleName]; - } + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); } + + current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; } return current; } -module.exports = (chalk, tmp) => { +module.exports = (chalk, temporary) => { 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)); + temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => { + if (escapeCharacter) { + chunk.push(unescape(escapeCharacter)); } else if (style) { - const str = chunk.join(''); + const string = chunk.join(''); chunk = []; - chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); + chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string)); styles.push({inverse, styles: parseStyle(style)}); } else if (close) { if (styles.length === 0) { @@ -47915,7 +47492,7 @@ module.exports = (chalk, tmp) => { chunk = []; styles.pop(); } else { - chunk.push(chr); + chunk.push(character); } }); @@ -47931,151 +47508,100 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 378 */ +/* 374 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const restoreCursor = __webpack_require__(379); - -let hidden = false; +const restoreCursor = __webpack_require__(375); -exports.show = stream => { - const s = stream || process.stderr; +let isHidden = false; - if (!s.isTTY) { +exports.show = (writableStream = process.stderr) => { + if (!writableStream.isTTY) { return; } - hidden = false; - s.write('\u001b[?25h'); + isHidden = false; + writableStream.write('\u001B[?25h'); }; -exports.hide = stream => { - const s = stream || process.stderr; - - if (!s.isTTY) { +exports.hide = (writableStream = process.stderr) => { + if (!writableStream.isTTY) { return; } restoreCursor(); - hidden = true; - s.write('\u001b[?25l'); + isHidden = true; + writableStream.write('\u001B[?25l'); }; -exports.toggle = (force, stream) => { +exports.toggle = (force, writableStream) => { if (force !== undefined) { - hidden = force; + isHidden = force; } - if (hidden) { - exports.show(stream); + if (isHidden) { + exports.show(writableStream); } else { - exports.hide(stream); + exports.hide(writableStream); } }; /***/ }), -/* 379 */ +/* 375 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const onetime = __webpack_require__(380); +const onetime = __webpack_require__(244); const signalExit = __webpack_require__(217); module.exports = onetime(() => { signalExit(() => { - process.stderr.write('\u001b[?25h'); + process.stderr.write('\u001B[?25h'); }, {alwaysLast: true}); }); /***/ }), -/* 380 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(381); - -module.exports = (fn, opts) => { - // TODO: Remove this in v3 - if (opts === true) { - throw new TypeError('The second argument is now an options object'); - } - - if (typeof fn !== 'function') { - throw new TypeError('Expected a function'); - } - - opts = opts || {}; - - let ret; - let called = false; - const fnName = fn.displayName || fn.name || '<anonymous>'; - - const onetime = function () { - if (called) { - if (opts.throw === true) { - throw new Error(`Function \`${fnName}\` can only be called once`); - } - - return ret; - } - - called = true; - ret = fn.apply(this, arguments); - fn = null; - return ret; - }; +const spinners = Object.assign({}, __webpack_require__(377)); - mimicFn(onetime, fn); +const spinnersList = Object.keys(spinners); - return onetime; -}; - - -/***/ }), -/* 381 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -module.exports = (to, from) => { - // TODO: use `Reflect.ownKeys()` when targeting Node.js 6 - for (const prop of Object.getOwnPropertyNames(from).concat(Object.getOwnPropertySymbols(from))) { - Object.defineProperty(to, prop, Object.getOwnPropertyDescriptor(from, prop)); +Object.defineProperty(spinners, 'random', { + get() { + const randomIndex = Math.floor(Math.random() * spinnersList.length); + const spinnerName = spinnersList[randomIndex]; + return spinners[spinnerName]; } +}); - return to; -}; - - -/***/ }), -/* 382 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - -module.exports = __webpack_require__(383); +module.exports = spinners; +// TODO: Remove this for the next major release +module.exports.default = spinners; /***/ }), -/* 383 */ +/* 377 */ /***/ (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\":[\"⢀⠀\",\"⡀⠀\",\"⠄⠀\",\"⢂⠀\",\"⡂⠀\",\"⠅⠀\",\"⢃⠀\",\"⡃⠀\",\"⠍⠀\",\"⢋⠀\",\"⡋⠀\",\"⠍⠁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⢈⠩\",\"⡀⢙\",\"⠄⡙\",\"⢂⠩\",\"⡂⢘\",\"⠅⡘\",\"⢃⠨\",\"⡃⢐\",\"⠍⡐\",\"⢋⠠\",\"⡋⢀\",\"⠍⡁\",\"⢋⠁\",\"⡋⠁\",\"⠍⠉\",\"⠋⠉\",\"⠋⠉\",\"⠉⠙\",\"⠉⠙\",\"⠉⠩\",\"⠈⢙\",\"⠈⡙\",\"⠈⠩\",\"⠀⢙\",\"⠀⡙\",\"⠀⠩\",\"⠀⢘\",\"⠀⡘\",\"⠀⠨\",\"⠀⢐\",\"⠀⡐\",\"⠀⠠\",\"⠀⢀\",\"⠀⡀\"]},\"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\":[\"🌍 \",\"🌎 \",\"🌏 \"]},\"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\":[\"🌲\",\"🎄\"]}}"); +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\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]}}"); /***/ }), -/* 384 */ +/* 378 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const chalk = __webpack_require__(385); +const chalk = __webpack_require__(379); const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; @@ -48097,16 +47623,16 @@ module.exports = isSupported ? main : fallbacks; /***/ }), -/* 385 */ +/* 379 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const escapeStringRegexp = __webpack_require__(178); -const ansiStyles = __webpack_require__(386); -const stdoutColor = __webpack_require__(184).stdout; +const ansiStyles = __webpack_require__(380); +const stdoutColor = __webpack_require__(381).stdout; -const template = __webpack_require__(387); +const template = __webpack_require__(383); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -48332,7 +47858,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 386 */ +/* 380 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48505,7 +48031,160 @@ Object.defineProperty(module, 'exports', { /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) /***/ }), -/* 387 */ +/* 381 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const os = __webpack_require__(120); +const hasFlag = __webpack_require__(382); + +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) +}; + + +/***/ }), +/* 382 */ +/***/ (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); +}; + + +/***/ }), +/* 383 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -48639,8 +48318,555 @@ module.exports = (chalk, tmp) => { }; +/***/ }), +/* 384 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const ansiRegex = __webpack_require__(385); + +module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; + + +/***/ }), +/* 385 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = ({onlyFirst = false} = {}) => { + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, onlyFirst ? undefined : 'g'); +}; + + +/***/ }), +/* 386 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var defaults = __webpack_require__(387) +var combining = __webpack_require__(389) + +var DEFAULTS = { + nul: 0, + control: 0 +} + +module.exports = function wcwidth(str) { + return wcswidth(str, DEFAULTS) +} + +module.exports.config = function(opts) { + opts = defaults(opts || {}, DEFAULTS) + return function wcwidth(str) { + return wcswidth(str, opts) + } +} + +/* + * 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. +*/ + +function wcswidth(str, opts) { + if (typeof str !== 'string') return wcwidth(str, opts) + + 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 + } + + return s +} + +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 + + // binary search in table of non-spacing characters + if (bisearch(ucs)) return 0 + + // 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 + + if (ucs < combining[0][0] || ucs > combining[max][1]) return false + + 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 +} + + +/***/ }), +/* 387 */ +/***/ (function(module, exports, __webpack_require__) { + +var clone = __webpack_require__(388); + +module.exports = function(options, defaults) { + options = options || {}; + + Object.keys(defaults).forEach(function(key) { + if (typeof options[key] === 'undefined') { + options[key] = clone(defaults[key]); + } + }); + + return options; +}; + /***/ }), /* 388 */ +/***/ (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; + + 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; + } + } + + if (circular) { + var index = allParents.indexOf(parent); + + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } + + for (var i in parent) { + var attrs; + if (proto) { + attrs = Object.getOwnPropertyDescriptor(proto, i); + } + + if (attrs && attrs.set == null) { + continue; + } + child[i] = _clone(parent[i], depth - 1); + } + + return child; + } + + return _clone(parent, depth); +} + +/** + * 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; + + var c = function () {}; + c.prototype = parent; + return new c(); +}; + +// private utility functions + +function __objToStr(o) { + return Object.prototype.toString.call(o); +}; +clone.__objToStr = __objToStr; + +function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; +}; +clone.__isDate = __isDate; + +function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; +}; +clone.__isArray = __isArray; + +function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; +}; +clone.__isRegExp = __isRegExp; + +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; + +return clone; +})(); + +if ( true && module.exports) { + module.exports = clone; +} + + +/***/ }), +/* 389 */ +/***/ (function(module, exports) { + +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 ] +] + + +/***/ }), +/* 390 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = ({stream = process.stdout} = {}) => { + return Boolean( + stream && stream.isTTY && + process.env.TERM !== 'dumb' && + !('CI' in process.env) + ); +}; + + +/***/ }), +/* 391 */ +/***/ (function(module, exports, __webpack_require__) { + +var Stream = __webpack_require__(137) + +module.exports = MuteStream + +// 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 +} + +MuteStream.prototype = Object.create(Stream.prototype) + +Object.defineProperty(MuteStream.prototype, 'constructor', { + value: MuteStream, + enumerable: false +}) + +MuteStream.prototype.mute = function () { + this.muted = true +} + +MuteStream.prototype.unmute = function () { + this.muted = false +} + +Object.defineProperty(MuteStream.prototype, '_onpipe', { + value: onPipe, + enumerable: false, + writable: true, + configurable: true +}) + +function onPipe (src) { + this._src = src +} + +Object.defineProperty(MuteStream.prototype, 'isTTY', { + get: getIsTTY, + set: setIsTTY, + enumerable: true, + configurable: true +}) + +function getIsTTY () { + return( (this._dest) ? this._dest.isTTY + : (this._src) ? this._src.isTTY + : false + ) +} + +// 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 + }) +} + +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 }) + + +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() +} + +MuteStream.prototype.resume = function () { + if (this._src) return this._src.resume() +} + +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) +} + +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') +} + +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) +}} + +MuteStream.prototype.destroy = proxy('destroy') +MuteStream.prototype.destroySoon = proxy('destroySoon') +MuteStream.prototype.close = proxy('close') + + +/***/ }), +/* 392 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -48701,7 +48927,7 @@ const RunCommand = { }; /***/ }), -/* 389 */ +/* 393 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -48711,7 +48937,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); /* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(390); +/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(394); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -48796,14 +49022,14 @@ const WatchCommand = { }; /***/ }), -/* 390 */ +/* 394 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitUntilWatchIsReady", function() { return waitUntilWatchIsReady; }); /* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(8); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(391); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(395); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -48870,141 +49096,141 @@ function waitUntilWatchIsReady(stream, opts = {}) { } /***/ }), -/* 391 */ +/* 395 */ /***/ (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__(392); +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(396); /* 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__(393); +/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(397); /* 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__(394); +/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(398); /* 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__(395); +/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(399); /* 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__(396); +/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(400); /* 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__(397); +/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(401); /* 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__(398); +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(402); /* 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__(399); +/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(403); /* 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__(400); +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(404); /* 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__(401); +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(405); /* 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__(402); +/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(406); /* 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__(80); /* 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__(403); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(407); /* 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__(404); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(408); /* 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__(405); +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(409); /* 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__(406); +/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(410); /* 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__(407); +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(411); /* 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__(408); +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(412); /* 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__(409); +/* harmony import */ var _internal_operators_delay__WEBPACK_IMPORTED_MODULE_18__ = __webpack_require__(413); /* 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__(411); +/* harmony import */ var _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__ = __webpack_require__(415); /* 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__(412); +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(416); /* 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__(413); +/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(417); /* 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__(414); +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(418); /* 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__(415); +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(419); /* 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__(416); +/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(420); /* 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__(419); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(423); /* 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__(420); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(424); /* 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__(421); +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(425); /* 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__(422); +/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(426); /* 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__(423); +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(427); /* 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__(104); /* 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__(424); +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(428); /* 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__(425); +/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(429); /* 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__(426); +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(430); /* 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__(427); +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(431); /* 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__(31); /* 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__(428); +/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(432); /* 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__(429); +/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(433); /* 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__(430); +/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(434); /* 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__(66); /* 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__(432); +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(436); /* 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__(433); +/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(437); /* 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__(434); +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(438); /* 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__(437); +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(441); /* 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__(81); @@ -49015,175 +49241,175 @@ __webpack_require__.r(__webpack_exports__); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(438); +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(442); /* 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__(439); +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(443); /* 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__(440); +/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(444); /* 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__(441); +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(445); /* 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__(41); /* 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__(442); +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(446); /* 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__(443); +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(447); /* 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__(444); +/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(448); /* 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__(445); +/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(449); /* 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__(446); +/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(450); /* 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__(447); +/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(451); /* 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__(448); +/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(452); /* 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__(449); +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(453); /* 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__(450); +/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(454); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(435); +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(439); /* 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__(451); +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(455); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(452); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(456); /* 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__(453); +/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(457); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(454); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(458); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); /* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(30); /* 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__(455); +/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(459); /* 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__(456); +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(460); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(436); +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(440); /* 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__(457); +/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(461); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(458); +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(462); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(459); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(463); /* 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__(460); +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(464); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(461); +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(465); /* 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__(462); +/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(466); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(463); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(467); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(464); +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(468); /* 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__(465); +/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(469); /* 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__(466); +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(470); /* 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__(468); +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(472); /* 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__(469); +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(473); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(470); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(474); /* 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__(418); +/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(422); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(431); +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(435); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(471); +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(475); /* 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__(472); +/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(476); /* 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__(473); +/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(477); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(474); +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(478); /* 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__(475); +/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(479); /* 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__(417); +/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(421); /* 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__(476); +/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(480); /* 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__(477); +/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(481); /* 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__(478); +/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(482); /* 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__(479); +/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(483); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(480); +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(484); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(481); +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(485); /* 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__(482); +/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(486); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(483); +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(487); /* 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__(484); +/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(488); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(485); +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(489); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(486); +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(490); /* 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__(487); +/* harmony import */ var _internal_operators_zip__WEBPACK_IMPORTED_MODULE_101__ = __webpack_require__(491); /* 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__(488); +/* harmony import */ var _internal_operators_zipAll__WEBPACK_IMPORTED_MODULE_102__ = __webpack_require__(492); /* 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 */ @@ -49295,7 +49521,7 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 392 */ +/* 396 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49376,14 +49602,14 @@ var AuditSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 393 */ +/* 397 */ /***/ (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__(55); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(392); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(396); /* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(107); /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ @@ -49399,7 +49625,7 @@ function auditTime(duration, scheduler) { /***/ }), -/* 394 */ +/* 398 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49448,7 +49674,7 @@ var BufferSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 395 */ +/* 399 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49549,7 +49775,7 @@ var BufferSkipCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 396 */ +/* 400 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49710,7 +49936,7 @@ function dispatchBufferClose(arg) { /***/ }), -/* 397 */ +/* 401 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49830,7 +50056,7 @@ var BufferToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 398 */ +/* 402 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49925,7 +50151,7 @@ var BufferWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 399 */ +/* 403 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -49989,7 +50215,7 @@ var CatchSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 400 */ +/* 404 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50005,7 +50231,7 @@ function combineAll(project) { /***/ }), -/* 401 */ +/* 405 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50037,7 +50263,7 @@ function combineLatest() { /***/ }), -/* 402 */ +/* 406 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50057,7 +50283,7 @@ function concat() { /***/ }), -/* 403 */ +/* 407 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50073,13 +50299,13 @@ function concatMap(project, resultSelector) { /***/ }), -/* 404 */ +/* 408 */ /***/ (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__(403); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(407); /** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ function concatMapTo(innerObservable, resultSelector) { @@ -50089,7 +50315,7 @@ function concatMapTo(innerObservable, resultSelector) { /***/ }), -/* 405 */ +/* 409 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50154,7 +50380,7 @@ var CountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 406 */ +/* 410 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50242,7 +50468,7 @@ var DebounceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 407 */ +/* 411 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50318,7 +50544,7 @@ function dispatchNext(subscriber) { /***/ }), -/* 408 */ +/* 412 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50368,7 +50594,7 @@ var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 409 */ +/* 413 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50376,7 +50602,7 @@ __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__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(410); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(414); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); /* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(42); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ @@ -50475,7 +50701,7 @@ var DelayMessage = /*@__PURE__*/ (function () { /***/ }), -/* 410 */ +/* 414 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50489,7 +50715,7 @@ function isDate(value) { /***/ }), -/* 411 */ +/* 415 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50635,7 +50861,7 @@ var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 412 */ +/* 416 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50673,7 +50899,7 @@ var DeMaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 413 */ +/* 417 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50751,7 +50977,7 @@ var DistinctSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 414 */ +/* 418 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50822,13 +51048,13 @@ var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 415 */ +/* 419 */ /***/ (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__(414); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(418); /** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ function distinctUntilKeyChanged(key, compare) { @@ -50838,7 +51064,7 @@ function distinctUntilKeyChanged(key, compare) { /***/ }), -/* 416 */ +/* 420 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50846,9 +51072,9 @@ __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__(62); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(417); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(408); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(418); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(412); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(422); /** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ @@ -50870,7 +51096,7 @@ function elementAt(index, defaultValue) { /***/ }), -/* 417 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50936,7 +51162,7 @@ function defaultErrorFactory() { /***/ }), -/* 418 */ +/* 422 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -50998,7 +51224,7 @@ var TakeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 419 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51020,7 +51246,7 @@ function endWith() { /***/ }), -/* 420 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51082,7 +51308,7 @@ var EverySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 421 */ +/* 425 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51139,7 +51365,7 @@ var SwitchFirstSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 422 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51239,7 +51465,7 @@ var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 423 */ +/* 427 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51358,7 +51584,7 @@ var ExpandSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 424 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51396,7 +51622,7 @@ var FinallySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 425 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51468,13 +51694,13 @@ var FindValueSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 426 */ +/* 430 */ /***/ (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__(425); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(429); /** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ function findIndex(predicate, thisArg) { @@ -51484,7 +51710,7 @@ function findIndex(predicate, thisArg) { /***/ }), -/* 427 */ +/* 431 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51492,9 +51718,9 @@ __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__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(418); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(408); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(417); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(422); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(412); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -51511,7 +51737,7 @@ function first(predicate, defaultValue) { /***/ }), -/* 428 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51548,7 +51774,7 @@ var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 429 */ +/* 433 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51592,7 +51818,7 @@ var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 430 */ +/* 434 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51600,9 +51826,9 @@ __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__(63); /* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(431); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(417); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(408); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(435); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(421); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(412); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); /** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ @@ -51619,7 +51845,7 @@ function last(predicate, defaultValue) { /***/ }), -/* 431 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51696,7 +51922,7 @@ var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 432 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51735,7 +51961,7 @@ var MapToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 433 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51785,13 +52011,13 @@ var MaterializeSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 434 */ +/* 438 */ /***/ (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__(435); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(439); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function max(comparer) { @@ -51804,15 +52030,15 @@ function max(comparer) { /***/ }), -/* 435 */ +/* 439 */ /***/ (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__(436); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(431); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(408); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(440); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(435); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(412); /* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24); /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ @@ -51833,7 +52059,7 @@ function reduce(accumulator, seed) { /***/ }), -/* 436 */ +/* 440 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51915,7 +52141,7 @@ var ScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 437 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51935,7 +52161,7 @@ function merge() { /***/ }), -/* 438 */ +/* 442 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -51960,7 +52186,7 @@ function mergeMapTo(innerObservable, resultSelector, concurrent) { /***/ }), -/* 439 */ +/* 443 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52075,13 +52301,13 @@ var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 440 */ +/* 444 */ /***/ (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__(435); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(439); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function min(comparer) { @@ -52094,7 +52320,7 @@ function min(comparer) { /***/ }), -/* 441 */ +/* 445 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52143,7 +52369,7 @@ var MulticastOperator = /*@__PURE__*/ (function () { /***/ }), -/* 442 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52237,7 +52463,7 @@ var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 443 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52285,7 +52511,7 @@ var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 444 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52308,7 +52534,7 @@ function partition(predicate, thisArg) { /***/ }), -/* 445 */ +/* 449 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52348,14 +52574,14 @@ function plucker(props, length) { /***/ }), -/* 446 */ +/* 450 */ /***/ (function(module, __webpack_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__(27); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); /** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ @@ -52368,14 +52594,14 @@ function publish(selector) { /***/ }), -/* 447 */ +/* 451 */ /***/ (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__(32); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); /** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ @@ -52386,14 +52612,14 @@ function publishBehavior(value) { /***/ }), -/* 448 */ +/* 452 */ /***/ (function(module, __webpack_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__(50); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); /** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ @@ -52404,14 +52630,14 @@ function publishLast() { /***/ }), -/* 449 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); /** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ @@ -52427,7 +52653,7 @@ function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { /***/ }), -/* 450 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52454,7 +52680,7 @@ function race() { /***/ }), -/* 451 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52519,7 +52745,7 @@ var RepeatSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 452 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52615,7 +52841,7 @@ var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 453 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52668,7 +52894,7 @@ var RetrySubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 454 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52756,7 +52982,7 @@ var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 455 */ +/* 459 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52813,7 +53039,7 @@ var SampleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 456 */ +/* 460 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52873,7 +53099,7 @@ function dispatchNotification(state) { /***/ }), -/* 457 */ +/* 461 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -52996,13 +53222,13 @@ var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 458 */ +/* 462 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(441); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(445); /* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); /* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ @@ -53019,7 +53245,7 @@ function share() { /***/ }), -/* 459 */ +/* 463 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53084,7 +53310,7 @@ function shareReplayOperator(_a) { /***/ }), -/* 460 */ +/* 464 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53164,7 +53390,7 @@ var SingleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 461 */ +/* 465 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53206,7 +53432,7 @@ var SkipSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 462 */ +/* 466 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53268,7 +53494,7 @@ var SkipLastSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 463 */ +/* 467 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53329,7 +53555,7 @@ var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 464 */ +/* 468 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53385,7 +53611,7 @@ var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 465 */ +/* 469 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53414,13 +53640,13 @@ function startWith() { /***/ }), -/* 466 */ +/* 470 */ /***/ (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__(467); +/* harmony import */ var _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(471); /** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ function subscribeOn(scheduler, delay) { @@ -53445,7 +53671,7 @@ var SubscribeOnOperator = /*@__PURE__*/ (function () { /***/ }), -/* 467 */ +/* 471 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53509,13 +53735,13 @@ var SubscribeOnObservable = /*@__PURE__*/ (function (_super) { /***/ }), -/* 468 */ +/* 472 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(469); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(473); /* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(25); /** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ @@ -53527,7 +53753,7 @@ function switchAll() { /***/ }), -/* 469 */ +/* 473 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53621,13 +53847,13 @@ var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 470 */ +/* 474 */ /***/ (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__(469); +/* harmony import */ var _switchMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(473); /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ function switchMapTo(innerObservable, resultSelector) { @@ -53637,7 +53863,7 @@ function switchMapTo(innerObservable, resultSelector) { /***/ }), -/* 471 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53687,7 +53913,7 @@ var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 472 */ +/* 476 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53755,7 +53981,7 @@ var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 473 */ +/* 477 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53843,7 +54069,7 @@ var TapSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 474 */ +/* 478 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53947,7 +54173,7 @@ var ThrottleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 475 */ +/* 479 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -53956,7 +54182,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(474); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(478); /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ @@ -54045,7 +54271,7 @@ function dispatchNext(arg) { /***/ }), -/* 476 */ +/* 480 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54053,7 +54279,7 @@ __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__(55); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(436); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(440); /* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); /* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(66); /** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ @@ -54089,7 +54315,7 @@ var TimeInterval = /*@__PURE__*/ (function () { /***/ }), -/* 477 */ +/* 481 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54097,7 +54323,7 @@ __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__(55); /* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(478); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(482); /* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(49); /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ @@ -54114,7 +54340,7 @@ function timeout(due, scheduler) { /***/ }), -/* 478 */ +/* 482 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54122,7 +54348,7 @@ __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__(12); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(410); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(414); /* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); /* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ @@ -54196,7 +54422,7 @@ var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 479 */ +/* 483 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54226,13 +54452,13 @@ var Timestamp = /*@__PURE__*/ (function () { /***/ }), -/* 480 */ +/* 484 */ /***/ (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__(435); +/* harmony import */ var _reduce__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(439); /** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ function toArrayReducer(arr, item, index) { @@ -54249,7 +54475,7 @@ function toArray() { /***/ }), -/* 481 */ +/* 485 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54329,7 +54555,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 482 */ +/* 486 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54419,7 +54645,7 @@ var WindowCountSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 483 */ +/* 487 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54589,7 +54815,7 @@ function dispatchWindowClose(state) { /***/ }), -/* 484 */ +/* 488 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54732,7 +54958,7 @@ var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 485 */ +/* 489 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54829,7 +55055,7 @@ var WindowSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 486 */ +/* 490 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54924,7 +55150,7 @@ var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 487 */ +/* 491 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54946,7 +55172,7 @@ function zip() { /***/ }), -/* 488 */ +/* 492 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54962,7 +55188,7 @@ function zipAll(project) { /***/ }), -/* 489 */ +/* 493 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -54971,8 +55197,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(162); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); /* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(490); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(491); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(494); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(495); 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; } @@ -55054,13 +55280,13 @@ function toArray(value) { } /***/ }), -/* 490 */ +/* 494 */ /***/ (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 import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(227); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(112); /* 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__); @@ -55207,7 +55433,7 @@ function addProjectToTree(tree, pathParts, project) { } /***/ }), -/* 491 */ +/* 495 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -55215,13 +55441,13 @@ __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 multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(492); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(496); /* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(366); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(361); /* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(279); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(274); /* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(145); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(276); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(271); 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; } @@ -55383,15 +55609,15 @@ class Kibana { } /***/ }), -/* 492 */ +/* 496 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const minimatch = __webpack_require__(149); -const arrayUnion = __webpack_require__(493); -const arrayDiffer = __webpack_require__(494); -const arrify = __webpack_require__(495); +const arrayUnion = __webpack_require__(497); +const arrayDiffer = __webpack_require__(498); +const arrify = __webpack_require__(499); module.exports = (list, patterns, options = {}) => { list = arrify(list); @@ -55415,7 +55641,7 @@ module.exports = (list, patterns, options = {}) => { /***/ }), -/* 493 */ +/* 497 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55427,7 +55653,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 494 */ +/* 498 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55442,7 +55668,7 @@ module.exports = arrayDiffer; /***/ }), -/* 495 */ +/* 499 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55472,12 +55698,12 @@ module.exports = arrify; /***/ }), -/* 496 */ +/* 500 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(497); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(501); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); /* @@ -55501,19 +55727,19 @@ __webpack_require__.r(__webpack_exports__); /***/ }), -/* 497 */ +/* 501 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(498); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(502); /* 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__(285); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(280); /* 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__(276); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(271); /* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(130); /* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(143); /* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(164); @@ -55649,7 +55875,7 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { } /***/ }), -/* 498 */ +/* 502 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55657,13 +55883,13 @@ async function copyToBuild(project, kibanaRoot, buildRoot) { const EventEmitter = __webpack_require__(155); const path = __webpack_require__(4); const os = __webpack_require__(120); -const pAll = __webpack_require__(499); -const arrify = __webpack_require__(501); -const globby = __webpack_require__(502); -const isGlob = __webpack_require__(700); -const cpFile = __webpack_require__(701); -const junk = __webpack_require__(713); -const CpyError = __webpack_require__(714); +const pAll = __webpack_require__(503); +const arrify = __webpack_require__(505); +const globby = __webpack_require__(506); +const isGlob = __webpack_require__(704); +const cpFile = __webpack_require__(705); +const junk = __webpack_require__(717); +const CpyError = __webpack_require__(718); const defaultOptions = { ignoreJunk: true @@ -55782,12 +56008,12 @@ module.exports = (source, destination, { /***/ }), -/* 499 */ +/* 503 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pMap = __webpack_require__(500); +const pMap = __webpack_require__(504); module.exports = (iterable, options) => pMap(iterable, element => element(), options); // TODO: Remove this for the next major release @@ -55795,7 +56021,7 @@ module.exports.default = module.exports; /***/ }), -/* 500 */ +/* 504 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55874,7 +56100,7 @@ module.exports.default = pMap; /***/ }), -/* 501 */ +/* 505 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -55904,17 +56130,17 @@ module.exports = arrify; /***/ }), -/* 502 */ +/* 506 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const arrayUnion = __webpack_require__(503); +const arrayUnion = __webpack_require__(507); const glob = __webpack_require__(146); -const fastGlob = __webpack_require__(505); -const dirGlob = __webpack_require__(693); -const gitignore = __webpack_require__(696); +const fastGlob = __webpack_require__(509); +const dirGlob = __webpack_require__(697); +const gitignore = __webpack_require__(700); const DEFAULT_FILTER = () => false; @@ -56059,12 +56285,12 @@ module.exports.gitignore = gitignore; /***/ }), -/* 503 */ +/* 507 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var arrayUniq = __webpack_require__(504); +var arrayUniq = __webpack_require__(508); module.exports = function () { return arrayUniq([].concat.apply([], arguments)); @@ -56072,7 +56298,7 @@ module.exports = function () { /***/ }), -/* 504 */ +/* 508 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56141,10 +56367,10 @@ if ('Set' in global) { /***/ }), -/* 505 */ +/* 509 */ /***/ (function(module, exports, __webpack_require__) { -const pkg = __webpack_require__(506); +const pkg = __webpack_require__(510); module.exports = pkg.async; module.exports.default = pkg.async; @@ -56157,19 +56383,19 @@ module.exports.generateTasks = pkg.generateTasks; /***/ }), -/* 506 */ +/* 510 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(507); -var taskManager = __webpack_require__(508); -var reader_async_1 = __webpack_require__(664); -var reader_stream_1 = __webpack_require__(688); -var reader_sync_1 = __webpack_require__(689); -var arrayUtils = __webpack_require__(691); -var streamUtils = __webpack_require__(692); +var optionsManager = __webpack_require__(511); +var taskManager = __webpack_require__(512); +var reader_async_1 = __webpack_require__(668); +var reader_stream_1 = __webpack_require__(692); +var reader_sync_1 = __webpack_require__(693); +var arrayUtils = __webpack_require__(695); +var streamUtils = __webpack_require__(696); /** * Synchronous API. */ @@ -56235,7 +56461,7 @@ function isString(source) { /***/ }), -/* 507 */ +/* 511 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56273,13 +56499,13 @@ exports.prepare = prepare; /***/ }), -/* 508 */ +/* 512 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(509); +var patternUtils = __webpack_require__(513); /** * Generate tasks based on parent directory of each pattern. */ @@ -56370,16 +56596,16 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 509 */ +/* 513 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var globParent = __webpack_require__(510); -var isGlob = __webpack_require__(513); -var micromatch = __webpack_require__(514); +var globParent = __webpack_require__(514); +var isGlob = __webpack_require__(517); +var micromatch = __webpack_require__(518); var GLOBSTAR = '**'; /** * Return true for static pattern. @@ -56525,15 +56751,15 @@ exports.matchAny = matchAny; /***/ }), -/* 510 */ +/* 514 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var path = __webpack_require__(4); -var isglob = __webpack_require__(511); -var pathDirname = __webpack_require__(512); +var isglob = __webpack_require__(515); +var pathDirname = __webpack_require__(516); var isWin32 = __webpack_require__(120).platform() === 'win32'; module.exports = function globParent(str) { @@ -56556,7 +56782,7 @@ module.exports = function globParent(str) { /***/ }), -/* 511 */ +/* 515 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -56566,7 +56792,7 @@ module.exports = function globParent(str) { * Licensed under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(294); module.exports = function isGlob(str) { if (typeof str !== 'string' || str === '') { @@ -56587,7 +56813,7 @@ module.exports = function isGlob(str) { /***/ }), -/* 512 */ +/* 516 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56737,7 +56963,7 @@ module.exports.win32 = win32; /***/ }), -/* 513 */ +/* 517 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -56747,7 +56973,7 @@ module.exports.win32 = win32; * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(294); var chars = { '{': '}', '(': ')', '[': ']'}; module.exports = function isGlob(str, options) { @@ -56789,7 +57015,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 514 */ +/* 518 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -56800,18 +57026,18 @@ module.exports = function isGlob(str, options) { */ var util = __webpack_require__(111); -var braces = __webpack_require__(515); -var toRegex = __webpack_require__(617); -var extend = __webpack_require__(625); +var braces = __webpack_require__(519); +var toRegex = __webpack_require__(621); +var extend = __webpack_require__(629); /** * Local dependencies */ -var compilers = __webpack_require__(628); -var parsers = __webpack_require__(660); -var cache = __webpack_require__(661); -var utils = __webpack_require__(662); +var compilers = __webpack_require__(632); +var parsers = __webpack_require__(664); +var cache = __webpack_require__(665); +var utils = __webpack_require__(666); var MAX_LENGTH = 1024 * 64; /** @@ -57673,7 +57899,7 @@ module.exports = micromatch; /***/ }), -/* 515 */ +/* 519 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -57683,18 +57909,18 @@ module.exports = micromatch; * Module dependencies */ -var toRegex = __webpack_require__(516); -var unique = __webpack_require__(528); -var extend = __webpack_require__(525); +var toRegex = __webpack_require__(520); +var unique = __webpack_require__(532); +var extend = __webpack_require__(529); /** * Local dependencies */ -var compilers = __webpack_require__(529); -var parsers = __webpack_require__(544); -var Braces = __webpack_require__(554); -var utils = __webpack_require__(530); +var compilers = __webpack_require__(533); +var parsers = __webpack_require__(548); +var Braces = __webpack_require__(558); +var utils = __webpack_require__(534); var MAX_LENGTH = 1024 * 64; var cache = {}; @@ -57998,15 +58224,15 @@ module.exports = braces; /***/ }), -/* 516 */ +/* 520 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(517); -var extend = __webpack_require__(525); -var not = __webpack_require__(527); +var define = __webpack_require__(521); +var extend = __webpack_require__(529); +var not = __webpack_require__(531); var MAX_LENGTH = 1024 * 64; /** @@ -58153,7 +58379,7 @@ module.exports.makeRe = makeRe; /***/ }), -/* 517 */ +/* 521 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58166,7 +58392,7 @@ module.exports.makeRe = makeRe; -var isDescriptor = __webpack_require__(518); +var isDescriptor = __webpack_require__(522); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -58191,7 +58417,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 518 */ +/* 522 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58204,9 +58430,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(519); -var isAccessor = __webpack_require__(520); -var isData = __webpack_require__(523); +var typeOf = __webpack_require__(523); +var isAccessor = __webpack_require__(524); +var isData = __webpack_require__(527); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -58220,7 +58446,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 519 */ +/* 523 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -58373,7 +58599,7 @@ function isBuffer(val) { /***/ }), -/* 520 */ +/* 524 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58386,7 +58612,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(521); +var typeOf = __webpack_require__(525); // accessor descriptor properties var accessor = { @@ -58449,10 +58675,10 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 521 */ +/* 525 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(522); +var isBuffer = __webpack_require__(526); var toString = Object.prototype.toString; /** @@ -58571,7 +58797,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 522 */ +/* 526 */ /***/ (function(module, exports) { /*! @@ -58598,7 +58824,7 @@ function isSlowBuffer (obj) { /***/ }), -/* 523 */ +/* 527 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58611,7 +58837,7 @@ function isSlowBuffer (obj) { -var typeOf = __webpack_require__(524); +var typeOf = __webpack_require__(528); // data descriptor properties var data = { @@ -58660,10 +58886,10 @@ module.exports = isDataDescriptor; /***/ }), -/* 524 */ +/* 528 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(522); +var isBuffer = __webpack_require__(526); var toString = Object.prototype.toString; /** @@ -58782,13 +59008,13 @@ module.exports = function kindOf(val) { /***/ }), -/* 525 */ +/* 529 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(526); +var isObject = __webpack_require__(530); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -58822,7 +59048,7 @@ function hasOwn(obj, key) { /***/ }), -/* 526 */ +/* 530 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58842,13 +59068,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 527 */ +/* 531 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(525); +var extend = __webpack_require__(529); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -58915,7 +59141,7 @@ module.exports = toRegex; /***/ }), -/* 528 */ +/* 532 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -58965,13 +59191,13 @@ module.exports.immutable = function uniqueImmutable(arr) { /***/ }), -/* 529 */ +/* 533 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(530); +var utils = __webpack_require__(534); module.exports = function(braces, options) { braces.compiler @@ -59254,25 +59480,25 @@ function hasQueue(node) { /***/ }), -/* 530 */ +/* 534 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(531); +var splitString = __webpack_require__(535); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(525); -utils.flatten = __webpack_require__(537); -utils.isObject = __webpack_require__(535); -utils.fillRange = __webpack_require__(538); -utils.repeat = __webpack_require__(543); -utils.unique = __webpack_require__(528); +utils.extend = __webpack_require__(529); +utils.flatten = __webpack_require__(541); +utils.isObject = __webpack_require__(539); +utils.fillRange = __webpack_require__(542); +utils.repeat = __webpack_require__(547); +utils.unique = __webpack_require__(532); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -59604,7 +59830,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 531 */ +/* 535 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59617,7 +59843,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(532); +var extend = __webpack_require__(536); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -59782,14 +60008,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 532 */ +/* 536 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(533); -var assignSymbols = __webpack_require__(536); +var isExtendable = __webpack_require__(537); +var assignSymbols = __webpack_require__(540); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -59849,7 +60075,7 @@ function isEnum(obj, key) { /***/ }), -/* 533 */ +/* 537 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59862,7 +60088,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(534); +var isPlainObject = __webpack_require__(538); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -59870,7 +60096,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 534 */ +/* 538 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59883,7 +60109,7 @@ module.exports = function isExtendable(val) { -var isObject = __webpack_require__(535); +var isObject = __webpack_require__(539); function isObjectObject(o) { return isObject(o) === true @@ -59914,7 +60140,7 @@ module.exports = function isPlainObject(o) { /***/ }), -/* 535 */ +/* 539 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59933,7 +60159,7 @@ module.exports = function isObject(val) { /***/ }), -/* 536 */ +/* 540 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59980,7 +60206,7 @@ module.exports = function(receiver, objects) { /***/ }), -/* 537 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60009,7 +60235,7 @@ function flat(arr, res) { /***/ }), -/* 538 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60023,10 +60249,10 @@ function flat(arr, res) { var util = __webpack_require__(111); -var isNumber = __webpack_require__(539); -var extend = __webpack_require__(525); -var repeat = __webpack_require__(541); -var toRegex = __webpack_require__(542); +var isNumber = __webpack_require__(543); +var extend = __webpack_require__(529); +var repeat = __webpack_require__(545); +var toRegex = __webpack_require__(546); /** * Return a range of numbers or letters. @@ -60224,7 +60450,7 @@ module.exports = fillRange; /***/ }), -/* 539 */ +/* 543 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60237,7 +60463,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(544); module.exports = function isNumber(num) { var type = typeOf(num); @@ -60253,10 +60479,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 540 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(522); +var isBuffer = __webpack_require__(526); var toString = Object.prototype.toString; /** @@ -60375,7 +60601,7 @@ module.exports = function kindOf(val) { /***/ }), -/* 541 */ +/* 545 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60452,7 +60678,7 @@ function repeat(str, num) { /***/ }), -/* 542 */ +/* 546 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60465,8 +60691,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(541); -var isNumber = __webpack_require__(539); +var repeat = __webpack_require__(545); +var isNumber = __webpack_require__(543); var cache = {}; function toRegexRange(min, max, options) { @@ -60753,7 +60979,7 @@ module.exports = toRegexRange; /***/ }), -/* 543 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60778,14 +61004,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 544 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(545); -var utils = __webpack_require__(530); +var Node = __webpack_require__(549); +var utils = __webpack_require__(534); /** * Braces parsers @@ -61145,15 +61371,15 @@ function concatNodes(pos, node, parent, options) { /***/ }), -/* 545 */ +/* 549 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(535); -var define = __webpack_require__(546); -var utils = __webpack_require__(553); +var isObject = __webpack_require__(539); +var define = __webpack_require__(550); +var utils = __webpack_require__(557); var ownNames; /** @@ -61644,7 +61870,7 @@ exports = module.exports = Node; /***/ }), -/* 546 */ +/* 550 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61657,7 +61883,7 @@ exports = module.exports = Node; -var isDescriptor = __webpack_require__(547); +var isDescriptor = __webpack_require__(551); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -61682,7 +61908,7 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 547 */ +/* 551 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61695,9 +61921,9 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(548); -var isAccessor = __webpack_require__(549); -var isData = __webpack_require__(551); +var typeOf = __webpack_require__(552); +var isAccessor = __webpack_require__(553); +var isData = __webpack_require__(555); module.exports = function isDescriptor(obj, key) { if (typeOf(obj) !== 'object') { @@ -61711,7 +61937,7 @@ module.exports = function isDescriptor(obj, key) { /***/ }), -/* 548 */ +/* 552 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -61846,7 +62072,7 @@ function isBuffer(val) { /***/ }), -/* 549 */ +/* 553 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61859,7 +62085,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(550); +var typeOf = __webpack_require__(554); // accessor descriptor properties var accessor = { @@ -61922,7 +62148,7 @@ module.exports = isAccessorDescriptor; /***/ }), -/* 550 */ +/* 554 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -62057,7 +62283,7 @@ function isBuffer(val) { /***/ }), -/* 551 */ +/* 555 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -62070,7 +62296,7 @@ function isBuffer(val) { -var typeOf = __webpack_require__(552); +var typeOf = __webpack_require__(556); module.exports = function isDataDescriptor(obj, prop) { // data descriptor properties @@ -62113,7 +62339,7 @@ module.exports = function isDataDescriptor(obj, prop) { /***/ }), -/* 552 */ +/* 556 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -62248,13 +62474,13 @@ function isBuffer(val) { /***/ }), -/* 553 */ +/* 557 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(544); var utils = module.exports; /** @@ -63274,17 +63500,17 @@ function assert(val, message) { /***/ }), -/* 554 */ +/* 558 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(525); -var Snapdragon = __webpack_require__(555); -var compilers = __webpack_require__(529); -var parsers = __webpack_require__(544); -var utils = __webpack_require__(530); +var extend = __webpack_require__(529); +var Snapdragon = __webpack_require__(559); +var compilers = __webpack_require__(533); +var parsers = __webpack_require__(548); +var utils = __webpack_require__(534); /** * Customize Snapdragon parser and renderer @@ -63385,17 +63611,17 @@ module.exports = Braces; /***/ }), -/* 555 */ +/* 559 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Base = __webpack_require__(556); -var define = __webpack_require__(517); -var Compiler = __webpack_require__(585); -var Parser = __webpack_require__(614); -var utils = __webpack_require__(594); +var Base = __webpack_require__(560); +var define = __webpack_require__(521); +var Compiler = __webpack_require__(589); +var Parser = __webpack_require__(618); +var utils = __webpack_require__(598); var regexCache = {}; var cache = {}; @@ -63566,20 +63792,20 @@ module.exports.Parser = Parser; /***/ }), -/* 556 */ +/* 560 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(111); -var define = __webpack_require__(557); -var CacheBase = __webpack_require__(558); -var Emitter = __webpack_require__(559); -var isObject = __webpack_require__(535); -var merge = __webpack_require__(576); -var pascal = __webpack_require__(579); -var cu = __webpack_require__(580); +var define = __webpack_require__(561); +var CacheBase = __webpack_require__(562); +var Emitter = __webpack_require__(563); +var isObject = __webpack_require__(539); +var merge = __webpack_require__(580); +var pascal = __webpack_require__(583); +var cu = __webpack_require__(584); /** * Optionally define a custom `cache` namespace to use. @@ -64008,7 +64234,7 @@ module.exports.namespace = namespace; /***/ }), -/* 557 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64021,7 +64247,7 @@ module.exports.namespace = namespace; -var isDescriptor = __webpack_require__(547); +var isDescriptor = __webpack_require__(551); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -64046,21 +64272,21 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 558 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(535); -var Emitter = __webpack_require__(559); -var visit = __webpack_require__(560); -var toPath = __webpack_require__(563); -var union = __webpack_require__(564); -var del = __webpack_require__(568); -var get = __webpack_require__(566); -var has = __webpack_require__(573); -var set = __webpack_require__(567); +var isObject = __webpack_require__(539); +var Emitter = __webpack_require__(563); +var visit = __webpack_require__(564); +var toPath = __webpack_require__(567); +var union = __webpack_require__(568); +var del = __webpack_require__(572); +var get = __webpack_require__(570); +var has = __webpack_require__(577); +var set = __webpack_require__(571); /** * Create a `Cache` constructor that when instantiated will @@ -64314,7 +64540,7 @@ module.exports.namespace = namespace; /***/ }), -/* 559 */ +/* 563 */ /***/ (function(module, exports, __webpack_require__) { @@ -64483,7 +64709,7 @@ Emitter.prototype.hasListeners = function(event){ /***/ }), -/* 560 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64496,8 +64722,8 @@ Emitter.prototype.hasListeners = function(event){ -var visit = __webpack_require__(561); -var mapVisit = __webpack_require__(562); +var visit = __webpack_require__(565); +var mapVisit = __webpack_require__(566); module.exports = function(collection, method, val) { var result; @@ -64520,7 +64746,7 @@ module.exports = function(collection, method, val) { /***/ }), -/* 561 */ +/* 565 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64533,7 +64759,7 @@ module.exports = function(collection, method, val) { -var isObject = __webpack_require__(535); +var isObject = __webpack_require__(539); module.exports = function visit(thisArg, method, target, val) { if (!isObject(thisArg) && typeof thisArg !== 'function') { @@ -64560,14 +64786,14 @@ module.exports = function visit(thisArg, method, target, val) { /***/ }), -/* 562 */ +/* 566 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(111); -var visit = __webpack_require__(561); +var visit = __webpack_require__(565); /** * Map `visit` over an array of objects. @@ -64604,7 +64830,7 @@ function isObject(val) { /***/ }), -/* 563 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64617,7 +64843,7 @@ function isObject(val) { -var typeOf = __webpack_require__(540); +var typeOf = __webpack_require__(544); module.exports = function toPath(args) { if (typeOf(args) !== 'arguments') { @@ -64644,16 +64870,16 @@ function filter(arr) { /***/ }), -/* 564 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(526); -var union = __webpack_require__(565); -var get = __webpack_require__(566); -var set = __webpack_require__(567); +var isObject = __webpack_require__(530); +var union = __webpack_require__(569); +var get = __webpack_require__(570); +var set = __webpack_require__(571); module.exports = function unionValue(obj, prop, value) { if (!isObject(obj)) { @@ -64681,7 +64907,7 @@ function arrayify(val) { /***/ }), -/* 565 */ +/* 569 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64717,7 +64943,7 @@ module.exports = function union(init) { /***/ }), -/* 566 */ +/* 570 */ /***/ (function(module, exports) { /*! @@ -64773,7 +64999,7 @@ function toString(val) { /***/ }), -/* 567 */ +/* 571 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64786,10 +65012,10 @@ function toString(val) { -var split = __webpack_require__(531); -var extend = __webpack_require__(525); -var isPlainObject = __webpack_require__(534); -var isObject = __webpack_require__(526); +var split = __webpack_require__(535); +var extend = __webpack_require__(529); +var isPlainObject = __webpack_require__(538); +var isObject = __webpack_require__(530); module.exports = function(obj, prop, val) { if (!isObject(obj)) { @@ -64835,7 +65061,7 @@ function isValidKey(key) { /***/ }), -/* 568 */ +/* 572 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64848,8 +65074,8 @@ function isValidKey(key) { -var isObject = __webpack_require__(535); -var has = __webpack_require__(569); +var isObject = __webpack_require__(539); +var has = __webpack_require__(573); module.exports = function unset(obj, prop) { if (!isObject(obj)) { @@ -64874,7 +65100,7 @@ module.exports = function unset(obj, prop) { /***/ }), -/* 569 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64887,9 +65113,9 @@ module.exports = function unset(obj, prop) { -var isObject = __webpack_require__(570); -var hasValues = __webpack_require__(572); -var get = __webpack_require__(566); +var isObject = __webpack_require__(574); +var hasValues = __webpack_require__(576); +var get = __webpack_require__(570); module.exports = function(obj, prop, noZero) { if (isObject(obj)) { @@ -64900,7 +65126,7 @@ module.exports = function(obj, prop, noZero) { /***/ }), -/* 570 */ +/* 574 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64913,7 +65139,7 @@ module.exports = function(obj, prop, noZero) { -var isArray = __webpack_require__(571); +var isArray = __webpack_require__(575); module.exports = function isObject(val) { return val != null && typeof val === 'object' && isArray(val) === false; @@ -64921,7 +65147,7 @@ module.exports = function isObject(val) { /***/ }), -/* 571 */ +/* 575 */ /***/ (function(module, exports) { var toString = {}.toString; @@ -64932,7 +65158,7 @@ module.exports = Array.isArray || function (arr) { /***/ }), -/* 572 */ +/* 576 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64975,7 +65201,7 @@ module.exports = function hasValue(o, noZero) { /***/ }), -/* 573 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -64988,9 +65214,9 @@ module.exports = function hasValue(o, noZero) { -var isObject = __webpack_require__(535); -var hasValues = __webpack_require__(574); -var get = __webpack_require__(566); +var isObject = __webpack_require__(539); +var hasValues = __webpack_require__(578); +var get = __webpack_require__(570); module.exports = function(val, prop) { return hasValues(isObject(val) && prop ? get(val, prop) : val); @@ -64998,7 +65224,7 @@ module.exports = function(val, prop) { /***/ }), -/* 574 */ +/* 578 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65011,8 +65237,8 @@ module.exports = function(val, prop) { -var typeOf = __webpack_require__(575); -var isNumber = __webpack_require__(539); +var typeOf = __webpack_require__(579); +var isNumber = __webpack_require__(543); module.exports = function hasValue(val) { // is-number checks for NaN and other edge cases @@ -65065,10 +65291,10 @@ module.exports = function hasValue(val) { /***/ }), -/* 575 */ +/* 579 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(522); +var isBuffer = __webpack_require__(526); var toString = Object.prototype.toString; /** @@ -65190,14 +65416,14 @@ module.exports = function kindOf(val) { /***/ }), -/* 576 */ +/* 580 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(577); -var forIn = __webpack_require__(578); +var isExtendable = __webpack_require__(581); +var forIn = __webpack_require__(582); function mixinDeep(target, objects) { var len = arguments.length, i = 0; @@ -65261,7 +65487,7 @@ module.exports = mixinDeep; /***/ }), -/* 577 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65274,7 +65500,7 @@ module.exports = mixinDeep; -var isPlainObject = __webpack_require__(534); +var isPlainObject = __webpack_require__(538); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -65282,7 +65508,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 578 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65305,7 +65531,7 @@ module.exports = function forIn(obj, fn, thisArg) { /***/ }), -/* 579 */ +/* 583 */ /***/ (function(module, exports) { /*! @@ -65332,14 +65558,14 @@ module.exports = pascalcase; /***/ }), -/* 580 */ +/* 584 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var util = __webpack_require__(111); -var utils = __webpack_require__(581); +var utils = __webpack_require__(585); /** * Expose class utils @@ -65704,7 +65930,7 @@ cu.bubble = function(Parent, events) { /***/ }), -/* 581 */ +/* 585 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65718,10 +65944,10 @@ var utils = {}; * Lazily required module dependencies */ -utils.union = __webpack_require__(565); -utils.define = __webpack_require__(517); -utils.isObj = __webpack_require__(535); -utils.staticExtend = __webpack_require__(582); +utils.union = __webpack_require__(569); +utils.define = __webpack_require__(521); +utils.isObj = __webpack_require__(539); +utils.staticExtend = __webpack_require__(586); /** @@ -65732,7 +65958,7 @@ module.exports = utils; /***/ }), -/* 582 */ +/* 586 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65745,8 +65971,8 @@ module.exports = utils; -var copy = __webpack_require__(583); -var define = __webpack_require__(517); +var copy = __webpack_require__(587); +var define = __webpack_require__(521); var util = __webpack_require__(111); /** @@ -65829,15 +66055,15 @@ module.exports = extend; /***/ }), -/* 583 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(540); -var copyDescriptor = __webpack_require__(584); -var define = __webpack_require__(517); +var typeOf = __webpack_require__(544); +var copyDescriptor = __webpack_require__(588); +var define = __webpack_require__(521); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -66010,7 +66236,7 @@ module.exports.has = has; /***/ }), -/* 584 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66098,16 +66324,16 @@ function isObject(val) { /***/ }), -/* 585 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(586); -var define = __webpack_require__(517); -var debug = __webpack_require__(588)('snapdragon:compiler'); -var utils = __webpack_require__(594); +var use = __webpack_require__(590); +var define = __webpack_require__(521); +var debug = __webpack_require__(592)('snapdragon:compiler'); +var utils = __webpack_require__(598); /** * Create a new `Compiler` with the given `options`. @@ -66261,7 +66487,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(613); + var sourcemaps = __webpack_require__(617); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -66282,7 +66508,7 @@ module.exports = Compiler; /***/ }), -/* 586 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66295,7 +66521,7 @@ module.exports = Compiler; -var utils = __webpack_require__(587); +var utils = __webpack_require__(591); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -66410,7 +66636,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 587 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66424,8 +66650,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(517); -utils.isObject = __webpack_require__(535); +utils.define = __webpack_require__(521); +utils.isObject = __webpack_require__(539); utils.isString = function(val) { @@ -66440,7 +66666,7 @@ module.exports = utils; /***/ }), -/* 588 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -66449,14 +66675,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(589); + module.exports = __webpack_require__(593); } else { - module.exports = __webpack_require__(592); + module.exports = __webpack_require__(596); } /***/ }), -/* 589 */ +/* 593 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -66465,7 +66691,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(590); +exports = module.exports = __webpack_require__(594); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -66647,7 +66873,7 @@ function localstorage() { /***/ }), -/* 590 */ +/* 594 */ /***/ (function(module, exports, __webpack_require__) { @@ -66663,7 +66889,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(591); +exports.humanize = __webpack_require__(595); /** * The currently active debug mode names, and names to skip. @@ -66855,7 +67081,7 @@ function coerce(val) { /***/ }), -/* 591 */ +/* 595 */ /***/ (function(module, exports) { /** @@ -67013,7 +67239,7 @@ function plural(ms, n, name) { /***/ }), -/* 592 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -67029,7 +67255,7 @@ var util = __webpack_require__(111); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(590); +exports = module.exports = __webpack_require__(594); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -67208,7 +67434,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(593); + var net = __webpack_require__(597); stream = new net.Socket({ fd: fd, readable: false, @@ -67267,13 +67493,13 @@ exports.enable(load()); /***/ }), -/* 593 */ +/* 597 */ /***/ (function(module, exports) { module.exports = require("net"); /***/ }), -/* 594 */ +/* 598 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -67283,9 +67509,9 @@ module.exports = require("net"); * Module dependencies */ -exports.extend = __webpack_require__(525); -exports.SourceMap = __webpack_require__(595); -exports.sourceMapResolve = __webpack_require__(606); +exports.extend = __webpack_require__(529); +exports.SourceMap = __webpack_require__(599); +exports.sourceMapResolve = __webpack_require__(610); /** * Convert backslash in the given string to forward slashes @@ -67328,7 +67554,7 @@ exports.last = function(arr, n) { /***/ }), -/* 595 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { /* @@ -67336,13 +67562,13 @@ exports.last = function(arr, n) { * Licensed under the New BSD license. See LICENSE.txt or: * http://opensource.org/licenses/BSD-3-Clause */ -exports.SourceMapGenerator = __webpack_require__(596).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(602).SourceMapConsumer; -exports.SourceNode = __webpack_require__(605).SourceNode; +exports.SourceMapGenerator = __webpack_require__(600).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(606).SourceMapConsumer; +exports.SourceNode = __webpack_require__(609).SourceNode; /***/ }), -/* 596 */ +/* 600 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -67352,10 +67578,10 @@ exports.SourceNode = __webpack_require__(605).SourceNode; * http://opensource.org/licenses/BSD-3-Clause */ -var base64VLQ = __webpack_require__(597); -var util = __webpack_require__(599); -var ArraySet = __webpack_require__(600).ArraySet; -var MappingList = __webpack_require__(601).MappingList; +var base64VLQ = __webpack_require__(601); +var util = __webpack_require__(603); +var ArraySet = __webpack_require__(604).ArraySet; +var MappingList = __webpack_require__(605).MappingList; /** * An instance of the SourceMapGenerator represents a source map which is @@ -67764,7 +67990,7 @@ exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 597 */ +/* 601 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -67804,7 +68030,7 @@ exports.SourceMapGenerator = SourceMapGenerator; * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var base64 = __webpack_require__(598); +var base64 = __webpack_require__(602); // 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, @@ -67910,7 +68136,7 @@ exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { /***/ }), -/* 598 */ +/* 602 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -67983,7 +68209,7 @@ exports.decode = function (charCode) { /***/ }), -/* 599 */ +/* 603 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68406,7 +68632,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate /***/ }), -/* 600 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68416,7 +68642,7 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(599); +var util = __webpack_require__(603); var has = Object.prototype.hasOwnProperty; var hasNativeMap = typeof Map !== "undefined"; @@ -68533,7 +68759,7 @@ exports.ArraySet = ArraySet; /***/ }), -/* 601 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68543,7 +68769,7 @@ exports.ArraySet = ArraySet; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(599); +var util = __webpack_require__(603); /** * Determine whether mappingB is after mappingA with respect to generated @@ -68618,7 +68844,7 @@ exports.MappingList = MappingList; /***/ }), -/* 602 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68628,11 +68854,11 @@ exports.MappingList = MappingList; * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(599); -var binarySearch = __webpack_require__(603); -var ArraySet = __webpack_require__(600).ArraySet; -var base64VLQ = __webpack_require__(597); -var quickSort = __webpack_require__(604).quickSort; +var util = __webpack_require__(603); +var binarySearch = __webpack_require__(607); +var ArraySet = __webpack_require__(604).ArraySet; +var base64VLQ = __webpack_require__(601); +var quickSort = __webpack_require__(608).quickSort; function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; @@ -69706,7 +69932,7 @@ exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; /***/ }), -/* 603 */ +/* 607 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -69823,7 +70049,7 @@ exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { /***/ }), -/* 604 */ +/* 608 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -69943,7 +70169,7 @@ exports.quickSort = function (ary, comparator) { /***/ }), -/* 605 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -69953,8 +70179,8 @@ exports.quickSort = function (ary, comparator) { * http://opensource.org/licenses/BSD-3-Clause */ -var SourceMapGenerator = __webpack_require__(596).SourceMapGenerator; -var util = __webpack_require__(599); +var SourceMapGenerator = __webpack_require__(600).SourceMapGenerator; +var util = __webpack_require__(603); // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other // operating systems these days (capturing the result). @@ -70362,17 +70588,17 @@ exports.SourceNode = SourceNode; /***/ }), -/* 606 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014, 2015, 2016, 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var sourceMappingURL = __webpack_require__(607) -var resolveUrl = __webpack_require__(608) -var decodeUriComponent = __webpack_require__(609) -var urix = __webpack_require__(611) -var atob = __webpack_require__(612) +var sourceMappingURL = __webpack_require__(611) +var resolveUrl = __webpack_require__(612) +var decodeUriComponent = __webpack_require__(613) +var urix = __webpack_require__(615) +var atob = __webpack_require__(616) @@ -70670,7 +70896,7 @@ module.exports = { /***/ }), -/* 607 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell @@ -70733,7 +70959,7 @@ void (function(root, factory) { /***/ }), -/* 608 */ +/* 612 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -70751,13 +70977,13 @@ module.exports = resolveUrl /***/ }), -/* 609 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2017 Simon Lydell // X11 (“MIT”) Licensed. (See LICENSE.) -var decodeUriComponent = __webpack_require__(610) +var decodeUriComponent = __webpack_require__(614) function customDecodeUriComponent(string) { // `decodeUriComponent` turns `+` into ` `, but that's not wanted. @@ -70768,7 +70994,7 @@ module.exports = customDecodeUriComponent /***/ }), -/* 610 */ +/* 614 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70869,7 +71095,7 @@ module.exports = function (encodedURI) { /***/ }), -/* 611 */ +/* 615 */ /***/ (function(module, exports, __webpack_require__) { // Copyright 2014 Simon Lydell @@ -70892,7 +71118,7 @@ module.exports = urix /***/ }), -/* 612 */ +/* 616 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70906,7 +71132,7 @@ module.exports = atob.atob = atob; /***/ }), -/* 613 */ +/* 617 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -70914,8 +71140,8 @@ module.exports = atob.atob = atob; var fs = __webpack_require__(133); var path = __webpack_require__(4); -var define = __webpack_require__(517); -var utils = __webpack_require__(594); +var define = __webpack_require__(521); +var utils = __webpack_require__(598); /** * Expose `mixin()`. @@ -71058,19 +71284,19 @@ exports.comment = function(node) { /***/ }), -/* 614 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(586); +var use = __webpack_require__(590); var util = __webpack_require__(111); -var Cache = __webpack_require__(615); -var define = __webpack_require__(517); -var debug = __webpack_require__(588)('snapdragon:parser'); -var Position = __webpack_require__(616); -var utils = __webpack_require__(594); +var Cache = __webpack_require__(619); +var define = __webpack_require__(521); +var debug = __webpack_require__(592)('snapdragon:parser'); +var Position = __webpack_require__(620); +var utils = __webpack_require__(598); /** * Create a new `Parser` with the given `input` and `options`. @@ -71598,7 +71824,7 @@ module.exports = Parser; /***/ }), -/* 615 */ +/* 619 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -71705,13 +71931,13 @@ MapCache.prototype.del = function mapDelete(key) { /***/ }), -/* 616 */ +/* 620 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var define = __webpack_require__(517); +var define = __webpack_require__(521); /** * Store position for a node @@ -71726,16 +71952,16 @@ module.exports = function Position(start, parser) { /***/ }), -/* 617 */ +/* 621 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(618); -var define = __webpack_require__(624); -var extend = __webpack_require__(625); -var not = __webpack_require__(627); +var safe = __webpack_require__(622); +var define = __webpack_require__(628); +var extend = __webpack_require__(629); +var not = __webpack_require__(631); var MAX_LENGTH = 1024 * 64; /** @@ -71888,10 +72114,10 @@ module.exports.makeRe = makeRe; /***/ }), -/* 618 */ +/* 622 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(619); +var parse = __webpack_require__(623); var types = parse.types; module.exports = function (re, opts) { @@ -71937,13 +72163,13 @@ function isRegExp (x) { /***/ }), -/* 619 */ +/* 623 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(620); -var types = __webpack_require__(621); -var sets = __webpack_require__(622); -var positions = __webpack_require__(623); +var util = __webpack_require__(624); +var types = __webpack_require__(625); +var sets = __webpack_require__(626); +var positions = __webpack_require__(627); module.exports = function(regexpStr) { @@ -72225,11 +72451,11 @@ module.exports.types = types; /***/ }), -/* 620 */ +/* 624 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(621); -var sets = __webpack_require__(622); +var types = __webpack_require__(625); +var sets = __webpack_require__(626); // All of these are private and only used by randexp. @@ -72342,7 +72568,7 @@ exports.error = function(regexp, msg) { /***/ }), -/* 621 */ +/* 625 */ /***/ (function(module, exports) { module.exports = { @@ -72358,10 +72584,10 @@ module.exports = { /***/ }), -/* 622 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(621); +var types = __webpack_require__(625); var INTS = function() { return [{ type: types.RANGE , from: 48, to: 57 }]; @@ -72446,10 +72672,10 @@ exports.anyChar = function() { /***/ }), -/* 623 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(621); +var types = __webpack_require__(625); exports.wordBoundary = function() { return { type: types.POSITION, value: 'b' }; @@ -72469,7 +72695,7 @@ exports.end = function() { /***/ }), -/* 624 */ +/* 628 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72482,8 +72708,8 @@ exports.end = function() { -var isobject = __webpack_require__(535); -var isDescriptor = __webpack_require__(547); +var isobject = __webpack_require__(539); +var isDescriptor = __webpack_require__(551); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -72514,14 +72740,14 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 625 */ +/* 629 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(626); -var assignSymbols = __webpack_require__(536); +var isExtendable = __webpack_require__(630); +var assignSymbols = __webpack_require__(540); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -72581,7 +72807,7 @@ function isEnum(obj, key) { /***/ }), -/* 626 */ +/* 630 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72594,7 +72820,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(534); +var isPlainObject = __webpack_require__(538); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -72602,14 +72828,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 627 */ +/* 631 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extend = __webpack_require__(625); -var safe = __webpack_require__(618); +var extend = __webpack_require__(629); +var safe = __webpack_require__(622); /** * The main export is a function that takes a `pattern` string and an `options` object. @@ -72681,14 +72907,14 @@ module.exports = toRegex; /***/ }), -/* 628 */ +/* 632 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(629); -var extglob = __webpack_require__(644); +var nanomatch = __webpack_require__(633); +var extglob = __webpack_require__(648); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -72765,7 +72991,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 629 */ +/* 633 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72776,17 +73002,17 @@ function escapeExtglobs(compiler) { */ var util = __webpack_require__(111); -var toRegex = __webpack_require__(516); -var extend = __webpack_require__(630); +var toRegex = __webpack_require__(520); +var extend = __webpack_require__(634); /** * Local dependencies */ -var compilers = __webpack_require__(632); -var parsers = __webpack_require__(633); -var cache = __webpack_require__(636); -var utils = __webpack_require__(638); +var compilers = __webpack_require__(636); +var parsers = __webpack_require__(637); +var cache = __webpack_require__(640); +var utils = __webpack_require__(642); var MAX_LENGTH = 1024 * 64; /** @@ -73610,14 +73836,14 @@ module.exports = nanomatch; /***/ }), -/* 630 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(631); -var assignSymbols = __webpack_require__(536); +var isExtendable = __webpack_require__(635); +var assignSymbols = __webpack_require__(540); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -73677,7 +73903,7 @@ function isEnum(obj, key) { /***/ }), -/* 631 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73690,7 +73916,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(534); +var isPlainObject = __webpack_require__(538); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -73698,7 +73924,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 632 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74044,15 +74270,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 633 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(527); -var toRegex = __webpack_require__(516); -var isOdd = __webpack_require__(634); +var regexNot = __webpack_require__(531); +var toRegex = __webpack_require__(520); +var isOdd = __webpack_require__(638); /** * Characters to use in negation regex (we want to "not" match @@ -74438,7 +74664,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 634 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74451,7 +74677,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(635); +var isNumber = __webpack_require__(639); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -74465,7 +74691,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 635 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74493,14 +74719,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 636 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(637))(); +module.exports = new (__webpack_require__(641))(); /***/ }), -/* 637 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74513,7 +74739,7 @@ module.exports = new (__webpack_require__(637))(); -var MapCache = __webpack_require__(615); +var MapCache = __webpack_require__(619); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -74635,7 +74861,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 638 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74648,14 +74874,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(639)(); -var Snapdragon = __webpack_require__(555); -utils.define = __webpack_require__(640); -utils.diff = __webpack_require__(641); -utils.extend = __webpack_require__(630); -utils.pick = __webpack_require__(642); -utils.typeOf = __webpack_require__(643); -utils.unique = __webpack_require__(528); +var isWindows = __webpack_require__(643)(); +var Snapdragon = __webpack_require__(559); +utils.define = __webpack_require__(644); +utils.diff = __webpack_require__(645); +utils.extend = __webpack_require__(634); +utils.pick = __webpack_require__(646); +utils.typeOf = __webpack_require__(647); +utils.unique = __webpack_require__(532); /** * Returns true if the given value is effectively an empty string @@ -75021,7 +75247,7 @@ utils.unixify = function(options) { /***/ }), -/* 639 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -75049,7 +75275,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 640 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75062,8 +75288,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(535); -var isDescriptor = __webpack_require__(547); +var isobject = __webpack_require__(539); +var isDescriptor = __webpack_require__(551); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -75094,7 +75320,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 641 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75148,7 +75374,7 @@ function diffArray(one, two) { /***/ }), -/* 642 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75161,7 +75387,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(535); +var isObject = __webpack_require__(539); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -75190,7 +75416,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 643 */ +/* 647 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -75325,7 +75551,7 @@ function isBuffer(val) { /***/ }), -/* 644 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75335,18 +75561,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(525); -var unique = __webpack_require__(528); -var toRegex = __webpack_require__(516); +var extend = __webpack_require__(529); +var unique = __webpack_require__(532); +var toRegex = __webpack_require__(520); /** * Local dependencies */ -var compilers = __webpack_require__(645); -var parsers = __webpack_require__(656); -var Extglob = __webpack_require__(659); -var utils = __webpack_require__(658); +var compilers = __webpack_require__(649); +var parsers = __webpack_require__(660); +var Extglob = __webpack_require__(663); +var utils = __webpack_require__(662); var MAX_LENGTH = 1024 * 64; /** @@ -75663,13 +75889,13 @@ module.exports = extglob; /***/ }), -/* 645 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(646); +var brackets = __webpack_require__(650); /** * Extglob compilers @@ -75839,7 +76065,7 @@ module.exports = function(extglob) { /***/ }), -/* 646 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75849,17 +76075,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(647); -var parsers = __webpack_require__(649); +var compilers = __webpack_require__(651); +var parsers = __webpack_require__(653); /** * Module dependencies */ -var debug = __webpack_require__(651)('expand-brackets'); -var extend = __webpack_require__(525); -var Snapdragon = __webpack_require__(555); -var toRegex = __webpack_require__(516); +var debug = __webpack_require__(655)('expand-brackets'); +var extend = __webpack_require__(529); +var Snapdragon = __webpack_require__(559); +var toRegex = __webpack_require__(520); /** * Parses the given POSIX character class `pattern` and returns a @@ -76057,13 +76283,13 @@ module.exports = brackets; /***/ }), -/* 647 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(648); +var posix = __webpack_require__(652); module.exports = function(brackets) { brackets.compiler @@ -76151,7 +76377,7 @@ module.exports = function(brackets) { /***/ }), -/* 648 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76180,14 +76406,14 @@ module.exports = { /***/ }), -/* 649 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(650); -var define = __webpack_require__(517); +var utils = __webpack_require__(654); +var define = __webpack_require__(521); /** * Text regex @@ -76406,14 +76632,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 650 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(516); -var regexNot = __webpack_require__(527); +var toRegex = __webpack_require__(520); +var regexNot = __webpack_require__(531); var cached; /** @@ -76447,7 +76673,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 651 */ +/* 655 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -76456,14 +76682,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(652); + module.exports = __webpack_require__(656); } else { - module.exports = __webpack_require__(655); + module.exports = __webpack_require__(659); } /***/ }), -/* 652 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -76472,7 +76698,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(653); +exports = module.exports = __webpack_require__(657); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -76654,7 +76880,7 @@ function localstorage() { /***/ }), -/* 653 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { @@ -76670,7 +76896,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(654); +exports.humanize = __webpack_require__(658); /** * The currently active debug mode names, and names to skip. @@ -76862,7 +77088,7 @@ function coerce(val) { /***/ }), -/* 654 */ +/* 658 */ /***/ (function(module, exports) { /** @@ -77020,7 +77246,7 @@ function plural(ms, n, name) { /***/ }), -/* 655 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -77036,7 +77262,7 @@ var util = __webpack_require__(111); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(653); +exports = module.exports = __webpack_require__(657); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -77215,7 +77441,7 @@ function createWritableStdioStream (fd) { case 'PIPE': case 'TCP': - var net = __webpack_require__(593); + var net = __webpack_require__(597); stream = new net.Socket({ fd: fd, readable: false, @@ -77274,15 +77500,15 @@ exports.enable(load()); /***/ }), -/* 656 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(646); -var define = __webpack_require__(657); -var utils = __webpack_require__(658); +var brackets = __webpack_require__(650); +var define = __webpack_require__(661); +var utils = __webpack_require__(662); /** * Characters to use in text regex (we want to "not" match @@ -77437,7 +77663,7 @@ module.exports = parsers; /***/ }), -/* 657 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77450,7 +77676,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(547); +var isDescriptor = __webpack_require__(551); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -77475,14 +77701,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 658 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(527); -var Cache = __webpack_require__(637); +var regex = __webpack_require__(531); +var Cache = __webpack_require__(641); /** * Utils @@ -77551,7 +77777,7 @@ utils.createRegex = function(str) { /***/ }), -/* 659 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77561,16 +77787,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(555); -var define = __webpack_require__(657); -var extend = __webpack_require__(525); +var Snapdragon = __webpack_require__(559); +var define = __webpack_require__(661); +var extend = __webpack_require__(529); /** * Local dependencies */ -var compilers = __webpack_require__(645); -var parsers = __webpack_require__(656); +var compilers = __webpack_require__(649); +var parsers = __webpack_require__(660); /** * Customize Snapdragon parser and renderer @@ -77636,16 +77862,16 @@ module.exports = Extglob; /***/ }), -/* 660 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(644); -var nanomatch = __webpack_require__(629); -var regexNot = __webpack_require__(527); -var toRegex = __webpack_require__(617); +var extglob = __webpack_require__(648); +var nanomatch = __webpack_require__(633); +var regexNot = __webpack_require__(531); +var toRegex = __webpack_require__(621); var not; /** @@ -77726,14 +77952,14 @@ function textRegex(pattern) { /***/ }), -/* 661 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(637))(); +module.exports = new (__webpack_require__(641))(); /***/ }), -/* 662 */ +/* 666 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77746,13 +77972,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(555); -utils.define = __webpack_require__(624); -utils.diff = __webpack_require__(641); -utils.extend = __webpack_require__(625); -utils.pick = __webpack_require__(642); -utils.typeOf = __webpack_require__(663); -utils.unique = __webpack_require__(528); +var Snapdragon = __webpack_require__(559); +utils.define = __webpack_require__(628); +utils.diff = __webpack_require__(645); +utils.extend = __webpack_require__(629); +utils.pick = __webpack_require__(646); +utils.typeOf = __webpack_require__(667); +utils.unique = __webpack_require__(532); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -78049,7 +78275,7 @@ utils.unixify = function(options) { /***/ }), -/* 663 */ +/* 667 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -78184,7 +78410,7 @@ function isBuffer(val) { /***/ }), -/* 664 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78203,9 +78429,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(665); -var reader_1 = __webpack_require__(678); -var fs_stream_1 = __webpack_require__(682); +var readdir = __webpack_require__(669); +var reader_1 = __webpack_require__(682); +var fs_stream_1 = __webpack_require__(686); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -78266,15 +78492,15 @@ exports.default = ReaderAsync; /***/ }), -/* 665 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(666); -const readdirAsync = __webpack_require__(674); -const readdirStream = __webpack_require__(677); +const readdirSync = __webpack_require__(670); +const readdirAsync = __webpack_require__(678); +const readdirStream = __webpack_require__(681); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -78358,7 +78584,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 666 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78366,11 +78592,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(667); +const DirectoryReader = __webpack_require__(671); let syncFacade = { - fs: __webpack_require__(672), - forEach: __webpack_require__(673), + fs: __webpack_require__(676), + forEach: __webpack_require__(677), sync: true }; @@ -78399,7 +78625,7 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 667 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78408,9 +78634,9 @@ function readdirSync (dir, options, internalOptions) { const Readable = __webpack_require__(137).Readable; const EventEmitter = __webpack_require__(155).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(668); -const stat = __webpack_require__(670); -const call = __webpack_require__(671); +const normalizeOptions = __webpack_require__(672); +const stat = __webpack_require__(674); +const call = __webpack_require__(675); /** * Asynchronously reads the contents of a directory and streams the results @@ -78786,14 +79012,14 @@ module.exports = DirectoryReader; /***/ }), -/* 668 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(669); +const globToRegExp = __webpack_require__(673); module.exports = normalizeOptions; @@ -78970,7 +79196,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 669 */ +/* 673 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -79107,13 +79333,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 670 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(671); +const call = __webpack_require__(675); module.exports = stat; @@ -79188,7 +79414,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 671 */ +/* 675 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79249,14 +79475,14 @@ function callOnce (fn) { /***/ }), -/* 672 */ +/* 676 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const call = __webpack_require__(671); +const call = __webpack_require__(675); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -79320,7 +79546,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 673 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79349,7 +79575,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 674 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79357,12 +79583,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(675); -const DirectoryReader = __webpack_require__(667); +const maybe = __webpack_require__(679); +const DirectoryReader = __webpack_require__(671); let asyncFacade = { fs: __webpack_require__(133), - forEach: __webpack_require__(676), + forEach: __webpack_require__(680), async: true }; @@ -79404,7 +79630,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 675 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79431,7 +79657,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 676 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79467,7 +79693,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 677 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79475,11 +79701,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(667); +const DirectoryReader = __webpack_require__(671); let streamFacade = { fs: __webpack_require__(133), - forEach: __webpack_require__(676), + forEach: __webpack_require__(680), async: true }; @@ -79499,16 +79725,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 678 */ +/* 682 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(679); -var entry_1 = __webpack_require__(681); -var pathUtil = __webpack_require__(680); +var deep_1 = __webpack_require__(683); +var entry_1 = __webpack_require__(685); +var pathUtil = __webpack_require__(684); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -79574,14 +79800,14 @@ exports.default = Reader; /***/ }), -/* 679 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(680); -var patternUtils = __webpack_require__(509); +var pathUtils = __webpack_require__(684); +var patternUtils = __webpack_require__(513); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -79664,7 +79890,7 @@ exports.default = DeepFilter; /***/ }), -/* 680 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79695,14 +79921,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 681 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(680); -var patternUtils = __webpack_require__(509); +var pathUtils = __webpack_require__(684); +var patternUtils = __webpack_require__(513); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -79787,7 +80013,7 @@ exports.default = EntryFilter; /***/ }), -/* 682 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79807,8 +80033,8 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(137); -var fsStat = __webpack_require__(683); -var fs_1 = __webpack_require__(687); +var fsStat = __webpack_require__(687); +var fs_1 = __webpack_require__(691); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -79858,14 +80084,14 @@ exports.default = FileSystemStream; /***/ }), -/* 683 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(684); -const statProvider = __webpack_require__(686); +const optionsManager = __webpack_require__(688); +const statProvider = __webpack_require__(690); /** * Asynchronous API. */ @@ -79896,13 +80122,13 @@ exports.statSync = statSync; /***/ }), -/* 684 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(685); +const fsAdapter = __webpack_require__(689); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -79915,7 +80141,7 @@ exports.prepare = prepare; /***/ }), -/* 685 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79938,7 +80164,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 686 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79990,7 +80216,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 687 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80021,7 +80247,7 @@ exports.default = FileSystem; /***/ }), -/* 688 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80041,9 +80267,9 @@ var __extends = (this && this.__extends) || (function () { })(); Object.defineProperty(exports, "__esModule", { value: true }); var stream = __webpack_require__(137); -var readdir = __webpack_require__(665); -var reader_1 = __webpack_require__(678); -var fs_stream_1 = __webpack_require__(682); +var readdir = __webpack_require__(669); +var reader_1 = __webpack_require__(682); +var fs_stream_1 = __webpack_require__(686); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -80111,7 +80337,7 @@ exports.default = ReaderStream; /***/ }), -/* 689 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80130,9 +80356,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(665); -var reader_1 = __webpack_require__(678); -var fs_sync_1 = __webpack_require__(690); +var readdir = __webpack_require__(669); +var reader_1 = __webpack_require__(682); +var fs_sync_1 = __webpack_require__(694); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -80192,7 +80418,7 @@ exports.default = ReaderSync; /***/ }), -/* 690 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80211,8 +80437,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(683); -var fs_1 = __webpack_require__(687); +var fsStat = __webpack_require__(687); +var fs_1 = __webpack_require__(691); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -80258,7 +80484,7 @@ exports.default = FileSystemSync; /***/ }), -/* 691 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80274,13 +80500,13 @@ exports.flatten = flatten; /***/ }), -/* 692 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(288); +var merge2 = __webpack_require__(283); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ @@ -80295,13 +80521,13 @@ exports.merge = merge; /***/ }), -/* 693 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(694); +const pathType = __webpack_require__(698); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -80367,13 +80593,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 694 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); -const pify = __webpack_require__(695); +const pify = __webpack_require__(699); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -80416,7 +80642,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 695 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80507,17 +80733,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 696 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const fs = __webpack_require__(133); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(505); -const gitIgnore = __webpack_require__(697); -const pify = __webpack_require__(698); -const slash = __webpack_require__(699); +const fastGlob = __webpack_require__(509); +const gitIgnore = __webpack_require__(701); +const pify = __webpack_require__(702); +const slash = __webpack_require__(703); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -80615,7 +80841,7 @@ module.exports.sync = options => { /***/ }), -/* 697 */ +/* 701 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -81084,7 +81310,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 698 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81159,7 +81385,7 @@ module.exports = (input, options) => { /***/ }), -/* 699 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81177,7 +81403,7 @@ module.exports = input => { /***/ }), -/* 700 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -81187,7 +81413,7 @@ module.exports = input => { * Released under the MIT License. */ -var isExtglob = __webpack_require__(299); +var isExtglob = __webpack_require__(294); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -81231,17 +81457,17 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 701 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); const {constants: fsConstants} = __webpack_require__(133); -const pEvent = __webpack_require__(702); -const CpFileError = __webpack_require__(705); -const fs = __webpack_require__(709); -const ProgressEmitter = __webpack_require__(712); +const pEvent = __webpack_require__(706); +const CpFileError = __webpack_require__(709); +const fs = __webpack_require__(713); +const ProgressEmitter = __webpack_require__(716); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -81355,12 +81581,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 702 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(703); +const pTimeout = __webpack_require__(707); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -81651,12 +81877,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 703 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(704); +const pFinally = __webpack_require__(708); class TimeoutError extends Error { constructor(message) { @@ -81702,7 +81928,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 704 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81724,12 +81950,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 705 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(706); +const NestedError = __webpack_require__(710); class CpFileError extends NestedError { constructor(message, nested) { @@ -81743,10 +81969,10 @@ module.exports = CpFileError; /***/ }), -/* 706 */ +/* 710 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(707); +var inherits = __webpack_require__(711); var NestedError = function (message, nested) { this.nested = nested; @@ -81797,7 +82023,7 @@ module.exports = NestedError; /***/ }), -/* 707 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { try { @@ -81805,12 +82031,12 @@ try { if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { - module.exports = __webpack_require__(708); + module.exports = __webpack_require__(712); } /***/ }), -/* 708 */ +/* 712 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -81839,16 +82065,16 @@ if (typeof Object.create === 'function') { /***/ }), -/* 709 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const {promisify} = __webpack_require__(111); const fs = __webpack_require__(132); -const makeDir = __webpack_require__(710); -const pEvent = __webpack_require__(702); -const CpFileError = __webpack_require__(705); +const makeDir = __webpack_require__(714); +const pEvent = __webpack_require__(706); +const CpFileError = __webpack_require__(709); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -81945,7 +82171,7 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 710 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81953,7 +82179,7 @@ exports.copyFileSync = (source, destination, flags) => { const fs = __webpack_require__(133); const path = __webpack_require__(4); const {promisify} = __webpack_require__(111); -const semver = __webpack_require__(711); +const semver = __webpack_require__(715); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -82108,7 +82334,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 711 */ +/* 715 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -83710,7 +83936,7 @@ function coerce (version, options) { /***/ }), -/* 712 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83751,7 +83977,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 713 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -83797,12 +84023,12 @@ exports.default = module.exports; /***/ }), -/* 714 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(715); +const NestedError = __webpack_require__(719); class CpyError extends NestedError { constructor(message, nested) { @@ -83816,7 +84042,7 @@ module.exports = CpyError; /***/ }), -/* 715 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { var inherits = __webpack_require__(111).inherits; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index c2f9236d9e7984..f4e9ee82499004 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -28,7 +28,7 @@ "@types/node": ">=10.17.17 <10.20.0", "@types/ora": "^1.3.5", "@types/read-pkg": "^4.0.0", - "@types/strip-ansi": "^3.0.0", + "@types/strip-ansi": "^5.2.1", "@types/strong-log-transformer": "^1.0.0", "@types/tempy": "^0.2.0", "@types/write-pkg": "^3.1.0", @@ -50,13 +50,13 @@ "log-symbols": "^2.2.0", "multimatch": "^4.0.0", "ncp": "^2.0.0", - "ora": "^1.4.0", + "ora": "^4.0.4", "prettier": "^2.1.1", "read-pkg": "^5.2.0", "rxjs": "^6.5.5", "spawn-sync": "^1.0.15", "string-replace-loader": "^2.2.0", - "strip-ansi": "^4.0.0", + "strip-ansi": "^6.0.0", "strong-log-transformer": "^2.1.0", "tempy": "^0.3.0", "typescript": "4.0.2", diff --git a/packages/kbn-pm/src/utils/validate_yarn_lock.ts b/packages/kbn-pm/src/utils/validate_yarn_lock.ts index e110dc4d921cf1..ec853a3a958fbd 100644 --- a/packages/kbn-pm/src/utils/validate_yarn_lock.ts +++ b/packages/kbn-pm/src/utils/validate_yarn_lock.ts @@ -25,6 +25,7 @@ import { writeFile } from './fs'; import { Kibana } from './kibana'; import { YarnLock } from './yarn_lock'; import { log } from './log'; +import { Project } from './project'; export async function validateYarnLock(kbn: Kibana, yarnLock: YarnLock) { // look through all of the packages in the yarn.lock file to see if @@ -95,5 +96,66 @@ export async function validateYarnLock(kbn: Kibana, yarnLock: YarnLock) { 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<string, Array<{ range: string; projects: Project[] }>>(); + for (const project of kbn.getAllProjects().values()) { + 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); + } + } + + const duplicateRanges = Array.from(depRanges.entries()) + .filter(([, ranges]) => ranges.length > 1) + .reduce( + (acc: string[], [dep, ranges]) => [ + ...acc, + dep, + ...ranges.map( + ({ range, projects }) => ` ${range} => ${projects.map((p) => p.name).join(', ')}` + ), + ], + [] + ) + .join('\n '); + + if (duplicateRanges) { + log.error(dedent` + + [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); + } + log.success('yarn.lock analysis completed without any issues'); } diff --git a/packages/kbn-release-notes/package.json b/packages/kbn-release-notes/package.json index f8971fa02aa872..268530c22399aa 100644 --- a/packages/kbn-release-notes/package.json +++ b/packages/kbn-release-notes/package.json @@ -13,7 +13,7 @@ "axios": "^0.19.2", "cheerio": "0.22.0", "dedent": "^0.7.0", - "graphql": "^14.0.0", + "graphql": "^0.13.2", "graphql-tag": "^2.10.3", "terminal-link": "^2.1.1" }, diff --git a/packages/kbn-storybook/package.json b/packages/kbn-storybook/package.json index 05fdb8489a1c34..58359159e950df 100644 --- a/packages/kbn-storybook/package.json +++ b/packages/kbn-storybook/package.json @@ -13,8 +13,8 @@ "@storybook/core": "^6.0.16", "@storybook/react": "^6.0.16", "@storybook/theming": "^6.0.16", - "@types/loader-utils": "^2.0.1", - "@types/webpack": "^4.41.5", + "@types/loader-utils": "^1.1.3", + "@types/webpack": "^4.41.3", "@types/webpack-env": "^1.15.2", "@types/webpack-merge": "^4.1.5", "@kbn/utils": "1.0.0", diff --git a/packages/kbn-storybook/webpack.config.ts b/packages/kbn-storybook/webpack.config.ts index 98fca597ffd78f..84f8cfaefd6694 100644 --- a/packages/kbn-storybook/webpack.config.ts +++ b/packages/kbn-storybook/webpack.config.ts @@ -100,9 +100,5 @@ export default function ({ config: storybookConfig }: { config: Configuration }) if (htmlWebpackPlugin) { htmlWebpackPlugin.options.template = require.resolve('../lib/templates/index.ejs'); } - - // @ts-expect-error There's a long error here about the types of the - // incompatibility of Configuration, but it looks like it just may be Webpack - // type definition related. return webpackMerge(storybookConfig, config); } diff --git a/packages/kbn-test/package.json b/packages/kbn-test/package.json index c616c836d5ff49..4e86ec4bd72e0e 100644 --- a/packages/kbn-test/package.json +++ b/packages/kbn-test/package.json @@ -32,7 +32,7 @@ "lodash": "^4.17.20", "parse-link-header": "^1.0.1", "rxjs": "^6.5.5", - "strip-ansi": "^5.2.0", + "strip-ansi": "^6.0.0", "tar-fs": "^2.1.0", "xml2js": "^0.4.22", "zlib": "^1.0.5" diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index be18b7cfc0d01f..676985fa247402 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -58,10 +58,10 @@ "postcss-loader": "^3.0.0", "raw-loader": "^3.1.0", "react-dom": "^16.12.0", - "react-redux": "^5.1.2", - "react-router": "^3.2.0", + "react-redux": "^7.2.0", + "react-router": "^5.2.0", "react-router-redux": "^4.0.8", - "redux": "3.7.2", + "redux": "^4.0.5", "redux-thunk": "^2.3.0", "regenerator-runtime": "^0.13.3", "sass-loader": "^8.0.2", diff --git a/tasks/config/run.js b/tasks/config/run.js index 148be6ea8afaa4..eddcb0bdd59d04 100644 --- a/tasks/config/run.js +++ b/tasks/config/run.js @@ -240,10 +240,6 @@ module.exports = function () { args: ['scripts/check_licenses', '--dev'], }), - verifyDependencyVersions: gruntTaskWithGithubChecks( - 'Verify dependency versions', - 'verifyDependencyVersions' - ), test_jest: gruntTaskWithGithubChecks('Jest tests', 'test:jest'), test_jest_integration: gruntTaskWithGithubChecks( 'Jest integration tests', diff --git a/tasks/jenkins.js b/tasks/jenkins.js index 90efadf41c4355..4e3358ce81bbd2 100644 --- a/tasks/jenkins.js +++ b/tasks/jenkins.js @@ -31,7 +31,6 @@ module.exports = function (grunt) { 'run:checkFileCasing', 'run:checkLockfileSymlinks', 'run:licenses', - 'run:verifyDependencyVersions', 'run:verifyNotice', 'run:mocha', 'run:test_jest', diff --git a/tasks/verify_dependency_versions.js b/tasks/verify_dependency_versions.js deleted file mode 100644 index 14ecbb9ba86038..00000000000000 --- a/tasks/verify_dependency_versions.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { size } from 'lodash'; -import kibana from '../package.json'; -import xpack from '../x-pack/package.json'; - -function getMismatches(depType) { - return Object.keys(kibana[depType]) - .map((key) => { - const xpackValue = xpack[depType][key]; - const kibanaValue = kibana[depType][key]; - if (xpackValue && kibanaValue && xpackValue !== kibanaValue && !key.includes('@kbn/')) { - return { - key, - xpack: xpackValue, - kibana: kibanaValue, - }; - } - }) - .filter((key) => !!key); -} - -export default function verifyDependencyVersions(grunt) { - grunt.registerTask('verifyDependencyVersions', 'Checks dependency versions', () => { - const devDependenciesMismatches = getMismatches('devDependencies'); - if (size(devDependenciesMismatches) > 0) { - grunt.log.error( - 'The following devDependencies do not match:', - JSON.stringify(devDependenciesMismatches, null, 4) - ); - return false; - } else { - grunt.log.writeln('devDependencies match!'); - } - }); -} diff --git a/test/scripts/checks/verify_dependency_versions.sh b/test/scripts/checks/verify_dependency_versions.sh deleted file mode 100755 index b73a71e7ff7fd5..00000000000000 --- a/test/scripts/checks/verify_dependency_versions.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -source src/dev/ci_setup/setup_env.sh - -yarn run grunt run:verifyDependencyVersions diff --git a/vars/tasks.groovy b/vars/tasks.groovy index edd2c0aa47401e..09ff1b0a7d95bd 100644 --- a/vars/tasks.groovy +++ b/vars/tasks.groovy @@ -12,7 +12,6 @@ def check() { kibanaPipeline.scriptTask('Check File Casing', 'test/scripts/checks/file_casing.sh'), kibanaPipeline.scriptTask('Check Lockfile Symlinks', 'test/scripts/checks/lock_file_symlinks.sh'), kibanaPipeline.scriptTask('Check Licenses', 'test/scripts/checks/licenses.sh'), - kibanaPipeline.scriptTask('Verify Dependency Versions', 'test/scripts/checks/verify_dependency_versions.sh'), kibanaPipeline.scriptTask('Verify NOTICE', 'test/scripts/checks/verify_notice.sh'), kibanaPipeline.scriptTask('Test Projects', 'test/scripts/checks/test_projects.sh'), kibanaPipeline.scriptTask('Test Hardening', 'test/scripts/checks/test_hardening.sh'), diff --git a/x-pack/package.json b/x-pack/package.json index f9b193bb4da06d..5742200b55d9f4 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -29,7 +29,7 @@ "**/@types/node": ">=10.17.17 <10.20.0" }, "devDependencies": { - "@cypress/webpack-preprocessor": "^4.1.0", + "@cypress/webpack-preprocessor": "^5.4.1", "@elastic/apm-rum-react": "^1.2.5", "@elastic/maki": "6.3.0", "@kbn/dev-utils": "1.0.0", @@ -159,7 +159,7 @@ "copy-to-clipboard": "^3.0.8", "copy-webpack-plugin": "^6.0.2", "cronstrue": "^1.51.0", - "cypress": "5.0.0", + "cypress": "^5.0.0", "cypress-multi-reporters": "^1.2.3", "cypress-promise": "^1.1.0", "d3": "3.5.17", @@ -257,7 +257,7 @@ "tinycolor2": "1.4.1", "topojson-client": "3.0.0", "tree-kill": "^1.2.2", - "ts-loader": "^6.0.4", + "ts-loader": "^7.0.5", "typescript": "4.0.2", "typescript-fsa": "^3.0.0", "typescript-fsa-reducers": "^1.2.1", @@ -309,7 +309,7 @@ "dedent": "^0.7.0", "del": "^5.1.0", "elasticsearch": "^16.7.0", - "extract-zip": "^1.7.0", + "extract-zip": "^2.0.1", "file-type": "^10.9.0", "font-awesome": "4.7.0", "fp-ts": "^2.3.1", @@ -321,7 +321,7 @@ "glob": "^7.1.2", "graphql": "^0.13.2", "graphql-fields": "^1.0.2", - "graphql-tag": "^2.9.2", + "graphql-tag": "^2.10.3", "graphql-tools": "^3.0.2", "h2o2": "^8.1.2", "handlebars": "4.7.6", @@ -372,7 +372,7 @@ "redux-observable": "^1.2.0", "redux-thunk": "^2.3.0", "request": "^2.88.0", - "rison-node": "0.3.1", + "rison-node": "1.0.2", "rxjs": "^6.5.5", "semver": "^5.7.0", "set-value": "^3.0.2", diff --git a/x-pack/plugins/apm/e2e/package.json b/x-pack/plugins/apm/e2e/package.json index 649198b7eae11a..041f9ea7f21c0e 100644 --- a/x-pack/plugins/apm/e2e/package.json +++ b/x-pack/plugins/apm/e2e/package.json @@ -14,7 +14,7 @@ "@types/node": ">=10.17.17 <10.20.0", "axios": "^0.19.2", "cypress-cucumber-preprocessor": "^2.5.2", - "cypress": "^4.9.0", + "cypress": "^5.0.0", "ora": "^4.0.4", "p-limit": "^3.0.1", "p-retry": "^4.2.0", diff --git a/x-pack/plugins/apm/scripts/package.json b/x-pack/plugins/apm/scripts/package.json index d3e2d42f972a9b..c68dc49cd9370c 100644 --- a/x-pack/plugins/apm/scripts/package.json +++ b/x-pack/plugins/apm/scripts/package.json @@ -4,7 +4,7 @@ "main": "index.js", "license": "MIT", "dependencies": { - "@elastic/elasticsearch": "7.9.0-rc.1", + "@elastic/elasticsearch": "7.9.1", "@octokit/rest": "^16.35.0", "console-stamp": "^0.2.9", "hdr-histogram-js": "^1.2.0" diff --git a/x-pack/plugins/reporting/server/browsers/extract/unzip.js b/x-pack/plugins/reporting/server/browsers/extract/unzip.js index d57d04a52f46ec..d5166f149372a2 100644 --- a/x-pack/plugins/reporting/server/browsers/extract/unzip.js +++ b/x-pack/plugins/reporting/server/browsers/extract/unzip.js @@ -7,14 +7,10 @@ import extractZip from 'extract-zip'; import { ExtractError } from './extract_error'; -export function unzip(filepath, target) { - return new Promise(function (resolve, reject) { - extractZip(filepath, { dir: target }, (err) => { - if (err) { - return reject(new ExtractError(err)); - } - - resolve(); - }); - }); +export async function unzip(filepath, target) { + try { + await extractZip(filepath, { dir: target }); + } catch (err) { + throw new ExtractError(err); + } } diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index 9f26fc22ede539..ed226fb0c984fe 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -32,7 +32,7 @@ export interface MockedProvidedQuery { export const mockOpenTimelineQueryResults: MockedProvidedQuery[] = [ { request: { - query: allTimelinesQuery, + query: (allTimelinesQuery as unknown) as GetAllTimeline.Query, variables: { onlyUserFavorite: false, pageInfo: { diff --git a/x-pack/plugins/security_solution/scripts/beat_docs/build.js b/x-pack/plugins/security_solution/scripts/beat_docs/build.js index 9b3607593a5db2..3dbfb75fbe32ea 100644 --- a/x-pack/plugins/security_solution/scripts/beat_docs/build.js +++ b/x-pack/plugins/security_solution/scripts/beat_docs/build.js @@ -135,25 +135,25 @@ const convertSchemaToHash = (schema, beatFields) => { }, beatFields); }; -const manageZipFields = async (beat, filePath, beatFields) => - new Promise((resolve, reject) => { - extract(filePath, { dir: beat.outputDir }, (err) => { - if (err) { - return reject(new Error(err)); - } - console.log('building fields', beat.index); - const obj = yaml.load( - fs.readFileSync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64/fields.yml`, { - encoding: 'utf-8', - }) - ); - const eBeatFields = convertSchemaToHash(obj, beatFields); - console.log('deleting files', beat.index); - rimraf.sync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64`); - rimraf.sync(beat.filePath); - resolve(eBeatFields); - }); - }); +const manageZipFields = async (beat, filePath, beatFields) => { + try { + await extract(filePath, { dir: beat.outputDir }); + console.log('building fields', beat.index); + const obj = yaml.load( + fs.readFileSync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64/fields.yml`, { + encoding: 'utf-8', + }) + ); + const eBeatFields = convertSchemaToHash(obj, beatFields); + console.log('deleting files', beat.index); + rimraf.sync(`${beat.outputDir}/winlogbeat-7.9.0-windows-x86_64`); + rimraf.sync(beat.filePath); + + return eBeatFields; + } catch (err) { + throw new Error(err); + } +}; const manageTarFields = async (beat, filePath, beatFields) => new Promise((resolve, reject) => { diff --git a/yarn.lock b/yarn.lock index d795a174cfaa0f..2d72b6d6c3bb6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,7 +63,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.0.1", "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.7.5", "@babel/core@^7.9.0": +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.11.1" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== @@ -926,7 +926,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.10.4" "@babel/helper-plugin-utils" "^7.10.4" -"@babel/preset-env@^7.0.0", "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.9.5", "@babel/preset-env@^7.9.6": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.9.5", "@babel/preset-env@^7.9.6": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.11.0.tgz#860ee38f2ce17ad60480c2021ba9689393efb796" integrity sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg== @@ -1168,17 +1168,14 @@ tunnel-agent "^0.6.0" uuid "^3.3.2" -"@cypress/webpack-preprocessor@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-4.1.0.tgz#8c4debc0b1abf045b62524d1996dd9aeaf7e86a8" - integrity sha512-LbxsdYVpHGoC2fMOdW0aQvuvVRD7aZx8p8DrP53HISpl7bD1PqLGWKzhHn7cGG24UHycBJrbaEeKEosW29W1dg== +"@cypress/webpack-preprocessor@^5.4.1": + version "5.4.6" + resolved "https://registry.yarnpkg.com/@cypress/webpack-preprocessor/-/webpack-preprocessor-5.4.6.tgz#667f8007cbe6ee219ce7e45a7f1400d3e2401032" + integrity sha512-78hWoTUUEncv647badwVbyszvmwI1r9GaY/xy7V0sz0VVC90ByuDkLpvN+J0VP6enthob4dIPXcm0f9Tb1UKQQ== dependencies: - bluebird "3.5.0" - debug "3.1.0" - optionalDependencies: - "@babel/core" "^7.0.1" - "@babel/preset-env" "^7.0.0" - babel-loader "^8.0.2" + bluebird "^3.7.1" + debug "^4.1.1" + lodash "^4.17.20" "@cypress/xvfb@^1.2.4": version "1.2.4" @@ -1246,17 +1243,6 @@ utility-types "^3.10.0" uuid "^3.3.2" -"@elastic/elasticsearch@7.9.0-rc.1": - version "7.9.0-rc.1" - resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.9.0-rc.1.tgz#50205507ec84ccb95cb7a6d36e5570808749fee9" - integrity sha512-rVjiVj7VPLCusJPfywpb3gvcaA99uylYSum1Frcq4vi2Iqg118KXgYW6GOis2Y70oDZ6w6XRlT0ze5NA6SBa+g== - dependencies: - debug "^4.1.1" - decompress-response "^4.2.0" - ms "^2.1.1" - pump "^3.0.0" - secure-json-parse "^2.1.0" - "@elastic/elasticsearch@7.9.1": version "7.9.1" resolved "https://registry.yarnpkg.com/@elastic/elasticsearch/-/elasticsearch-7.9.1.tgz#40f1c38e8f70d783851c13be78a7cb346891c15e" @@ -4487,14 +4473,6 @@ "@types/node" "*" "@types/webpack" "*" -"@types/loader-utils@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/loader-utils/-/loader-utils-2.0.1.tgz#4073425aca25762099823f7b922e86599c2b85ec" - integrity sha512-X3jTNi/I2AEd2WrHdSqRppPkYzWkRMNGxJzeMwS0o3hVi8ZB6JCnf/XyQmqpUuCidld5lC/1VxVgTktEweRK+w== - dependencies: - "@types/node" "*" - "@types/webpack" "*" - "@types/lodash.difference@^4.5.6": version "4.5.6" resolved "https://registry.yarnpkg.com/@types/lodash.difference/-/lodash.difference-4.5.6.tgz#41ec5c4e684eeacf543848a9a1b2a4856ccf9853" @@ -5085,11 +5063,6 @@ resolved "https://registry.yarnpkg.com/@types/stats-lite/-/stats-lite-2.2.0.tgz#bc8190bf9dfa1e16b89eaa2b433c99dff0804de9" integrity sha512-YV6SS4QC+pbzqjMIV8qVSTDOOazgKBLTVaN+7PfuxELjz/eyzc20KwDVGPrbHt2OcYMA7K2ezLB45Cp6DpNOSQ== -"@types/strip-ansi@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/strip-ansi/-/strip-ansi-3.0.0.tgz#9b63d453a6b54aa849182207711a08be8eea48ae" - integrity sha1-m2PUU6a1SqhJGCIHcRoIvo7qSK4= - "@types/strip-ansi@^5.2.1": version "5.2.1" resolved "https://registry.yarnpkg.com/@types/strip-ansi/-/strip-ansi-5.2.1.tgz#acd97f1f091e332bb7ce697c4609eb2370fa2a92" @@ -5339,7 +5312,7 @@ "@types/webpack-sources" "*" source-map "^0.6.0" -"@types/webpack@^4.41.21", "@types/webpack@^4.41.5", "@types/webpack@^4.41.8": +"@types/webpack@^4.41.8": version "4.41.21" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.21.tgz#cc685b332c33f153bb2f5fc1fa3ac8adeb592dee" integrity sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA== @@ -6283,11 +6256,6 @@ ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= -ansi-escapes@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" - integrity sha1-W65SvkJIeN2Xg+iRDj/Cki6DyBs= - ansi-escapes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" @@ -7348,7 +7316,7 @@ babel-jest@^26.3.0: graceful-fs "^4.2.4" slash "^3.0.0" -babel-loader@^8.0.2, babel-loader@^8.0.6: +babel-loader@^8.0.6: version "8.0.6" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.0.6.tgz#e33bdb6f362b03f4bb141a0c21ab87c501b70dfb" integrity sha512-4BmWKtBOBm13uoUwd08UwjZlaw3O9GWf456R9j+5YykFZ6LUIjIKLc0zEZf+hauxPOJs96C8k6FvYD09vWzhYw== @@ -7985,17 +7953,12 @@ bluebird-retry@^0.11.0: resolved "https://registry.yarnpkg.com/bluebird-retry/-/bluebird-retry-0.11.0.tgz#1289ab22cbbc3a02587baad35595351dd0c1c047" integrity sha1-EomrIsu8OgJYe6rTVZU1HdDBwEc= -bluebird@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" - integrity sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw= - bluebird@3.5.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5: version "3.5.5" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== -bluebird@3.7.2, bluebird@^3.7.2: +bluebird@3.7.2, bluebird@^3.7.1, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -8672,15 +8635,6 @@ camelcase-keys@^2.0.0: camelcase "^2.0.0" map-obj "^1.0.0" -camelcase-keys@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" - integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= - dependencies: - camelcase "^4.1.0" - map-obj "^2.0.0" - quick-lru "^1.0.0" - camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -8705,7 +8659,7 @@ camelcase@^3.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= -camelcase@^4.0.0, camelcase@^4.1.0: +camelcase@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= @@ -9245,17 +9199,12 @@ cli-spinners@^0.1.2: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= -cli-spinners@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.1.0.tgz#f1847b168844d917a671eb9d147e3df497c90d06" - integrity sha1-8YR7FohE2RemceudFH499JfJDQY= - cli-spinners@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.1.0.tgz#22c34b4d51f573240885b201efda4e4ec9fff3c7" integrity sha512-8B00fJOEh1HPrx4fo5eW16XmE1PcL1tGpGrxy63CXGP9nHdPBN63X75hA1zhvQuhVztJWLqV58Roj2qlNM7cAA== -cli-spinners@^2.4.0: +cli-spinners@^2.2.0, cli-spinners@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== @@ -10219,7 +10168,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" -create-react-class@^15.5.1, create-react-class@^15.5.2: +create-react-class@^15.5.2: version "15.6.3" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036" integrity sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg== @@ -10586,10 +10535,10 @@ cypress-promise@^1.1.0: resolved "https://registry.yarnpkg.com/cypress-promise/-/cypress-promise-1.1.0.tgz#f2d66965945fe198431aaf692d5157cea9d47b25" integrity sha512-DhIf5PJ/a0iY+Yii6n7Rbwq+9TJxU4pupXYzf9mZd8nPG0AzQrj9i+pqINv4xbI2EV1p+PKW3maCkR7oPG4GrA== -cypress@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-5.0.0.tgz#6957e299b790af8b1cd7bea68261b8935646f72e" - integrity sha512-jhPd0PMO1dPSBNpx6pHVLkmnnaTfMy3wCoacHAKJ9LJG06y16zqUNSFri64N4BjuGe8y6mNMt8TdgKnmy9muCg== +cypress@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-5.2.0.tgz#6902efd90703242a2539f0623c6e1118aff01f95" + integrity sha512-9S2spcrpIXrQ+CQIKHsjRoLQyRc2ehB06clJXPXXp1zyOL/uZMM3Qc20ipNki4CcNwY0nBTQZffPbRpODeGYQg== dependencies: "@cypress/listr-verbose-renderer" "^0.4.1" "@cypress/request" "^2.88.5" @@ -11042,7 +10991,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: +decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -11098,7 +11047,7 @@ deep-eql@^0.1.3: dependencies: type-detect "0.1.1" -deep-equal@^1.0.0, deep-equal@^1.1.1, deep-equal@~1.1.1: +deep-equal@^1.0.0, deep-equal@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== @@ -11843,7 +11792,7 @@ dotenv@^8.1.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== -dotignore@^0.1.2, dotignore@~0.1.2: +dotignore@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/dotignore/-/dotignore-0.1.2.tgz#f942f2200d28c3a76fbdd6f0ee9f3257c8a2e905" integrity sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw== @@ -12640,17 +12589,6 @@ eslint-config-prettier@^6.11.0: dependencies: get-stdin "^6.0.0" -eslint-formatter-pretty@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-1.3.0.tgz#985d9e41c1f8475f4a090c5dbd2dfcf2821d607e" - integrity sha512-5DY64Y1rYCm7cfFDHEGUn54bvCnK+wSUVF07N8oXeqUJFSd+gnYOTXbzelQ1HurESluY6gnEQPmXOIkB4Wa+gA== - dependencies: - ansi-escapes "^2.0.0" - chalk "^2.1.0" - log-symbols "^2.0.0" - plur "^2.1.2" - string-width "^2.0.0" - eslint-formatter-pretty@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/eslint-formatter-pretty/-/eslint-formatter-pretty-4.0.0.tgz#dc15f3bf4fb51b7ba5fbedb77f57ba8841140ce2" @@ -14801,7 +14739,7 @@ glob@^6.0.1, glob@^6.0.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1, glob@~7.1.4, glob@~7.1.6: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1, glob@~7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -15015,7 +14953,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -globby@^9.1.0, globby@^9.2.0: +globby@^9.2.0: version "9.2.0" resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== @@ -15383,7 +15321,7 @@ graphql-tag-pluck@0.6.0: source-map-support "^0.5.9" typescript "^3.2.2" -graphql-tag@2.10.1, graphql-tag@^2.9.2: +graphql-tag@2.10.1: version "2.10.1" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.1.tgz#10aa41f1cd8fae5373eaf11f1f67260a3cad5e02" integrity sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg== @@ -15439,13 +15377,6 @@ graphql@^0.13.2: dependencies: iterall "^1.2.1" -graphql@^14.0.0: - version "14.6.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.6.0.tgz#57822297111e874ea12f5cd4419616930cd83e49" - integrity sha512-VKzfvHEKybTKjQVpTFrA5yUq2S9ihcZvfJAtsDBBCuV6wauPu1xl/f9ehgVf0FcEJJs4vz6ysb/ZMkGigQZseg== - dependencies: - iterall "^1.2.2" - graphviz@^0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.8.tgz#e599e40733ef80e1653bfe89a5f031ecf2aa4aaa" @@ -16102,16 +16033,6 @@ history-extra@^5.0.1: resolved "https://registry.yarnpkg.com/history-extra/-/history-extra-5.0.1.tgz#95a2e59dda526c4241d0ae1b124a77a5e4675ce8" integrity sha512-6XV1L1lHgporVWgppa/Kq+Fnz4lhBew7iMxYCTfzVmoEywsAKJnTjdw1zOd+EGLHGYp0/V8jSVMEgqx4QbHLTw== -history@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/history/-/history-3.3.0.tgz#fcedcce8f12975371545d735461033579a6dae9c" - integrity sha1-/O3M6PEpdTcVRdc1RhAzV5ptrpw= - dependencies: - invariant "^2.2.1" - loose-envify "^1.2.0" - query-string "^4.2.2" - warning "^3.0.0" - history@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" @@ -16773,7 +16694,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.4, inherits@~2.0.3, inherits@~2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -16870,7 +16791,7 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" -inquirer@^1.0.2, inquirer@^1.2.2: +inquirer@^1.0.2: version "1.2.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918" integrity sha1-TexvMvN+97sLLtPx0aXD9UUHSRg= @@ -17038,7 +16959,7 @@ invariant@2.2.4, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.3, invariant dependencies: loose-envify "^1.0.0" -invariant@^2.2.1, invariant@^2.2.2: +invariant@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" integrity sha1-nh9WrArNtr8wMwbzOL47IErmA2A= @@ -17089,11 +17010,6 @@ iron@5.x.x: cryptiles "4.x.x" hoek "5.x.x" -irregular-plurals@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" - integrity sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y= - irregular-plurals@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-3.2.0.tgz#b19c490a0723798db51b235d7e39add44dab0822" @@ -17637,7 +17553,7 @@ is-redirect@^1.0.0: resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= -is-regex@^1.0.4, is-regex@^1.0.5, is-regex@~1.0.5: +is-regex@^1.0.4, is-regex@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== @@ -18066,11 +17982,6 @@ iterall@^1.1.3, iterall@^1.2.1: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7" integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA== -iterall@^1.2.2: - version "1.3.0" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" - integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== - iterate-iterator@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6" @@ -19737,7 +19648,7 @@ locutus@^2.0.5: resolved "https://registry.yarnpkg.com/locutus/-/locutus-2.0.10.tgz#f903619466a98a4ab76e8b87a5854b55a743b917" integrity sha512-AZg2kCqrquMJ5FehDsBidV0qHl98NrsYtseUClzjAQ3HFnsDBJTCwGVplSQ82t9/QfgahqvTjaKcZqZkHmS0wQ== -lodash-es@^4.17.11, lodash-es@^4.2.1: +lodash-es@^4.17.11: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== @@ -19997,7 +19908,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5: +lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -20010,14 +19921,14 @@ log-ok@^0.1.1: ansi-green "^0.1.1" success-symbol "^0.1.0" -log-symbols@2.2.0, log-symbols@^2.0.0, log-symbols@^2.1.0, log-symbols@^2.2.0: +log-symbols@2.2.0, log-symbols@^2.1.0, log-symbols@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== dependencies: chalk "^2.0.1" -log-symbols@3.0.0: +log-symbols@3.0.0, log-symbols@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== @@ -20326,11 +20237,6 @@ map-obj@^1.0.0, map-obj@^1.0.1: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= -map-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" - integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= - map-obj@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.1.0.tgz#b91221b542734b9f14256c0132c897c5d7256fd5" @@ -20647,21 +20553,6 @@ meow@^3.0.0, meow@^3.3.0, meow@^3.7.0: redent "^1.0.0" trim-newlines "^1.0.0" -meow@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" - integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== - dependencies: - camelcase-keys "^4.0.0" - decamelize-keys "^1.0.0" - loud-rejection "^1.0.0" - minimist-options "^3.0.1" - normalize-package-data "^2.3.4" - read-pkg-up "^3.0.0" - redent "^2.0.0" - trim-newlines "^2.0.0" - yargs-parser "^10.0.0" - meow@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/meow/-/meow-6.1.1.tgz#1ad64c4b76b2a24dfb2f635fddcadf320d251467" @@ -20927,14 +20818,6 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimist-options@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" - integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== - dependencies: - arrify "^1.0.1" - is-plain-obj "^1.1.0" - minimist-options@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -22161,7 +22044,7 @@ object-identity-map@^1.0.2: dependencies: object.entries "^1.1.0" -object-inspect@^1.6.0, object-inspect@^1.7.0, object-inspect@~1.7.0: +object-inspect@^1.6.0, object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== @@ -22485,16 +22368,6 @@ ora@^0.2.3: cli-spinners "^0.1.2" object-assign "^4.0.1" -ora@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-1.4.0.tgz#884458215b3a5d4097592285f93321bb7a79e2e5" - integrity sha512-iMK1DOQxzzh2MBlVsU42G80mnrvUhqsMh74phHtDlrcTZPK0pH6o7l7DRshK+0YsxDyEuaOkziVdvM3T0QTzpw== - dependencies: - chalk "^2.1.0" - cli-cursor "^2.1.0" - cli-spinners "^1.0.1" - log-symbols "^2.1.0" - ora@^3.0.0: version "3.4.0" resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" @@ -22507,6 +22380,20 @@ ora@^3.0.0: strip-ansi "^5.2.0" wcwidth "^1.0.1" +ora@^4.0.4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-4.1.1.tgz#566cc0348a15c36f5f0e979612842e02ba9dddbc" + integrity sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A== + dependencies: + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-spinners "^2.2.0" + is-interactive "^1.0.0" + log-symbols "^3.0.0" + mute-stream "0.0.8" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + ora@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ora/-/ora-5.1.0.tgz#b188cf8cd2d4d9b13fd25383bc3e5cba352c94f8" @@ -23450,13 +23337,6 @@ plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" -plur@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" - integrity sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo= - dependencies: - irregular-plurals "^1.0.0" - plur@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84" @@ -23911,7 +23791,7 @@ prop-types-exact@^1.2.0: object.assign "^4.1.0" reflect.ownkeys "^0.2.0" -prop-types@15.7.2, prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@15.7.2, prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -24149,7 +24029,7 @@ qs@^6.6.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.8.0.tgz#87b763f0d37ca54200334cd57bb2ef8f68a1d081" integrity sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w== -query-string@^4.1.0, query-string@^4.2.2: +query-string@^4.1.0: version "4.3.4" resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= @@ -24197,11 +24077,6 @@ queue@6.0.1: dependencies: inherits "~2.0.3" -quick-lru@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" - integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= - quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -24706,7 +24581,7 @@ react-lib-adler32@^1.0.3: resolved "https://registry.yarnpkg.com/react-lib-adler32/-/react-lib-adler32-1.0.3.tgz#63df1aed274eabcc1c5067077ea281ec30888ba7" integrity sha512-AqFqdt4cP0RPffHNjVHZ7tyIgnoSzNxgFhG8XKMXCtA1dZ72gTPO4iYFwWDKHqvE8sHS14rhltQTdbXU5G4BFA== -react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.4: +react-lifecycles-compat@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== @@ -24783,19 +24658,6 @@ react-portal@^3.2.0: dependencies: prop-types "^15.5.8" -react-redux@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" - integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== - dependencies: - "@babel/runtime" "^7.1.2" - hoist-non-react-statics "^3.3.0" - invariant "^2.2.4" - loose-envify "^1.1.0" - prop-types "^15.6.1" - react-is "^16.6.0" - react-lifecycles-compat "^3.0.0" - react-redux@^7.1.0, react-redux@^7.1.1, react-redux@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.0.tgz#f970f62192b3981642fec46fd0db18a074fe879d" @@ -24884,19 +24746,6 @@ react-router@5.2.0, react-router@^5.2.0: tiny-invariant "^1.0.2" tiny-warning "^1.0.0" -react-router@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.2.1.tgz#b9a3279962bdfbe684c8bd0482b81ef288f0f244" - integrity sha512-SXkhC0nr3G0ltzVU07IN8jYl0bB6FsrDIqlLC9dK3SITXqyTJyM7yhXlUqs89w3Nqi5OkXsfRUeHX+P874HQrg== - dependencies: - create-react-class "^15.5.1" - history "^3.0.0" - hoist-non-react-statics "^2.3.1" - invariant "^2.2.1" - loose-envify "^1.2.0" - prop-types "^15.5.6" - warning "^3.0.0" - react-select@^3.0.8: version "3.1.0" resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27" @@ -25174,14 +25023,6 @@ read-pkg-up@^2.0.0: find-up "^2.0.0" read-pkg "^2.0.0" -read-pkg-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" - integrity sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc= - dependencies: - find-up "^2.0.0" - read-pkg "^3.0.0" - read-pkg-up@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978" @@ -25389,14 +25230,6 @@ redent@^1.0.0: indent-string "^2.1.0" strip-indent "^1.0.1" -redent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" - integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= - dependencies: - indent-string "^3.0.0" - strip-indent "^2.0.0" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -25460,16 +25293,6 @@ redux-thunks@^1.0.0: resolved "https://registry.yarnpkg.com/redux-thunks/-/redux-thunks-1.0.0.tgz#56e03b86d281a2664c884ab05c543d9ab1673658" integrity sha1-VuA7htKBomZMiEqwXFQ9mrFnNlg= -redux@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/redux/-/redux-3.7.2.tgz#06b73123215901d25d065be342eb026bc1c8537b" - integrity sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A== - dependencies: - lodash "^4.2.1" - lodash-es "^4.2.1" - loose-envify "^1.1.0" - symbol-observable "^1.0.3" - redux@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.0.tgz#aa698a92b729315d22b34a0553d7e6533555cc03" @@ -26216,13 +26039,6 @@ resolve@~1.10.1: dependencies: path-parse "^1.0.6" -resolve@~1.14.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" - integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== - dependencies: - path-parse "^1.0.6" - responselike@1.0.2, responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -26352,11 +26168,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" -rison-node@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/rison-node/-/rison-node-0.3.1.tgz#fc540015500fc146f3b27d8d25dd5742122552a6" - integrity sha1-/FQAFVAPwUbzsn2NJd1XQhIlUqY= - rison-node@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/rison-node/-/rison-node-1.0.2.tgz#b7b5f37f39f5ae2a51a973a33c9aa17239a33e4b" @@ -27877,7 +27688,7 @@ string.prototype.padstart@^3.0.0: es-abstract "^1.4.3" function-bind "^1.0.2" -string.prototype.trim@^1.2.1, string.prototype.trim@~1.2.1: +string.prototype.trim@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz#141233dff32c82bfad80684d7e5f0869ee0fb782" integrity sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw== @@ -28053,11 +27864,6 @@ strip-indent@^1.0.1: dependencies: get-stdin "^4.0.1" -strip-indent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" - integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= - strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -28358,7 +28164,7 @@ symbol-observable@1.0.1: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ= -symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: +symbol-observable@^1.0.2, symbol-observable@^1.0.4, symbol-observable@^1.1.0, symbol-observable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -28452,27 +28258,6 @@ tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tape@^4.13.0: - version "4.13.0" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.13.0.tgz#e2f581ff5f12a7cbd787e9f83c76c2851782fce2" - integrity sha512-J/hvA+GJnuWJ0Sj8Z0dmu3JgMNU+MmusvkCT7+SN4/2TklW18FNCp/UuHIEhPZwHfy4sXfKYgC7kypKg4umbOw== - dependencies: - deep-equal "~1.1.1" - defined "~1.0.0" - dotignore "~0.1.2" - for-each "~0.3.3" - function-bind "~1.1.1" - glob "~7.1.6" - has "~1.0.3" - inherits "~2.0.4" - is-regex "~1.0.5" - minimist "~1.2.0" - object-inspect "~1.7.0" - resolve "~1.14.2" - resumer "~0.0.0" - string.prototype.trim "~1.2.1" - through "~2.3.8" - tape@^4.5.1: version "4.10.2" resolved "https://registry.yarnpkg.com/tape/-/tape-4.10.2.tgz#129fcf62f86df92687036a52cce7b8ddcaffd7a6" @@ -29176,11 +28961,6 @@ trim-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= -trim-newlines@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" - integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= - trim-newlines@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" @@ -29259,10 +29039,10 @@ ts-invariant@^0.4.0: dependencies: tslib "^1.9.3" -ts-loader@^6.0.4: - version "6.0.4" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-6.0.4.tgz#bc331ad91a887a60632d94c9f79448666f2c4b63" - integrity sha512-p2zJYe7OtwR+49kv4gs7v4dMrfYD1IPpOtqiSPCbe8oR+4zEBtdHwzM7A7M91F+suReqgzZrlClk4LRSSp882g== +ts-loader@^7.0.5: + version "7.0.5" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-7.0.5.tgz#789338fb01cb5dc0a33c54e50558b34a73c9c4c5" + integrity sha512-zXypEIT6k3oTc+OZNx/cqElrsbBtYqDknf48OZos0NQ3RTt045fBIU8RRSu+suObBzYB355aIPGOe/3kj9h7Ig== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" @@ -29292,19 +29072,6 @@ tsd@^0.13.1: read-pkg-up "^7.0.0" update-notifier "^4.1.0" -tsd@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/tsd/-/tsd-0.7.4.tgz#d9aba567f1394641821a6800dcee60746c87bd03" - integrity sha512-cqr1s2GHtVkU3L/4BXDaeJOjFEuZ7iOVC+hwmyx4G7Eo26mSXCFNnwFm4EasK/MW2HdY3AQWux+AjYzDYLzZow== - dependencies: - eslint-formatter-pretty "^1.3.0" - globby "^9.1.0" - meow "^5.0.0" - path-exists "^3.0.0" - read-pkg-up "^4.0.0" - typescript "^3.0.1" - update-notifier "^2.5.0" - tslib@^1, tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.13.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" @@ -29476,7 +29243,7 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@4.0.2, typescript@^3.0.1, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2: +typescript@4.0.2, typescript@^3.0.3, typescript@^3.2.2, typescript@^3.3.3333, typescript@^3.4.5, typescript@~3.7.2: version "4.0.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.2.tgz#7ea7c88777c723c681e33bf7988be5d008d05ac2" integrity sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ== @@ -30982,13 +30749,6 @@ walker@^1.0.7, walker@~1.0.5: dependencies: makeerror "1.0.x" -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" - integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= - dependencies: - loose-envify "^1.0.0" - warning@^4.0.2, warning@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" @@ -31868,13 +31628,6 @@ yargs-parser@5.0.0-security.0: camelcase "^3.0.0" object.assign "^4.1.0" -yargs-parser@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" - integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== - dependencies: - camelcase "^4.1.0" - yargs-parser@^18.1.2, yargs-parser@^18.1.3: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" From 1841495322d4abe63c3985e9c7456bda01652292 Mon Sep 17 00:00:00 2001 From: Phillip Burch <phillip.burch@live.com> Date: Thu, 1 Oct 2020 18:54:46 -0500 Subject: [PATCH 064/128] [Metrics UI] Override anomaly detection partition field (#79214) * Add ability to override datafeeds and job config for partition field * Remove debug * UX cleanup * Fix types, delete dead code * Fix types --- .../containers/ml/infra_ml_module_types.ts | 4 +- .../containers/ml/infra_ml_setup_state.ts | 289 ------------------ .../metrics_hosts/module_descriptor.ts | 135 +++++--- .../modules/metrics_k8s/module_descriptor.ts | 143 ++++++--- .../anomoly_detection_flyout.tsx | 4 +- .../ml/anomaly_detection/flyout_home.tsx | 113 +++---- .../ml/anomaly_detection/job_setup_screen.tsx | 3 +- 7 files changed, 247 insertions(+), 444 deletions(-) delete mode 100644 x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts index a9f2671de82598..e36f38add641aa 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts @@ -33,11 +33,11 @@ export interface ModuleDescriptor<JobType extends string> { partitionField?: string ) => Promise<SetupMlModuleResponsePayload>; cleanUpModule: (spaceId: string, sourceId: string) => Promise<DeleteJobsResponsePayload>; - validateSetupIndices: ( + validateSetupIndices?: ( indices: string[], timestampField: string ) => Promise<ValidationIndicesResponsePayload>; - validateSetupDatasets: ( + validateSetupDatasets?: ( indices: string[], timestampField: string, startTime: number, diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts deleted file mode 100644 index 0dfe3b301f2400..00000000000000 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_setup_state.ts +++ /dev/null @@ -1,289 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { isEqual } from 'lodash'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { usePrevious } from 'react-use'; -import { - combineDatasetFilters, - DatasetFilter, - filterDatasetFilter, - isExampleDataIndex, -} from '../../../common/infra_ml'; -import { - AvailableIndex, - ValidationIndicesError, - ValidationUIError, -} from '../../components/logging/log_analysis_setup/initial_configuration_step'; -import { useTrackedPromise } from '../../utils/use_tracked_promise'; -import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; - -type SetupHandler = ( - indices: string[], - startTime: number | undefined, - endTime: number | undefined, - datasetFilter: DatasetFilter -) => void; - -interface AnalysisSetupStateArguments<JobType extends string> { - cleanUpAndSetUpModule: SetupHandler; - moduleDescriptor: ModuleDescriptor<JobType>; - setUpModule: SetupHandler; - sourceConfiguration: ModuleSourceConfiguration; -} - -const fourWeeksInMs = 86400000 * 7 * 4; - -export const useAnalysisSetupState = <JobType extends string>({ - cleanUpAndSetUpModule, - moduleDescriptor: { validateSetupDatasets, validateSetupIndices }, - setUpModule, - sourceConfiguration, -}: AnalysisSetupStateArguments<JobType>) => { - const [startTime, setStartTime] = useState<number | undefined>(Date.now() - fourWeeksInMs); - const [endTime, setEndTime] = useState<number | undefined>(undefined); - - const isTimeRangeValid = useMemo( - () => (startTime != null && endTime != null ? startTime < endTime : true), - [endTime, startTime] - ); - - const [validatedIndices, setValidatedIndices] = useState<AvailableIndex[]>( - sourceConfiguration.indices.map((indexName) => ({ - name: indexName, - validity: 'unknown' as const, - })) - ); - - const updateIndicesWithValidationErrors = useCallback( - (validationErrors: ValidationIndicesError[]) => - setValidatedIndices((availableIndices) => - availableIndices.map((previousAvailableIndex) => { - const indexValiationErrors = validationErrors.filter( - ({ index }) => index === previousAvailableIndex.name - ); - - if (indexValiationErrors.length > 0) { - return { - validity: 'invalid', - name: previousAvailableIndex.name, - errors: indexValiationErrors, - }; - } else if (previousAvailableIndex.validity === 'valid') { - return { - ...previousAvailableIndex, - validity: 'valid', - errors: [], - }; - } else { - return { - validity: 'valid', - name: previousAvailableIndex.name, - isSelected: !isExampleDataIndex(previousAvailableIndex.name), - availableDatasets: [], - datasetFilter: { - type: 'includeAll' as const, - }, - }; - } - }) - ), - [] - ); - - const updateIndicesWithAvailableDatasets = useCallback( - (availableDatasets: Array<{ indexName: string; datasets: string[] }>) => - setValidatedIndices((availableIndices) => - availableIndices.map((previousAvailableIndex) => { - if (previousAvailableIndex.validity !== 'valid') { - return previousAvailableIndex; - } - - const availableDatasetsForIndex = availableDatasets.filter( - ({ indexName }) => indexName === previousAvailableIndex.name - ); - const newAvailableDatasets = availableDatasetsForIndex.flatMap( - ({ datasets }) => datasets - ); - - // filter out datasets that have disappeared if this index' datasets were updated - const newDatasetFilter: DatasetFilter = - availableDatasetsForIndex.length > 0 - ? filterDatasetFilter(previousAvailableIndex.datasetFilter, (dataset) => - newAvailableDatasets.includes(dataset) - ) - : previousAvailableIndex.datasetFilter; - - return { - ...previousAvailableIndex, - availableDatasets: newAvailableDatasets, - datasetFilter: newDatasetFilter, - }; - }) - ), - [] - ); - - const validIndexNames = useMemo( - () => validatedIndices.filter((index) => index.validity === 'valid').map((index) => index.name), - [validatedIndices] - ); - - const selectedIndexNames = useMemo( - () => - validatedIndices - .filter((index) => index.validity === 'valid' && index.isSelected) - .map((i) => i.name), - [validatedIndices] - ); - - const datasetFilter = useMemo( - () => - validatedIndices - .flatMap((validatedIndex) => - validatedIndex.validity === 'valid' - ? validatedIndex.datasetFilter - : { type: 'includeAll' as const } - ) - .reduce(combineDatasetFilters, { type: 'includeAll' as const }), - [validatedIndices] - ); - - const [validateIndicesRequest, validateIndices] = useTrackedPromise( - { - cancelPreviousOn: 'resolution', - createPromise: async () => { - return await validateSetupIndices( - sourceConfiguration.indices, - sourceConfiguration.timestampField - ); - }, - onResolve: ({ data: { errors } }) => { - updateIndicesWithValidationErrors(errors); - }, - onReject: () => { - setValidatedIndices([]); - }, - }, - [sourceConfiguration.indices, sourceConfiguration.timestampField] - ); - - const [validateDatasetsRequest, validateDatasets] = useTrackedPromise( - { - cancelPreviousOn: 'resolution', - createPromise: async () => { - if (validIndexNames.length === 0) { - return { data: { datasets: [] } }; - } - - return await validateSetupDatasets( - validIndexNames, - sourceConfiguration.timestampField, - startTime ?? 0, - endTime ?? Date.now() - ); - }, - onResolve: ({ data: { datasets } }) => { - updateIndicesWithAvailableDatasets(datasets); - }, - }, - [validIndexNames, sourceConfiguration.timestampField, startTime, endTime] - ); - - const setUp = useCallback(() => { - return setUpModule(selectedIndexNames, startTime, endTime, datasetFilter); - }, [setUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); - - const cleanUpAndSetUp = useCallback(() => { - return cleanUpAndSetUpModule(selectedIndexNames, startTime, endTime, datasetFilter); - }, [cleanUpAndSetUpModule, selectedIndexNames, startTime, endTime, datasetFilter]); - - const isValidating = useMemo( - () => validateIndicesRequest.state === 'pending' || validateDatasetsRequest.state === 'pending', - [validateDatasetsRequest.state, validateIndicesRequest.state] - ); - - const validationErrors = useMemo<ValidationUIError[]>(() => { - if (isValidating) { - return []; - } - - return [ - // validate request status - ...(validateIndicesRequest.state === 'rejected' || - validateDatasetsRequest.state === 'rejected' - ? [{ error: 'NETWORK_ERROR' as const }] - : []), - // validation request results - ...validatedIndices.reduce<ValidationUIError[]>((errors, index) => { - return index.validity === 'invalid' && selectedIndexNames.includes(index.name) - ? [...errors, ...index.errors] - : errors; - }, []), - // index count - ...(selectedIndexNames.length === 0 ? [{ error: 'TOO_FEW_SELECTED_INDICES' as const }] : []), - // time range - ...(!isTimeRangeValid ? [{ error: 'INVALID_TIME_RANGE' as const }] : []), - ]; - }, [ - isValidating, - validateIndicesRequest.state, - validateDatasetsRequest.state, - validatedIndices, - selectedIndexNames, - isTimeRangeValid, - ]); - - const prevStartTime = usePrevious(startTime); - const prevEndTime = usePrevious(endTime); - const prevValidIndexNames = usePrevious(validIndexNames); - - useEffect(() => { - if (!isTimeRangeValid) { - return; - } - - validateIndices(); - }, [isTimeRangeValid, validateIndices]); - - useEffect(() => { - if (!isTimeRangeValid) { - return; - } - - if ( - startTime !== prevStartTime || - endTime !== prevEndTime || - !isEqual(validIndexNames, prevValidIndexNames) - ) { - validateDatasets(); - } - }, [ - endTime, - isTimeRangeValid, - prevEndTime, - prevStartTime, - prevValidIndexNames, - startTime, - validIndexNames, - validateDatasets, - ]); - - return { - cleanUpAndSetUp, - datasetFilter, - endTime, - isValidating, - selectedIndexNames, - setEndTime, - setStartTime, - setUp, - startTime, - validatedIndices, - setValidatedIndices, - validationErrors, - }; -}; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts index cec87fb1144e33..7ea87c3d21322d 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts @@ -10,17 +10,27 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; -import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; -import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; import { metricsHostsJobTypes, getJobId, MetricsHostsJobType, DatasetFilter, bucketSpan, - partitionField, } from '../../../../../common/infra_ml'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/hosts_network_out.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_hosts/ml/datafeed_hosts_network_out.json'; +type JobType = 'hosts_memory_usage' | 'hosts_network_in' | 'hosts_network_out'; const moduleId = 'metrics_ui_hosts'; const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { defaultMessage: 'Metrics anomanly detection', @@ -54,23 +64,68 @@ const setUpModule = async ( end: number | undefined, datasetFilter: DatasetFilter, { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - pField?: string + partitionField?: string ) => { const indexNamePattern = indices.join(','); - const jobIds = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; - const jobOverrides = jobIds.map((id) => ({ - job_id: id, - data_description: { - time_field: timestampField, - }, - custom_settings: { - metrics_source_config: { - indexPattern: indexNamePattern, - timestampField, - bucketSpan, + const jobIds: JobType[] = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; + + const jobOverrides = jobIds.map((id) => { + const { job: defaultJobConfig } = getDefaultJobConfigs(id); + + // eslint-disable-next-line @typescript-eslint/naming-convention + const analysis_config: any = { + ...defaultJobConfig.analysis_config, + }; + + if (partitionField) { + analysis_config.detectors[0].partition_field_name = partitionField; + if (analysis_config.influencers.indexOf(partitionField) === -1) { + analysis_config.influencers.push(partitionField); + } + } + + return { + job_id: id, + data_description: { + time_field: timestampField, + }, + analysis_config, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + }; + }); + + const datafeedOverrides = jobIds.map((id) => { + const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id); + + if (!partitionField || id === 'hosts_memory_usage') { + // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field + return defaultDatafeedConfig; + } + + // If we have a partition field, we need to change the aggregation to do a terms agg at the top level + const aggregations = { + [partitionField]: { + terms: { + field: partitionField, + }, + aggregations: { + ...defaultDatafeedConfig.aggregations, + }, }, - }, - })); + }; + + return { + ...defaultDatafeedConfig, + job_id: id, + aggregations, + }; + }); return callSetupMlModuleAPI( moduleId, @@ -80,34 +135,32 @@ const setUpModule = async ( sourceId, indexNamePattern, jobOverrides, - [] + datafeedOverrides ); }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); -}; - -const validateSetupIndices = async (indices: string[], timestampField: string) => { - return await callValidateIndicesAPI(indices, [ - { - name: timestampField, - validTypes: ['date'], - }, - { - name: partitionField, - validTypes: ['keyword'], - }, - ]); +const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => { + switch (jobId) { + case 'hosts_memory_usage': + return { + datafeed: MemoryDatafeed, + job: MemoryJob, + }; + case 'hosts_network_in': + return { + datafeed: NetworkInDatafeed, + job: NetworkInJob, + }; + case 'hosts_network_out': + return { + datafeed: NetworkOutDatafeed, + job: NetworkOutJob, + }; + } }; -const validateSetupDatasets = async ( - indices: string[], - timestampField: string, - startTime: number, - endTime: number -) => { - return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); }; export const metricHostsModule: ModuleDescriptor<MetricsHostsJobType> = { @@ -121,6 +174,4 @@ export const metricHostsModule: ModuleDescriptor<MetricsHostsJobType> = { getModuleDefinition, setUpModule, cleanUpModule, - validateSetupDatasets, - validateSetupIndices, }; diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts index cbcff1c307af6e..eaf7489c84eb4d 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts @@ -10,17 +10,28 @@ import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; import { callSetupMlModuleAPI } from '../../api/ml_setup_module_api'; -import { callValidateIndicesAPI } from '../../../logs/log_analysis/api/validate_indices'; -import { callValidateDatasetsAPI } from '../../../logs/log_analysis/api/validate_datasets'; import { metricsK8SJobTypes, getJobId, MetricK8sJobType, DatasetFilter, bucketSpan, - partitionField, } from '../../../../../common/infra_ml'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import MemoryDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_memory_usage.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkInDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_in.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutJob from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/k8s_network_out.json'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import NetworkOutDatafeed from '../../../../../../ml/server/models/data_recognizer/modules/metrics_ui_k8s/ml/datafeed_k8s_network_out.json'; +type JobType = 'k8s_memory_usage' | 'k8s_network_in' | 'k8s_network_out'; +export const DEFAULT_K8S_PARTITION_FIELD = 'kubernetes.namespace'; const moduleId = 'metrics_ui_k8s'; const moduleName = i18n.translate('xpack.infra.ml.metricsModuleName', { defaultMessage: 'Metrics anomanly detection', @@ -54,26 +65,72 @@ const setUpModule = async ( end: number | undefined, datasetFilter: DatasetFilter, { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - pField?: string + partitionField?: string ) => { const indexNamePattern = indices.join(','); - const jobIds = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; - const jobOverrides = jobIds.map((id) => ({ - job_id: id, - analysis_config: { - bucket_span: `${bucketSpan}ms`, - }, - data_description: { - time_field: timestampField, - }, - custom_settings: { - metrics_source_config: { - indexPattern: indexNamePattern, - timestampField, - bucketSpan, + const jobIds: JobType[] = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; + const jobOverrides = jobIds.map((id) => { + const { job: defaultJobConfig } = getDefaultJobConfigs(id); + + // eslint-disable-next-line @typescript-eslint/naming-convention + const analysis_config: any = { + ...defaultJobConfig.analysis_config, + }; + + if (partitionField) { + analysis_config.detectors[0].partition_field_name = partitionField; + if (analysis_config.influencers.indexOf(partitionField) === -1) { + analysis_config.influencers.push(partitionField); + } + } + + return { + job_id: id, + data_description: { + time_field: timestampField, + }, + analysis_config, + custom_settings: { + metrics_source_config: { + indexPattern: indexNamePattern, + timestampField, + bucketSpan, + }, + }, + }; + }); + + const datafeedOverrides = jobIds.map((id) => { + const { datafeed: defaultDatafeedConfig } = getDefaultJobConfigs(id); + + if (!partitionField || id === 'k8s_memory_usage') { + // Since the host memory usage doesn't have custom aggs, we don't need to do anything to add a partition field + return defaultDatafeedConfig; + } + + // Because the ML K8s jobs ship with a default partition field of {kubernetes.namespace}, ignore that agg and wrap it in our own agg. + const innerAggregation = + defaultDatafeedConfig.aggregations[DEFAULT_K8S_PARTITION_FIELD].aggregations; + + // If we have a partition field, we need to change the aggregation to do a terms agg to partition the data at the top level + const aggregations = { + [partitionField]: { + terms: { + field: partitionField, + size: 25, // 25 is arbitratry and only used to keep the number of buckets to a managable level in the event that the user choose a high cardinality partition field. + }, + aggregations: { + ...innerAggregation, + }, }, - }, - })); + }; + + return { + ...defaultDatafeedConfig, + job_id: id, + aggregations, + }; + }); return callSetupMlModuleAPI( moduleId, @@ -83,34 +140,32 @@ const setUpModule = async ( sourceId, indexNamePattern, jobOverrides, - [] + datafeedOverrides ); }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); -}; - -const validateSetupIndices = async (indices: string[], timestampField: string) => { - return await callValidateIndicesAPI(indices, [ - { - name: timestampField, - validTypes: ['date'], - }, - { - name: partitionField, - validTypes: ['keyword'], - }, - ]); +const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => { + switch (jobId) { + case 'k8s_memory_usage': + return { + datafeed: MemoryDatafeed, + job: MemoryJob, + }; + case 'k8s_network_in': + return { + datafeed: NetworkInDatafeed, + job: NetworkInJob, + }; + case 'k8s_network_out': + return { + datafeed: NetworkOutDatafeed, + job: NetworkOutJob, + }; + } }; -const validateSetupDatasets = async ( - indices: string[], - timestampField: string, - startTime: number, - endTime: number -) => { - return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); +const cleanUpModule = async (spaceId: string, sourceId: string) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); }; export const metricHostsModule: ModuleDescriptor<MetricK8sJobType> = { @@ -124,6 +179,4 @@ export const metricHostsModule: ModuleDescriptor<MetricK8sJobType> = { getModuleDefinition, setUpModule, cleanUpModule, - validateSetupDatasets, - validateSetupIndices, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx index b063713fa2c971..b5d224910e819d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/anomoly_detection_flyout.tsx @@ -50,10 +50,10 @@ export const AnomalyDetectionFlyout = () => { return ( <> - <EuiButtonEmpty iconSide={'right'} onClick={openFlyout}> + <EuiButtonEmpty iconSide={'left'} iconType={'inspect'} onClick={openFlyout}> <FormattedMessage id="xpack.infra.ml.anomalyDetectionButton" - defaultMessage="Anomaly Detection" + defaultMessage="Anomaly detection" /> </EuiButtonEmpty> {showFlyout && ( diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx index 801dff9c4a17a5..5b520084ebb744 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/flyout_home.tsx @@ -5,7 +5,7 @@ */ import React, { useState, useCallback, useEffect } from 'react'; -import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiTabs, EuiTab, EuiSpacer } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiText, EuiFlexGroup, EuiFlexItem, EuiCard, EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -30,7 +30,7 @@ interface Props { } export const FlyoutHome = (props: Props) => { - const [tab, setTab] = useState<'jobs' | 'anomalies'>('jobs'); + const [tab] = useState<'jobs' | 'anomalies'>('jobs'); const { goToSetup } = props; const { fetchJobStatus: fetchHostJobStatus, @@ -56,18 +56,10 @@ export const FlyoutHome = (props: Props) => { goToSetup('kubernetes'); }, [goToSetup]); - const goToJobs = useCallback(() => { - setTab('jobs'); - }, []); - const jobIds = [ ...(k8sJobSummaries || []).map((k) => k.id), ...(hostJobSummaries || []).map((h) => h.id), ]; - const anomaliesUrl = useLinkProps({ - app: 'ml', - pathname: `/explorer?_g=${createResultsUrl(jobIds)}`, - }); useEffect(() => { if (hasInfraMLReadCapabilities) { @@ -105,30 +97,24 @@ export const FlyoutHome = (props: Props) => { </EuiFlyoutHeader> <EuiFlyoutBody> - <EuiTabs> - <EuiTab isSelected={tab === 'jobs'} onClick={goToJobs}> - <FormattedMessage - defaultMessage="Jobs" - id="xpack.infra.ml.anomalyFlyout.jobsTabLabel" - /> - </EuiTab> - <EuiTab - disabled={jobIds.length === 0} - isSelected={tab === 'anomalies'} - {...anomaliesUrl} - > - <FormattedMessage - defaultMessage="Anomalies" - id="xpack.infra.ml.anomalyFlyout.anomaliesTabLabel" - /> - </EuiTab> - </EuiTabs> + <div> + <EuiText> + <p> + <FormattedMessage + defaultMessage="Anomaly detection is powered by machine learning. Machine learning jobs are available for the following resource types. Enable these jobs to begin detecting anomalies in your infrastructure metrics." + id="xpack.infra.ml.anomalyFlyout.create.description" + /> + </p> + </EuiText> + </div> + <EuiSpacer size="l" /> {hostJobSummaries.length > 0 && ( <> <JobsEnabledCallout hasHostJobs={hostJobSummaries.length > 0} hasK8sJobs={k8sJobSummaries.length > 0} + jobIds={jobIds} /> <EuiSpacer size="l" /> </> @@ -151,6 +137,7 @@ export const FlyoutHome = (props: Props) => { interface CalloutProps { hasHostJobs: boolean; hasK8sJobs: boolean; + jobIds: string[]; } const JobsEnabledCallout = (props: CalloutProps) => { let target = ''; @@ -175,8 +162,34 @@ const JobsEnabledCallout = (props: CalloutProps) => { pathname: '/jobs', }); + const anomaliesUrl = useLinkProps({ + app: 'ml', + pathname: `/explorer?_g=${createResultsUrl(props.jobIds)}`, + }); + return ( <> + <EuiFlexGroup gutterSize={'s'}> + <EuiFlexItem grow={false}> + <EuiButton {...manageJobsLinkProps} style={{ marginRight: 5 }}> + <FormattedMessage + defaultMessage="Manage jobs" + id="xpack.infra.ml.anomalyFlyout.manageJobs" + /> + </EuiButton> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiButton {...anomaliesUrl}> + <FormattedMessage + defaultMessage="View anomalies" + id="xpack.infra.ml.anomalyFlyout.anomaliesTabLabel" + /> + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + + <EuiSpacer size="l" /> + <EuiCallOut size="m" color="success" @@ -189,13 +202,6 @@ const JobsEnabledCallout = (props: CalloutProps) => { } iconType="check" /> - <EuiSpacer size="l" /> - <EuiButton {...manageJobsLinkProps}> - <FormattedMessage - defaultMessage="Manage Jobs" - id="xpack.infra.ml.anomalyFlyout.manageJobs" - /> - </EuiButton> </> ); }; @@ -211,30 +217,11 @@ interface CreateJobTab { const CreateJobTab = (props: CreateJobTab) => { return ( <> - <div> - <EuiText> - <h3> - <FormattedMessage - defaultMessage="Create ML Jobs" - id="xpack.infra.ml.anomalyFlyout.create.jobsTitle" - /> - </h3> - </EuiText> - <EuiText> - <p> - <FormattedMessage - defaultMessage="Machine Learning jobs are available for the following resource types. Enable these jobs to begin detecting anomalies in your infrastructure metrics" - id="xpack.infra.ml.anomalyFlyout.create.description" - /> - </p> - </EuiText> - </div> - - <EuiSpacer size="l" /> + {/* <EuiSpacer size="l" /> */} <EuiFlexGroup gutterSize={'m'}> <EuiFlexItem> <EuiCard - // isDisabled={props.hasSetupCapabilities} + isDisabled={!props.hasSetupCapabilities} icon={<EuiIcon type={'storage'} />} // title="Hosts" title={ @@ -245,7 +232,7 @@ const CreateJobTab = (props: CreateJobTab) => { } description={ <FormattedMessage - defaultMessage="Detect anomalies for CPU usage, memory usage, network traffic, and load." + defaultMessage="Detect anomalies for memory usage and network traffic." id="xpack.infra.ml.anomalyFlyout.create.hostDescription" /> } @@ -254,7 +241,7 @@ const CreateJobTab = (props: CreateJobTab) => { {props.hasHostJobs && ( <EuiButtonEmpty onClick={props.createHosts}> <FormattedMessage - defaultMessage="Recreate Jobs" + defaultMessage="Recreate jobs" id="xpack.infra.ml.anomalyFlyout.create.recreateButton" /> </EuiButtonEmpty> @@ -262,7 +249,7 @@ const CreateJobTab = (props: CreateJobTab) => { {!props.hasHostJobs && ( <EuiButton onClick={props.createHosts}> <FormattedMessage - defaultMessage="Create Jobs" + defaultMessage="Enable" id="xpack.infra.ml.anomalyFlyout.create.createButton" /> </EuiButton> @@ -273,7 +260,7 @@ const CreateJobTab = (props: CreateJobTab) => { </EuiFlexItem> <EuiFlexItem> <EuiCard - // isDisabled={props.hasSetupCapabilities} + isDisabled={!props.hasSetupCapabilities} icon={<EuiIcon type={'logoKubernetes'} />} title={ <FormattedMessage @@ -283,7 +270,7 @@ const CreateJobTab = (props: CreateJobTab) => { } description={ <FormattedMessage - defaultMessage="Detect anomalies for CPU usage, memory usage, network traffic, and load." + defaultMessage="Detect anomalies for memory usage and network traffic." id="xpack.infra.ml.anomalyFlyout.create.k8sDescription" /> } @@ -292,7 +279,7 @@ const CreateJobTab = (props: CreateJobTab) => { {props.hasK8sJobs && ( <EuiButtonEmpty onClick={props.createK8s}> <FormattedMessage - defaultMessage="Recreate Jobs" + defaultMessage="Recreate jobs" id="xpack.infra.ml.anomalyFlyout.create.recreateButton" /> </EuiButtonEmpty> @@ -300,7 +287,7 @@ const CreateJobTab = (props: CreateJobTab) => { {!props.hasK8sJobs && ( <EuiButton onClick={props.createK8s}> <FormattedMessage - defaultMessage="Create Jobs" + defaultMessage="Enable" id="xpack.infra.ml.anomalyFlyout.create.createButton" /> </EuiButton> diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx index 428c002da63838..c327d187f6bc20 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/components/ml/anomaly_detection/job_setup_screen.tsx @@ -20,6 +20,7 @@ import { useSourceViaHttp } from '../../../../../../containers/source/use_source import { useMetricK8sModuleContext } from '../../../../../../containers/ml/modules/metrics_k8s/module'; import { useMetricHostsModuleContext } from '../../../../../../containers/ml/modules/metrics_hosts/module'; import { FixedDatePicker } from '../../../../../../components/fixed_datepicker'; +import { DEFAULT_K8S_PARTITION_FIELD } from '../../../../../../containers/ml/modules/metrics_k8s/module_descriptor'; interface Props { jobType: 'hosts' | 'kubernetes'; @@ -107,7 +108,7 @@ export const JobSetupScreen = (props: Props) => { useEffect(() => { if (props.jobType === 'kubernetes') { - setPartitionField(['kubernetes.namespace']); + setPartitionField([DEFAULT_K8S_PARTITION_FIELD]); } }, [props.jobType]); From 6c015cfbef12189eb5aec8fa42f5ea2743be2971 Mon Sep 17 00:00:00 2001 From: Nathan Reese <reese.nathan@gmail.com> Date: Thu, 1 Oct 2020 19:04:26 -0600 Subject: [PATCH 065/128] Convert VectorLayer to typescript (#78490) * [maps] convert VectorLayer to TS * more tslint fixes * clean up * more tslint fixes * more tslint fixes * remove unneeded casts * remove unneeded VectorStyle casts * revert changes to layer.getQuery * fix * update tile layer constructor * review feedback Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../data_request_descriptor_types.ts | 19 +- .../common/descriptor_types/map_descriptor.ts | 2 +- .../common/elasticsearch_util/es_agg_utils.ts | 4 +- .../public/actions/data_request_actions.ts | 2 +- .../maps/public/classes/joins/inner_join.d.ts | 23 +- .../plugins/maps/public/classes/joins/join.ts | 32 +- .../blended_vector_layer.ts | 11 +- .../layers/heatmap_layer/heatmap_layer.js | 12 + .../maps/public/classes/layers/layer.test.ts | 6 - .../maps/public/classes/layers/layer.tsx | 19 +- .../classes/layers/tile_layer/tile_layer.js | 15 +- .../tiled_vector_layer/tiled_vector_layer.tsx | 43 +-- .../layers/vector_layer/vector_layer.d.ts | 85 ----- .../{vector_layer.js => vector_layer.tsx} | 291 ++++++++++++------ .../sources/es_agg_source/es_agg_source.ts | 2 +- .../es_geo_grid_source.d.ts | 9 +- .../es_geo_grid_source.test.ts | 3 +- .../classes/sources/es_source/es_source.d.ts | 48 ++- .../classes/sources/es_source/es_source.js | 15 +- .../es_term_source/es_term_source.d.ts | 13 +- .../sources/es_term_source/es_term_source.js | 4 +- .../kibana_regionmap_source.js | 1 + .../mvt_single_layer_vector_source.tsx | 14 +- .../sources/vector_source/vector_source.d.ts | 28 +- .../classes/styles/vector/vector_style.tsx | 55 +++- .../toc_entry_actions_popover.test.tsx | 14 +- 26 files changed, 478 insertions(+), 292 deletions(-) delete mode 100644 x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts rename x-pack/plugins/maps/public/classes/layers/vector_layer/{vector_layer.js => vector_layer.tsx} (78%) diff --git a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts index f3521cca2e456f..16b60492c9b78c 100644 --- a/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts +++ b/x-pack/plugins/maps/common/descriptor_types/data_request_descriptor_types.ts @@ -38,15 +38,21 @@ export type VectorSourceRequestMeta = MapFilters & { applyGlobalQuery: boolean; fieldNames: string[]; geogridPrecision?: number; - sourceQuery: MapQuery; + sourceQuery?: MapQuery; sourceMeta: VectorSourceSyncMeta; }; +export type VectorJoinSourceRequestMeta = MapFilters & { + applyGlobalQuery: boolean; + fieldNames: string[]; + sourceQuery: MapQuery; +}; + export type VectorStyleRequestMeta = MapFilters & { dynamicStyleFields: string[]; isTimeAware: boolean; sourceQuery: MapQuery; - timeFilters: unknown; + timeFilters: TimeRange; }; export type ESSearchSourceResponseMeta = { @@ -59,9 +65,12 @@ export type ESSearchSourceResponseMeta = { }; // Partial because objects are justified downstream in constructors -export type DataMeta = Partial<VectorSourceRequestMeta> & - Partial<VectorStyleRequestMeta> & - Partial<ESSearchSourceResponseMeta>; +export type DataMeta = Partial< + VectorSourceRequestMeta & + VectorJoinSourceRequestMeta & + VectorStyleRequestMeta & + ESSearchSourceResponseMeta +>; type NumericalStyleFieldData = { avg: number; diff --git a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts index d064dfb1c4a372..b769b125cf0f81 100644 --- a/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts +++ b/x-pack/plugins/maps/common/descriptor_types/map_descriptor.ts @@ -17,7 +17,7 @@ export type MapExtent = { }; export type MapQuery = Query & { - queryLastTriggeredAt: string; + queryLastTriggeredAt?: string; }; export type MapRefreshConfig = { diff --git a/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts index 7828c3cc6410b5..f157ffe9f1c809 100644 --- a/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts +++ b/x-pack/plugins/maps/common/elasticsearch_util/es_agg_utils.ts @@ -33,8 +33,10 @@ export function addFieldToDSL(dsl: object, field: IFieldType) { }; } +export type BucketProperties = Record<string | number, unknown>; + export function extractPropertiesFromBucket(bucket: any, ignoreKeys: string[] = []) { - const properties: Record<string | number, unknown> = {}; + const properties: BucketProperties = {}; for (const key in bucket) { if (ignoreKeys.includes(key) || !bucket.hasOwnProperty(key)) { continue; diff --git a/x-pack/plugins/maps/public/actions/data_request_actions.ts b/x-pack/plugins/maps/public/actions/data_request_actions.ts index 14d81969005068..d7d9259e1539e4 100644 --- a/x-pack/plugins/maps/public/actions/data_request_actions.ts +++ b/x-pack/plugins/maps/public/actions/data_request_actions.ts @@ -47,7 +47,7 @@ const FIT_TO_BOUNDS_SCALE_FACTOR = 0.1; export type DataRequestContext = { startLoading(dataId: string, requestToken: symbol, meta: DataMeta): void; - stopLoading(dataId: string, requestToken: symbol, data: object, meta: DataMeta): void; + stopLoading(dataId: string, requestToken: symbol, data: object, meta?: DataMeta): void; onLoadError(dataId: string, requestToken: symbol, errorMessage: string): void; updateSourceData(newData: unknown): void; isRequestStillActive(dataId: string, requestToken: symbol): boolean; diff --git a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts index befff0965fb70c..3e2ceac4971c45 100644 --- a/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts +++ b/x-pack/plugins/maps/public/classes/joins/inner_join.d.ts @@ -4,19 +4,40 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Feature, GeoJsonProperties } from 'geojson'; import { IESTermSource } from '../sources/es_term_source'; -import { IJoin } from './join'; +import { IJoin, PropertiesMap } from './join'; import { JoinDescriptor } from '../../../common/descriptor_types'; import { ISource } from '../sources/source'; +import { ITooltipProperty } from '../tooltips/tooltip_property'; +import { IField } from '../fields/field'; export class InnerJoin implements IJoin { constructor(joinDescriptor: JoinDescriptor, leftSource: ISource); + destroy: () => void; + getRightJoinSource(): IESTermSource; toDescriptor(): JoinDescriptor; + getJoinFields: () => IField[]; + + getLeftField: () => IField; + + getIndexPatternIds: () => string[]; + + getQueryableIndexPatternIds: () => string[]; + + getSourceDataRequestId: () => string; + getSourceMetaDataRequestId(): string; getSourceFormattersDataRequestId(): string; + + getTooltipProperties(properties: GeoJsonProperties): Promise<ITooltipProperty[]>; + + hasCompleteConfig: () => boolean; + + joinPropertiesToFeature: (feature: Feature, propertiesMap?: PropertiesMap) => boolean; } diff --git a/x-pack/plugins/maps/public/classes/joins/join.ts b/x-pack/plugins/maps/public/classes/joins/join.ts index 5bcc4bfdec87e5..df6f6f684f4d2c 100644 --- a/x-pack/plugins/maps/public/classes/joins/join.ts +++ b/x-pack/plugins/maps/public/classes/joins/join.ts @@ -4,15 +4,39 @@ * you may not use this file except in compliance with the Elastic License. */ +import { Feature, GeoJsonProperties } from 'geojson'; import { IESTermSource } from '../sources/es_term_source'; import { JoinDescriptor } from '../../../common/descriptor_types'; +import { ITooltipProperty } from '../tooltips/tooltip_property'; +import { IField } from '../fields/field'; +import { BucketProperties } from '../../../common/elasticsearch_util'; + +export type PropertiesMap = Map<string, BucketProperties>; export interface IJoin { - getRightJoinSource(): IESTermSource; + destroy: () => void; + + getRightJoinSource: () => IESTermSource; + + toDescriptor: () => JoinDescriptor; + + getJoinFields: () => IField[]; + + getLeftField: () => IField; + + getIndexPatternIds: () => string[]; + + getQueryableIndexPatternIds: () => string[]; + + getSourceDataRequestId: () => string; + + getSourceMetaDataRequestId: () => string; + + getSourceFormattersDataRequestId: () => string; - toDescriptor(): JoinDescriptor; + getTooltipProperties: (properties: GeoJsonProperties) => Promise<ITooltipProperty[]>; - getSourceMetaDataRequestId(): string; + hasCompleteConfig: () => boolean; - getSourceFormattersDataRequestId(): string; + joinPropertiesToFeature: (feature: Feature, propertiesMap?: PropertiesMap) => boolean; } diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts index 90e8d25a77958d..9b6a67ac28ad09 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts @@ -37,7 +37,6 @@ import { LayerDescriptor, VectorLayerDescriptor, } from '../../../../common/descriptor_types'; -import { IStyle } from '../../styles/style'; import { IVectorSource } from '../../sources/vector_source'; const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID'; @@ -257,7 +256,7 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { return clonedDescriptor; } - getSource() { + getSource(): IVectorSource { return this._isClustered ? this._clusterSource : this._documentSource; } @@ -268,11 +267,11 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { return this._documentSource; } - getCurrentStyle(): IStyle { + getCurrentStyle(): IVectorStyle { return this._isClustered ? this._clusterStyle : this._documentStyle; } - getStyleForEditing(): IStyle { + getStyleForEditing(): IVectorStyle { return this._documentStyle; } @@ -281,8 +280,8 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { const requestToken = Symbol(`layer-active-count:${this.getId()}`); const searchFilters = this._getSearchFilters( syncContext.dataFilters, - this.getSource() as IVectorSource, - this.getCurrentStyle() as IVectorStyle + this.getSource(), + this.getCurrentStyle() ); const canSkipFetch = await canSkipSourceUpdate({ source: this.getSource(), diff --git a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js index adcc86b9d1546e..33e82db79f3cfa 100644 --- a/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/heatmap_layer/heatmap_layer.js @@ -31,6 +31,18 @@ export class HeatmapLayer extends VectorLayer { } } + getStyleForEditing() { + return this._style; + } + + getStyle() { + return this._style; + } + + getCurrentStyle() { + return this._style; + } + _getPropKeyOfSelectedMetric() { const metricfields = this.getSource().getMetricFields(); return metricfields[0].getName(); diff --git a/x-pack/plugins/maps/public/classes/layers/layer.test.ts b/x-pack/plugins/maps/public/classes/layers/layer.test.ts index 7bc91d71f83e22..76df7c2af840a7 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.test.ts +++ b/x-pack/plugins/maps/public/classes/layers/layer.test.ts @@ -7,7 +7,6 @@ import { AbstractLayer } from './layer'; import { ISource } from '../sources/source'; -import { IStyle } from '../styles/style'; import { AGG_TYPE, FIELD_ORIGIN, LAYER_STYLE_TYPE, VECTOR_STYLES } from '../../../common/constants'; import { ESTermSourceDescriptor, VectorStyleDescriptor } from '../../../common/descriptor_types'; import { getDefaultDynamicProperties } from '../styles/vector/vector_style_defaults'; @@ -38,8 +37,6 @@ class MockSource { } } -class MockStyle {} - describe('cloneDescriptor', () => { describe('with joins', () => { const styleDescriptor = { @@ -84,7 +81,6 @@ describe('cloneDescriptor', () => { const layer = new MockLayer({ layerDescriptor, source: (new MockSource() as unknown) as ISource, - style: (new MockStyle() as unknown) as IStyle, }); const clonedDescriptor = await layer.cloneDescriptor(); const clonedStyleProps = (clonedDescriptor.style as VectorStyleDescriptor).properties; @@ -122,7 +118,6 @@ describe('cloneDescriptor', () => { const layer = new MockLayer({ layerDescriptor, source: (new MockSource() as unknown) as ISource, - style: (new MockStyle() as unknown) as IStyle, }); const clonedDescriptor = await layer.cloneDescriptor(); const clonedStyleProps = (clonedDescriptor.style as VectorStyleDescriptor).properties; @@ -165,7 +160,6 @@ describe('isFittable', () => { const layer = new MockLayer({ layerDescriptor, source: (new MockSource({ fitToBounds: test.fitToBounds }) as unknown) as ISource, - style: (new MockStyle() as unknown) as IStyle, }); expect(await layer.isFittable()).toBe(test.canFit); }); diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index cd720063c67031..d6bd5180375ce5 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -110,13 +110,11 @@ export type CustomIconAndTooltipContent = { export interface ILayerArguments { layerDescriptor: LayerDescriptor; source: ISource; - style: IStyle; } export class AbstractLayer implements ILayer { protected readonly _descriptor: LayerDescriptor; protected readonly _source: ISource; - protected readonly _style: IStyle; protected readonly _dataRequests: DataRequest[]; static createDescriptor(options: Partial<LayerDescriptor>): LayerDescriptor { @@ -140,10 +138,9 @@ export class AbstractLayer implements ILayer { } } - constructor({ layerDescriptor, source, style }: ILayerArguments) { + constructor({ layerDescriptor, source }: ILayerArguments) { this._descriptor = AbstractLayer.createDescriptor(layerDescriptor); this._source = source; - this._style = style; if (this._descriptor.__dataRequests) { this._dataRequests = this._descriptor.__dataRequests.map( (dataRequest) => new DataRequest(dataRequest) @@ -257,11 +254,15 @@ export class AbstractLayer implements ILayer { } getStyleForEditing(): IStyle { - return this._style; + throw new Error('Should implement AbstractLayer#getStyleForEditing'); } - getStyle() { - return this._style; + getStyle(): IStyle { + throw new Error('Should implement AbstractLayer#getStyle'); + } + + getCurrentStyle(): IStyle { + throw new Error('Should implement AbstractLayer#getCurrentStyle'); } getLabel(): string { @@ -412,10 +413,6 @@ export class AbstractLayer implements ILayer { return this._descriptor.query ? this._descriptor.query : null; } - getCurrentStyle(): IStyle { - return this._style; - } - async getImmutableSourceProperties() { const source = this.getSource(); return await source.getImmutableProperties(); diff --git a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js index 3e2009c24a2e4d..fa60017f0eaf7b 100644 --- a/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/tile_layer/tile_layer.js @@ -21,7 +21,20 @@ export class TileLayer extends AbstractLayer { } constructor({ source, layerDescriptor }) { - super({ source, layerDescriptor, style: new TileStyle() }); + super({ source, layerDescriptor }); + this._style = new TileStyle(); + } + + getStyleForEditing() { + return this._style; + } + + getStyle() { + return this._style; + } + + getCurrentStyle() { + return this._style; } async syncData({ startLoading, stopLoading, onLoadError, dataFilters }) { diff --git a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx index 70bf8ea3883b72..68b9f2931f398c 100644 --- a/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tiled_vector_layer/tiled_vector_layer.tsx @@ -5,9 +5,14 @@ */ import React from 'react'; +import { + Map as MbMap, + GeoJSONSource as MbGeoJSONSource, + VectorSource as MbVectorSource, +} from 'mapbox-gl'; import { EuiIcon } from '@elastic/eui'; import { Feature } from 'geojson'; -import { VectorStyle } from '../../styles/vector/vector_style'; +import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style'; import { SOURCE_DATA_REQUEST_ID, LAYER_TYPE } from '../../../../common/constants'; import { VectorLayer, VectorLayerArguments } from '../vector_layer/vector_layer'; import { ITiledSingleLayerVectorSource } from '../../sources/vector_source'; @@ -59,7 +64,7 @@ export class TiledVectorLayer extends VectorLayer { const searchFilters: VectorSourceRequestMeta = this._getSearchFilters( dataFilters, this.getSource(), - this._style + this._style as IVectorStyle ); const prevDataRequest = this.getSourceDataRequest(); @@ -88,13 +93,12 @@ export class TiledVectorLayer extends VectorLayer { } async syncData(syncContext: DataRequestContext) { - await this._syncSourceStyleMeta(syncContext, this._source, this._style); - await this._syncSourceFormatters(syncContext, this._source, this._style); + await this._syncSourceStyleMeta(syncContext, this._source, this._style as IVectorStyle); + await this._syncSourceFormatters(syncContext, this._source, this._style as IVectorStyle); await this._syncMVTUrlTemplate(syncContext); } - _syncSourceBindingWithMb(mbMap: unknown) { - // @ts-expect-error + _syncSourceBindingWithMb(mbMap: MbMap) { const mbSource = mbMap.getSource(this._getMbSourceId()); if (mbSource) { return; @@ -113,7 +117,6 @@ export class TiledVectorLayer extends VectorLayer { } const mbSourceId = this._getMbSourceId(); - // @ts-expect-error mbMap.addSource(mbSourceId, { type: 'vector', tiles: [sourceMeta.urlTemplate], @@ -126,7 +129,7 @@ export class TiledVectorLayer extends VectorLayer { return this._getMbSourceId() === mbSourceId; } - _syncStylePropertiesWithMb(mbMap: unknown) { + _syncStylePropertiesWithMb(mbMap: MbMap) { // @ts-ignore const mbSource = mbMap.getSource(this._getMbSourceId()); if (!mbSource) { @@ -146,12 +149,16 @@ export class TiledVectorLayer extends VectorLayer { this._setMbLinePolygonProperties(mbMap, sourceMeta.layerName); } - _requiresPrevSourceCleanup(mbMap: unknown): boolean { - // @ts-expect-error - const mbTileSource = mbMap.getSource(this._getMbSourceId()); - if (!mbTileSource) { + _requiresPrevSourceCleanup(mbMap: MbMap): boolean { + const mbSource = mbMap.getSource(this._getMbSourceId()) as MbVectorSource | MbGeoJSONSource; + if (!mbSource) { return false; } + if (!('tiles' in mbSource)) { + // Expected source is not compatible, so remove. + return true; + } + const mbTileSource = mbSource as MbVectorSource; const dataRequest = this.getSourceDataRequest(); if (!dataRequest) { @@ -163,13 +170,8 @@ export class TiledVectorLayer extends VectorLayer { return false; } - if (!mbTileSource.tiles) { - // Expected source is not compatible, so remove. - return true; - } - const isSourceDifferent = - mbTileSource.tiles[0] !== tiledSourceMeta.urlTemplate || + mbTileSource.tiles?.[0] !== tiledSourceMeta.urlTemplate || mbTileSource.minzoom !== tiledSourceMeta.minSourceZoom || mbTileSource.maxzoom !== tiledSourceMeta.maxSourceZoom; @@ -179,9 +181,8 @@ export class TiledVectorLayer extends VectorLayer { const layerIds = this.getMbLayerIds(); for (let i = 0; i < layerIds.length; i++) { - // @ts-expect-error const mbLayer = mbMap.getLayer(layerIds[i]); - if (mbLayer && mbLayer.sourceLayer !== tiledSourceMeta.layerName) { + if (mbLayer && mbLayer['source-layer'] !== tiledSourceMeta.layerName) { // If the source-pointer of one of the layers is stale, they will all be stale. // In this case, all the mb-layers need to be removed and re-added. return true; @@ -191,7 +192,7 @@ export class TiledVectorLayer extends VectorLayer { return false; } - syncLayerWithMB(mbMap: unknown) { + syncLayerWithMB(mbMap: MbMap) { this._removeStaleMbSourcesAndLayers(mbMap); this._syncSourceBindingWithMb(mbMap); this._syncStylePropertiesWithMb(mbMap); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts deleted file mode 100644 index fa614ae87b2905..00000000000000 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.d.ts +++ /dev/null @@ -1,85 +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; - * you may not use this file except in compliance with the Elastic License. - */ -/* eslint-disable @typescript-eslint/consistent-type-definitions */ - -import { Feature, GeoJsonProperties } from 'geojson'; -import { AbstractLayer } from '../layer'; -import { IVectorSource } from '../../sources/vector_source'; -import { - MapFilters, - VectorLayerDescriptor, - VectorSourceRequestMeta, -} from '../../../../common/descriptor_types'; -import { ILayer } from '../layer'; -import { IJoin } from '../../joins/join'; -import { IVectorStyle } from '../../styles/vector/vector_style'; -import { IField } from '../../fields/field'; -import { DataRequestContext } from '../../../actions'; -import { ITooltipProperty } from '../../tooltips/tooltip_property'; - -export type VectorLayerArguments = { - source: IVectorSource; - joins?: IJoin[]; - layerDescriptor: VectorLayerDescriptor; -}; - -export interface IVectorLayer extends ILayer { - getFields(): Promise<IField[]>; - getStyleEditorFields(): Promise<IField[]>; - getJoins(): IJoin[]; - getValidJoins(): IJoin[]; - getSource(): IVectorSource; - getFeatureById(id: string | number): Feature | null; - getPropertiesForTooltip(properties: GeoJsonProperties): Promise<ITooltipProperty[]>; - hasJoins(): boolean; -} - -export class VectorLayer extends AbstractLayer implements IVectorLayer { - static type: string; - - protected readonly _style: IVectorStyle; - static createDescriptor( - options: Partial<VectorLayerDescriptor>, - mapColors?: string[] - ): VectorLayerDescriptor; - - constructor(options: VectorLayerArguments); - getLayerTypeIconName(): string; - getFields(): Promise<IField[]>; - getStyleEditorFields(): Promise<IField[]>; - getJoins(): IJoin[]; - getValidJoins(): IJoin[]; - _syncSourceStyleMeta( - syncContext: DataRequestContext, - source: IVectorSource, - style: IVectorStyle - ): Promise<void>; - _syncSourceFormatters( - syncContext: DataRequestContext, - source: IVectorSource, - style: IVectorStyle - ): Promise<void>; - syncLayerWithMB(mbMap: unknown): void; - _getSearchFilters( - dataFilters: MapFilters, - source: IVectorSource, - style: IVectorStyle - ): VectorSourceRequestMeta; - _syncData( - syncContext: DataRequestContext, - source: IVectorSource, - style: IVectorStyle - ): Promise<void>; - ownsMbSourceId(sourceId: string): boolean; - ownsMbLayerId(sourceId: string): boolean; - _setMbPointsProperties(mbMap: unknown, mvtSourceLayer?: string): void; - _setMbLinePolygonProperties(mbMap: unknown, mvtSourceLayer?: string): void; - getSource(): IVectorSource; - getFeatureById(id: string | number): Feature | null; - getPropertiesForTooltip(properties: GeoJsonProperties): Promise<ITooltipProperty[]>; - hasJoins(): boolean; - isFittable(): Promise<boolean>; -} diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx similarity index 78% rename from x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js rename to x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index 27c344b713a60f..a2532d4e7b10ef 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.js +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -5,8 +5,13 @@ */ import React from 'react'; +import { Map as MbMap, Layer as MbLayer, GeoJSONSource as MbGeoJSONSource } from 'mapbox-gl'; +import { Feature, FeatureCollection, GeoJsonProperties } from 'geojson'; +import _ from 'lodash'; +import { EuiIcon } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { AbstractLayer } from '../layer'; -import { VectorStyle } from '../../styles/vector/vector_style'; +import { IVectorStyle, VectorStyle } from '../../styles/vector/vector_style'; import { FEATURE_ID_PROPERTY_NAME, SOURCE_DATA_REQUEST_ID, @@ -20,11 +25,9 @@ import { FIELD_ORIGIN, LAYER_STYLE_TYPE, KBN_TOO_MANY_FEATURES_IMAGE_ID, + FieldFormatter, } from '../../../../common/constants'; -import _ from 'lodash'; import { JoinTooltipProperty } from '../../tooltips/join_tooltip_property'; -import { EuiIcon } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import { DataRequestAbortError } from '../../util/data_request'; import { canSkipSourceUpdate, @@ -39,15 +42,66 @@ import { getPointFilterExpression, } from '../../util/mb_filter_expressions'; +import { + DynamicStylePropertyOptions, + MapFilters, + MapQuery, + VectorLayerDescriptor, + VectorSourceRequestMeta, + VectorStyleRequestMeta, +} from '../../../../common/descriptor_types'; +import { IVectorSource } from '../../sources/vector_source'; +import { CustomIconAndTooltipContent, ILayer } from '../layer'; +import { IJoin, PropertiesMap } from '../../joins/join'; +import { IField } from '../../fields/field'; +import { DataRequestContext } from '../../../actions'; +import { ITooltipProperty } from '../../tooltips/tooltip_property'; +import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; +import { IESSource } from '../../sources/es_source'; + +interface SourceResult { + refreshed: boolean; + featureCollection?: FeatureCollection; +} + +interface JoinState { + dataHasChanged: boolean; + join: IJoin; + propertiesMap?: PropertiesMap; +} + +export interface VectorLayerArguments { + source: IVectorSource; + joins?: IJoin[]; + layerDescriptor: VectorLayerDescriptor; +} + +export interface IVectorLayer extends ILayer { + getFields(): Promise<IField[]>; + getStyleEditorFields(): Promise<IField[]>; + getJoins(): IJoin[]; + getValidJoins(): IJoin[]; + getSource(): IVectorSource; + getFeatureById(id: string | number): Feature | null; + getPropertiesForTooltip(properties: GeoJsonProperties): Promise<ITooltipProperty[]>; + hasJoins(): boolean; +} + export class VectorLayer extends AbstractLayer { static type = LAYER_TYPE.VECTOR; - static createDescriptor(options, mapColors) { + protected readonly _style: IVectorStyle; + private readonly _joins: IJoin[]; + + static createDescriptor( + options: Partial<VectorLayerDescriptor>, + mapColors?: string[] + ): VectorLayerDescriptor { const layerDescriptor = super.createDescriptor(options); layerDescriptor.type = VectorLayer.type; if (!options.style) { - const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors); + const styleProperties = VectorStyle.createDefaultStyleProperties(mapColors ? mapColors : []); layerDescriptor.style = VectorStyle.createDescriptor(styleProperties); } @@ -55,16 +109,31 @@ export class VectorLayer extends AbstractLayer { layerDescriptor.joins = []; } - return layerDescriptor; + return layerDescriptor as VectorLayerDescriptor; } - constructor({ layerDescriptor, source, joins = [] }) { - super({ layerDescriptor, source }); + constructor({ layerDescriptor, source, joins = [] }: VectorLayerArguments) { + super({ + layerDescriptor, + source, + }); this._joins = joins; - this._style = new VectorStyle(this._descriptor.style, source, this); + this._style = new VectorStyle(layerDescriptor.style, source, this); + } + + getSource(): IVectorSource { + return super.getSource() as IVectorSource; + } + + getStyleForEditing(): IVectorStyle { + return this._style; + } + + getStyle(): IVectorStyle { + return this._style; } - getStyle() { + getCurrentStyle(): IVectorStyle { return this._style; } @@ -108,7 +177,7 @@ export class VectorLayer extends AbstractLayer { return true; } - getCustomIconAndTooltipContent() { + getCustomIconAndTooltipContent(): CustomIconAndTooltipContent { const featureCollection = this._getSourceFeatureCollection(); const noResultsIcon = <EuiIcon size="m" color="subdued" type="minusInCircle" />; @@ -124,7 +193,7 @@ export class VectorLayer extends AbstractLayer { if ( this.getJoins().length && !featureCollection.features.some( - (feature) => feature.properties[FEATURE_VISIBLE_PROPERTY_NAME] + (feature) => feature.properties?.[FEATURE_VISIBLE_PROPERTY_NAME] ) ) { return { @@ -141,8 +210,8 @@ export class VectorLayer extends AbstractLayer { ); return { icon: this.getCurrentStyle().getIcon(), - tooltipContent: tooltipContent, - areResultsTrimmed: areResultsTrimmed, + tooltipContent, + areResultsTrimmed, }; } @@ -158,7 +227,12 @@ export class VectorLayer extends AbstractLayer { return this.getCurrentStyle().renderLegendDetails(); } - async getBounds({ startLoading, stopLoading, registerCancelCallback, dataFilters }) { + async getBounds({ + startLoading, + stopLoading, + registerCancelCallback, + dataFilters, + }: DataRequestContext) { const isStaticLayer = !this.getSource().isBoundsAware(); if (isStaticLayer || this.hasJoins()) { return getFeatureCollectionBounds(this._getSourceFeatureCollection(), this.hasJoins()); @@ -190,7 +264,7 @@ export class VectorLayer extends AbstractLayer { } finally { // Use stopLoading callback instead of onLoadError callback. // Function is loading bounds and not feature data. - stopLoading(SOURCE_BOUNDS_DATA_REQUEST_ID, requestToken, bounds, boundsFilters); + stopLoading(SOURCE_BOUNDS_DATA_REQUEST_ID, requestToken, bounds ? bounds : {}, boundsFilters); } return bounds; } @@ -205,7 +279,7 @@ export class VectorLayer extends AbstractLayer { } _getJoinFields() { - const joinFields = []; + const joinFields: IField[] = []; this.getValidJoins().forEach((join) => { const fields = join.getJoinFields(); joinFields.push(...fields); @@ -219,7 +293,7 @@ export class VectorLayer extends AbstractLayer { } async getStyleEditorFields() { - const sourceFields = await this.getSourceForEditing().getFields(); + const sourceFields = await (this.getSourceForEditing() as IVectorSource).getFields(); return [...sourceFields, ...this._getJoinFields()]; } @@ -246,7 +320,7 @@ export class VectorLayer extends AbstractLayer { onLoadError, registerCancelCallback, dataFilters, - }) { + }: { join: IJoin } & DataRequestContext): Promise<JoinState> { const joinSource = join.getRightJoinSource(); const sourceDataId = join.getSourceDataRequestId(); const requestToken = Symbol(`layer-join-refresh:${this.getId()} - ${sourceDataId}`); @@ -266,15 +340,15 @@ export class VectorLayer extends AbstractLayer { if (canSkipFetch) { return { dataHasChanged: false, - join: join, - propertiesMap: prevDataRequest.getData(), + join, + propertiesMap: prevDataRequest?.getData() as PropertiesMap, }; } try { startLoading(sourceDataId, requestToken, searchFilters); const leftSourceName = await this._source.getDisplayName(); - const { propertiesMap } = await joinSource.getPropertiesMap( + const propertiesMap = await joinSource.getPropertiesMap( searchFilters, leftSourceName, join.getLeftField().getName(), @@ -283,8 +357,8 @@ export class VectorLayer extends AbstractLayer { stopLoading(sourceDataId, requestToken, propertiesMap); return { dataHasChanged: true, - join: join, - propertiesMap: propertiesMap, + join, + propertiesMap, }; } catch (e) { if (!(e instanceof DataRequestAbortError)) { @@ -292,13 +366,12 @@ export class VectorLayer extends AbstractLayer { } return { dataHasChanged: true, - join: join, - propertiesMap: null, + join, }; } } - async _syncJoins(syncContext, style) { + async _syncJoins(syncContext: DataRequestContext, style: IVectorStyle) { const joinSyncs = this.getValidJoins().map(async (join) => { await this._syncJoinStyleMeta(syncContext, join, style); await this._syncJoinFormatters(syncContext, join, style); @@ -308,28 +381,37 @@ export class VectorLayer extends AbstractLayer { return await Promise.all(joinSyncs); } - _getSearchFilters(dataFilters, source, style) { + _getSearchFilters( + dataFilters: MapFilters, + source: IVectorSource, + style: IVectorStyle + ): VectorSourceRequestMeta { const fieldNames = [ ...source.getFieldNames(), ...(style.getType() === LAYER_STYLE_TYPE.VECTOR ? style.getSourceFieldNames() : []), ...this.getValidJoins().map((join) => join.getLeftField().getName()), ]; + const sourceQuery = this.getQuery() as MapQuery; return { ...dataFilters, fieldNames: _.uniq(fieldNames).sort(), geogridPrecision: source.getGeoGridPrecision(dataFilters.zoom), - sourceQuery: this.getQuery(), + sourceQuery: sourceQuery ? sourceQuery : undefined, applyGlobalQuery: source.getApplyGlobalQuery(), sourceMeta: source.getSyncMeta(), }; } - async _performInnerJoins(sourceResult, joinStates, updateSourceData) { - //should update the store if - //-- source result was refreshed - //-- any of the join configurations changed (joinState changed) - //-- visibility of any of the features has changed + async _performInnerJoins( + sourceResult: SourceResult, + joinStates: JoinState[], + updateSourceData: DataRequestContext['updateSourceData'] + ) { + // should update the store if + // -- source result was refreshed + // -- any of the join configurations changed (joinState changed) + // -- visibility of any of the features has changed let shouldUpdateStore = sourceResult.refreshed || joinStates.some((joinState) => joinState.dataHasChanged); @@ -338,8 +420,11 @@ export class VectorLayer extends AbstractLayer { return; } - for (let i = 0; i < sourceResult.featureCollection.features.length; i++) { - const feature = sourceResult.featureCollection.features[i]; + for (let i = 0; i < sourceResult.featureCollection!.features.length; i++) { + const feature = sourceResult.featureCollection!.features[i]; + if (!feature.properties) { + feature.properties = {}; + } const oldVisbility = feature.properties[FEATURE_VISIBLE_PROPERTY_NAME]; let isFeatureVisible = true; for (let j = 0; j < joinStates.length; j++) { @@ -364,7 +449,11 @@ export class VectorLayer extends AbstractLayer { } } - async _syncSource(syncContext, source, style) { + async _syncSource( + syncContext: DataRequestContext, + source: IVectorSource, + style: IVectorStyle + ): Promise<SourceResult> { const { startLoading, stopLoading, @@ -385,7 +474,9 @@ export class VectorLayer extends AbstractLayer { if (canSkipFetch) { return { refreshed: false, - featureCollection: prevDataRequest.getData(), + featureCollection: prevDataRequest + ? (prevDataRequest.getData() as FeatureCollection) + : EMPTY_FEATURE_COLLECTION, }; } @@ -416,15 +507,20 @@ export class VectorLayer extends AbstractLayer { } } - async _syncSourceStyleMeta(syncContext, source, style) { + async _syncSourceStyleMeta( + syncContext: DataRequestContext, + source: IVectorSource, + style: IVectorStyle + ) { if (this.getCurrentStyle().getType() !== LAYER_STYLE_TYPE.VECTOR) { return; } + const sourceQuery = this.getQuery() as MapQuery; return this._syncStyleMeta({ source, style, - sourceQuery: this.getQuery(), + sourceQuery: sourceQuery ? sourceQuery : undefined, dataRequestId: SOURCE_META_DATA_REQUEST_ID, dynamicStyleProps: style.getDynamicPropertiesArray().filter((dynamicStyleProp) => { return ( @@ -436,7 +532,7 @@ export class VectorLayer extends AbstractLayer { }); } - async _syncJoinStyleMeta(syncContext, join, style) { + async _syncJoinStyleMeta(syncContext: DataRequestContext, join: IJoin, style: IVectorStyle) { const joinSource = join.getRightJoinSource(); return this._syncStyleMeta({ source: joinSource, @@ -446,9 +542,7 @@ export class VectorLayer extends AbstractLayer { dynamicStyleProps: this.getCurrentStyle() .getDynamicPropertiesArray() .filter((dynamicStyleProp) => { - const matchingField = joinSource.getMetricFieldForName( - dynamicStyleProp.getField().getName() - ); + const matchingField = joinSource.getMetricFieldForName(dynamicStyleProp.getFieldName()); return ( dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField && @@ -470,13 +564,19 @@ export class VectorLayer extends AbstractLayer { stopLoading, onLoadError, registerCancelCallback, - }) { + }: { + dataRequestId: string; + dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>; + source: IVectorSource; + sourceQuery?: MapQuery; + style: IVectorStyle; + } & DataRequestContext) { if (!source.isESSource() || dynamicStyleProps.length === 0) { return; } const dynamicStyleFields = dynamicStyleProps.map((dynamicStyleProp) => { - return `${dynamicStyleProp.getField().getName()}${dynamicStyleProp.getNumberOfCategories()}`; + return `${dynamicStyleProp.getFieldName()}${dynamicStyleProp.getNumberOfCategories()}`; }); const nextMeta = { @@ -484,7 +584,7 @@ export class VectorLayer extends AbstractLayer { sourceQuery, isTimeAware: this.getCurrentStyle().isTimeAware() && (await source.isTimeAware()), timeFilters: dataFilters.timeFilters, - }; + } as VectorStyleRequestMeta; const prevDataRequest = this.getDataRequest(dataRequestId); const canSkipFetch = canSkipStyleMetaUpdate({ prevDataRequest, nextMeta }); if (canSkipFetch) { @@ -496,14 +596,14 @@ export class VectorLayer extends AbstractLayer { startLoading(dataRequestId, requestToken, nextMeta); const layerName = await this.getDisplayName(source); - //todo: cast source to ESSource when migrating to TS - const styleMeta = await source.loadStylePropsMeta( + const styleMeta = await (source as IESSource).loadStylePropsMeta({ layerName, style, dynamicStyleProps, - registerCancelCallback.bind(null, requestToken), - nextMeta - ); + registerCancelCallback: registerCancelCallback.bind(null, requestToken), + sourceQuery: nextMeta.sourceQuery, + timeFilters: nextMeta.timeFilters, + }); stopLoading(dataRequestId, requestToken, styleMeta, nextMeta); } catch (error) { if (!(error instanceof DataRequestAbortError)) { @@ -512,7 +612,11 @@ export class VectorLayer extends AbstractLayer { } } - async _syncSourceFormatters(syncContext, source, style) { + async _syncSourceFormatters( + syncContext: DataRequestContext, + source: IVectorSource, + style: IVectorStyle + ) { if (style.getType() !== LAYER_STYLE_TYPE.VECTOR) { return; } @@ -526,13 +630,13 @@ export class VectorLayer extends AbstractLayer { return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.SOURCE; }) .map((dynamicStyleProp) => { - return dynamicStyleProp.getField(); + return dynamicStyleProp.getField()!; }), ...syncContext, }); } - async _syncJoinFormatters(syncContext, join, style) { + async _syncJoinFormatters(syncContext: DataRequestContext, join: IJoin, style: IVectorStyle) { const joinSource = join.getRightJoinSource(); return this._syncFormatters({ source: joinSource, @@ -540,19 +644,28 @@ export class VectorLayer extends AbstractLayer { fields: style .getDynamicPropertiesArray() .filter((dynamicStyleProp) => { - const matchingField = joinSource.getMetricFieldForName( - dynamicStyleProp.getField().getName() - ); + const matchingField = joinSource.getMetricFieldForName(dynamicStyleProp.getFieldName()); return dynamicStyleProp.getFieldOrigin() === FIELD_ORIGIN.JOIN && !!matchingField; }) .map((dynamicStyleProp) => { - return dynamicStyleProp.getField(); + return dynamicStyleProp.getField()!; }), ...syncContext, }); } - async _syncFormatters({ source, dataRequestId, fields, startLoading, stopLoading, onLoadError }) { + async _syncFormatters({ + source, + dataRequestId, + fields, + startLoading, + stopLoading, + onLoadError, + }: { + dataRequestId: string; + fields: IField[]; + source: IVectorSource; + } & DataRequestContext) { if (fields.length === 0) { return; } @@ -573,7 +686,7 @@ export class VectorLayer extends AbstractLayer { try { startLoading(dataRequestId, requestToken, nextMeta); - const formatters = {}; + const formatters: { [key: string]: FieldFormatter | null } = {}; const promises = fields .filter((field) => { return field.canValueBeFormatted(); @@ -589,7 +702,7 @@ export class VectorLayer extends AbstractLayer { } } - async syncData(syncContext) { + async syncData(syncContext: DataRequestContext) { await this._syncData(syncContext, this.getSource(), this.getCurrentStyle()); } @@ -603,7 +716,7 @@ export class VectorLayer extends AbstractLayer { // Given 1 above, which source/style to use can not be stored in Layer instance state. // Given 2 above, which source/style to use can not be pulled from data request state. // Therefore, source and style are provided as arugments and must be used instead of calling getSource or getCurrentStyle. - async _syncData(syncContext, source, style) { + async _syncData(syncContext: DataRequestContext, source: IVectorSource, style: IVectorStyle) { if (this.isLoadingBounds()) { return; } @@ -624,11 +737,11 @@ export class VectorLayer extends AbstractLayer { _getSourceFeatureCollection() { const sourceDataRequest = this.getSourceDataRequest(); - return sourceDataRequest ? sourceDataRequest.getData() : null; + return sourceDataRequest ? (sourceDataRequest.getData() as FeatureCollection) : null; } - _syncFeatureCollectionWithMb(mbMap) { - const mbGeoJSONSource = mbMap.getSource(this.getId()); + _syncFeatureCollectionWithMb(mbMap: MbMap) { + const mbGeoJSONSource = mbMap.getSource(this.getId()) as MbGeoJSONSource; const featureCollection = this._getSourceFeatureCollection(); const featureCollectionOnMap = AbstractLayer.getBoundDataForSource(mbMap, this.getId()); @@ -653,7 +766,7 @@ export class VectorLayer extends AbstractLayer { } } - _setMbPointsProperties(mbMap, mvtSourceLayer) { + _setMbPointsProperties(mbMap: MbMap, mvtSourceLayer?: string) { const pointLayerId = this._getMbPointLayerId(); const symbolLayerId = this._getMbSymbolLayerId(); const pointLayer = mbMap.getLayer(pointLayerId); @@ -689,12 +802,12 @@ export class VectorLayer extends AbstractLayer { } } - _setMbCircleProperties(mbMap, mvtSourceLayer) { + _setMbCircleProperties(mbMap: MbMap, mvtSourceLayer?: string) { const sourceId = this.getId(); const pointLayerId = this._getMbPointLayerId(); const pointLayer = mbMap.getLayer(pointLayerId); if (!pointLayer) { - const mbLayer = { + const mbLayer: MbLayer = { id: pointLayerId, type: 'circle', source: sourceId, @@ -710,7 +823,7 @@ export class VectorLayer extends AbstractLayer { const textLayerId = this._getMbTextLayerId(); const textLayer = mbMap.getLayer(textLayerId); if (!textLayer) { - const mbLayer = { + const mbLayer: MbLayer = { id: textLayerId, type: 'symbol', source: sourceId, @@ -740,13 +853,13 @@ export class VectorLayer extends AbstractLayer { }); } - _setMbSymbolProperties(mbMap, mvtSourceLayer) { + _setMbSymbolProperties(mbMap: MbMap, mvtSourceLayer?: string) { const sourceId = this.getId(); const symbolLayerId = this._getMbSymbolLayerId(); const symbolLayer = mbMap.getLayer(symbolLayerId); if (!symbolLayer) { - const mbLayer = { + const mbLayer: MbLayer = { id: symbolLayerId, type: 'symbol', source: sourceId, @@ -775,7 +888,7 @@ export class VectorLayer extends AbstractLayer { }); } - _setMbLinePolygonProperties(mbMap, mvtSourceLayer) { + _setMbLinePolygonProperties(mbMap: MbMap, mvtSourceLayer?: string) { const sourceId = this.getId(); const fillLayerId = this._getMbPolygonLayerId(); const lineLayerId = this._getMbLineLayerId(); @@ -783,7 +896,7 @@ export class VectorLayer extends AbstractLayer { const hasJoins = this.hasJoins(); if (!mbMap.getLayer(fillLayerId)) { - const mbLayer = { + const mbLayer: MbLayer = { id: fillLayerId, type: 'fill', source: sourceId, @@ -795,7 +908,7 @@ export class VectorLayer extends AbstractLayer { mbMap.addLayer(mbLayer); } if (!mbMap.getLayer(lineLayerId)) { - const mbLayer = { + const mbLayer: MbLayer = { id: lineLayerId, type: 'line', source: sourceId, @@ -807,7 +920,7 @@ export class VectorLayer extends AbstractLayer { mbMap.addLayer(mbLayer); } if (!mbMap.getLayer(tooManyFeaturesLayerId)) { - const mbLayer = { + const mbLayer: MbLayer = { id: tooManyFeaturesLayerId, type: 'fill', source: sourceId, @@ -855,12 +968,12 @@ export class VectorLayer extends AbstractLayer { mbMap.setLayerZoomRange(tooManyFeaturesLayerId, this.getMinZoom(), this.getMaxZoom()); } - _syncStylePropertiesWithMb(mbMap) { + _syncStylePropertiesWithMb(mbMap: MbMap) { this._setMbPointsProperties(mbMap); this._setMbLinePolygonProperties(mbMap); } - _syncSourceBindingWithMb(mbMap) { + _syncSourceBindingWithMb(mbMap: MbMap) { const mbSource = mbMap.getSource(this._getMbSourceId()); if (!mbSource) { mbMap.addSource(this._getMbSourceId(), { @@ -883,7 +996,7 @@ export class VectorLayer extends AbstractLayer { } } - syncLayerWithMB(mbMap) { + syncLayerWithMB(mbMap: MbMap) { this._syncSourceBindingWithMb(mbMap); this._syncFeatureCollectionWithMb(mbMap); this._syncStylePropertiesWithMb(mbMap); @@ -924,15 +1037,15 @@ export class VectorLayer extends AbstractLayer { ]; } - ownsMbLayerId(mbLayerId) { + ownsMbLayerId(mbLayerId: string) { return this.getMbLayerIds().includes(mbLayerId); } - ownsMbSourceId(mbSourceId) { + ownsMbSourceId(mbSourceId: string) { return this.getId() === mbSourceId; } - _addJoinsToSourceTooltips(tooltipsFromSource) { + _addJoinsToSourceTooltips(tooltipsFromSource: ITooltipProperty[]) { for (let i = 0; i < tooltipsFromSource.length; i++) { const tooltipProperty = tooltipsFromSource[i]; const matchingJoins = []; @@ -947,7 +1060,7 @@ export class VectorLayer extends AbstractLayer { } } - async getPropertiesForTooltip(properties) { + async getPropertiesForTooltip(properties: GeoJsonProperties) { const vectorSource = this.getSource(); let allProperties = await vectorSource.getTooltipProperties(properties); this._addJoinsToSourceTooltips(allProperties); @@ -961,18 +1074,20 @@ export class VectorLayer extends AbstractLayer { canShowTooltip() { return ( - this.isVisible() && (this.getSource().canFormatFeatureProperties() || this.getJoins().length) + this.isVisible() && + (this.getSource().canFormatFeatureProperties() || this.getJoins().length > 0) ); } - getFeatureById(id) { + getFeatureById(id: string | number) { const featureCollection = this._getSourceFeatureCollection(); if (!featureCollection) { return null; } - return featureCollection.features.find((feature) => { - return feature.properties[FEATURE_ID_PROPERTY_NAME] === id; + const targetFeature = featureCollection.features.find((feature) => { + return feature.properties?.[FEATURE_ID_PROPERTY_NAME] === id; }); + return targetFeature ? targetFeature : null; } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts index be947d79f4e395..5c062f3419e28d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_agg_source/es_agg_source.ts @@ -29,7 +29,7 @@ export interface IESAggSource extends IESSource { getValueAggsDsl(indexPattern: IndexPattern): { [key: string]: unknown }; } -export class AbstractESAggSource extends AbstractESSource { +export abstract class AbstractESAggSource extends AbstractESSource { private readonly _metricFields: IESAggField[]; private readonly _canReadFromGeoJson: boolean; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts index ada76b8e4e674e..b221d13bb0f8ac 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.d.ts @@ -10,6 +10,7 @@ import { MapFilters, MapQuery, VectorSourceSyncMeta, + VectorSourceRequestMeta, } from '../../../../common/descriptor_types'; import { GRID_RESOLUTION } from '../../../../common/constants'; import { IField } from '../../fields/field'; @@ -35,13 +36,7 @@ export class ESGeoGridSource extends AbstractESAggSource implements ITiledSingle getLayerName(): string; getUrlTemplateWithMeta( - searchFilters: MapFilters & { - applyGlobalQuery: boolean; - fieldNames: string[]; - geogridPrecision?: number; - sourceQuery: MapQuery; - sourceMeta: VectorSourceSyncMeta; - } + searchFilters: VectorSourceRequestMeta ): Promise<{ layerName: string; urlTemplate: string; diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index 189e7dea1b0c1b..06df68283c434e 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -160,7 +160,8 @@ describe('ESGeoGridSource', () => { const { data, meta } = await geogridSource.getGeoJsonWithMeta( 'foobarLayer', vectorSourceRequestMeta, - () => {} + () => {}, + () => true ); expect(meta && meta.areResultsTrimmed).toEqual(false); diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts index 01fde589dcb84d..c11b6f0853cc7a 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.d.ts @@ -6,12 +6,14 @@ import { AbstractVectorSource } from '../vector_source'; import { IVectorSource } from '../vector_source'; +import { TimeRange } from '../../../../../../../src/plugins/data/common'; import { IndexPattern, ISearchSource } from '../../../../../../../src/plugins/data/public'; import { DynamicStylePropertyOptions, + MapQuery, VectorSourceRequestMeta, } from '../../../../common/descriptor_types'; -import { VectorStyle } from '../../styles/vector/vector_style'; +import { IVectorStyle } from '../../styles/vector/vector_style'; import { IDynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; export interface IESSource extends IVectorSource { @@ -25,13 +27,21 @@ export interface IESSource extends IVectorSource { limit: number, initialSearchContext?: object ): Promise<ISearchSource>; - loadStylePropsMeta( - layerName: string, - style: VectorStyle, - dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>, - registerCancelCallback: (requestToken: symbol, callback: () => void) => void, - searchFilters: VectorSourceRequestMeta - ): Promise<unknown>; + loadStylePropsMeta({ + layerName, + style, + dynamicStyleProps, + registerCancelCallback, + sourceQuery, + timeFilters, + }: { + layerName: string; + style: IVectorStyle; + dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>; + registerCancelCallback: (callback: () => void) => void; + sourceQuery?: MapQuery; + timeFilters: TimeRange; + }): Promise<object>; } export class AbstractESSource extends AbstractVectorSource implements IESSource { @@ -45,13 +55,21 @@ export class AbstractESSource extends AbstractVectorSource implements IESSource limit: number, initialSearchContext?: object ): Promise<ISearchSource>; - loadStylePropsMeta( - layerName: string, - style: VectorStyle, - dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>, - registerCancelCallback: (requestToken: symbol, callback: () => void) => void, - searchFilters: VectorSourceRequestMeta - ): Promise<unknown>; + loadStylePropsMeta({ + layerName, + style, + dynamicStyleProps, + registerCancelCallback, + sourceQuery, + timeFilters, + }: { + layerName: string; + style: IVectorStyle; + dynamicStyleProps: Array<IDynamicStyleProperty<DynamicStylePropertyOptions>>; + registerCancelCallback: (callback: () => void) => void; + sourceQuery?: MapQuery; + timeFilters: TimeRange; + }): Promise<object>; _runEsQuery: ({ requestId, requestName, diff --git a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js index ab56ceeab4e771..0c8cb5f5142470 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_source/es_source.js @@ -284,13 +284,14 @@ export class AbstractESSource extends AbstractVectorSource { return indexPattern.getFormatterForField(fieldFromIndexPattern).getConverterFor('text'); } - async loadStylePropsMeta( + async loadStylePropsMeta({ layerName, style, dynamicStyleProps, registerCancelCallback, - searchFilters - ) { + sourceQuery, + timeFilters, + }) { const promises = dynamicStyleProps.map((dynamicStyleProp) => { return dynamicStyleProp.getFieldMetaRequest(); }); @@ -307,13 +308,11 @@ export class AbstractESSource extends AbstractVectorSource { searchSource.setField('index', indexPattern); searchSource.setField('size', 0); searchSource.setField('aggs', aggs); - if (searchFilters.sourceQuery) { - searchSource.setField('query', searchFilters.sourceQuery); + if (sourceQuery) { + searchSource.setField('query', sourceQuery); } if (style.isTimeAware() && (await this.isTimeAware())) { - searchSource.setField('filter', [ - getTimeFilter().createFilter(indexPattern, searchFilters.timeFilters), - ]); + searchSource.setField('filter', [getTimeFilter().createFilter(indexPattern, timeFilters)]); } const resp = await this._runEsQuery({ diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts index 248ca2b9212b4d..ef1ada8da82899 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.d.ts @@ -4,10 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ +import { MapQuery, VectorJoinSourceRequestMeta } from '../../../../common/descriptor_types'; import { IField } from '../../fields/field'; import { IESAggSource } from '../es_agg_source'; +import { PropertiesMap } from '../../joins/join'; export interface IESTermSource extends IESAggSource { - getTermField(): IField; - hasCompleteConfig(): boolean; + getTermField: () => IField; + hasCompleteConfig: () => boolean; + getWhereQuery: () => MapQuery; + getPropertiesMap: ( + searchFilters: VectorJoinSourceRequestMeta, + leftSourceName: string, + leftFieldName: string, + registerCancelCallback: (callback: () => void) => void + ) => PropertiesMap; } diff --git a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js index 359d22d2c44ce7..ff52dccdd2ef40 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_term_source/es_term_source.js @@ -119,9 +119,7 @@ export class ESTermSource extends AbstractESAggSource { }); const countPropertyName = this.getAggKey(AGG_TYPE.COUNT); - return { - propertiesMap: extractPropertiesMap(rawEsData, countPropertyName), - }; + return extractPropertiesMap(rawEsData, countPropertyName); } isFilterByMapBounds() { diff --git a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js index eeb34ed672221c..d937edb4ed3620 100644 --- a/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js +++ b/x-pack/plugins/maps/public/classes/sources/kibana_regionmap_source/kibana_regionmap_source.js @@ -74,6 +74,7 @@ export class KibanaRegionmapSource extends AbstractVectorSource { }); return { data: featureCollection, + meta: {}, }; } diff --git a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx index 3e515613b3fd03..440f0cb4457e84 100644 --- a/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx +++ b/x-pack/plugins/maps/public/classes/sources/mvt_single_layer_vector_source/mvt_single_layer_vector_source.tsx @@ -179,7 +179,7 @@ export class MVTSingleLayerVectorSource getBoundsForFilters( boundsFilters: BoundsFilters, - registerCancelCallback: (requestToken: symbol, callback: () => void) => void + registerCancelCallback: (callback: () => void) => void ): MapExtent | null { return null; } @@ -192,6 +192,18 @@ export class MVTSingleLayerVectorSource return false; } + isBoundsAware() { + return false; + } + + getSourceTooltipContent() { + return { tooltipContent: null, areResultsTrimmed: false }; + } + + async getLeftJoinFields() { + return []; + } + async getTooltipProperties( properties: GeoJsonProperties, featureId?: string | number diff --git a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts index a481e273bc33e0..7bf1db43c28712 100644 --- a/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts +++ b/x-pack/plugins/maps/public/classes/sources/vector_source/vector_source.d.ts @@ -19,6 +19,12 @@ import { } from '../../../../common/descriptor_types'; import { VECTOR_SHAPE_TYPE } from '../../../../common/constants'; import { ITooltipProperty } from '../../tooltips/tooltip_property'; +import { DataRequest } from '../../util/data_request'; + +export interface SourceTooltipConfig { + tooltipContent: string | null; + areResultsTrimmed: boolean; +} export type GeoJsonFetchMeta = ESSearchSourceResponseMeta; @@ -30,8 +36,8 @@ export type GeoJsonWithMeta = { export type BoundsFilters = { applyGlobalQuery: boolean; filters: Filter[]; - query: MapQuery; - sourceQuery: MapQuery; + query?: MapQuery; + sourceQuery?: MapQuery; timeFilters: TimeRange; }; @@ -39,44 +45,52 @@ export interface IVectorSource extends ISource { getTooltipProperties(properties: GeoJsonProperties): Promise<ITooltipProperty[]>; getBoundsForFilters( boundsFilters: BoundsFilters, - registerCancelCallback: (requestToken: symbol, callback: () => void) => void + registerCancelCallback: (callback: () => void) => void ): MapExtent | null; getGeoJsonWithMeta( - layerName: 'string', + layerName: string, searchFilters: MapFilters, - registerCancelCallback: (callback: () => void) => void + registerCancelCallback: (callback: () => void) => void, + isRequestStillActive: () => boolean ): Promise<GeoJsonWithMeta>; getFields(): Promise<IField[]>; getFieldByName(fieldName: string): IField | null; + getLeftJoinFields(): Promise<IField[]>; getSyncMeta(): VectorSourceSyncMeta; getFieldNames(): string[]; getApplyGlobalQuery(): boolean; createField({ fieldName }: { fieldName: string }): IField; canFormatFeatureProperties(): boolean; getSupportedShapeTypes(): Promise<VECTOR_SHAPE_TYPE[]>; + isBoundsAware(): boolean; + getSourceTooltipContent(sourceDataRequest?: DataRequest): SourceTooltipConfig; } export class AbstractVectorSource extends AbstractSource implements IVectorSource { getTooltipProperties(properties: GeoJsonProperties): Promise<ITooltipProperty[]>; getBoundsForFilters( boundsFilters: BoundsFilters, - registerCancelCallback: (requestToken: symbol, callback: () => void) => void + registerCancelCallback: (callback: () => void) => void ): MapExtent | null; getGeoJsonWithMeta( layerName: string, searchFilters: VectorSourceRequestMeta, - registerCancelCallback: (callback: () => void) => void + registerCancelCallback: (callback: () => void) => void, + isRequestStillActive: () => boolean ): Promise<GeoJsonWithMeta>; getFields(): Promise<IField[]>; getFieldByName(fieldName: string): IField | null; + getLeftJoinFields(): Promise<IField[]>; getSyncMeta(): VectorSourceSyncMeta; getSupportedShapeTypes(): Promise<VECTOR_SHAPE_TYPE[]>; canFormatFeatureProperties(): boolean; getApplyGlobalQuery(): boolean; getFieldNames(): string[]; createField({ fieldName }: { fieldName: string }): IField; + isBoundsAware(): boolean; + getSourceTooltipContent(sourceDataRequest?: DataRequest): SourceTooltipConfig; } export interface ITiledSingleLayerVectorSource extends IVectorSource { diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx index 1244c53afe9a6c..5d0d9712ef988f 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx @@ -5,7 +5,7 @@ */ import _ from 'lodash'; -import React from 'react'; +import React, { ReactElement } from 'react'; import { Map as MbMap, FeatureIdentifier } from 'mapbox-gl'; import { FeatureCollection } from 'geojson'; // @ts-expect-error @@ -92,6 +92,55 @@ export interface IVectorStyle extends IStyle { mapColors: string[] ): { hasChanges: boolean; nextStyleDescriptor?: VectorStyleDescriptor }; pluckStyleMetaFromSourceDataRequest(sourceDataRequest: DataRequest): Promise<StyleMetaDescriptor>; + isTimeAware: () => boolean; + getIcon: () => ReactElement<any>; + hasLegendDetails: () => Promise<boolean>; + renderLegendDetails: () => ReactElement<any>; + clearFeatureState: (featureCollection: FeatureCollection, mbMap: MbMap, sourceId: string) => void; + setFeatureStateAndStyleProps: ( + featureCollection: FeatureCollection, + mbMap: MbMap, + mbSourceId: string + ) => boolean; + arePointsSymbolizedAsCircles: () => boolean; + setMBPaintProperties: ({ + alpha, + mbMap, + fillLayerId, + lineLayerId, + }: { + alpha: number; + mbMap: MbMap; + fillLayerId: string; + lineLayerId: string; + }) => void; + setMBPaintPropertiesForPoints: ({ + alpha, + mbMap, + pointLayerId, + }: { + alpha: number; + mbMap: MbMap; + pointLayerId: string; + }) => void; + setMBPropertiesForLabelText: ({ + alpha, + mbMap, + textLayerId, + }: { + alpha: number; + mbMap: MbMap; + textLayerId: string; + }) => void; + setMBSymbolPropertiesForPoints: ({ + mbMap, + symbolLayerId, + alpha, + }: { + alpha: number; + mbMap: MbMap; + symbolLayerId: string; + }) => void; } export class VectorStyle implements IVectorStyle { @@ -594,12 +643,12 @@ export class VectorStyle implements IVectorStyle { mbSourceId: string ) { if (!featureCollection) { - return; + return false; } const dynamicStyleProps = this.getDynamicPropertiesArray(); if (dynamicStyleProps.length === 0) { - return; + return false; } const tmpFeatureIdentifier: FeatureIdentifier = { diff --git a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx index 6c6cb6ba143cda..24728465de3bdd 100644 --- a/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/widget_overlay/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/toc_entry_actions_popover.test.tsx @@ -9,7 +9,6 @@ import React from 'react'; import { shallow } from 'enzyme'; import { AbstractLayer, ILayer } from '../../../../../../classes/layers/layer'; import { AbstractSource, ISource } from '../../../../../../classes/sources/source'; -import { IStyle } from '../../../../../../classes/styles/style'; import { TOCEntryActionsPopover } from './toc_entry_actions_popover'; @@ -17,28 +16,17 @@ let supportsFitToBounds: boolean; class MockSource extends AbstractSource implements ISource {} -class MockStyle implements IStyle { - renderEditor() { - return null; - } - - getType() { - return 'mockStyle'; - } -} - class LayerMock extends AbstractLayer implements ILayer { constructor() { const sourceDescriptor = { type: 'mySourceType', }; const source = new MockSource(sourceDescriptor); - const style = new MockStyle(); const layerDescriptor = { id: 'testLayer', sourceDescriptor, }; - super({ layerDescriptor, source, style }); + super({ layerDescriptor, source }); } async supportsFitToBounds(): Promise<boolean> { From e92a4ab4bf5ec175fc572f4d14b5da173de266a7 Mon Sep 17 00:00:00 2001 From: Ashik Meerankutty <ashik9591@gmail.com> Date: Fri, 2 Oct 2020 07:34:22 +0530 Subject: [PATCH 066/128] [APM] Service Inventory Updated the `EuiBadge` to use the `behind_text` vars instead of the base colors for the health status badges (#77844) * Use behind_text colors in health status * Separated badge color usage from getSeverityColor --- .../plugins/apm/common/service_health_status.ts | 16 ++++++++++++++++ .../ServiceOverview/ServiceList/HealthBadge.tsx | 4 ++-- .../__snapshots__/ServiceOverview.test.tsx.snap | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/apm/common/service_health_status.ts b/x-pack/plugins/apm/common/service_health_status.ts index 1d4bcfb3b0e07b..f66e03a9733a3e 100644 --- a/x-pack/plugins/apm/common/service_health_status.ts +++ b/x-pack/plugins/apm/common/service_health_status.ts @@ -54,6 +54,22 @@ export function getServiceHealthStatusColor( } } +export function getServiceHealthStatusBadgeColor( + theme: EuiTheme, + status: ServiceHealthStatus +) { + switch (status) { + case ServiceHealthStatus.healthy: + return theme.eui.euiColorVis0_behindText; + case ServiceHealthStatus.warning: + return theme.eui.euiColorVis5_behindText; + case ServiceHealthStatus.critical: + return theme.eui.euiColorVis9_behindText; + case ServiceHealthStatus.unknown: + return theme.eui.euiColorMediumShade; + } +} + export function getServiceHealthStatusLabel(status: ServiceHealthStatus) { switch (status) { case ServiceHealthStatus.critical: diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx index c6be0a352ef666..e8ad3e65b1a47e 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/ServiceList/HealthBadge.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { EuiBadge } from '@elastic/eui'; import { - getServiceHealthStatusColor, + getServiceHealthStatusBadgeColor, getServiceHealthStatusLabel, ServiceHealthStatus, } from '../../../../../common/service_health_status'; @@ -20,7 +20,7 @@ export function HealthBadge({ const theme = useTheme(); return ( - <EuiBadge color={getServiceHealthStatusColor(theme, healthStatus)}> + <EuiBadge color={getServiceHealthStatusBadgeColor(theme, healthStatus)}> {getServiceHealthStatusLabel(healthStatus)} </EuiBadge> ); diff --git a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap index 40a2b6a5fa81b1..ee3a4fce0dbaae 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/app/ServiceOverview/__test__/__snapshots__/ServiceOverview.test.tsx.snap @@ -153,7 +153,7 @@ NodeList [ > <span class="euiBadge euiBadge--iconLeft" - style="background-color: rgb(214, 191, 87); color: rgb(0, 0, 0);" + style="background-color: rgb(241, 216, 111); color: rgb(0, 0, 0);" title="Warning" > <span From 1b61cc6b5d5c8fdf0ee92056e2a5c82579a00d4f Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli <efstratia.kalafateli@elastic.co> Date: Fri, 2 Oct 2020 09:03:08 +0300 Subject: [PATCH 067/128] [Functional] Add retry on custom formatter test (#78729) Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- test/functional/apps/visualize/_tsvb_time_series.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index 0b2a52b367a20a..d4a079a38c8143 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -84,8 +84,10 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await visualBuilder.clickSeriesOption(); await visualBuilder.enterSeriesTemplate('$ {{value}}'); - const actualCount = await visualBuilder.getRhythmChartLegendValue(); - expect(actualCount).to.be(expectedLegendValue); + await retry.try(async () => { + const actualCount = await visualBuilder.getRhythmChartLegendValue(); + expect(actualCount).to.be(expectedLegendValue); + }); }); it('should show the correct count in the legend with percent formatter', async () => { From 4ddcd1d2a6bf3ffbec37844468c4e4af827f739f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= <sorenlouv@gmail.com> Date: Fri, 2 Oct 2020 09:45:50 +0200 Subject: [PATCH 068/128] [APM] Fix anomalies not showing up on transaction charts (#76930) * [APM] Fix anomalies not showing up on transaction charts * Added API tests to check transaction groups charts for anomaly data * Improve test names and assertions from PR feedback * Updated the transaction groups chart API to make `environment` a required param while making `uiFilters` optional * updates the basic API tests for transaction_groups/charts with the required `environment` param * makes uiFIltersES default to [] on core setup and removes SetupUIFilters type * fixes vertical shade * - replaces uiFiltersES with esFilter & uiFilters and cleans up related code around these - deduplicates the required environment in the transaction_groups/charts API * updates basic apm_api_integration tests * pr feedback * updates api test snapshots with correct anomaly data * removed environment query param from useTransactionCharts and ensures it's included in uiFilters returned from useUrlParams Co-authored-by: Oliver Gupte <olivergupte@gmail.com> --- .../example_response_opbeans_beats.json | 42 ++--- .../public/context/UrlParamsContext/index.tsx | 7 +- .../plugins/apm/public/utils/testHelpers.tsx | 7 +- .../errors/__snapshots__/queries.test.ts.snap | 6 +- .../__snapshots__/queries.test.ts.snap | 4 +- .../__tests__/get_buckets.test.ts | 5 +- .../lib/errors/distribution/get_buckets.ts | 12 +- .../errors/distribution/get_distribution.ts | 8 +- .../apm/server/lib/errors/get_error_group.ts | 12 +- .../apm/server/lib/errors/get_error_groups.ts | 8 +- ...{get_ui_filters_es.ts => get_es_filter.ts} | 2 +- .../get_parsed_ui_filters.ts | 23 --- .../apm/server/lib/helpers/setup_request.ts | 41 ++--- .../__snapshots__/queries.test.ts.snap | 54 +++--- .../server/lib/metrics/by_agent/default.ts | 8 +- .../java/gc/fetch_and_transform_gc_metrics.ts | 8 +- .../by_agent/java/gc/get_gc_rate_chart.ts | 8 +- .../by_agent/java/gc/get_gc_time_chart.ts | 8 +- .../by_agent/java/heap_memory/index.ts | 8 +- .../server/lib/metrics/by_agent/java/index.ts | 8 +- .../by_agent/java/non_heap_memory/index.ts | 8 +- .../by_agent/java/thread_count/index.ts | 8 +- .../lib/metrics/by_agent/shared/cpu/index.ts | 8 +- .../metrics/by_agent/shared/memory/index.ts | 10 +- .../metrics/fetch_and_transform_metrics.ts | 8 +- .../get_metrics_chart_data_by_agent.ts | 8 +- .../__snapshots__/queries.test.ts.snap | 14 +- .../lib/rum_client/get_client_metrics.ts | 8 +- .../server/lib/rum_client/get_js_errors.ts | 8 +- .../lib/rum_client/get_long_task_metrics.ts | 8 +- .../rum_client/get_page_load_distribution.ts | 10 +- .../lib/rum_client/get_page_view_trends.ts | 8 +- .../lib/rum_client/get_pl_dist_breakdown.ts | 8 +- .../server/lib/rum_client/get_rum_services.ts | 8 +- .../server/lib/rum_client/get_url_search.ts | 8 +- .../lib/rum_client/get_visitor_breakdown.ts | 8 +- .../lib/rum_client/get_web_core_vitals.ts | 8 +- .../server/lib/service_map/get_service_map.ts | 2 +- .../get_service_map_service_node_info.test.ts | 6 +- .../get_service_map_service_node_info.ts | 9 +- .../group_resource_nodes_grouped.json | 4 +- .../group_resource_nodes_pregrouped.json | 4 +- .../__snapshots__/queries.test.ts.snap | 6 +- .../apm/server/lib/service_nodes/index.ts | 8 +- .../__snapshots__/queries.test.ts.snap | 10 +- .../lib/services/get_service_node_metadata.ts | 8 +- .../get_services/get_services_items.ts | 12 +- .../server/lib/services/get_services/index.ts | 11 +- .../__snapshots__/queries.test.ts.snap | 14 +- .../server/lib/transaction_groups/fetcher.ts | 8 +- .../lib/transaction_groups/get_error_rate.ts | 12 +- .../get_transaction_sample_for_group.ts | 12 +- .../server/lib/transaction_groups/index.ts | 8 +- .../__snapshots__/queries.test.ts.snap | 12 +- .../lib/transactions/breakdown/index.test.ts | 3 +- .../lib/transactions/breakdown/index.ts | 12 +- .../charts/get_anomaly_data/fetcher.ts | 11 +- .../charts/get_anomaly_data/index.ts | 40 ++--- .../get_timeseries_data/fetcher.test.ts | 5 +- .../charts/get_timeseries_data/fetcher.ts | 12 +- .../charts/get_timeseries_data/index.ts | 8 +- .../server/lib/transactions/charts/index.ts | 10 +- .../distribution/get_buckets/index.ts | 12 +- .../distribution/get_distribution_max.ts | 12 +- .../lib/transactions/distribution/index.ts | 8 +- .../lib/transactions/get_transaction/index.ts | 8 +- .../server/lib/transactions/queries.test.ts | 3 - .../__snapshots__/queries.test.ts.snap | 2 +- .../get_local_filter_query.ts | 4 +- .../local_ui_filters/queries.test.ts | 2 +- .../plugins/apm/server/projections/errors.ts | 12 +- .../plugins/apm/server/projections/metrics.ts | 12 +- .../projections/rum_page_load_transactions.ts | 18 +- .../apm/server/projections/service_nodes.ts | 8 +- .../apm/server/projections/services.ts | 12 +- .../server/projections/transaction_groups.ts | 8 +- .../apm/server/projections/transactions.ts | 12 +- .../plugins/apm/server/routes/service_map.ts | 5 - x-pack/plugins/apm/server/routes/services.ts | 12 +- .../apm/server/routes/transaction_groups.ts | 21 +-- .../plugins/apm/server/routes/ui_filters.ts | 13 +- .../plugins/apm/server/utils/test_helpers.tsx | 7 +- .../basic/tests/feature_controls.ts | 6 +- .../transaction_groups/transaction_charts.ts | 2 +- .../apm_api_integration/trial/tests/index.ts | 1 + .../transaction_groups_charts.snap | 43 +++++ .../services/transaction_groups_charts.ts | 161 ++++++++++++++++++ 87 files changed, 528 insertions(+), 548 deletions(-) rename x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/{get_ui_filters_es.ts => get_es_filter.ts} (96%) delete mode 100644 x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts create mode 100644 x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap create mode 100644 x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts diff --git a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json index 153fa57bb05e70..cfd905f145fe2c 100644 --- a/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json +++ b/x-pack/plugins/apm/public/components/app/ServiceMap/__stories__/example_response_opbeans_beats.json @@ -83,7 +83,7 @@ "id": "opbeans-go~>postgresql", "sourceData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" }, @@ -103,7 +103,7 @@ "id": "opbeans-go~opbeans-java", "sourceData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" }, @@ -123,13 +123,13 @@ "id": "opbeans-go~opbeans-node", "sourceData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" }, "targetData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, @@ -143,7 +143,7 @@ "id": "opbeans-go~opbeans-ruby", "sourceData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" }, @@ -189,7 +189,7 @@ }, "targetData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" }, @@ -209,7 +209,7 @@ }, "targetData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" } @@ -242,7 +242,7 @@ "id": "opbeans-node~>postgresql", "sourceData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, @@ -262,7 +262,7 @@ "id": "opbeans-node~>redis", "sourceData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, @@ -282,13 +282,13 @@ "id": "opbeans-node~opbeans-go", "sourceData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, "targetData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" }, @@ -302,7 +302,7 @@ "id": "opbeans-node~opbeans-python", "sourceData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, @@ -322,7 +322,7 @@ "id": "opbeans-node~opbeans-ruby", "sourceData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, @@ -408,7 +408,7 @@ }, "targetData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" } @@ -427,7 +427,7 @@ }, "targetData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, @@ -487,7 +487,7 @@ }, "targetData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" }, @@ -527,7 +527,7 @@ }, "targetData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" }, @@ -566,7 +566,7 @@ }, "targetData": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go" } @@ -602,7 +602,7 @@ }, "targetData": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" } @@ -673,7 +673,7 @@ { "data": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs", "anomaly_score": 41.31593099784474, @@ -733,7 +733,7 @@ { "data": { "id": "opbeans-go", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-go", "agent.name": "go", "anomaly_score": 0.2633884161762746, diff --git a/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx b/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx index 9eb4704a2ca29a..5682009019d7f8 100644 --- a/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx +++ b/x-pack/plugins/apm/public/context/UrlParamsContext/index.tsx @@ -25,6 +25,7 @@ import { import { pickKeys } from '../../../common/utils/pick_keys'; import { useDeepObjectIdentity } from '../../hooks/useDeepObjectIdentity'; import { LocalUIFilterName } from '../../../common/ui_filter'; +import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; interface TimeRange { rangeFrom: string; @@ -38,7 +39,11 @@ function useUiFilters(params: IUrlParams): UIFilters { (val) => (val ? val.split(',') : []) ) as Partial<Record<LocalUIFilterName, string[]>>; - return useDeepObjectIdentity({ kuery, environment, ...localUiFilters }); + return useDeepObjectIdentity({ + kuery, + environment: environment || ENVIRONMENT_ALL.value, + ...localUiFilters, + }); } const defaultRefresh = (_time: TimeRange) => {}; diff --git a/x-pack/plugins/apm/public/utils/testHelpers.tsx b/x-pack/plugins/apm/public/utils/testHelpers.tsx index 971455fde39462..7826e9672a3bbe 100644 --- a/x-pack/plugins/apm/public/utils/testHelpers.tsx +++ b/x-pack/plugins/apm/public/utils/testHelpers.tsx @@ -25,6 +25,7 @@ import { } from '../../typings/elasticsearch'; import { MockApmPluginContextWrapper } from '../context/ApmPluginContext/MockApmPluginContext'; import { UrlParamsProvider } from '../context/UrlParamsContext'; +import { UIFilters } from '../../typings/ui_filters'; const originalConsoleWarn = console.warn; // eslint-disable-line no-console /** @@ -118,7 +119,8 @@ interface MockSetup { apmEventClient: any; internalClient: any; config: APMConfig; - uiFiltersES: ESFilter[]; + uiFilters: UIFilters; + esFilter: ESFilter[]; indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': string; @@ -179,7 +181,8 @@ export async function inspectSearchParams( }, } ) as APMConfig, - uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }], + uiFilters: { environment: 'test' }, + esFilter: [{ term: { 'service.environment': 'test' } }], indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': 'myIndex', diff --git a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap index 63b6c9cde4d0d1..632232ffb075d8 100644 --- a/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/errors/__snapshots__/queries.test.ts.snap @@ -32,7 +32,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -119,7 +119,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -194,7 +194,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap index ea142ca2acc002..b329499c8b0452 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/errors/distribution/__snapshots__/queries.test.ts.snap @@ -40,7 +40,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -91,7 +91,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts index 1a83113de35f2a..50da1f9c20d16c 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/__tests__/get_buckets.test.ts @@ -41,7 +41,10 @@ describe('timeseriesFetcher', () => { get: () => 'myIndex', } ) as APMConfig, - uiFiltersES: [ + uiFilters: { + environment: 'prod', + }, + esFilter: [ { term: { 'service.environment': 'prod' }, }, diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts index de6df15354e79c..a42710947a7924 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_buckets.ts @@ -11,11 +11,7 @@ import { SERVICE_NAME, } from '../../../../common/elasticsearch_fieldnames'; import { rangeFilter } from '../../../../common/utils/range_filter'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; export async function getBuckets({ serviceName, @@ -26,13 +22,13 @@ export async function getBuckets({ serviceName: string; groupId?: string; bucketSize: number; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { - const { start, end, uiFiltersES, apmEventClient } = setup; + const { start, end, esFilter, apmEventClient } = setup; const filter: ESFilter[] = [ { term: { [SERVICE_NAME]: serviceName } }, { range: rangeFilter(start, end) }, - ...uiFiltersES, + ...esFilter, ]; if (groupId) { diff --git a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts index 3b48b6c5be5944..dea518cad8e405 100644 --- a/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts +++ b/x-pack/plugins/apm/server/lib/errors/distribution/get_distribution.ts @@ -5,11 +5,7 @@ */ import { PromiseReturnType } from '../../../../typings/common'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getBuckets } from './get_buckets'; import { BUCKET_TARGET_COUNT } from '../../transactions/constants'; @@ -28,7 +24,7 @@ export async function getErrorDistribution({ }: { serviceName: string; groupId?: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { const bucketSize = getBucketSize({ start: setup.start, end: setup.end }); const { buckets, noHits } = await getBuckets({ diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_group.ts b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts index b23c955b57183b..0fbc7720f7111f 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_group.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_group.ts @@ -12,11 +12,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { PromiseReturnType } from '../../../typings/common'; import { rangeFilter } from '../../../common/utils/range_filter'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getTransaction } from '../transactions/get_transaction'; export type ErrorGroupAPIResponse = PromiseReturnType<typeof getErrorGroup>; @@ -29,9 +25,9 @@ export async function getErrorGroup({ }: { serviceName: string; groupId: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { - const { start, end, uiFiltersES, apmEventClient } = setup; + const { start, end, esFilter, apmEventClient } = setup; const params = { apm: { @@ -45,7 +41,7 @@ export async function getErrorGroup({ { term: { [SERVICE_NAME]: serviceName } }, { term: { [ERROR_GROUP_ID]: groupId } }, { range: rangeFilter(start, end) }, - ...uiFiltersES, + ...esFilter, ], should: [{ term: { [TRANSACTION_SAMPLED]: true } }], }, diff --git a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts index ab1c2149be3436..006d2fae3d4fb1 100644 --- a/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts +++ b/x-pack/plugins/apm/server/lib/errors/get_error_groups.ts @@ -13,11 +13,7 @@ import { ERROR_LOG_MESSAGE, } from '../../../common/elasticsearch_fieldnames'; import { PromiseReturnType } from '../../../typings/common'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getErrorGroupsProjection } from '../../projections/errors'; import { mergeProjection } from '../../projections/util/merge_projection'; import { SortOptions } from '../../../typings/elasticsearch/aggregations'; @@ -35,7 +31,7 @@ export async function getErrorGroups({ serviceName: string; sortField?: string; sortDirection?: 'asc' | 'desc'; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { const { apmEventClient } = setup; diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts similarity index 96% rename from x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts rename to x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts index c1405b44f2a8ac..1b8f32d4de8b93 100644 --- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_ui_filters_es.ts +++ b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_es_filter.ts @@ -13,7 +13,7 @@ import { } from '../../ui_filters/local_ui_filters/config'; import { esKuery } from '../../../../../../../src/plugins/data/server'; -export function getUiFiltersES(uiFilters: UIFilters) { +export function getEsFilter(uiFilters: UIFilters) { const { kuery, environment, ...localFilterValues } = uiFilters; const mappedFilters = localUIFilterNames .filter((name) => name in localFilterValues) diff --git a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts b/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts deleted file mode 100644 index 324da199807c7d..00000000000000 --- a/x-pack/plugins/apm/server/lib/helpers/convert_ui_filters/get_parsed_ui_filters.ts +++ /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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { Logger } from 'src/core/server'; -import { UIFilters } from '../../../../typings/ui_filters'; - -export function getParsedUiFilters({ - uiFilters, - logger, -}: { - uiFilters: string; - logger: Logger; -}): UIFilters { - try { - return JSON.parse(uiFilters); - } catch (error) { - logger.error(error); - } - return {}; -} diff --git a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts index eba75433a51486..26896a050dd882 100644 --- a/x-pack/plugins/apm/server/lib/helpers/setup_request.ts +++ b/x-pack/plugins/apm/server/lib/helpers/setup_request.ts @@ -5,6 +5,7 @@ */ import moment from 'moment'; +import { Logger } from 'kibana/server'; import { isActivePlatinumLicense } from '../../../common/service_map'; import { UI_SETTINGS } from '../../../../../../src/plugins/data/common'; import { KibanaRequest } from '../../../../../../src/core/server'; @@ -14,7 +15,7 @@ import { ApmIndicesConfig, } from '../settings/apm_indices/get_apm_indices'; import { ESFilter } from '../../../typings/elasticsearch'; -import { getUiFiltersES } from './convert_ui_filters/get_ui_filters_es'; +import { getEsFilter } from './convert_ui_filters/get_es_filter'; import { APMRequestHandlerContext } from '../../routes/typings'; import { ProcessorEvent } from '../../../common/processor_event'; import { @@ -25,14 +26,8 @@ import { APMInternalClient, createInternalESClient, } from './create_es_client/create_internal_es_client'; +import { UIFilters } from '../../../typings/ui_filters'; -function decodeUiFilters(uiFiltersEncoded?: string) { - if (!uiFiltersEncoded) { - return []; - } - const uiFilters = JSON.parse(uiFiltersEncoded); - return getUiFiltersES(uiFilters); -} // Explicitly type Setup to prevent TS initialization errors // https://github.com/microsoft/TypeScript/issues/34933 @@ -42,6 +37,8 @@ export interface Setup { ml?: ReturnType<typeof getMlSetup>; config: APMConfig; indices: ApmIndicesConfig; + uiFilters: UIFilters; + esFilter: ESFilter[]; } export interface SetupTimeRange { @@ -49,10 +46,6 @@ export interface SetupTimeRange { end: number; } -export interface SetupUIFilters { - uiFiltersES: ESFilter[]; -} - interface SetupRequestParams { query?: { _debug?: boolean; @@ -65,16 +58,13 @@ interface SetupRequestParams { type InferSetup<TParams extends SetupRequestParams> = Setup & (TParams extends { query: { start: string } } ? { start: number } : {}) & - (TParams extends { query: { end: string } } ? { end: number } : {}) & - (TParams extends { query: { uiFilters: string } } - ? { uiFiltersES: ESFilter[] } - : {}); + (TParams extends { query: { end: string } } ? { end: number } : {}); export async function setupRequest<TParams extends SetupRequestParams>( context: APMRequestHandlerContext<TParams>, request: KibanaRequest ): Promise<InferSetup<TParams>> { - const { config } = context; + const { config, logger } = context; const { query } = context.params; const [indices, includeFrozen] = await Promise.all([ @@ -85,7 +75,7 @@ export async function setupRequest<TParams extends SetupRequestParams>( context.core.uiSettings.client.get(UI_SETTINGS.SEARCH_INCLUDE_FROZEN), ]); - const uiFiltersES = decodeUiFilters(query.uiFilters); + const uiFilters = decodeUiFilters(logger, query.uiFilters); const coreSetupRequest = { indices, @@ -108,12 +98,13 @@ export async function setupRequest<TParams extends SetupRequestParams>( ) : undefined, config, + uiFilters, + esFilter: getEsFilter(uiFilters), }; return { ...('start' in query ? { start: moment.utc(query.start).valueOf() } : {}), ...('end' in query ? { end: moment.utc(query.end).valueOf() } : {}), - ...('uiFilters' in query ? { uiFiltersES } : {}), ...coreSetupRequest, } as InferSetup<TParams>; } @@ -129,3 +120,15 @@ function getMlSetup( modules: ml.modulesProvider(request, savedObjectsClient), }; } + +function decodeUiFilters(logger: Logger, uiFiltersEncoded?: string): UIFilters { + if (!uiFiltersEncoded) { + return {}; + } + try { + return JSON.parse(uiFiltersEncoded); + } catch (error) { + logger.error(error); + return {}; + } +} diff --git a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap index 2868dcfda97b6e..961a1eee61d1d5 100644 --- a/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/metrics/__snapshots__/queries.test.ts.snap @@ -87,7 +87,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -175,7 +175,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -206,7 +206,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -231,7 +231,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -258,7 +258,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -283,7 +283,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -338,7 +338,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -431,7 +431,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -514,7 +514,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -623,7 +623,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -717,7 +717,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -748,7 +748,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -773,7 +773,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -800,7 +800,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -825,7 +825,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -886,7 +886,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -985,7 +985,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -1074,7 +1074,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -1172,7 +1172,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -1255,7 +1255,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -1286,7 +1286,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -1311,7 +1311,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -1338,7 +1338,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -1363,7 +1363,7 @@ Object { "lang": "painless", "source": " /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -1413,7 +1413,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -1501,7 +1501,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -1579,7 +1579,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts index 6ee507d7b9bb16..fbcbc9f12791fa 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/default.ts @@ -4,16 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getCPUChartData } from './shared/cpu'; import { getMemoryChartData } from './shared/memory'; export async function getDefaultMetricsCharts( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string ) { const charts = await Promise.all([ diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts index d7e64bdcacd12b..2ed11480a75857 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/fetch_and_transform_gc_metrics.ts @@ -11,11 +11,7 @@ import { sum, round } from 'lodash'; import theme from '@elastic/eui/dist/eui_theme_light.json'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { getMetricsDateHistogramParams } from '../../../../helpers/metrics'; import { ChartBase } from '../../../types'; import { getMetricsProjection } from '../../../../../projections/metrics'; @@ -36,7 +32,7 @@ export async function fetchAndTransformGcMetrics({ chartBase, fieldName, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; chartBase: ChartBase; diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts index 6e562b9a8ee870..7cedeb828e3b73 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_rate_chart.ts @@ -7,11 +7,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import { METRIC_JAVA_GC_COUNT } from '../../../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics'; import { ChartBase } from '../../../types'; @@ -35,7 +31,7 @@ const chartBase: ChartBase = { }; const getGcRateChart = ( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) => { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts index 0b9d6240fc1c9b..f21d3d8e7c0565 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/gc/get_gc_time_chart.ts @@ -7,11 +7,7 @@ import theme from '@elastic/eui/dist/eui_theme_light.json'; import { i18n } from '@kbn/i18n'; import { METRIC_JAVA_GC_TIME } from '../../../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { fetchAndTransformGcMetrics } from './fetch_and_transform_gc_metrics'; import { ChartBase } from '../../../types'; @@ -35,7 +31,7 @@ const chartBase: ChartBase = { }; const getGcTimeChart = ( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) => { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts index ba3183c0fa7d7a..eb79897f9f0558 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/heap_memory/index.ts @@ -12,11 +12,7 @@ import { METRIC_JAVA_HEAP_MEMORY_USED, AGENT_NAME, } from '../../../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics'; import { ChartBase } from '../../../types'; @@ -55,7 +51,7 @@ const chartBase: ChartBase = { }; export async function getHeapMemoryChart( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts index 21caab6590fc46..d4084701f0f49a 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/index.ts @@ -5,11 +5,7 @@ */ import { getHeapMemoryChart } from './heap_memory'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../helpers/setup_request'; import { getNonHeapMemoryChart } from './non_heap_memory'; import { getThreadCountChart } from './thread_count'; import { getCPUChartData } from '../shared/cpu'; @@ -18,7 +14,7 @@ import { getGcRateChart } from './gc/get_gc_rate_chart'; import { getGcTimeChart } from './gc/get_gc_time_chart'; export async function getJavaMetricsCharts( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts index 1a2d5bd0b0e68a..50cc449da3c159 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/non_heap_memory/index.ts @@ -12,11 +12,7 @@ import { METRIC_JAVA_NON_HEAP_MEMORY_USED, AGENT_NAME, } from '../../../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { ChartBase } from '../../../types'; import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics'; @@ -52,7 +48,7 @@ const chartBase: ChartBase = { }; export async function getNonHeapMemoryChart( - setup: Setup & SetupUIFilters & SetupTimeRange, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts index 01cc6d84952446..0062f0a423970c 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/java/thread_count/index.ts @@ -10,11 +10,7 @@ import { METRIC_JAVA_THREAD_COUNT, AGENT_NAME, } from '../../../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { ChartBase } from '../../../types'; import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics'; @@ -44,7 +40,7 @@ const chartBase: ChartBase = { }; export async function getThreadCountChart( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts index 066ef40b4ab6ce..ca642aa12fff1d 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/cpu/index.ts @@ -10,11 +10,7 @@ import { METRIC_SYSTEM_CPU_PERCENT, METRIC_PROCESS_CPU_PERCENT, } from '../../../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { ChartBase } from '../../../types'; import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics'; @@ -56,7 +52,7 @@ const chartBase: ChartBase = { }; export async function getCPUChartData( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) { diff --git a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts index a60576ca0c175e..e6ee47cc815efd 100644 --- a/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts +++ b/x-pack/plugins/apm/server/lib/metrics/by_agent/shared/memory/index.ts @@ -11,11 +11,7 @@ import { METRIC_SYSTEM_FREE_MEMORY, METRIC_SYSTEM_TOTAL_MEMORY, } from '../../../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../../helpers/setup_request'; import { fetchAndTransformMetrics } from '../../../fetch_and_transform_metrics'; import { ChartBase } from '../../../types'; @@ -54,7 +50,7 @@ export const percentCgroupMemoryUsedScript = { lang: 'painless', source: ` /* - When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. + When no limit is specified in the container, docker allows the app as much memory / swap memory as it wants. This number represents the max possible value for the limit field. */ double CGROUP_LIMIT_MAX_VALUE = 9223372036854771712L; @@ -73,7 +69,7 @@ export const percentCgroupMemoryUsedScript = { }; export async function getMemoryChartData( - setup: Setup & SetupTimeRange & SetupUIFilters, + setup: Setup & SetupTimeRange, serviceName: string, serviceNodeName?: string ) { diff --git a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts index a42a10d6518a01..3ccba8c7586dcf 100644 --- a/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts +++ b/x-pack/plugins/apm/server/lib/metrics/fetch_and_transform_metrics.ts @@ -5,11 +5,7 @@ */ import { Unionize, Overwrite } from 'utility-types'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getMetricsDateHistogramParams } from '../helpers/metrics'; import { ChartBase } from './types'; import { transformDataToMetricsChart } from './transform_metrics_chart'; @@ -58,7 +54,7 @@ export async function fetchAndTransformMetrics<T extends MetricAggs>({ aggs, additionalFilters = [], }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; chartBase: ChartBase; diff --git a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts index 059e1ce48c83df..72cd65deebff63 100644 --- a/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts +++ b/x-pack/plugins/apm/server/lib/metrics/get_metrics_chart_data_by_agent.ts @@ -3,11 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getJavaMetricsCharts } from './by_agent/java'; import { getDefaultMetricsCharts } from './by_agent/default'; import { GenericMetricsChart } from './transform_metrics_chart'; @@ -22,7 +18,7 @@ export async function getMetricsChartDataByAgent({ serviceNodeName, agentName, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; agentName: string; diff --git a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap index dcafe092211646..1fafa080824439 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/rum_client/__snapshots__/queries.test.ts.snap @@ -61,7 +61,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -151,7 +151,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -230,7 +230,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -500,7 +500,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -552,7 +552,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -667,7 +667,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -723,7 +723,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts index 6566ea4f5e29bf..6d596246d6af92 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_client_metrics.ts @@ -7,11 +7,7 @@ import { TRANSACTION_DURATION } from '../../../common/elasticsearch_fieldnames'; import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { TRANSACTION_DOM_INTERACTIVE, TRANSACTION_TIME_TO_FIRST_BYTE, @@ -22,7 +18,7 @@ export async function getClientMetrics({ urlQuery, percentile = 50, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; urlQuery?: string; percentile?: number; }) { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts b/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts index 0540ea4bf09dd8..a8a4e2714c86e0 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_js_errors.ts @@ -5,11 +5,7 @@ */ import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getRumErrorsProjection } from '../../projections/rum_page_load_transactions'; import { ERROR_EXC_MESSAGE, @@ -23,7 +19,7 @@ export async function getJSErrors({ pageSize, pageIndex, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; pageSize: number; pageIndex: number; }) { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts index c2c86ae05d57c6..dfb31de8f10f78 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_long_task_metrics.ts @@ -6,11 +6,7 @@ import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; const LONG_TASK_SUM_FIELD = 'transaction.experience.longtask.sum'; const LONG_TASK_COUNT_FIELD = 'transaction.experience.longtask.count'; @@ -21,7 +17,7 @@ export async function getLongTaskMetrics({ urlQuery, percentile = 50, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; urlQuery?: string; percentile?: number; }) { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts index 5f666feb8a18f2..225afff2818abb 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_load_distribution.ts @@ -7,11 +7,7 @@ import { TRANSACTION_DURATION } from '../../../common/elasticsearch_fieldnames'; import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; export const MICRO_TO_SEC = 1000000; @@ -56,7 +52,7 @@ export async function getPageLoadDistribution({ maxPercentile, urlQuery, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; minPercentile?: string; maxPercentile?: string; urlQuery?: string; @@ -168,7 +164,7 @@ const getPercentilesDistribution = async ({ minDuration, maxDuration, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; minDuration: number; maxDuration: number; }) => { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts index 40f8a8bc58a546..c1a602c33feae1 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_page_view_trends.ts @@ -5,11 +5,7 @@ */ import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { BreakdownItem } from '../../../typings/ui_filters'; export async function getPageViewTrends({ @@ -17,7 +13,7 @@ export async function getPageViewTrends({ breakdowns, urlQuery, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; breakdowns?: string; urlQuery?: string; }) { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts index bebf9c0bc99c92..e2ec59d232b211 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_pl_dist_breakdown.ts @@ -7,11 +7,7 @@ import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { ProcessorEvent } from '../../../common/processor_event'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { CLIENT_GEO_COUNTRY_ISO_CODE, USER_AGENT_DEVICE, @@ -46,7 +42,7 @@ export const getPageLoadDistBreakdown = async ({ breakdown, urlQuery, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; minPercentile: number; maxPercentile: number; breakdown: string; diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts b/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts index 3adad0868ed4b5..e9bd203e354cb7 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_rum_services.ts @@ -5,18 +5,14 @@ */ import { SERVICE_NAME } from '../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; export async function getRumServices({ setup, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { const projection = getRumPageLoadTransactionsProjection({ setup, diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts b/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts index 6aa39c7ef961fc..febfd66897e182 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_url_search.ts @@ -5,11 +5,7 @@ */ import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { TRANSACTION_DURATION, @@ -21,7 +17,7 @@ export async function getUrlSearch({ urlQuery, percentile, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; urlQuery?: string; percentile: number; }) { diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts index 52d089e4e29c99..6350bc2c070160 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_visitor_breakdown.ts @@ -6,11 +6,7 @@ import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { USER_AGENT_NAME, USER_AGENT_OS, @@ -20,7 +16,7 @@ export async function getVisitorBreakdown({ setup, urlQuery, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; urlQuery?: string; }) { const projection = getRumPageLoadTransactionsProjection({ diff --git a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts index 676b3506397a71..c5baf0b529eb42 100644 --- a/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts +++ b/x-pack/plugins/apm/server/lib/rum_client/get_web_core_vitals.ts @@ -6,11 +6,7 @@ import { getRumPageLoadTransactionsProjection } from '../../projections/rum_page_load_transactions'; import { mergeProjection } from '../../projections/util/merge_projection'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { CLS_FIELD, FCP_FIELD, @@ -25,7 +21,7 @@ export async function getWebCoreVitals({ urlQuery, percentile = 50, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; urlQuery?: string; percentile?: number; }) { diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts index 75acebe7ed56ce..330bb936c9e889 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map.ts @@ -82,7 +82,7 @@ async function getServicesData(options: IEnvOptions) { const { setup, searchAggregatedTransactions } = options; const projection = getServicesProjection({ - setup: { ...setup, uiFiltersES: [] }, + setup: { ...setup, esFilter: [] }, searchAggregatedTransactions, }); diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts index 7af1607697ef39..eb2ddbf38b2746 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.test.ts @@ -19,11 +19,10 @@ describe('getServiceMapServiceNodeInfo', () => { }), }, indices: {}, + uiFilters: { environment: 'test environment' }, } as unknown) as Setup & SetupTimeRange; - const environment = 'test environment'; const serviceName = 'test service name'; const result = await getServiceMapServiceNodeInfo({ - uiFilters: { environment }, setup, serviceName, searchAggregatedTransactions: false, @@ -67,11 +66,10 @@ describe('getServiceMapServiceNodeInfo', () => { config: { 'xpack.apm.metricsInterval': 30, }, + uiFilters: { environment: 'test environment' }, } as unknown) as Setup & SetupTimeRange; - const environment = 'test environment'; const serviceName = 'test service name'; const result = await getServiceMapServiceNodeInfo({ - uiFilters: { environment }, setup, serviceName, searchAggregatedTransactions: false, diff --git a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts index 7c2137ce65d839..37b34641435fbc 100644 --- a/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts +++ b/x-pack/plugins/apm/server/lib/service_map/get_service_map_service_node_info.ts @@ -8,7 +8,6 @@ import { TRANSACTION_REQUEST, TRANSACTION_PAGE_LOAD, } from '../../../common/transaction_types'; -import { UIFilters } from '../../../typings/ui_filters'; import { SERVICE_NAME, METRIC_SYSTEM_CPU_PERCENT, @@ -53,9 +52,8 @@ export async function getServiceMapServiceNodeInfo({ serviceName, setup, searchAggregatedTransactions, - uiFilters, -}: Options & { serviceName: string; uiFilters: UIFilters }) { - const { start, end } = setup; +}: Options & { serviceName: string }) { + const { start, end, uiFilters } = setup; const filter: ESFilter[] = [ { range: rangeFilter(start, end) }, @@ -105,7 +103,8 @@ async function getErrorStats({ }) { const setupWithBlankUiFilters = { ...setup, - uiFiltersES: getEnvironmentUiFilterES(environment), + uiFilters: { environment }, + esFilter: getEnvironmentUiFilterES(environment), }; const { noHits, average } = await getErrorRate({ setup: setupWithBlankUiFilters, diff --git a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json index e7bba585de1807..94c508fe90230d 100644 --- a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json +++ b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_grouped.json @@ -3,7 +3,7 @@ { "data": { "id": "opbeans-rum", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-rum", "agent.name": "rum-js" } @@ -18,7 +18,7 @@ { "data": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" } diff --git a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json index 22c5c50de74727..58469f607ac130 100644 --- a/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json +++ b/x-pack/plugins/apm/server/lib/service_map/mock_responses/group_resource_nodes_pregrouped.json @@ -3,7 +3,7 @@ { "data": { "id": "opbeans-rum", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-rum", "agent.name": "rum-js" } @@ -18,7 +18,7 @@ { "data": { "id": "opbeans-node", - "service.environment": "testing", + "service.environment": "test", "service.name": "opbeans-node", "agent.name": "nodejs" } diff --git a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap index 87aca0d0569091..d83e558775be40 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/service_nodes/__snapshots__/queries.test.ts.snap @@ -51,7 +51,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -119,7 +119,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -188,7 +188,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], diff --git a/x-pack/plugins/apm/server/lib/service_nodes/index.ts b/x-pack/plugins/apm/server/lib/service_nodes/index.ts index a83aba192dba93..d5e29532e3d7b9 100644 --- a/x-pack/plugins/apm/server/lib/service_nodes/index.ts +++ b/x-pack/plugins/apm/server/lib/service_nodes/index.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getServiceNodesProjection } from '../../projections/service_nodes'; import { mergeProjection } from '../../projections/util/merge_projection'; import { SERVICE_NODE_NAME_MISSING } from '../../../common/service_nodes'; @@ -23,7 +19,7 @@ const getServiceNodes = async ({ setup, serviceName, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; }) => { const { apmEventClient } = setup; diff --git a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap index 431f11066aaff0..3a38f80c87b35e 100644 --- a/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/services/__snapshots__/queries.test.ts.snap @@ -144,7 +144,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -194,7 +194,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -257,7 +257,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -334,7 +334,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -389,7 +389,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], diff --git a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts index fca472b0ce8c22..d6ba9f5447ba50 100644 --- a/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts +++ b/x-pack/plugins/apm/server/lib/services/get_service_node_metadata.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { HOST_NAME, CONTAINER_ID, @@ -24,7 +20,7 @@ export async function getServiceNodeMetadata({ }: { serviceName: string; serviceNodeName: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { const { apmEventClient } = setup; diff --git a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts index c09be7aacc784f..092485c46fb083 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/get_services_items.ts @@ -5,11 +5,7 @@ */ import { joinByKey } from '../../../../common/utils/join_by_key'; import { PromiseReturnType } from '../../../../typings/common'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getServicesProjection } from '../../../projections/services'; import { getTransactionDurationAverages, @@ -21,17 +17,15 @@ import { } from './get_services_items_stats'; export type ServiceListAPIResponse = PromiseReturnType<typeof getServicesItems>; -export type ServicesItemsSetup = Setup & SetupTimeRange & SetupUIFilters; +export type ServicesItemsSetup = Setup & SetupTimeRange; export type ServicesItemsProjection = ReturnType<typeof getServicesProjection>; export async function getServicesItems({ setup, searchAggregatedTransactions, - mlAnomaliesEnvironment, }: { setup: ServicesItemsSetup; searchAggregatedTransactions: boolean; - mlAnomaliesEnvironment?: string; }) { const params = { projection: getServicesProjection({ @@ -55,7 +49,7 @@ export async function getServicesItems({ getTransactionRates(params), getTransactionErrorRates(params), getEnvironments(params), - getHealthStatuses(params, mlAnomaliesEnvironment), + getHealthStatuses(params, setup.uiFilters.environment), ]); const allMetrics = [ diff --git a/x-pack/plugins/apm/server/lib/services/get_services/index.ts b/x-pack/plugins/apm/server/lib/services/get_services/index.ts index 351457b2a815ee..04744a9c791bb1 100644 --- a/x-pack/plugins/apm/server/lib/services/get_services/index.ts +++ b/x-pack/plugins/apm/server/lib/services/get_services/index.ts @@ -6,11 +6,7 @@ import { isEmpty } from 'lodash'; import { PromiseReturnType } from '../../../../typings/common'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { hasHistoricalAgentData } from './has_historical_agent_data'; import { getLegacyDataStatus } from './get_legacy_data_status'; import { getServicesItems } from './get_services_items'; @@ -20,17 +16,14 @@ export type ServiceListAPIResponse = PromiseReturnType<typeof getServices>; export async function getServices({ setup, searchAggregatedTransactions, - mlAnomaliesEnvironment, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; - mlAnomaliesEnvironment?: string; }) { const [items, hasLegacyData] = await Promise.all([ getServicesItems({ setup, searchAggregatedTransactions, - mlAnomaliesEnvironment, }), getLegacyDataStatus(setup), ]); diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap index bd6cefa793467f..c678e7db711b65 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transaction_groups/__snapshots__/queries.test.ts.snap @@ -61,7 +61,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -128,7 +128,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -195,7 +195,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -270,7 +270,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -325,7 +325,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -380,7 +380,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -441,7 +441,7 @@ Array [ }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts index 5d581149db6678..0a4d9748f25977 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/fetcher.ts @@ -15,11 +15,7 @@ import { getTransactionGroupsProjection } from '../../projections/transaction_gr import { mergeProjection } from '../../projections/util/merge_projection'; import { PromiseReturnType } from '../../../../observability/typings/common'; import { AggregationOptionsByType } from '../../../typings/elasticsearch/aggregations'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getAverages, getSums, @@ -57,7 +53,7 @@ export type TransactionGroupRequestBase = ReturnType< }; }; -export type TransactionGroupSetup = Setup & SetupTimeRange & SetupUIFilters; +export type TransactionGroupSetup = Setup & SetupTimeRange; function getItemsWithRelativeImpact( setup: TransactionGroupSetup, diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts index 3dc126c45d328a..d5289430b2698d 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_error_rate.ts @@ -12,11 +12,7 @@ import { EVENT_OUTCOME, } from '../../../common/elasticsearch_fieldnames'; import { rangeFilter } from '../../../common/utils/range_filter'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { getBucketSize } from '../helpers/get_bucket_size'; import { getProcessorEventForAggregatedTransactions, @@ -33,10 +29,10 @@ export async function getErrorRate({ serviceName: string; transactionType?: string; transactionName?: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { - const { start, end, uiFiltersES, apmEventClient } = setup; + const { start, end, esFilter, apmEventClient } = setup; const transactionNamefilter = transactionName ? [{ term: { [TRANSACTION_NAME]: transactionName } }] @@ -53,7 +49,7 @@ export async function getErrorRate({ }, ...transactionNamefilter, ...transactionTypefilter, - ...uiFiltersES, + ...esFilter, ]; const params = { diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts index 6c9b23b3dc0793..7e1aad075fb161 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/get_transaction_sample_for_group.ts @@ -12,11 +12,7 @@ import { } from '../../../common/elasticsearch_fieldnames'; import { ProcessorEvent } from '../../../common/processor_event'; import { rangeFilter } from '../../../common/utils/range_filter'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; export async function getTransactionSampleForGroup({ serviceName, @@ -25,9 +21,9 @@ export async function getTransactionSampleForGroup({ }: { serviceName: string; transactionName: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { - const { apmEventClient, start, end, uiFiltersES } = setup; + const { apmEventClient, start, end, esFilter } = setup; const filter = [ { @@ -43,7 +39,7 @@ export async function getTransactionSampleForGroup({ [TRANSACTION_NAME]: transactionName, }, }, - ...uiFiltersES, + ...esFilter, ]; const getSampledTransaction = async () => { diff --git a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts index 6e0d619268d444..3796511029243a 100644 --- a/x-pack/plugins/apm/server/lib/transaction_groups/index.ts +++ b/x-pack/plugins/apm/server/lib/transaction_groups/index.ts @@ -4,16 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../helpers/setup_request'; import { transactionGroupsFetcher, Options } from './fetcher'; export async function getTransactionGroupList( options: Options, - setup: Setup & SetupTimeRange & SetupUIFilters + setup: Setup & SetupTimeRange ) { const bucketSize = setup.config['xpack.apm.ui.transactionGroupBucketSize']; return await transactionGroupsFetcher(options, setup, bucketSize); diff --git a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap index c63dfcc0c0ec70..3e0a7317afd706 100644 --- a/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/transactions/__snapshots__/queries.test.ts.snap @@ -161,7 +161,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -295,7 +295,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -401,7 +401,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], @@ -502,7 +502,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -608,7 +608,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, Object { @@ -673,7 +673,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts index 34863c64f98041..8bbcaebe06513d 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.test.ts @@ -36,7 +36,8 @@ function getMockSetup(esResponse: any) { get: () => 'myIndex', } ) as APMConfig, - uiFiltersES: [], + uiFilters: {}, + esFilter: [], indices: mockIndices, dynamicIndexPattern: null as any, }; diff --git a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts index 9730ddbbf38d7c..8febdc898ab97f 100644 --- a/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/breakdown/index.ts @@ -16,11 +16,7 @@ import { TRANSACTION_NAME, TRANSACTION_BREAKDOWN_COUNT, } from '../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { rangeFilter } from '../../../../common/utils/range_filter'; import { getMetricsDateHistogramParams } from '../../helpers/metrics'; import { MAX_KPIS } from './constants'; @@ -32,12 +28,12 @@ export async function getTransactionBreakdown({ transactionName, transactionType, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; transactionName?: string; transactionType: string; }) { - const { uiFiltersES, apmEventClient, start, end, config } = setup; + const { esFilter, apmEventClient, start, end, config } = setup; const subAggs = { sum_all_self_times: { @@ -84,7 +80,7 @@ export async function getTransactionBreakdown({ { term: { [SERVICE_NAME]: serviceName } }, { term: { [TRANSACTION_TYPE]: transactionType } }, { range: rangeFilter(start, end) }, - ...uiFiltersES, + ...esFilter, ]; if (transactionName) { diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts index 3cf9a54e3fe9be..287c7bc2c47f9f 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/fetcher.ts @@ -5,6 +5,7 @@ */ import { Logger } from 'kibana/server'; +import { ESSearchResponse } from '../../../../../typings/elasticsearch'; import { PromiseReturnType } from '../../../../../../observability/typings/common'; import { Setup, SetupTimeRange } from '../../../helpers/setup_request'; @@ -47,7 +48,7 @@ export async function anomalySeriesFetcher({ filter: [ { term: { job_id: jobId } }, { exists: { field: 'bucket_span' } }, - { term: { result_type: 'model_plot' } }, + { terms: { result_type: ['model_plot', 'record'] } }, { term: { partition_field_value: serviceName } }, { term: { by_field_value: transactionType } }, { @@ -67,7 +68,7 @@ export async function anomalySeriesFetcher({ extended_bounds: { min: newStart, max: end }, }, aggs: { - anomaly_score: { max: { field: 'anomaly_score' } }, + anomaly_score: { max: { field: 'record_score' } }, lower: { min: { field: 'model_lower' } }, upper: { max: { field: 'model_upper' } }, }, @@ -77,7 +78,11 @@ export async function anomalySeriesFetcher({ }; try { - const response = await ml.mlSystem.mlAnomalySearch(params); + const response: ESSearchResponse< + unknown, + typeof params + > = (await ml.mlSystem.mlAnomalySearch(params)) as any; + return response; } catch (err) { const isHttpError = 'statusCode' in err; diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts index d8865f0049d357..f11623eaa2daea 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/index.ts @@ -5,17 +5,13 @@ */ import { Logger } from 'kibana/server'; import { isNumber } from 'lodash'; +import { ENVIRONMENT_ALL } from '../../../../../common/environment_filter_values'; import { getBucketSize } from '../../../helpers/get_bucket_size'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../helpers/setup_request'; import { anomalySeriesFetcher } from './fetcher'; import { getMlBucketSize } from './get_ml_bucket_size'; import { anomalySeriesTransform } from './transform'; import { getMLJobIds } from '../../../service_map/get_service_anomalies'; -import { UIFilters } from '../../../../../typings/ui_filters'; export async function getAnomalySeries({ serviceName, @@ -24,15 +20,13 @@ export async function getAnomalySeries({ timeSeriesDates, setup, logger, - uiFilters, }: { serviceName: string; transactionType: string | undefined; transactionName: string | undefined; timeSeriesDates: number[]; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; logger: Logger; - uiFilters: UIFilters; }) { // don't fetch anomalies for transaction details page if (transactionName) { @@ -44,12 +38,22 @@ export async function getAnomalySeries({ return; } + const { uiFilters, start, end } = setup; + const { environment } = uiFilters; + + // don't fetch anomalies when no specific environment is selected + if (environment === ENVIRONMENT_ALL.value) { + return; + } + // don't fetch anomalies if unknown uiFilters are applied const knownFilters = ['environment', 'serviceName']; - const uiFilterNames = Object.keys(uiFilters); - if ( - uiFilterNames.some((uiFilterName) => !knownFilters.includes(uiFilterName)) - ) { + const hasUnknownFiltersApplied = Object.entries(setup.uiFilters) + .filter(([key, value]) => !!value) + .map(([key]) => key) + .some((uiFilterName) => !knownFilters.includes(uiFilterName)); + + if (hasUnknownFiltersApplied) { return; } @@ -64,15 +68,8 @@ export async function getAnomalySeries({ return; } - const mlJobIds = await getMLJobIds( - setup.ml.anomalyDetectors, - uiFilters.environment - ); + const mlJobIds = await getMLJobIds(setup.ml.anomalyDetectors, environment); - // don't fetch anomalies if there are isn't exaclty 1 ML job match for the given environment - if (mlJobIds.length !== 1) { - return; - } const jobId = mlJobIds[0]; const mlBucketSize = await getMlBucketSize({ setup, jobId, logger }); @@ -80,7 +77,6 @@ export async function getAnomalySeries({ return; } - const { start, end } = setup; const { intervalString, bucketSize } = getBucketSize(start, end); const esResponse = await anomalySeriesFetcher({ diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts index fdbd99bf274d6d..75dfae3e7375f3 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.test.ts @@ -29,7 +29,10 @@ describe('timeseriesFetcher', () => { get: () => 'myIndex', } ) as APMConfig, - uiFiltersES: [ + uiFilters: { + environment: 'test', + }, + esFilter: [ { term: { 'service.environment': 'test' }, }, diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts index 5a3948f5774306..e2edbbec63d475 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/fetcher.ts @@ -14,11 +14,7 @@ import { import { PromiseReturnType } from '../../../../../../observability/typings/common'; import { getBucketSize } from '../../../helpers/get_bucket_size'; import { rangeFilter } from '../../../../../common/utils/range_filter'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions, getTransactionDurationFieldForAggregatedTransactions, @@ -36,10 +32,10 @@ export function timeseriesFetcher({ serviceName: string; transactionType: string | undefined; transactionName: string | undefined; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { - const { start, end, uiFiltersES, apmEventClient } = setup; + const { start, end, apmEventClient } = setup; const { intervalString } = getBucketSize(start, end); const filter: ESFilter[] = [ @@ -48,7 +44,7 @@ export function timeseriesFetcher({ ...getDocumentTypeFilterForAggregatedTransactions( searchAggregatedTransactions ), - ...uiFiltersES, + ...setup.esFilter, ]; if (transactionName) { diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts index 81dca447f16cab..c0421005dd06eb 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/index.ts @@ -5,11 +5,7 @@ */ import { getBucketSize } from '../../../helpers/get_bucket_size'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../helpers/setup_request'; import { timeseriesFetcher } from './fetcher'; import { timeseriesTransformer } from './transform'; @@ -17,7 +13,7 @@ export async function getApmTimeseriesData(options: { serviceName: string; transactionType: string | undefined; transactionName: string | undefined; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { const { start, end } = options.setup; diff --git a/x-pack/plugins/apm/server/lib/transactions/charts/index.ts b/x-pack/plugins/apm/server/lib/transactions/charts/index.ts index 43abf0b1a1d33d..d8593612c05828 100644 --- a/x-pack/plugins/apm/server/lib/transactions/charts/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/charts/index.ts @@ -6,15 +6,10 @@ import { Logger } from 'kibana/server'; import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getAnomalySeries } from './get_anomaly_data'; import { getApmTimeseriesData } from './get_timeseries_data'; import { ApmTimeSeriesResponse } from './get_timeseries_data/transform'; -import { UIFilters } from '../../../../typings/ui_filters'; function getDates(apmTimeseries: ApmTimeSeriesResponse) { return apmTimeseries.responseTimes.avg.map((p) => p.x); @@ -27,10 +22,9 @@ export async function getTransactionCharts(options: { serviceName: string; transactionType: string | undefined; transactionName: string | undefined; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; logger: Logger; - uiFilters: UIFilters; }) { const apmTimeseries = await getApmTimeseriesData(options); const anomalyTimeseries = await getAnomalySeries({ diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts index 6e2fe34a5f5ef5..34d01627a2869d 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts @@ -17,11 +17,7 @@ import { TRANSACTION_TYPE, } from '../../../../../common/elasticsearch_fieldnames'; import { rangeFilter } from '../../../../../common/utils/range_filter'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../../helpers/setup_request'; import { getDocumentTypeFilterForAggregatedTransactions, getProcessorEventForAggregatedTransactions, @@ -66,17 +62,17 @@ export async function getBuckets({ traceId: string; distributionMax: number; bucketSize: number; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { - const { start, end, uiFiltersES, apmEventClient } = setup; + const { start, end, esFilter, apmEventClient } = setup; const commonFilters = [ { term: { [SERVICE_NAME]: serviceName } }, { term: { [TRANSACTION_TYPE]: transactionType } }, { term: { [TRANSACTION_NAME]: transactionName } }, { range: rangeFilter(start, end) }, - ...uiFiltersES, + ...esFilter, ]; async function getSamplesForDistributionBuckets() { diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts index 24ca2a4a07b68c..249b1c4fbb20a5 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts @@ -9,11 +9,7 @@ import { TRANSACTION_NAME, TRANSACTION_TYPE, } from '../../../../common/elasticsearch_fieldnames'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getProcessorEventForAggregatedTransactions, getTransactionDurationFieldForAggregatedTransactions, @@ -29,10 +25,10 @@ export async function getDistributionMax({ serviceName: string; transactionName: string; transactionType: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { - const { start, end, uiFiltersES, apmEventClient } = setup; + const { start, end, esFilter, apmEventClient } = setup; const params = { apm: { @@ -59,7 +55,7 @@ export async function getDistributionMax({ }, }, }, - ...uiFiltersES, + ...esFilter, ], }, }, diff --git a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts index b9ab36fb08d422..deafc37ee42e20 100644 --- a/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/distribution/index.ts @@ -5,11 +5,7 @@ */ import { PromiseReturnType } from '../../../../../observability/typings/common'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { getBuckets } from './get_buckets'; import { getDistributionMax } from './get_distribution_max'; import { roundToNearestFiveOrTen } from '../../helpers/round_to_nearest_five_or_ten'; @@ -39,7 +35,7 @@ export async function getTransactionDistribution({ transactionType: string; transactionId: string; traceId: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { const distributionMax = await getDistributionMax({ diff --git a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts index 9aa1a8f4de87fc..8958be0819613d 100644 --- a/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts +++ b/x-pack/plugins/apm/server/lib/transactions/get_transaction/index.ts @@ -9,11 +9,7 @@ import { TRANSACTION_ID, } from '../../../../common/elasticsearch_fieldnames'; import { rangeFilter } from '../../../../common/utils/range_filter'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../helpers/setup_request'; import { ProcessorEvent } from '../../../../common/processor_event'; export async function getTransaction({ @@ -23,7 +19,7 @@ export async function getTransaction({ }: { transactionId: string; traceId: string; - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { const { start, end, apmEventClient } = setup; diff --git a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts index 87b8bc7c4ae90f..eff9451c9e1cd4 100644 --- a/x-pack/plugins/apm/server/lib/transactions/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/transactions/queries.test.ts @@ -56,7 +56,6 @@ describe('transaction queries', () => { setup, searchAggregatedTransactions: false, logger: loggerMock.create(), - uiFilters: {}, }) ); expect(mock.params).toMatchSnapshot(); @@ -71,7 +70,6 @@ describe('transaction queries', () => { setup, searchAggregatedTransactions: false, logger: loggerMock.create(), - uiFilters: {}, }) ); expect(mock.params).toMatchSnapshot(); @@ -86,7 +84,6 @@ describe('transaction queries', () => { setup, searchAggregatedTransactions: false, logger: loggerMock.create(), - uiFilters: {}, }) ); diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap index 5f384327192800..e7ca65eb740b6a 100644 --- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap +++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/__snapshots__/queries.test.ts.snap @@ -46,7 +46,7 @@ Object { }, Object { "term": Object { - "my.custom.ui.filter": "foo-bar", + "service.environment": "test", }, }, ], diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts index 10f6e93c1cfc13..9fbdba679b6675 100644 --- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts +++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/get_local_filter_query.ts @@ -8,7 +8,7 @@ import { omit } from 'lodash'; import { mergeProjection } from '../../../projections/util/merge_projection'; import { Projection } from '../../../projections/typings'; import { UIFilters } from '../../../../typings/ui_filters'; -import { getUiFiltersES } from '../../helpers/convert_ui_filters/get_ui_filters_es'; +import { getEsFilter } from '../../helpers/convert_ui_filters/get_es_filter'; import { localUIFilters } from './config'; import { LocalUIFilterName } from '../../../../common/ui_filter'; @@ -22,7 +22,7 @@ export const getLocalFilterQuery = ({ localUIFilterName: LocalUIFilterName; }) => { const field = localUIFilters[localUIFilterName]; - const filter = getUiFiltersES(omit(uiFilters, field.name)); + const filter = getEsFilter(omit(uiFilters, field.name)); const bucketCountAggregation = projection.body.aggs ? { diff --git a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts index 22fa20e255f6ed..f4e8aafc1bcf5f 100644 --- a/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts +++ b/x-pack/plugins/apm/server/lib/ui_filters/local_ui_filters/queries.test.ts @@ -15,7 +15,7 @@ describe('local ui filter queries', () => { let mock: SearchParamsMock; beforeEach(() => { - jest.mock('../../helpers/convert_ui_filters/get_ui_filters_es', () => { + jest.mock('../../helpers/convert_ui_filters/get_es_filter', () => { return []; }); }); diff --git a/x-pack/plugins/apm/server/projections/errors.ts b/x-pack/plugins/apm/server/projections/errors.ts index 49a0e9f479d263..173dc94a0840cd 100644 --- a/x-pack/plugins/apm/server/projections/errors.ts +++ b/x-pack/plugins/apm/server/projections/errors.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../server/lib/helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { SERVICE_NAME, ERROR_GROUP_ID, @@ -20,10 +16,10 @@ export function getErrorGroupsProjection({ setup, serviceName, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; }) { - const { start, end, uiFiltersES } = setup; + const { start, end, esFilter } = setup; return { apm: { @@ -35,7 +31,7 @@ export function getErrorGroupsProjection({ filter: [ { term: { [SERVICE_NAME]: serviceName } }, { range: rangeFilter(start, end) }, - ...uiFiltersES, + ...esFilter, ], }, }, diff --git a/x-pack/plugins/apm/server/projections/metrics.ts b/x-pack/plugins/apm/server/projections/metrics.ts index eb80a6bc73248d..c3b5db5be6af88 100644 --- a/x-pack/plugins/apm/server/projections/metrics.ts +++ b/x-pack/plugins/apm/server/projections/metrics.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../server/lib/helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { SERVICE_NAME, SERVICE_NODE_NAME, @@ -34,17 +30,17 @@ export function getMetricsProjection({ serviceName, serviceNodeName, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; }) { - const { start, end, uiFiltersES } = setup; + const { start, end, esFilter } = setup; const filter = [ { term: { [SERVICE_NAME]: serviceName } }, { range: rangeFilter(start, end) }, ...getServiceNodeNameFilters(serviceNodeName), - ...uiFiltersES, + ...esFilter, ]; return { diff --git a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts index c27314923f6bd6..96ee26c6e65f58 100644 --- a/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts +++ b/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../server/lib/helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { AGENT_NAME, TRANSACTION_TYPE, @@ -22,10 +18,10 @@ export function getRumPageLoadTransactionsProjection({ setup, urlQuery, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; urlQuery?: string; }) { - const { start, end, uiFiltersES } = setup; + const { start, end, esFilter } = setup; const bool = { filter: [ @@ -49,7 +45,7 @@ export function getRumPageLoadTransactionsProjection({ }, ] : []), - ...uiFiltersES, + ...esFilter, ], }; @@ -68,9 +64,9 @@ export function getRumPageLoadTransactionsProjection({ export function getRumErrorsProjection({ setup, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; }) { - const { start, end, uiFiltersES } = setup; + const { start, end, esFilter: esFilter } = setup; const bool = { filter: [ @@ -82,7 +78,7 @@ export function getRumErrorsProjection({ [SERVICE_LANGUAGE_NAME]: 'javascript', }, }, - ...uiFiltersES, + ...esFilter, ], }; diff --git a/x-pack/plugins/apm/server/projections/service_nodes.ts b/x-pack/plugins/apm/server/projections/service_nodes.ts index 87fe815a12d0de..ed8d4c7409eda0 100644 --- a/x-pack/plugins/apm/server/projections/service_nodes.ts +++ b/x-pack/plugins/apm/server/projections/service_nodes.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../server/lib/helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { SERVICE_NODE_NAME } from '../../common/elasticsearch_fieldnames'; import { mergeProjection } from './util/merge_projection'; import { getMetricsProjection } from './metrics'; @@ -18,7 +14,7 @@ export function getServiceNodesProjection({ serviceName, serviceNodeName, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName: string; serviceNodeName?: string; }) { diff --git a/x-pack/plugins/apm/server/projections/services.ts b/x-pack/plugins/apm/server/projections/services.ts index ba61f72519a237..d912a95546515e 100644 --- a/x-pack/plugins/apm/server/projections/services.ts +++ b/x-pack/plugins/apm/server/projections/services.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupUIFilters, - SetupTimeRange, -} from '../../server/lib/helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { SERVICE_NAME } from '../../common/elasticsearch_fieldnames'; import { rangeFilter } from '../../common/utils/range_filter'; import { ProcessorEvent } from '../../common/processor_event'; @@ -18,10 +14,10 @@ export function getServicesProjection({ setup, searchAggregatedTransactions, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; searchAggregatedTransactions: boolean; }) { - const { start, end, uiFiltersES } = setup; + const { start, end, esFilter } = setup; return { apm: { @@ -37,7 +33,7 @@ export function getServicesProjection({ size: 0, query: { bool: { - filter: [{ range: rangeFilter(start, end) }, ...uiFiltersES], + filter: [{ range: rangeFilter(start, end) }, ...esFilter], }, }, aggs: { diff --git a/x-pack/plugins/apm/server/projections/transaction_groups.ts b/x-pack/plugins/apm/server/projections/transaction_groups.ts index 0cc3a7a35d2143..2ce720eb121672 100644 --- a/x-pack/plugins/apm/server/projections/transaction_groups.ts +++ b/x-pack/plugins/apm/server/projections/transaction_groups.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { omit } from 'lodash'; -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../server/lib/helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { TRANSACTION_NAME, PARENT_ID, @@ -22,7 +18,7 @@ export function getTransactionGroupsProjection({ setup, options, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; options: Options; }) { const transactionsProjection = getTransactionsProjection({ diff --git a/x-pack/plugins/apm/server/projections/transactions.ts b/x-pack/plugins/apm/server/projections/transactions.ts index 8e9bb3bf321f62..548e77b5d2cd99 100644 --- a/x-pack/plugins/apm/server/projections/transactions.ts +++ b/x-pack/plugins/apm/server/projections/transactions.ts @@ -4,11 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { - Setup, - SetupTimeRange, - SetupUIFilters, -} from '../../server/lib/helpers/setup_request'; +import { Setup, SetupTimeRange } from '../../server/lib/helpers/setup_request'; import { SERVICE_NAME, TRANSACTION_TYPE, @@ -27,13 +23,13 @@ export function getTransactionsProjection({ transactionType, searchAggregatedTransactions, }: { - setup: Setup & SetupTimeRange & SetupUIFilters; + setup: Setup & SetupTimeRange; serviceName?: string; transactionName?: string; transactionType?: string; searchAggregatedTransactions: boolean; }) { - const { start, end, uiFiltersES } = setup; + const { start, end, esFilter } = setup; const transactionNameFilter = transactionName ? [{ term: { [TRANSACTION_NAME]: transactionName } }] @@ -51,7 +47,7 @@ export function getTransactionsProjection({ ...transactionNameFilter, ...transactionTypeFilter, ...serviceNameFilter, - ...uiFiltersES, + ...esFilter, ...getDocumentTypeFilterForAggregatedTransactions( searchAggregatedTransactions ), diff --git a/x-pack/plugins/apm/server/routes/service_map.ts b/x-pack/plugins/apm/server/routes/service_map.ts index 1996d4d4a262dc..6e86ececd1bfe4 100644 --- a/x-pack/plugins/apm/server/routes/service_map.ts +++ b/x-pack/plugins/apm/server/routes/service_map.ts @@ -17,7 +17,6 @@ import { createRoute } from './create_route'; import { rangeRt, uiFiltersRt } from './default_api_types'; import { notifyFeatureUsage } from '../feature'; import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; -import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters'; export const serviceMapRoute = createRoute(() => ({ path: '/api/apm/service-map', @@ -77,24 +76,20 @@ export const serviceMapServiceNodeRoute = createRoute(() => ({ if (!isActivePlatinumLicense(context.licensing.license)) { throw Boom.forbidden(invalidLicenseMessage); } - const logger = context.logger; const setup = await setupRequest(context, request); const { - query: { uiFilters: uiFiltersJson }, path: { serviceName }, } = context.params; const searchAggregatedTransactions = await getSearchAggregatedTransactions( setup ); - const uiFilters = getParsedUiFilters({ uiFilters: uiFiltersJson, logger }); return getServiceMapServiceNodeInfo({ setup, serviceName, searchAggregatedTransactions, - uiFilters, }); }, })); diff --git a/x-pack/plugins/apm/server/routes/services.ts b/x-pack/plugins/apm/server/routes/services.ts index 4bb10f31ba6a1a..538ba3926c792d 100644 --- a/x-pack/plugins/apm/server/routes/services.ts +++ b/x-pack/plugins/apm/server/routes/services.ts @@ -17,7 +17,6 @@ import { uiFiltersRt, rangeRt } from './default_api_types'; import { getServiceAnnotations } from '../lib/services/annotations'; import { dateAsStringRt } from '../../common/runtime_types/date_as_string_rt'; import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; -import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters'; export const servicesRoute = createRoute(() => ({ path: '/api/apm/services', @@ -25,22 +24,13 @@ export const servicesRoute = createRoute(() => ({ query: t.intersection([uiFiltersRt, rangeRt]), }, handler: async ({ context, request }) => { - const { environment } = getParsedUiFilters({ - uiFilters: context.params.query.uiFilters, - logger: context.logger, - }); - const setup = await setupRequest(context, request); const searchAggregatedTransactions = await getSearchAggregatedTransactions( setup ); - const services = await getServices({ - setup, - searchAggregatedTransactions, - mlAnomaliesEnvironment: environment, - }); + const services = await getServices({ setup, searchAggregatedTransactions }); return services; }, diff --git a/x-pack/plugins/apm/server/routes/transaction_groups.ts b/x-pack/plugins/apm/server/routes/transaction_groups.ts index dd1335fb2c2a19..18fc73b468cd4d 100644 --- a/x-pack/plugins/apm/server/routes/transaction_groups.ts +++ b/x-pack/plugins/apm/server/routes/transaction_groups.ts @@ -5,6 +5,7 @@ */ import * as t from 'io-ts'; +import Boom from 'boom'; import { setupRequest } from '../lib/helpers/setup_request'; import { getTransactionCharts } from '../lib/transactions/charts'; import { getTransactionDistribution } from '../lib/transactions/distribution'; @@ -15,7 +16,6 @@ import { uiFiltersRt, rangeRt } from './default_api_types'; import { getTransactionSampleForGroup } from '../lib/transaction_groups/get_transaction_sample_for_group'; import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions'; import { getErrorRate } from '../lib/transaction_groups/get_error_rate'; -import { getParsedUiFilters } from '../lib/helpers/convert_ui_filters/get_parsed_ui_filters'; export const transactionGroupsRoute = createRoute(() => ({ path: '/api/apm/services/{serviceName}/transaction_groups', @@ -71,27 +71,28 @@ export const transactionGroupsChartsRoute = createRoute(() => ({ const setup = await setupRequest(context, request); const logger = context.logger; const { serviceName } = context.params.path; - const { - transactionType, - transactionName, - uiFilters: uiFiltersJson, - } = context.params.query; + const { transactionType, transactionName } = context.params.query; - const uiFilters = getParsedUiFilters({ uiFilters: uiFiltersJson, logger }); + if (!setup.uiFilters.environment) { + throw Boom.badRequest( + `environment is a required property of the ?uiFilters JSON for transaction_groups/charts.` + ); + } const searchAggregatedTransactions = await getSearchAggregatedTransactions( setup ); - return getTransactionCharts({ + const options = { serviceName, transactionType, transactionName, setup, searchAggregatedTransactions, logger, - uiFilters, - }); + }; + + return getTransactionCharts(options); }, })); diff --git a/x-pack/plugins/apm/server/routes/ui_filters.ts b/x-pack/plugins/apm/server/routes/ui_filters.ts index 936d460102dcec..26fe0118c02ed9 100644 --- a/x-pack/plugins/apm/server/routes/ui_filters.ts +++ b/x-pack/plugins/apm/server/routes/ui_filters.ts @@ -9,13 +9,12 @@ import { omit } from 'lodash'; import { setupRequest, Setup, - SetupUIFilters, SetupTimeRange, } from '../lib/helpers/setup_request'; import { getEnvironments } from '../lib/ui_filters/get_environments'; import { Projection } from '../projections/typings'; import { localUIFilterNames } from '../lib/ui_filters/local_ui_filters/config'; -import { getUiFiltersES } from '../lib/helpers/convert_ui_filters/get_ui_filters_es'; +import { getEsFilter } from '../lib/helpers/convert_ui_filters/get_es_filter'; import { getLocalUIFilters } from '../lib/ui_filters/local_ui_filters'; import { getServicesProjection } from '../projections/services'; import { getTransactionGroupsProjection } from '../projections/transaction_groups'; @@ -97,23 +96,23 @@ function createLocalFiltersRoute< }, handler: async ({ context, request }) => { const setup = await setupRequest(context, request); + const { uiFilters } = setup; const { query } = context.params; - const { uiFilters, filterNames } = query; - const parsedUiFilters = JSON.parse(uiFilters); + const { filterNames } = query; const projection = await getProjection({ query, context, setup: { ...setup, - uiFiltersES: getUiFiltersES(omit(parsedUiFilters, filterNames)), + esFilter: getEsFilter(omit(uiFilters, filterNames)), }, }); return getLocalUIFilters({ projection, setup, - uiFilters: parsedUiFilters, + uiFilters, localFilterNames: filterNames, }); }, @@ -271,6 +270,6 @@ type GetProjection< context, }: { query: t.TypeOf<TQueryRT>; - setup: Setup & SetupUIFilters & SetupTimeRange; + setup: Setup & SetupTimeRange; context: APMRequestHandlerContext; }) => Promise<TProjection> | TProjection; diff --git a/x-pack/plugins/apm/server/utils/test_helpers.tsx b/x-pack/plugins/apm/server/utils/test_helpers.tsx index 98c1436b2b9b8f..18b990b35b5a58 100644 --- a/x-pack/plugins/apm/server/utils/test_helpers.tsx +++ b/x-pack/plugins/apm/server/utils/test_helpers.tsx @@ -9,6 +9,7 @@ import { ESSearchRequest, } from '../../typings/elasticsearch'; import { PromiseReturnType } from '../../typings/common'; +import { UIFilters } from '../../typings/ui_filters'; import { APMConfig } from '..'; interface Options { @@ -23,7 +24,8 @@ interface MockSetup { apmEventClient: any; internalClient: any; config: APMConfig; - uiFiltersES: ESFilter[]; + uiFilters: UIFilters; + esFilter: ESFilter[]; indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': string; @@ -78,7 +80,8 @@ export async function inspectSearchParams( }, } ) as APMConfig, - uiFiltersES: [{ term: { 'my.custom.ui.filter': 'foo-bar' } }], + uiFilters: { environment: 'test' }, + esFilter: [{ term: { 'service.environment': 'test' } }], indices: { /* eslint-disable @typescript-eslint/naming-convention */ 'apm_oss.sourcemapIndices': 'myIndex', diff --git a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts b/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts index e0e13b7b7fb98d..a2223e55602888 100644 --- a/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts +++ b/x-pack/test/apm_api_integration/basic/tests/feature_controls.ts @@ -107,21 +107,21 @@ export default function featureControlsTests({ getService }: FtrProviderContext) }, { req: { - url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, }, expectForbidden: expect404, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, }, expectForbidden: expect404, expectResponse: expect200, }, { req: { - url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%7D`, + url: `/api/apm/services/foo/transaction_groups/charts?start=${start}&end=${end}&transactionType=bar&transactionName=baz&uiFilters=%7B%22environment%22%3A%22testing%22%7D`, }, expectForbidden: expect404, expectResponse: expect200, diff --git a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts index c9581079b99520..d7d6d613281ef7 100644 --- a/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts +++ b/x-pack/test/apm_api_integration/basic/tests/transaction_groups/transaction_charts.ts @@ -19,7 +19,7 @@ export default function ApiTest({ getService }: FtrProviderContext) { // url parameters const start = encodeURIComponent(metadata.start); const end = encodeURIComponent(metadata.end); - const uiFilters = encodeURIComponent(JSON.stringify({})); + const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'testing' })); describe('Transaction charts', () => { describe('when data is not loaded ', () => { diff --git a/x-pack/test/apm_api_integration/trial/tests/index.ts b/x-pack/test/apm_api_integration/trial/tests/index.ts index a6a031def34ea4..e609279366390f 100644 --- a/x-pack/test/apm_api_integration/trial/tests/index.ts +++ b/x-pack/test/apm_api_integration/trial/tests/index.ts @@ -16,6 +16,7 @@ export default function observabilityApiIntegrationTests({ loadTestFile }: FtrPr describe('Services', function () { loadTestFile(require.resolve('./services/annotations')); loadTestFile(require.resolve('./services/top_services.ts')); + loadTestFile(require.resolve('./services/transaction_groups_charts')); }); describe('Settings', function () { diff --git a/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap b/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap new file mode 100644 index 00000000000000..8169e73202fbc7 --- /dev/null +++ b/x-pack/test/apm_api_integration/trial/tests/services/__snapshots__/transaction_groups_charts.snap @@ -0,0 +1,43 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters when not defined environments selected should return the correct anomaly boundaries 1`] = `Array []`; + +exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters with environment selected and empty kuery filter should return a non-empty anomaly series 1`] = ` +Array [ + Object { + "x": 1601389800000, + "y": 1206111.33487531, + "y0": 10555.1290143587, + }, + Object { + "x": 1601390700000, + "y": 1223987.49321778, + "y0": 10177.4677901726, + }, + Object { + "x": 1601391600000, + "y": 1223987.49321778, + "y0": 10177.4677901726, + }, +] +`; + +exports[`APM Transaction Overview when data is loaded and fetching transaction groups charts with uiFilters with environment selected in uiFilters should return a non-empty anomaly series 1`] = ` +Array [ + Object { + "x": 1601389800000, + "y": 1206111.33487531, + "y0": 10555.1290143587, + }, + Object { + "x": 1601390700000, + "y": 1223987.49321778, + "y0": 10177.4677901726, + }, + Object { + "x": 1601391600000, + "y": 1223987.49321778, + "y0": 10177.4677901726, + }, +] +`; diff --git a/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts b/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts new file mode 100644 index 00000000000000..c35dfcc3817a42 --- /dev/null +++ b/x-pack/test/apm_api_integration/trial/tests/services/transaction_groups_charts.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { expectSnapshot } from '../../../common/match_snapshot'; +import { PromiseReturnType } from '../../../../../plugins/apm/typings/common'; +import { FtrProviderContext } from '../../../common/ftr_provider_context'; +import archives_metadata from '../../../common/archives_metadata'; + +export default function ApiTest({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const esArchiver = getService('esArchiver'); + + const archiveName = 'apm_8.0.0'; + + const range = archives_metadata[archiveName]; + + // url parameters + const start = encodeURIComponent(range.start); + const end = encodeURIComponent(range.end); + const transactionType = 'request'; + + describe('APM Transaction Overview', () => { + describe('when data is loaded', () => { + before(() => esArchiver.load(archiveName)); + after(() => esArchiver.unload(archiveName)); + + describe('and fetching transaction groups charts with uiFilters', () => { + const serviceName = 'opbeans-java'; + let response: PromiseReturnType<typeof supertest.get>; + + describe('without environment', () => { + const uiFilters = encodeURIComponent(JSON.stringify({})); + before(async () => { + response = await supertest.get( + `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + ); + }); + it('should return an error response', () => { + expect(response.status).to.eql(400); + }); + }); + + describe('without uiFilters', () => { + before(async () => { + response = await supertest.get( + `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}` + ); + }); + it('should return an error response', () => { + expect(response.status).to.eql(400); + }); + }); + + describe('with environment selected in uiFilters', () => { + const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'production' })); + before(async () => { + response = await supertest.get( + `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should return the ML job id for anomalies of the selected environment', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries).to.have.property('jobId'); + expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( + `"apm-production-229a-high_mean_transaction_duration"` + ); + }); + + it('should return a non-empty anomaly series', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0); + expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); + }); + }); + + describe('when not defined environments selected', () => { + const uiFilters = encodeURIComponent( + JSON.stringify({ environment: 'ENVIRONMENT_NOT_DEFINED' }) + ); + before(async () => { + response = await supertest.get( + `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should return the ML job id for anomalies with no defined environment', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries).to.have.property('jobId'); + expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( + `"apm-environment_not_defined-7ed6-high_mean_transaction_duration"` + ); + }); + + it('should return the correct anomaly boundaries', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); + }); + }); + + describe('with all environments selected', () => { + const uiFilters = encodeURIComponent(JSON.stringify({ environment: 'ENVIRONMENT_ALL' })); + before(async () => { + response = await supertest.get( + `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should not return anomaly timeseries data', () => { + expect(response.body).to.not.have.property('anomalyTimeseries'); + }); + }); + + describe('with environment selected and empty kuery filter', () => { + const uiFilters = encodeURIComponent( + JSON.stringify({ kuery: '', environment: 'production' }) + ); + before(async () => { + response = await supertest.get( + `/api/apm/services/${serviceName}/transaction_groups/charts?start=${start}&end=${end}&transactionType=${transactionType}&uiFilters=${uiFilters}` + ); + }); + + it('should have a successful response', () => { + expect(response.status).to.eql(200); + }); + + it('should return the ML job id for anomalies of the selected environment', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries).to.have.property('jobId'); + expectSnapshot(response.body.anomalyTimeseries.jobId).toMatchInline( + `"apm-production-229a-high_mean_transaction_duration"` + ); + }); + + it('should return a non-empty anomaly series', () => { + expect(response.body).to.have.property('anomalyTimeseries'); + expect(response.body.anomalyTimeseries.anomalyBoundaries?.length).to.be.greaterThan(0); + expectSnapshot(response.body.anomalyTimeseries.anomalyBoundaries).toMatch(); + }); + }); + }); + }); + }); +} From bd9a9a7a2bf65c465dd805815a2b1a6eea03bc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= <patryk.kopycinski@elastic.co> Date: Fri, 2 Oct 2020 10:45:53 +0200 Subject: [PATCH 069/128] [Security Solution] Refactor react-beautiful-dnd render props (#78128) --- .../timeline/events/all/index.ts | 7 +- .../common/search_strategy/timeline/index.ts | 18 +- .../add_filter_to_global_search_bar/index.tsx | 56 +- .../drag_drop_context_wrapper.tsx | 13 +- .../drag_and_drop/draggable_wrapper.tsx | 131 +- .../drag_and_drop/droppable_wrapper.tsx | 33 +- .../common/components/draggables/index.tsx | 2 +- .../events_viewer/events_viewer.tsx | 28 +- .../common/components/events_viewer/mock.ts | 2 +- .../common/components/ml/entity_draggable.tsx | 67 +- .../components/ml/score/draggable_score.tsx | 76 +- .../recent_timelines/recent_timelines.tsx | 160 +- .../components/flyout/button/index.tsx | 34 +- .../components/formatted_ip/index.tsx | 157 +- .../__snapshots__/timeline.test.tsx.snap | 34 +- .../timeline/auto_save_warning/index.tsx | 121 +- .../body/column_headers/actions/index.tsx | 29 +- .../body/column_headers/column_header.tsx | 43 +- .../timeline/body/column_headers/index.tsx | 29 +- .../body/events/event_column_view.tsx | 18 +- .../timeline/body/events/stateful_event.tsx | 228 +- .../renderers/suricata/suricata_signature.tsx | 82 +- .../body/renderers/zeek/zeek_signature.tsx | 76 +- .../data_providers.test.tsx.snap | 5 - .../__snapshots__/providers.test.tsx.snap | 2243 +++++++++++++---- .../data_providers/data_providers.test.tsx | 27 +- .../timeline/data_providers/index.tsx | 80 +- .../data_providers/provider_badge.tsx | 4 +- .../data_providers/provider_item_badge.tsx | 47 +- .../data_providers/providers.test.tsx | 264 +- .../timeline/data_providers/providers.tsx | 460 ++-- .../timelines/components/timeline/events.ts | 32 +- .../footer/__snapshots__/index.test.tsx.snap | 2 +- .../components/timeline/footer/index.test.tsx | 39 +- .../components/timeline/footer/index.tsx | 90 +- .../header/__snapshots__/index.test.tsx.snap | 5 - .../components/timeline/header/index.tsx | 27 - .../timelines/components/timeline/index.tsx | 78 +- .../timeline/properties/helpers.tsx | 42 +- .../components/timeline/timeline.test.tsx | 210 -- .../components/timeline/timeline.tsx | 26 +- .../public/timelines/containers/index.tsx | 20 +- .../timeline/epic_local_storage.test.tsx | 5 - .../timelines/store/timeline/helpers.ts | 15 +- .../timeline/factory/events/all/index.ts | 9 +- 45 files changed, 3039 insertions(+), 2135 deletions(-) diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts index 0503a9c327467e..f673fca290a292 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/events/all/index.ts @@ -6,7 +6,7 @@ import { IEsSearchResponse } from '../../../../../../../../src/plugins/data/common'; import { Ecs } from '../../../../ecs'; -import { CursorType, Inspect, Maybe } from '../../../common'; +import { CursorType, Inspect, Maybe, PageInfoPaginated } from '../../../common'; import { TimelineRequestOptionsPaginated } from '../..'; export interface TimelineEdges { @@ -29,10 +29,7 @@ export interface TimelineNonEcsData { export interface TimelineEventsAllStrategyResponse extends IEsSearchResponse { edges: TimelineEdges[]; totalCount: number; - pageInfo: { - activePage: number; - totalPages: number; - }; + pageInfo: PageInfoPaginated; inspect?: Maybe<Inspect>; } diff --git a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts index 773ee608558866..6b96783adc25a3 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/timeline/index.ts @@ -14,7 +14,13 @@ import { TimelineEventsLastEventTimeRequestOptions, TimelineEventsLastEventTimeStrategyResponse, } from './events'; -import { DocValueFields, TimerangeInput, SortField } from '../common'; +import { + DocValueFields, + PaginationInput, + PaginationInputPaginated, + TimerangeInput, + SortField, +} from '../common'; export * from './events'; @@ -29,19 +35,13 @@ export interface TimelineRequestBasicOptions extends IEsSearchRequest { } export interface TimelineRequestOptions<Field = string> extends TimelineRequestBasicOptions { - pagination: { - activePage: number; - querySize: number; - }; + pagination: PaginationInput; sort: SortField<Field>; } export interface TimelineRequestOptionsPaginated<Field = string> extends TimelineRequestBasicOptions { - pagination: { - activePage: number; - querySize: number; - }; + pagination: PaginationInputPaginated; sort: SortField<Field>; } diff --git a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx index 8a294ec1b71fdd..9f273b4f293bac 100644 --- a/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/add_filter_to_global_search_bar/index.tsx @@ -5,7 +5,7 @@ */ import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Filter } from '../../../../../../../src/plugins/data/public'; import { WithHoverActions } from '../with_hover_actions'; @@ -47,34 +47,36 @@ export const AddFilterToGlobalSearchBar = React.memo<OwnProps>( } }, [filterManager, filter, onFilterAdded]); - return ( - <WithHoverActions - hoverContent={ - <div data-test-subj="hover-actions-container"> - <EuiToolTip content={i18n.FILTER_FOR_VALUE}> - <EuiButtonIcon - aria-label={i18n.FILTER_FOR_VALUE} - color="text" - data-test-subj="add-to-filter" - iconType="magnifyWithPlus" - onClick={filterForValue} - /> - </EuiToolTip> + const HoverContent = useMemo( + () => ( + <div data-test-subj="hover-actions-container"> + <EuiToolTip content={i18n.FILTER_FOR_VALUE}> + <EuiButtonIcon + aria-label={i18n.FILTER_FOR_VALUE} + color="text" + data-test-subj="add-to-filter" + iconType="magnifyWithPlus" + onClick={filterForValue} + /> + </EuiToolTip> - <EuiToolTip content={i18n.FILTER_OUT_VALUE}> - <EuiButtonIcon - aria-label={i18n.FILTER_OUT_VALUE} - color="text" - data-test-subj="filter-out-value" - iconType="magnifyWithMinus" - onClick={filterOutValue} - /> - </EuiToolTip> - </div> - } - render={() => children} - /> + <EuiToolTip content={i18n.FILTER_OUT_VALUE}> + <EuiButtonIcon + aria-label={i18n.FILTER_OUT_VALUE} + color="text" + data-test-subj="filter-out-value" + iconType="magnifyWithMinus" + onClick={filterOutValue} + /> + </EuiToolTip> + </div> + ), + [filterForValue, filterOutValue] ); + + const render = useCallback(() => children, [children]); + + return <WithHoverActions hoverContent={HoverContent} render={render} />; } ); diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx index 74efe2d34fcca7..4efb662a4aab60 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -9,6 +9,7 @@ import React, { useCallback } from 'react'; import { DropResult, DragDropContext } from 'react-beautiful-dnd'; import { connect, ConnectedProps } from 'react-redux'; import { Dispatch } from 'redux'; +import deepEqual from 'fast-deep-equal'; import { BeforeCapture } from './drag_drop_context'; import { BrowserFields } from '../../containers/source'; @@ -134,13 +135,11 @@ export const DragDropContextWrapperComponent = React.memo<Props & PropsFromRedux </DragDropContext> ); }, - (prevProps, nextProps) => { - return ( - prevProps.children === nextProps.children && - prevProps.dataProviders === nextProps.dataProviders && - prevProps.activeTimelineDataProviders === nextProps.activeTimelineDataProviders - ); // prevent re-renders when data providers are added or removed, but all other props are the same - } + // prevent re-renders when data providers are added or removed, but all other props are the same + (prevProps, nextProps) => + prevProps.children === nextProps.children && + deepEqual(prevProps.dataProviders, nextProps.dataProviders) && + prevProps.activeTimelineDataProviders === nextProps.activeTimelineDataProviders ); DragDropContextWrapperComponent.displayName = 'DragDropContextWrapperComponent'; diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx index 64f6699d21dacc..bd22811612a672 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/draggable_wrapper.tsx @@ -196,76 +196,93 @@ const DraggableWrapperComponent: React.FC<Props> = ({ ] ); - const renderContent = useCallback( + const RenderClone = useCallback( + (provided, snapshot) => ( + <ConditionalPortal registerProvider={registerProvider}> + <div + {...provided.draggableProps} + {...provided.dragHandleProps} + style={getStyle(provided.draggableProps.style, snapshot)} + ref={provided.innerRef} + data-test-subj="providerContainer" + > + <ProviderContentWrapper + data-test-subj={`draggable-content-${dataProvider.queryMatch.field}`} + > + {render(dataProvider, provided, snapshot)} + </ProviderContentWrapper> + </div> + </ConditionalPortal> + ), + [dataProvider, registerProvider, render] + ); + + const DraggableContent = useCallback( + (provided, snapshot) => ( + <ProviderContainer + {...provided.draggableProps} + {...provided.dragHandleProps} + ref={(e: HTMLDivElement) => { + provided.innerRef(e); + draggableRef.current = e; + }} + data-test-subj="providerContainer" + isDragging={snapshot.isDragging} + registerProvider={registerProvider} + > + {truncate && !snapshot.isDragging ? ( + <TruncatableText data-test-subj="draggable-truncatable-content"> + {render(dataProvider, provided, snapshot)} + </TruncatableText> + ) : ( + <ProviderContentWrapper + data-test-subj={`draggable-content-${dataProvider.queryMatch.field}`} + > + {render(dataProvider, provided, snapshot)} + </ProviderContentWrapper> + )} + </ProviderContainer> + ), + [dataProvider, registerProvider, render, truncate] + ); + + const DroppableContent = useCallback( + (droppableProvided) => ( + <div ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}> + <Draggable + draggableId={getDraggableId(dataProvider.id)} + index={0} + key={getDraggableId(dataProvider.id)} + isDragDisabled={isDisabled} + > + {DraggableContent} + </Draggable> + {droppableProvided.placeholder} + </div> + ), + [DraggableContent, dataProvider.id, isDisabled] + ); + + const content = useMemo( () => ( <Wrapper data-test-subj="draggableWrapperDiv" disabled={isDisabled}> <DragDropErrorBoundary> <Droppable isDropDisabled={true} droppableId={getDroppableId(dataProvider.id)} - renderClone={(provided, snapshot) => ( - <ConditionalPortal registerProvider={registerProvider}> - <div - {...provided.draggableProps} - {...provided.dragHandleProps} - style={getStyle(provided.draggableProps.style, snapshot)} - ref={provided.innerRef} - data-test-subj="providerContainer" - > - <ProviderContentWrapper - data-test-subj={`draggable-content-${dataProvider.queryMatch.field}`} - > - {render(dataProvider, provided, snapshot)} - </ProviderContentWrapper> - </div> - </ConditionalPortal> - )} + renderClone={RenderClone} > - {(droppableProvided) => ( - <div ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}> - <Draggable - draggableId={getDraggableId(dataProvider.id)} - index={0} - key={getDraggableId(dataProvider.id)} - isDragDisabled={isDisabled} - > - {(provided, snapshot) => ( - <ProviderContainer - {...provided.draggableProps} - {...provided.dragHandleProps} - ref={(e: HTMLDivElement) => { - provided.innerRef(e); - draggableRef.current = e; - }} - data-test-subj="providerContainer" - isDragging={snapshot.isDragging} - registerProvider={registerProvider} - > - {truncate && !snapshot.isDragging ? ( - <TruncatableText data-test-subj="draggable-truncatable-content"> - {render(dataProvider, provided, snapshot)} - </TruncatableText> - ) : ( - <ProviderContentWrapper - data-test-subj={`draggable-content-${dataProvider.queryMatch.field}`} - > - {render(dataProvider, provided, snapshot)} - </ProviderContentWrapper> - )} - </ProviderContainer> - )} - </Draggable> - {droppableProvided.placeholder} - </div> - )} + {DroppableContent} </Droppable> </DragDropErrorBoundary> </Wrapper> ), - [dataProvider, registerProvider, render, isDisabled, truncate] + [DroppableContent, RenderClone, dataProvider.id, isDisabled] ); - if (isDisabled) return <>{renderContent()}</>; + const renderContent = useCallback(() => content, [content]); + + if (isDisabled) return <>{content}</>; return ( <WithHoverActions diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.tsx index 30c54a8b44a757..6818b03d450a77 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/droppable_wrapper.tsx @@ -5,7 +5,7 @@ */ import { rgba } from 'polished'; -import React from 'react'; +import React, { useCallback } from 'react'; import { Droppable, DraggableChildrenFn } from 'react-beautiful-dnd'; import styled from 'styled-components'; @@ -96,15 +96,9 @@ export const DroppableWrapper = React.memo<Props>( type, render = null, renderClone, - }) => ( - <Droppable - isDropDisabled={isDropDisabled} - droppableId={droppableId} - direction={'horizontal'} - type={type} - renderClone={renderClone} - > - {(provided, snapshot) => ( + }) => { + const DroppableContent = useCallback( + (provided, snapshot) => ( <ReactDndDropTarget height={height} ref={provided.innerRef} @@ -114,8 +108,21 @@ export const DroppableWrapper = React.memo<Props>( {render == null ? children : render({ isDraggingOver: snapshot.isDraggingOver })} {provided.placeholder} </ReactDndDropTarget> - )} - </Droppable> - ) + ), + [children, height, render] + ); + + return ( + <Droppable + isDropDisabled={isDropDisabled} + droppableId={droppableId} + direction={'horizontal'} + type={type} + renderClone={renderClone} + > + {DroppableContent} + </Droppable> + ); + } ); DroppableWrapper.displayName = 'DroppableWrapper'; diff --git a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx index 4dc3c6fcbe440c..d37de2cd3ec3da 100644 --- a/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/draggables/index.tsx @@ -135,7 +135,7 @@ DefaultDraggable.displayName = 'DefaultDraggable'; export const Badge = styled(EuiBadge)` vertical-align: top; -` as any; // eslint-disable-line @typescript-eslint/no-explicit-any +`; Badge.displayName = 'Badge'; diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx index 2c8c8136a47332..7859f5584b0e54 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/events_viewer.tsx @@ -5,7 +5,7 @@ */ import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; -import { getOr, isEmpty, union } from 'lodash/fp'; +import { isEmpty, union } from 'lodash/fp'; import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import deepEqual from 'fast-deep-equal'; @@ -239,6 +239,19 @@ const EventsViewerComponent: React.FC<Props> = ({ events, ]); + const HeaderSectionContent = useMemo( + () => + headerFilterGroup && ( + <HeaderFilterGroupWrapper + data-test-subj="header-filter-group-wrapper" + show={!resolverIsShowing(graphEventId)} + > + {headerFilterGroup} + </HeaderFilterGroupWrapper> + ), + [graphEventId, headerFilterGroup] + ); + useEffect(() => { setIsQueryLoading(loading); }, [loading]); @@ -257,14 +270,7 @@ const EventsViewerComponent: React.FC<Props> = ({ subtitle={utilityBar ? undefined : subtitle} title={inspect ? justTitle : titleWithExitFullScreen} > - {headerFilterGroup && ( - <HeaderFilterGroupWrapper - data-test-subj="header-filter-group-wrapper" - show={!resolverIsShowing(graphEventId)} - > - {headerFilterGroup} - </HeaderFilterGroupWrapper> - )} + {HeaderSectionContent} </HeaderSection> {utilityBar && !resolverIsShowing(graphEventId) && ( <UtilityBar>{utilityBar?.(refetch, totalCountMinusDeleted)}</UtilityBar> @@ -293,7 +299,7 @@ const EventsViewerComponent: React.FC<Props> = ({ /** Hide the footer if Resolver is showing. */ !graphEventId && ( <Footer - activePage={getOr(0, 'activePage', pageInfo)} + activePage={pageInfo.activePage} data-test-subj="events-viewer-footer" updatedAt={updatedAt} height={footerHeight} @@ -306,7 +312,7 @@ const EventsViewerComponent: React.FC<Props> = ({ onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadPage} serverSideEventCount={totalCountMinusDeleted} - totalPages={getOr(0, 'totalPages', pageInfo)} + totalCount={pageInfo.fakeTotalCount} /> ) } diff --git a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts index f6fb01be4371f1..d2bd940dcc266b 100644 --- a/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts +++ b/x-pack/plugins/security_solution/public/common/components/events_viewer/mock.ts @@ -8,7 +8,7 @@ export const mockEventViewerResponse = { totalCount: 12, pageInfo: { activePage: 0, - totalPages: 10, + fakeTotalCount: 100, }, events: [], }; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx index 9024aec17400c8..1c5b13acb0c227 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/entity_draggable.tsx @@ -4,9 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { DraggableWrapper, DragEffects } from '../drag_and_drop/draggable_wrapper'; -import { IS_OPERATOR } from '../../../timelines/components/timeline/data_providers/data_provider'; +import { + IS_OPERATOR, + QueryOperator, +} from '../../../timelines/components/timeline/data_providers/data_provider'; import { Provider } from '../../../timelines/components/timeline/data_providers/provider'; import { escapeDataProviderId } from '../drag_and_drop/helpers'; @@ -16,39 +19,43 @@ interface Props { entityValue: string; } -export const EntityDraggableComponent = ({ +export const EntityDraggableComponent: React.FC<Props> = ({ idPrefix, entityName, entityValue, -}: Props): JSX.Element => { +}) => { const id = escapeDataProviderId(`entity-draggable-${idPrefix}-${entityName}-${entityValue}`); - return ( - <DraggableWrapper - key={id} - dataProvider={{ - and: [], - enabled: true, - id, - name: entityValue, - excluded: false, - kqlQuery: '', - queryMatch: { - field: entityName, - value: entityValue, - operator: IS_OPERATOR, - }, - }} - render={(dataProvider, _, snapshot) => - snapshot.isDragging ? ( - <DragEffects> - <Provider dataProvider={dataProvider} /> - </DragEffects> - ) : ( - <>{`${entityName}: "${entityValue}"`}</> - ) - } - /> + + const dataProviderProp = useMemo( + () => ({ + and: [], + enabled: true, + id, + name: entityValue, + excluded: false, + kqlQuery: '', + queryMatch: { + field: entityName, + value: entityValue, + operator: IS_OPERATOR as QueryOperator, + }, + }), + [entityName, entityValue, id] + ); + + const render = useCallback( + (dataProvider, _, snapshot) => + snapshot.isDragging ? ( + <DragEffects> + <Provider dataProvider={dataProvider} /> + </DragEffects> + ) : ( + <>{`${entityName}: "${entityValue}"`}</> + ), + [entityName, entityValue] ); + + return <DraggableWrapper key={id} dataProvider={dataProviderProp} render={render} />; }; EntityDraggableComponent.displayName = 'EntityDraggableComponent'; diff --git a/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx b/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx index c849476f0c3db7..668a374e57f0d6 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx +++ b/x-pack/plugins/security_solution/public/common/components/ml/score/draggable_score.tsx @@ -4,10 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { DraggableWrapper, DragEffects } from '../../drag_and_drop/draggable_wrapper'; import { Anomaly } from '../types'; -import { IS_OPERATOR } from '../../../../timelines/components/timeline/data_providers/data_provider'; +import { + IS_OPERATOR, + QueryOperator, +} from '../../../../timelines/components/timeline/data_providers/data_provider'; import { Provider } from '../../../../timelines/components/timeline/data_providers/provider'; import { Spacer } from '../../page'; import { getScoreString } from './score_health'; @@ -23,39 +26,48 @@ export const DraggableScoreComponent = ({ }): JSX.Element => { const scoreString = getScoreString(score.severity); + const dataProviderProp = useMemo( + () => ({ + and: [], + enabled: true, + id, + name: score.entityName, + excluded: false, + kqlQuery: '', + queryMatch: { + field: score.entityName, + value: score.entityValue, + operator: IS_OPERATOR as QueryOperator, + }, + }), + [id, score.entityName, score.entityValue] + ); + + const render = useCallback( + (dataProvider, _, snapshot) => + snapshot.isDragging ? ( + <DragEffects> + <Provider dataProvider={dataProvider} /> + </DragEffects> + ) : ( + <> + {index !== 0 && ( + <> + {','} + <Spacer /> + </> + )} + {scoreString} + </> + ), + [index, scoreString] + ); + return ( <DraggableWrapper key={`draggable-score-draggable-wrapper-${id}`} - dataProvider={{ - and: [], - enabled: true, - id, - name: score.entityName, - excluded: false, - kqlQuery: '', - queryMatch: { - field: score.entityName, - value: score.entityValue, - operator: IS_OPERATOR, - }, - }} - render={(dataProvider, _, snapshot) => - snapshot.isDragging ? ( - <DragEffects> - <Provider dataProvider={dataProvider} /> - </DragEffects> - ) : ( - <> - {index !== 0 && ( - <> - {','} - <Spacer /> - </> - )} - {scoreString} - </> - ) - } + dataProvider={dataProviderProp} + render={render} /> ); }; diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx index ddad72081645bf..598849c917d335 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/recent_timelines.tsx @@ -12,7 +12,7 @@ import { EuiToolTip, EuiButtonIcon, } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { RecentTimelineHeader } from './header'; import { @@ -25,76 +25,110 @@ import { TimelineType } from '../../../../common/types/timeline'; import { RecentTimelineCounts } from './counts'; import * as i18n from './translations'; -export const RecentTimelines = React.memo<{ - noTimelinesMessage: string; +interface RecentTimelinesItemProps { + timeline: OpenTimelineResult; onOpenTimeline: OnOpenTimeline; - timelines: OpenTimelineResult[]; -}>(({ noTimelinesMessage, onOpenTimeline, timelines }) => { - if (timelines.length === 0) { + isLastItem: boolean; +} + +const RecentTimelinesItem = React.memo<RecentTimelinesItemProps>( + ({ timeline, onOpenTimeline, isLastItem }) => { + const handleClick = useCallback( + () => + onOpenTimeline({ + duplicate: true, + timelineId: `${timeline.savedObjectId}`, + }), + [onOpenTimeline, timeline.savedObjectId] + ); + + const render = useCallback( + (showHoverContent) => ( + <EuiFlexGroup gutterSize="none" justifyContent="spaceBetween"> + <EuiFlexItem grow={false}> + <RecentTimelineHeader onOpenTimeline={onOpenTimeline} timeline={timeline} /> + <RecentTimelineCounts timeline={timeline} /> + {timeline.description && timeline.description.length && ( + <EuiText color="subdued" size="xs"> + {timeline.description} + </EuiText> + )} + </EuiFlexItem> + + {showHoverContent && ( + <EuiFlexItem grow={false}> + <EuiToolTip + content={ + timeline.timelineType === TimelineType.default + ? i18n.OPEN_AS_DUPLICATE + : i18n.OPEN_AS_DUPLICATE_TEMPLATE + } + > + <EuiButtonIcon + aria-label={ + timeline.timelineType === TimelineType.default + ? i18n.OPEN_AS_DUPLICATE + : i18n.OPEN_AS_DUPLICATE_TEMPLATE + } + data-test-subj="open-duplicate" + isDisabled={timeline.savedObjectId == null} + iconSize="s" + iconType="copy" + onClick={handleClick} + size="s" + /> + </EuiToolTip> + </EuiFlexItem> + )} + </EuiFlexGroup> + ), + [handleClick, onOpenTimeline, timeline] + ); + return ( <> - <EuiText color="subdued" size="s"> - {noTimelinesMessage} - </EuiText> + <WithHoverActions render={render} /> + <>{!isLastItem && <EuiSpacer size="l" />}</> </> ); } +); - return ( - <> - {timelines.map((t, i) => ( - <React.Fragment key={`${t.savedObjectId}-${i}`}> - <WithHoverActions - render={(showHoverContent) => ( - <EuiFlexGroup gutterSize="none" justifyContent="spaceBetween"> - <EuiFlexItem grow={false}> - <RecentTimelineHeader onOpenTimeline={onOpenTimeline} timeline={t} /> - <RecentTimelineCounts timeline={t} /> - {t.description && t.description.length && ( - <EuiText color="subdued" size="xs"> - {t.description} - </EuiText> - )} - </EuiFlexItem> +RecentTimelinesItem.displayName = 'RecentTimelinesItem'; - {showHoverContent && ( - <EuiFlexItem grow={false}> - <EuiToolTip - content={ - t.timelineType === TimelineType.default - ? i18n.OPEN_AS_DUPLICATE - : i18n.OPEN_AS_DUPLICATE_TEMPLATE - } - > - <EuiButtonIcon - aria-label={ - t.timelineType === TimelineType.default - ? i18n.OPEN_AS_DUPLICATE - : i18n.OPEN_AS_DUPLICATE_TEMPLATE - } - data-test-subj="open-duplicate" - isDisabled={t.savedObjectId == null} - iconSize="s" - iconType="copy" - onClick={() => - onOpenTimeline({ - duplicate: true, - timelineId: `${t.savedObjectId}`, - }) - } - size="s" - /> - </EuiToolTip> - </EuiFlexItem> - )} - </EuiFlexGroup> - )} +interface RecentTimelinesProps { + noTimelinesMessage: string; + onOpenTimeline: OnOpenTimeline; + timelines: OpenTimelineResult[]; +} + +export const RecentTimelines = React.memo<RecentTimelinesProps>( + ({ noTimelinesMessage, onOpenTimeline, timelines }) => { + const content = useMemo( + () => + timelines.map((timeline, index) => ( + <RecentTimelinesItem + key={`${timeline.savedObjectId}-${timeline.title}`} + timeline={timeline} + onOpenTimeline={onOpenTimeline} + isLastItem={index === timelines.length - 1} /> - <>{i !== timelines.length - 1 && <EuiSpacer size="l" />}</> - </React.Fragment> - ))} - </> - ); -}); + )), + [onOpenTimeline, timelines] + ); + + if (timelines.length === 0) { + return ( + <> + <EuiText color="subdued" size="s"> + {noTimelinesMessage} + </EuiText> + </> + ); + } + + return <>{content}</>; + } +); RecentTimelines.displayName = 'RecentTimelines'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx index 954ae0b6a0d40e..72fa20c9f152d9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/button/index.tsx @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { noop } from 'lodash/fp'; import { EuiButton, EuiNotificationBadge, EuiPanel } from '@elastic/eui'; import { rgba } from 'polished'; import React, { useMemo } from 'react'; import styled from 'styled-components'; +import deepEqual from 'fast-deep-equal'; import { IS_DRAGGING_CLASS_NAME } from '../../../../common/components/drag_and_drop/helpers'; import { DataProvider } from '../../timeline/data_providers/data_provider'; @@ -88,6 +88,18 @@ export const FlyoutButton = React.memo<FlyoutButtonProps>( const badgeCount = useMemo(() => getBadgeCount(dataProviders), [dataProviders]); const { browserFields } = useSourcererScope(SourcererScopeName.timeline); + const badgeStyles: React.CSSProperties = useMemo( + () => ({ + left: '-9px', + position: 'relative', + top: '-6px', + transform: 'rotate(90deg)', + visibility: dataProviders.length !== 0 ? 'inherit' : 'hidden', + zIndex: 10, + }), + [dataProviders.length] + ); + if (!show) { return null; } @@ -108,18 +120,7 @@ export const FlyoutButton = React.memo<FlyoutButtonProps>( > {i18n.FLYOUT_BUTTON} </EuiButton> - <EuiNotificationBadge - color="accent" - data-test-subj="badge" - style={{ - left: '-9px', - position: 'relative', - top: '-6px', - transform: 'rotate(90deg)', - visibility: dataProviders.length !== 0 ? 'inherit' : 'hidden', - zIndex: 10, - }} - > + <EuiNotificationBadge color="accent" data-test-subj="badge" style={badgeStyles}> {badgeCount} </EuiNotificationBadge> </BadgeButtonContainer> @@ -128,11 +129,6 @@ export const FlyoutButton = React.memo<FlyoutButtonProps>( browserFields={browserFields} timelineId={timelineId} dataProviders={dataProviders} - onDataProviderEdited={noop} - onDataProviderRemoved={noop} - onToggleDataProviderEnabled={noop} - onToggleDataProviderExcluded={noop} - onToggleDataProviderType={noop} /> </DataProvidersPanel> </Container> @@ -140,7 +136,7 @@ export const FlyoutButton = React.memo<FlyoutButtonProps>( }, (prevProps, nextProps) => prevProps.show === nextProps.show && - prevProps.dataProviders === nextProps.dataProviders && + deepEqual(prevProps.dataProviders, nextProps.dataProviders) && prevProps.timelineId === nextProps.timelineId ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx index e4148b55814356..091bb41bc20804 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.tsx @@ -5,7 +5,7 @@ */ import { isArray, isEmpty, isString, uniq } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import { DragEffects, @@ -71,16 +71,25 @@ const NonDecoratedIpComponent: React.FC<{ fieldName: string; truncate?: boolean; value: string | object | null | undefined; -}> = ({ contextId, eventId, fieldName, truncate, value }) => ( - <DraggableWrapper - dataProvider={getDataProvider({ contextId, eventId, fieldName, address: value })} - key={`non-decorated-ip-draggable-wrapper-${getUniqueId({ - contextId, - eventId, - fieldName, - address: value, - })}`} - render={(dataProvider, _, snapshot) => +}> = ({ contextId, eventId, fieldName, truncate, value }) => { + const key = useMemo( + () => + `non-decorated-ip-draggable-wrapper-${getUniqueId({ + contextId, + eventId, + fieldName, + address: value, + })}`, + [contextId, eventId, fieldName, value] + ); + + const dataProviderProp = useMemo( + () => getDataProvider({ contextId, eventId, fieldName, address: value }), + [contextId, eventId, fieldName, value] + ); + + const render = useCallback( + (dataProvider, _, snapshot) => snapshot.isDragging ? ( <DragEffects> <Provider dataProvider={dataProvider} /> @@ -89,47 +98,107 @@ const NonDecoratedIpComponent: React.FC<{ getOrEmptyTagFromValue(value) ) : ( getOrEmptyTagFromValue(tryStringify(value)) - ) - } - truncate={truncate} - /> -); + ), + [value] + ); + + return ( + <DraggableWrapper + dataProvider={dataProviderProp} + key={key} + render={render} + truncate={truncate} + /> + ); +}; const NonDecoratedIp = React.memo(NonDecoratedIpComponent); -const AddressLinksComponent: React.FC<{ +interface AddressLinksItemProps extends Omit<AddressLinksProps, 'addresses'> { + address: string; +} + +const AddressLinksItemComponent: React.FC<AddressLinksItemProps> = ({ + address, + contextId, + eventId, + fieldName, + truncate, +}) => { + const key = useMemo( + () => + `address-links-draggable-wrapper-${getUniqueId({ + contextId, + eventId, + fieldName, + address, + })}`, + [address, contextId, eventId, fieldName] + ); + + const dataProviderProp = useMemo( + () => getDataProvider({ contextId, eventId, fieldName, address }), + [address, contextId, eventId, fieldName] + ); + + const render = useCallback( + (_props, _provided, snapshot) => + snapshot.isDragging ? ( + <DragEffects> + <Provider dataProvider={dataProviderProp} /> + </DragEffects> + ) : ( + <NetworkDetailsLink data-test-subj="network-details" ip={address} /> + ), + [address, dataProviderProp] + ); + + return ( + <DraggableWrapper + dataProvider={dataProviderProp} + key={key} + render={render} + truncate={truncate} + /> + ); +}; + +const AddressLinksItem = React.memo(AddressLinksItemComponent); + +interface AddressLinksProps { addresses: string[]; contextId: string; eventId: string; fieldName: string; truncate?: boolean; -}> = ({ addresses, contextId, eventId, fieldName, truncate }) => ( - <> - {uniq(addresses).map((address) => ( - <DraggableWrapper - dataProvider={getDataProvider({ contextId, eventId, fieldName, address })} - key={`address-links-draggable-wrapper-${getUniqueId({ - contextId, - eventId, - fieldName, - address, - })}`} - render={(_props, _provided, snapshot) => - snapshot.isDragging ? ( - <DragEffects> - <Provider - dataProvider={getDataProvider({ contextId, eventId, fieldName, address })} - /> - </DragEffects> - ) : ( - <NetworkDetailsLink data-test-subj="network-details" ip={address} /> - ) - } - truncate={truncate} - /> - ))} - </> -); +} + +const AddressLinksComponent: React.FC<AddressLinksProps> = ({ + addresses, + contextId, + eventId, + fieldName, + truncate, +}) => { + const uniqAddresses = useMemo(() => uniq(addresses), [addresses]); + + const content = useMemo( + () => + uniqAddresses.map((address) => ( + <AddressLinksItem + key={address} + address={address} + contextId={contextId} + eventId={eventId} + fieldName={fieldName} + truncate={truncate} + /> + )), + [contextId, eventId, fieldName, truncate, uniqAddresses] + ); + + return <>{content}</>; +}; const AddressLinks = React.memo(AddressLinksComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap index 18a648f2abfaab..153128fb418262 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/__snapshots__/timeline.test.tsx.snap @@ -646,34 +646,7 @@ In other use cases the message field can be used to concatenate different values dataProviders={ Array [ Object { - "and": Array [ - Object { - "and": Array [], - "enabled": true, - "excluded": false, - "id": "id-Provider 2", - "kqlQuery": "", - "name": "Provider 2", - "queryMatch": Object { - "field": "name", - "operator": ":", - "value": "Provider 2", - }, - }, - Object { - "and": Array [], - "enabled": true, - "excluded": false, - "id": "id-Provider 3", - "kqlQuery": "", - "name": "Provider 3", - "queryMatch": Object { - "field": "name", - "operator": ":", - "value": "Provider 3", - }, - }, - ], + "and": Array [], "enabled": true, "excluded": false, "id": "id-Provider 1", @@ -921,11 +894,6 @@ In other use cases the message field can be used to concatenate different values loadingSourcerer={false} onChangeItemsPerPage={[MockFunction]} onClose={[MockFunction]} - onDataProviderEdited={[MockFunction]} - onDataProviderRemoved={[MockFunction]} - onToggleDataProviderEnabled={[MockFunction]} - onToggleDataProviderExcluded={[MockFunction]} - onToggleDataProviderType={[MockFunction]} show={true} showCallOutUnauthorizedMsg={false} sort={ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx index 210af7a571569a..98faa84db851e6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/auto_save_warning/index.tsx @@ -11,84 +11,69 @@ import { EuiGlobalToastListToast as Toast, } from '@elastic/eui'; import { getOr } from 'lodash/fp'; -import React from 'react'; -import { connect, ConnectedProps } from 'react-redux'; +import React, { useCallback, useMemo } from 'react'; +import { useDispatch, useSelector, shallowEqual } from 'react-redux'; -import { State } from '../../../../common/store'; -import { setTimelineRangeDatePicker as dispatchSetTimelineRangeDatePicker } from '../../../../common/store/inputs/actions'; +import { setTimelineRangeDatePicker } from '../../../../common/store/inputs/actions'; import { timelineActions, timelineSelectors } from '../../../store/timeline'; -import { AutoSavedWarningMsg } from '../../../store/timeline/types'; import { useStateToaster } from '../../../../common/components/toasters'; import * as i18n from './translations'; -const AutoSaveWarningMsgComponent = React.memo<PropsFromRedux>( - ({ - newTimelineModel, - setTimelineRangeDatePicker, - timelineId, - updateAutoSaveMsg, - updateTimeline, - }) => { - const dispatchToaster = useStateToaster()[1]; +const AutoSaveWarningMsgComponent = () => { + const dispatch = useDispatch(); + const dispatchToaster = useStateToaster()[1]; + const { timelineId, newTimelineModel } = useSelector( + timelineSelectors.autoSaveMsgSelector, + shallowEqual + ); + + const handleClick = useCallback(() => { if (timelineId != null && newTimelineModel != null) { - const toast: Toast = { - id: 'AutoSaveWarningMsg', - title: i18n.TITLE, - color: 'warning', - iconType: 'alert', - toastLifeTimeMs: 10000, - text: ( - <> - <p>{i18n.DESCRIPTION}</p> - <EuiFlexGroup justifyContent="flexEnd" gutterSize="s"> - <EuiFlexItem grow={false}> - <EuiButton - size="s" - onClick={() => { - updateTimeline({ id: timelineId, timeline: newTimelineModel }); - updateAutoSaveMsg({ timelineId: null, newTimelineModel: null }); - setTimelineRangeDatePicker({ - from: getOr(0, 'dateRange.start', newTimelineModel), - to: getOr(0, 'dateRange.end', newTimelineModel), - }); - }} - > - {i18n.REFRESH_TIMELINE} - </EuiButton> - </EuiFlexItem> - </EuiFlexGroup> - </> - ), - }; - dispatchToaster({ - type: 'addToaster', - toast, - }); + dispatch(timelineActions.updateTimeline({ id: timelineId, timeline: newTimelineModel })); + dispatch(timelineActions.updateAutoSaveMsg({ timelineId: null, newTimelineModel: null })); + dispatch( + setTimelineRangeDatePicker({ + from: getOr(0, 'dateRange.start', newTimelineModel), + to: getOr(0, 'dateRange.end', newTimelineModel), + }) + ); } + }, [dispatch, newTimelineModel, timelineId]); - return null; - } -); - -AutoSaveWarningMsgComponent.displayName = 'AutoSaveWarningMsgComponent'; - -const mapStateToProps = (state: State) => { - const autoSaveMessage: AutoSavedWarningMsg = timelineSelectors.autoSaveMsgSelector(state); + const TextComponent = useMemo( + () => ( + <> + <p>{i18n.DESCRIPTION}</p> + <EuiFlexGroup justifyContent="flexEnd" gutterSize="s"> + <EuiFlexItem grow={false}> + <EuiButton size="s" onClick={handleClick}> + {i18n.REFRESH_TIMELINE} + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + </> + ), + [handleClick] + ); - return { - timelineId: autoSaveMessage.timelineId, - newTimelineModel: autoSaveMessage.newTimelineModel, - }; -}; + if (timelineId != null && newTimelineModel != null) { + const toast: Toast = { + id: 'AutoSaveWarningMsg', + title: i18n.TITLE, + color: 'warning', + iconType: 'alert', + toastLifeTimeMs: 10000, + text: TextComponent, + }; + dispatchToaster({ + type: 'addToaster', + toast, + }); + } -const mapDispatchToProps = { - setTimelineRangeDatePicker: dispatchSetTimelineRangeDatePicker, - updateAutoSaveMsg: timelineActions.updateAutoSaveMsg, - updateTimeline: timelineActions.updateTimeline, + return null; }; -const connector = connect(mapStateToProps, mapDispatchToProps); - -type PropsFromRedux = ConnectedProps<typeof connector>; +AutoSaveWarningMsgComponent.displayName = 'AutoSaveWarningMsgComponent'; -export const AutoSaveWarningMsg = connector(AutoSaveWarningMsgComponent); +export const AutoSaveWarningMsg = React.memo(AutoSaveWarningMsgComponent); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx index 3352639fa95f82..c4c4e0e0c70657 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/actions/index.tsx @@ -5,7 +5,7 @@ */ import { EuiButtonIcon } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import { ColumnHeaderOptions } from '../../../../../../timelines/store/timeline/model'; import { OnColumnRemoved } from '../../../events'; @@ -26,20 +26,27 @@ interface Props { export const CloseButton = React.memo<{ columnId: string; onColumnRemoved: OnColumnRemoved; -}>(({ columnId, onColumnRemoved }) => ( - <EuiButtonIcon - aria-label={i18n.REMOVE_COLUMN} - color="text" - data-test-subj="remove-column" - iconType="cross" - onClick={(event: React.MouseEvent<HTMLButtonElement>) => { +}>(({ columnId, onColumnRemoved }) => { + const handleClick = useCallback( + (event: React.MouseEvent<HTMLButtonElement>) => { // To avoid a re-sorting when you delete a column event.preventDefault(); event.stopPropagation(); onColumnRemoved(columnId); - }} - /> -)); + }, + [columnId, onColumnRemoved] + ); + + return ( + <EuiButtonIcon + aria-label={i18n.REMOVE_COLUMN} + color="text" + data-test-subj="remove-column" + iconType="cross" + onClick={handleClick} + /> + ); +}); CloseButton.displayName = 'CloseButton'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx index 617b2935ee75cc..6e21446944573b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/column_header.tsx @@ -78,6 +78,29 @@ const ColumnHeaderComponent: React.FC<ColumneHeaderProps> = ({ [timelineId, header.id] ); + const DraggableContent = useCallback( + (dragProvided) => ( + <EventsTh + data-test-subj="draggable-header" + {...dragProvided.draggableProps} + {...dragProvided.dragHandleProps} + ref={dragProvided.innerRef} + > + <EventsThContent> + <Header + timelineId={timelineId} + header={header} + onColumnRemoved={onColumnRemoved} + onColumnSorted={onColumnSorted} + onFilterChange={onFilterChange} + sort={sort} + /> + </EventsThContent> + </EventsTh> + ), + [header, onColumnRemoved, onColumnSorted, onFilterChange, sort, timelineId] + ); + return ( <Resizable enable={RESIZABLE_ENABLE} @@ -94,25 +117,7 @@ const ColumnHeaderComponent: React.FC<ColumneHeaderProps> = ({ index={draggableIndex} key={header.id} > - {(dragProvided) => ( - <EventsTh - data-test-subj="draggable-header" - {...dragProvided.draggableProps} - {...dragProvided.dragHandleProps} - ref={dragProvided.innerRef} - > - <EventsThContent> - <Header - timelineId={timelineId} - header={header} - onColumnRemoved={onColumnRemoved} - onColumnSorted={onColumnSorted} - onFilterChange={onFilterChange} - sort={sort} - /> - </EventsThContent> - </EventsTh> - )} + {DraggableContent} </Draggable> </Resizable> ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx index 6e802053ab29f6..f4d4cf29ba38bc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx @@ -200,6 +200,22 @@ export const ColumnHeadersComponent = ({ [globalFullScreen, timelineId, timelineFullScreen] ); + const DroppableContent = useCallback( + (dropProvided, snapshot) => ( + <> + <EventsThGroupData + data-test-subj="headers-group" + ref={dropProvided.innerRef} + isDragging={snapshot.isDraggingOver} + {...dropProvided.droppableProps} + > + {ColumnHeaderList} + </EventsThGroupData> + </> + ), + [ColumnHeaderList] + ); + return ( <EventsThead data-test-subj="column-headers"> <EventsTrHeader> @@ -275,18 +291,7 @@ export const ColumnHeadersComponent = ({ type={DRAG_TYPE_FIELD} renderClone={renderClone} > - {(dropProvided, snapshot) => ( - <> - <EventsThGroupData - data-test-subj="headers-group" - ref={dropProvided.innerRef} - isDragging={snapshot.isDraggingOver} - {...dropProvided.droppableProps} - > - {ColumnHeaderList} - </EventsThGroupData> - </> - )} + {DroppableContent} </Droppable> </EventsTrHeader> </EventsThead> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx index 3b6585013c8d36..df5c48ad012a6b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/event_column_view.tsx @@ -151,13 +151,17 @@ export const EventColumnView = React.memo<Props>( />, ] : []), - <AlertContextMenu - key="alert-context-menu" - ecsRowData={ecsData} - timelineId={timelineId} - disabled={eventType !== 'signal'} - refetch={refetch} - />, + ...(eventType !== 'raw' + ? [ + <AlertContextMenu + key="alert-context-menu" + ecsRowData={ecsData} + timelineId={timelineId} + disabled={eventType !== 'signal'} + refetch={refetch} + />, + ] + : []), ], [ associateNote, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx index d71af86c802476..ee68c270e9abad 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx @@ -97,6 +97,8 @@ const TOP_OFFSET = 50; */ const BOTTOM_OFFSET = -500; +const VISIBILITY_SENSOR_OFFSET = { top: TOP_OFFSET, bottom: BOTTOM_OFFSET }; + const emptyNotes: string[] = []; const EventsTrSupplementContainerWrapper = React.memo(({ children }) => { @@ -173,105 +175,145 @@ const StatefulEventComponent: React.FC<Props> = ({ // Number of current columns plus one for actions. const columnCount = columnHeaders.length + 1; + const VisibilitySensorContent = useCallback( + ({ isVisible }) => { + if (isVisible || disableSensorVisibility) { + return ( + <EventsTrGroup + className={STATEFUL_EVENT_CSS_CLASS_NAME} + data-test-subj="event" + eventType={getEventType(event.ecs)} + showLeftBorder={!isEventViewer} + ref={divElement} + > + <EventColumnView + id={event._id} + actionsColumnWidth={actionsColumnWidth} + associateNote={associateNote} + columnHeaders={columnHeaders} + columnRenderers={columnRenderers} + data={event.data} + ecsData={event.ecs} + expanded={!!expanded[event._id]} + eventIdToNoteIds={eventIdToNoteIds} + getNotesByIds={getNotesByIds} + isEventPinned={isEventPinned} + isEventViewer={isEventViewer} + loading={loading} + loadingEventIds={loadingEventIds} + onColumnResized={onColumnResized} + onEventToggled={onToggleExpanded} + onPinEvent={onPinEvent} + onRowSelected={onRowSelected} + onUnPinEvent={onUnPinEvent} + refetch={refetch} + selectedEventIds={selectedEventIds} + showCheckboxes={showCheckboxes} + showNotes={!!showNotes[event._id]} + timelineId={timelineId} + toggleShowNotes={onToggleShowNotes} + updateNote={updateNote} + /> + + <EventsTrSupplementContainerWrapper> + <EventsTrSupplement + className="siemEventsTable__trSupplement--notes" + data-test-subj="event-notes-flex-item" + > + <NoteCards + associateNote={associateNote} + data-test-subj="note-cards" + getNewNoteId={getNewNoteId} + getNotesByIds={getNotesByIds} + noteIds={eventIdToNoteIds[event._id] || emptyNotes} + showAddNote={!!showNotes[event._id]} + status={timelineStatus} + toggleShowAddNote={onToggleShowNotes} + updateNote={updateNote} + /> + </EventsTrSupplement> + + {getRowRenderer(event.ecs, rowRenderers).renderRow({ + browserFields, + data: event.ecs, + timelineId, + })} + + <EventsTrSupplement + className="siemEventsTable__trSupplement--attributes" + data-test-subj="event-details" + > + <ExpandableEvent + browserFields={browserFields} + columnHeaders={columnHeaders} + event={detailsData || emptyDetails} + forceExpand={!!expanded[event._id] && !loading} + id={event._id} + onEventToggled={onToggleExpanded} + onUpdateColumns={onUpdateColumns} + timelineId={timelineId} + toggleColumn={toggleColumn} + /> + </EventsTrSupplement> + </EventsTrSupplementContainerWrapper> + </EventsTrGroup> + ); + } else { + // Height place holder for visibility detection as well as re-rendering sections. + const height = + divElement.current != null && divElement.current!.clientHeight + ? `${divElement.current!.clientHeight}px` + : DEFAULT_ROW_HEIGHT; + + return <SkeletonRow cellCount={columnCount} rowHeight={height} />; + } + }, + [ + actionsColumnWidth, + associateNote, + browserFields, + columnCount, + columnHeaders, + columnRenderers, + detailsData, + disableSensorVisibility, + event._id, + event.data, + event.ecs, + eventIdToNoteIds, + expanded, + getNotesByIds, + isEventPinned, + isEventViewer, + loading, + loadingEventIds, + onColumnResized, + onPinEvent, + onRowSelected, + onToggleExpanded, + onToggleShowNotes, + onUnPinEvent, + onUpdateColumns, + refetch, + rowRenderers, + selectedEventIds, + showCheckboxes, + showNotes, + timelineId, + timelineStatus, + toggleColumn, + updateNote, + ] + ); + return ( <VisibilitySensor partialVisibility={true} scrollCheck={true} containment={containerElementRef} - offset={{ top: TOP_OFFSET, bottom: BOTTOM_OFFSET }} + offset={VISIBILITY_SENSOR_OFFSET} > - {({ isVisible }) => { - if (isVisible || disableSensorVisibility) { - return ( - <EventsTrGroup - className={STATEFUL_EVENT_CSS_CLASS_NAME} - data-test-subj="event" - eventType={getEventType(event.ecs)} - showLeftBorder={!isEventViewer} - ref={divElement} - > - <EventColumnView - id={event._id} - actionsColumnWidth={actionsColumnWidth} - associateNote={associateNote} - columnHeaders={columnHeaders} - columnRenderers={columnRenderers} - data={event.data} - ecsData={event.ecs} - expanded={!!expanded[event._id]} - eventIdToNoteIds={eventIdToNoteIds} - getNotesByIds={getNotesByIds} - isEventPinned={isEventPinned} - isEventViewer={isEventViewer} - loading={loading} - loadingEventIds={loadingEventIds} - onColumnResized={onColumnResized} - onEventToggled={onToggleExpanded} - onPinEvent={onPinEvent} - onRowSelected={onRowSelected} - onUnPinEvent={onUnPinEvent} - refetch={refetch} - selectedEventIds={selectedEventIds} - showCheckboxes={showCheckboxes} - showNotes={!!showNotes[event._id]} - timelineId={timelineId} - toggleShowNotes={onToggleShowNotes} - updateNote={updateNote} - /> - - <EventsTrSupplementContainerWrapper> - <EventsTrSupplement - className="siemEventsTable__trSupplement--notes" - data-test-subj="event-notes-flex-item" - > - <NoteCards - associateNote={associateNote} - data-test-subj="note-cards" - getNewNoteId={getNewNoteId} - getNotesByIds={getNotesByIds} - noteIds={eventIdToNoteIds[event._id] || emptyNotes} - showAddNote={!!showNotes[event._id]} - status={timelineStatus} - toggleShowAddNote={onToggleShowNotes} - updateNote={updateNote} - /> - </EventsTrSupplement> - - {getRowRenderer(event.ecs, rowRenderers).renderRow({ - browserFields, - data: event.ecs, - timelineId, - })} - - <EventsTrSupplement - className="siemEventsTable__trSupplement--attributes" - data-test-subj="event-details" - > - <ExpandableEvent - browserFields={browserFields} - columnHeaders={columnHeaders} - event={detailsData || emptyDetails} - forceExpand={!!expanded[event._id] && !loading} - id={event._id} - onEventToggled={onToggleExpanded} - onUpdateColumns={onUpdateColumns} - timelineId={timelineId} - toggleColumn={toggleColumn} - /> - </EventsTrSupplement> - </EventsTrSupplementContainerWrapper> - </EventsTrGroup> - ); - } else { - // Height place holder for visibility detection as well as re-rendering sections. - const height = - divElement.current != null && divElement.current!.clientHeight - ? `${divElement.current!.clientHeight}px` - : DEFAULT_ROW_HEIGHT; - - return <SkeletonRow cellCount={columnCount} rowHeight={height} />; - } - }} + {VisibilitySensorContent} </VisibilitySensor> ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx index 1cd78178d017f6..5d4821757d7749 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/suricata/suricata_signature.tsx @@ -5,7 +5,7 @@ */ import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { @@ -19,7 +19,7 @@ import { Provider } from '../../../data_providers/provider'; import { TokensFlexItem } from '../helpers'; import { getBeginningTokens } from './suricata_links'; import { DefaultDraggable } from '../../../../../../common/components/draggables'; -import { IS_OPERATOR } from '../../../data_providers/data_provider'; +import { IS_OPERATOR, QueryOperator } from '../../../data_providers/data_provider'; export const SURICATA_SIGNATURE_FIELD_NAME = 'suricata.eve.alert.signature'; export const SURICATA_SIGNATURE_ID_FIELD_NAME = 'suricata.eve.alert.signature_id'; @@ -57,41 +57,49 @@ export const Tokens = React.memo<{ tokens: string[] }>(({ tokens }) => ( Tokens.displayName = 'Tokens'; export const DraggableSignatureId = React.memo<{ id: string; signatureId: number }>( - ({ id, signatureId }) => ( - <SignatureFlexItem grow={false}> - <DraggableWrapper - dataProvider={{ - and: [], - enabled: true, - id: escapeDataProviderId(`suricata-draggable-signature-id-${id}-sig-${signatureId}`), - name: String(signatureId), - excluded: false, - kqlQuery: '', - queryMatch: { - field: SURICATA_SIGNATURE_ID_FIELD_NAME, - value: signatureId, - operator: IS_OPERATOR, - }, - }} - render={(dataProvider, _, snapshot) => - snapshot.isDragging ? ( - <DragEffects> - <Provider dataProvider={dataProvider} /> - </DragEffects> - ) : ( - <EuiToolTip - data-test-subj="signature-id-tooltip" - content={SURICATA_SIGNATURE_ID_FIELD_NAME} - > - <Badge iconType="number" color="hollow" title=""> - {signatureId} - </Badge> - </EuiToolTip> - ) - } - /> - </SignatureFlexItem> - ) + ({ id, signatureId }) => { + const dataProviderProp = useMemo( + () => ({ + and: [], + enabled: true, + id: escapeDataProviderId(`suricata-draggable-signature-id-${id}-sig-${signatureId}`), + name: String(signatureId), + excluded: false, + kqlQuery: '', + queryMatch: { + field: SURICATA_SIGNATURE_ID_FIELD_NAME, + value: signatureId, + operator: IS_OPERATOR as QueryOperator, + }, + }), + [id, signatureId] + ); + + const render = useCallback( + (dataProvider, _, snapshot) => + snapshot.isDragging ? ( + <DragEffects> + <Provider dataProvider={dataProvider} /> + </DragEffects> + ) : ( + <EuiToolTip + data-test-subj="signature-id-tooltip" + content={SURICATA_SIGNATURE_ID_FIELD_NAME} + > + <Badge iconType="number" color="hollow" title=""> + {signatureId} + </Badge> + </EuiToolTip> + ), + [signatureId] + ); + + return ( + <SignatureFlexItem grow={false}> + <DraggableWrapper dataProvider={dataProviderProp} render={render} /> + </SignatureFlexItem> + ); + } ); DraggableSignatureId.displayName = 'DraggableSignatureId'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx index 07e32a9a4e5d1f..9ef579706f1189 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/zeek/zeek_signature.tsx @@ -6,7 +6,7 @@ import { EuiBadge, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui'; import { get } from 'lodash/fp'; -import React from 'react'; +import React, { useCallback, useMemo } from 'react'; import styled from 'styled-components'; import { Ecs } from '../../../../../../../common/ecs'; @@ -17,7 +17,7 @@ import { import { escapeDataProviderId } from '../../../../../../common/components/drag_and_drop/helpers'; import { GoogleLink, ReputationLink } from '../../../../../../common/components/links'; import { Provider } from '../../../data_providers/provider'; -import { IS_OPERATOR } from '../../../data_providers/data_provider'; +import { IS_OPERATOR, QueryOperator } from '../../../data_providers/data_provider'; import * as i18n from './translations'; @@ -68,42 +68,46 @@ export const DraggableZeekElement = React.memo<{ field: string; value: string | null | undefined; stringRenderer?: StringRenderer; -}>(({ id, field, value, stringRenderer = defaultStringRenderer }) => - value != null ? ( +}>(({ id, field, value, stringRenderer = defaultStringRenderer }) => { + const dataProviderProp = useMemo( + () => ({ + and: [], + enabled: true, + id: escapeDataProviderId(`draggable-zeek-element-draggable-wrapper-${id}-${field}-${value}`), + name: value!, + excluded: false, + kqlQuery: '', + queryMatch: { + field, + value: value!, + operator: IS_OPERATOR as QueryOperator, + }, + }), + [field, id, value] + ); + + const render = useCallback( + (dataProvider, _, snapshot) => + snapshot.isDragging ? ( + <DragEffects> + <Provider dataProvider={dataProvider} /> + </DragEffects> + ) : ( + <EuiToolTip data-test-subj="badge-tooltip" content={field}> + <Badge iconType="tag" color="hollow" title=""> + {stringRenderer(value!)} + </Badge> + </EuiToolTip> + ), + [field, stringRenderer, value] + ); + + return value != null ? ( <TokensFlexItem grow={false}> - <DraggableWrapper - dataProvider={{ - and: [], - enabled: true, - id: escapeDataProviderId( - `draggable-zeek-element-draggable-wrapper-${id}-${field}-${value}` - ), - name: value, - excluded: false, - kqlQuery: '', - queryMatch: { - field, - value, - operator: IS_OPERATOR, - }, - }} - render={(dataProvider, _, snapshot) => - snapshot.isDragging ? ( - <DragEffects> - <Provider dataProvider={dataProvider} /> - </DragEffects> - ) : ( - <EuiToolTip data-test-subj="badge-tooltip" content={field}> - <Badge iconType="tag" color="hollow" title=""> - {stringRenderer(value)} - </Badge> - </EuiToolTip> - ) - } - /> + <DraggableWrapper dataProvider={dataProviderProp} render={render} /> </TokensFlexItem> - ) : null -); + ) : null; +}); DraggableZeekElement.displayName = 'DraggableZeekElement'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap index 14304b99263acb..a8818517fb94b1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/data_providers.test.tsx.snap @@ -144,11 +144,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = ` }, ] } - onDataProviderEdited={[MockFunction]} - onDataProviderRemoved={[MockFunction]} - onToggleDataProviderEnabled={[MockFunction]} - onToggleDataProviderExcluded={[MockFunction]} - onToggleDataProviderType={[MockFunction]} timelineId="foo" /> </DropTargetDataProviders> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap index a86c99cbc094ae..281a26b08df670 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/__snapshots__/providers.test.tsx.snap @@ -2,556 +2,1717 @@ exports[`Providers rendering renders correctly against snapshot 1`] = ` <div> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={true} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.0" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + ] + } + groupIndex={0} + isLastGroup={false} + key="droppable-0" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.1" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + ] + } + groupIndex={1} + isLastGroup={false} + key="droppable-1" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.2" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + ] + } + groupIndex={2} + isLastGroup={false} + key="droppable-2" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.3" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + ] + } + groupIndex={3} + isLastGroup={false} + key="droppable-3" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.4" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + ] + } + groupIndex={4} + isLastGroup={false} + key="droppable-4" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.5" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + ] + } + groupIndex={5} + isLastGroup={false} + key="droppable-5" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.6" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + ] + } + groupIndex={6} + isLastGroup={false} + key="droppable-6" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.7" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + ] + } + groupIndex={7} + isLastGroup={false} + key="droppable-7" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.8" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + ] + } + groupIndex={8} + isLastGroup={false} + key="droppable-8" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.9" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - </EuiFlexGroup> - <EuiSpacer - size="xs" + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={ + Array [ + Object { + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + groupIndex={9} + isLastGroup={false} + key="droppable-9" + timelineId="test" + /> + <DataProvidersGroup + browserFields={Object {}} + dataProviders={ + Array [ + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 1", + "kqlQuery": "", + "name": "Provider 1", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 1", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 2", + "kqlQuery": "", + "name": "Provider 2", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 2", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 3", + "kqlQuery": "", + "name": "Provider 3", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 3", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 4", + "kqlQuery": "", + "name": "Provider 4", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 4", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 5", + "kqlQuery": "", + "name": "Provider 5", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 5", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 6", + "kqlQuery": "", + "name": "Provider 6", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 6", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 7", + "kqlQuery": "", + "name": "Provider 7", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 7", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 8", + "kqlQuery": "", + "name": "Provider 8", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 8", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 9", + "kqlQuery": "", + "name": "Provider 9", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 9", + }, + }, + Object { + "and": Array [], + "enabled": true, + "excluded": false, + "id": "id-Provider 10", + "kqlQuery": "", + "name": "Provider 10", + "queryMatch": Object { + "field": "name", + "operator": ":", + "value": "Provider 10", + }, + }, + ] + } + group={Array []} + groupIndex={10} + isLastGroup={true} + key="droppable-10" + timelineId="test" /> - <EuiFlexGroup - alignItems="center" - gutterSize="none" - > - <Styled(EuiFlexItem) - grow={false} - > - <styled.div - hideBadge={false} - > - <AndOrBadge - type="or" - /> - </styled.div> - </Styled(EuiFlexItem)> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ( - </styled.span> - </Styled(EuiFlexItem)> - <EuiFlexItem - grow={false} - > - <Connect(Droppable) - direction="horizontal" - droppableId="droppableId.timelineProviders.foo.group.10" - getContainerForClone={[Function]} - ignoreContainerClipping={false} - isCombineEnabled={false} - isDropDisabled={false} - mode="standard" - renderClone={null} - type="DEFAULT" - > - <Component /> - </Connect(Droppable)> - </EuiFlexItem> - <Styled(EuiFlexItem) - grow={false} - > - <styled.span> - ) - </styled.span> - </Styled(EuiFlexItem)> - <AddDataProviderPopover - browserFields={Object {}} - timelineId="foo" - /> - </EuiFlexGroup> </div> `; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx index d48be25b08897d..a7ae14dea510f7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/data_providers.test.tsx @@ -41,11 +41,6 @@ describe('DataProviders', () => { data-test-subj="dataProviders-container" dataProviders={mockDataProviders} timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} /> </ManageGlobalTimeline> </TestProviders> @@ -58,16 +53,7 @@ describe('DataProviders', () => { const wrapper = mount( <TestProviders> - <DataProviders - browserFields={{}} - timelineId="foo" - dataProviders={dataProviders} - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <DataProviders browserFields={{}} timelineId="foo" dataProviders={dataProviders} /> </TestProviders> ); @@ -77,16 +63,7 @@ describe('DataProviders', () => { test('it renders the data providers', () => { const wrapper = mount( <TestProviders> - <DataProviders - browserFields={{}} - timelineId="foo" - dataProviders={mockDataProviders} - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <DataProviders browserFields={{}} timelineId="foo" dataProviders={mockDataProviders} /> </TestProviders> ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx index c9e06f89af41c7..b892ca089eb4c4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx @@ -14,13 +14,6 @@ import { droppableTimelineProvidersPrefix, IS_DRAGGING_CLASS_NAME, } from '../../../../common/components/drag_and_drop/helpers'; -import { - OnDataProviderEdited, - OnDataProviderRemoved, - OnToggleDataProviderEnabled, - OnToggleDataProviderExcluded, - OnToggleDataProviderType, -} from '../events'; import { DataProvider } from './data_provider'; import { Empty } from './empty'; @@ -31,11 +24,6 @@ interface Props { browserFields: BrowserFields; timelineId: string; dataProviders: DataProvider[]; - onDataProviderEdited: OnDataProviderEdited; - onDataProviderRemoved: OnDataProviderRemoved; - onToggleDataProviderEnabled: OnToggleDataProviderEnabled; - onToggleDataProviderExcluded: OnToggleDataProviderExcluded; - onToggleDataProviderType: OnToggleDataProviderType; } const DropTargetDataProvidersContainer = styled.div` @@ -91,48 +79,32 @@ const getDroppableId = (id: string): string => `${droppableTimelineProvidersPref * the user to drop anything with a facet count into * the data pro section. */ -export const DataProviders = React.memo<Props>( - ({ - browserFields, - dataProviders, +export const DataProviders = React.memo<Props>(({ browserFields, dataProviders, timelineId }) => { + const { getManageTimelineById } = useManageTimeline(); + const isLoading = useMemo(() => getManageTimelineById(timelineId).isLoading, [ + getManageTimelineById, timelineId, - onDataProviderEdited, - onDataProviderRemoved, - onToggleDataProviderEnabled, - onToggleDataProviderExcluded, - onToggleDataProviderType, - }) => { - const { getManageTimelineById } = useManageTimeline(); - const isLoading = useMemo(() => getManageTimelineById(timelineId).isLoading, [ - getManageTimelineById, - timelineId, - ]); - return ( - <DropTargetDataProvidersContainer className="drop-target-data-providers-container"> - <DropTargetDataProviders - className="drop-target-data-providers" - data-test-subj="dataProviders" - > - {dataProviders != null && dataProviders.length ? ( - <Providers - browserFields={browserFields} - timelineId={timelineId} - dataProviders={dataProviders} - onDataProviderEdited={onDataProviderEdited} - onDataProviderRemoved={onDataProviderRemoved} - onToggleDataProviderEnabled={onToggleDataProviderEnabled} - onToggleDataProviderExcluded={onToggleDataProviderExcluded} - onToggleDataProviderType={onToggleDataProviderType} - /> - ) : ( - <DroppableWrapper isDropDisabled={isLoading} droppableId={getDroppableId(timelineId)}> - <Empty browserFields={browserFields} timelineId={timelineId} /> - </DroppableWrapper> - )} - </DropTargetDataProviders> - </DropTargetDataProvidersContainer> - ); - } -); + ]); + return ( + <DropTargetDataProvidersContainer className="drop-target-data-providers-container"> + <DropTargetDataProviders + className="drop-target-data-providers" + data-test-subj="dataProviders" + > + {dataProviders != null && dataProviders.length ? ( + <Providers + browserFields={browserFields} + timelineId={timelineId} + dataProviders={dataProviders} + /> + ) : ( + <DroppableWrapper isDropDisabled={isLoading} droppableId={getDroppableId(timelineId)}> + <Empty browserFields={browserFields} timelineId={timelineId} /> + </DroppableWrapper> + )} + </DropTargetDataProviders> + </DropTargetDataProvidersContainer> + ); +}); DataProviders.displayName = 'DataProviders'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx index bf2094e7659eee..b66a332fc977d7 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_badge.tsx @@ -91,7 +91,7 @@ const ConvertFieldBadge = styled(ProviderFieldBadge)` } `; -const TemplateFieldBadge: React.FC<TemplateFieldBadgeProps> = ({ type, toggleType }) => { +const TemplateFieldBadgeComponent: React.FC<TemplateFieldBadgeProps> = ({ type, toggleType }) => { if (type !== DataProviderType.template) { return ( <ConvertFieldBadge onClick={toggleType}>{i18n.CONVERT_TO_TEMPLATE_FIELD}</ConvertFieldBadge> @@ -101,6 +101,8 @@ const TemplateFieldBadge: React.FC<TemplateFieldBadgeProps> = ({ type, toggleTyp return <StyledTemplateFieldBadge>{i18n.TEMPLATE_FIELD_LABEL}</StyledTemplateFieldBadge>; }; +const TemplateFieldBadge = React.memo(TemplateFieldBadgeComponent); + interface ProviderBadgeProps { deleteProvider: () => void; field: string; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx index 0093c43a17137e..fc06d37b9663fd 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx @@ -117,21 +117,38 @@ export const ProviderItemBadge = React.memo<ProviderItemBadgeProps>( [unRegisterProvider] ); - const button = ( - <ProviderBadge - deleteProvider={!isLoading ? deleteProvider : noop} - field={field} - kqlQuery={kqlQuery} - isEnabled={isEnabled} - isExcluded={isExcluded} - providerId={providerId} - togglePopover={togglePopover} - toggleType={onToggleTypeProvider} - val={val} - operator={operator} - type={type} - timelineType={timelineType} - /> + const button = useMemo( + () => ( + <ProviderBadge + deleteProvider={!isLoading ? deleteProvider : noop} + field={field} + kqlQuery={kqlQuery} + isEnabled={isEnabled} + isExcluded={isExcluded} + providerId={providerId} + togglePopover={togglePopover} + toggleType={onToggleTypeProvider} + val={val} + operator={operator} + type={type} + timelineType={timelineType} + /> + ), + [ + deleteProvider, + field, + isEnabled, + isExcluded, + isLoading, + kqlQuery, + onToggleTypeProvider, + operator, + providerId, + timelineType, + togglePopover, + type, + val, + ] ); return ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx index 3f371349aa7502..2df19605f813c1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx @@ -12,6 +12,7 @@ import { TestProviders } from '../../../../common/mock/test_providers'; import { DroppableWrapper } from '../../../../common/components/drag_and_drop/droppable_wrapper'; import { FilterManager } from '../../../../../../../../src/plugins/data/public'; +import { timelineActions } from '../../../store/timeline'; import { mockDataProviders } from './mock/mock_data_providers'; import { Providers } from './providers'; import { DELETE_CLASS_NAME, ENABLE_CLASS_NAME, EXCLUDE_CLASS_NAME } from './provider_item_actions'; @@ -24,27 +25,24 @@ describe('Providers', () => { const isLoading: boolean = true; const mount = useMountAppended(); const filterManager = new FilterManager(mockUiSettingsForFilterManager); + const mockOnDataProviderRemoved = jest.spyOn(timelineActions, 'removeProvider'); const manageTimelineForTesting = { - foo: { - ...getTimelineDefaults('foo'), + test: { + ...getTimelineDefaults('test'), filterManager, isLoading, }, }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('rendering', () => { test('renders correctly against snapshot', () => { const wrapper = shallow( - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> ); expect(wrapper).toMatchSnapshot(); }); @@ -53,16 +51,7 @@ describe('Providers', () => { const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -77,20 +66,10 @@ describe('Providers', () => { describe('#onDataProviderRemoved', () => { test('it invokes the onDataProviderRemoved callback when the close button is clicked', () => { - const mockOnDataProviderRemoved = jest.fn(); const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={mockOnDataProviderRemoved} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -98,25 +77,15 @@ describe('Providers', () => { .find('[data-test-subj="providerBadge"] [data-euiicon-type]') .first() .simulate('click'); - expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1'); + expect(mockOnDataProviderRemoved.mock.calls[0][0].providerId).toEqual('id-Provider 1'); }); test('while loading data, it does NOT invoke the onDataProviderRemoved callback when the close button is clicked', () => { - const mockOnDataProviderRemoved = jest.fn(); const wrapper = mount( <TestProviders> <ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={mockOnDataProviderRemoved} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </ManageGlobalTimeline> </TestProviders> @@ -131,20 +100,10 @@ describe('Providers', () => { }); test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => { - const mockOnDataProviderRemoved = jest.fn(); const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={mockOnDataProviderRemoved} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -156,25 +115,15 @@ describe('Providers', () => { .find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`) .first() .simulate('click'); - expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual('id-Provider 1'); + expect(mockOnDataProviderRemoved.mock.calls[0][0].providerId).toEqual('id-Provider 1'); }); test('while loading data, it does NOT invoke the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => { - const mockOnDataProviderRemoved = jest.fn(); const wrapper = mount( <TestProviders> <ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={mockOnDataProviderRemoved} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </ManageGlobalTimeline> </TestProviders> @@ -194,20 +143,14 @@ describe('Providers', () => { describe('#onToggleDataProviderEnabled', () => { test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => { - const mockOnToggleDataProviderEnabled = jest.fn(); + const mockOnToggleDataProviderEnabled = jest.spyOn( + timelineActions, + 'updateDataProviderEnabled' + ); const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -220,27 +163,23 @@ describe('Providers', () => { .first() .simulate('click'); expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({ + andProviderId: undefined, enabled: false, + id: 'test', providerId: 'id-Provider 1', }); }); test('while loading data, it does NOT invoke the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => { - const mockOnToggleDataProviderEnabled = jest.fn(); + const mockOnToggleDataProviderEnabled = jest.spyOn( + timelineActions, + 'updateDataProviderEnabled' + ); const wrapper = mount( <TestProviders> <ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </ManageGlobalTimeline> </TestProviders> @@ -260,21 +199,15 @@ describe('Providers', () => { describe('#onToggleDataProviderExcluded', () => { test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => { - const onToggleDataProviderExcluded = jest.fn(); + const mockOnToggleDataProviderExcluded = jest.spyOn( + timelineActions, + 'updateDataProviderExcluded' + ); const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={onToggleDataProviderExcluded} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -288,29 +221,25 @@ describe('Providers', () => { .first() .simulate('click'); - expect(onToggleDataProviderExcluded.mock.calls[0][0]).toEqual({ + expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({ + andProviderId: undefined, excluded: true, + id: 'test', providerId: 'id-Provider 1', }); }); test('while loading data, it does NOT invoke the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => { - const onToggleDataProviderExcluded = jest.fn(); + const mockOnToggleDataProviderExcluded = jest.spyOn( + timelineActions, + 'updateDataProviderExcluded' + ); const wrapper = mount( <TestProviders> <ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={onToggleDataProviderExcluded} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </ManageGlobalTimeline> </TestProviders> @@ -325,7 +254,7 @@ describe('Providers', () => { .first() .simulate('click'); - expect(onToggleDataProviderExcluded).not.toBeCalled(); + expect(mockOnToggleDataProviderExcluded).not.toBeCalled(); }); }); @@ -337,16 +266,7 @@ describe('Providers', () => { const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={dataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={dataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -364,21 +284,11 @@ describe('Providers', () => { test('it invokes the onDataProviderRemoved callback when you click on the close button is clicked', () => { const dataProviders = mockDataProviders.slice(0, 1); dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnDataProviderRemoved = jest.fn(); const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={mockOnDataProviderRemoved} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -392,28 +302,22 @@ describe('Providers', () => { wrapper.update(); - expect(mockOnDataProviderRemoved.mock.calls[0]).toEqual(['id-Provider 1', 'id-Provider 2']); + expect(mockOnDataProviderRemoved.mock.calls[0][0]).toEqual({ + andProviderId: 'id-Provider 2', + id: 'test', + providerId: 'id-Provider 1', + }); }); test('while loading data, it does NOT invoke the onDataProviderRemoved callback when you click on the close button is clicked', () => { const dataProviders = mockDataProviders.slice(0, 1); dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnDataProviderRemoved = jest.fn(); const wrapper = mount( <TestProviders> <ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={mockDataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={mockOnDataProviderRemoved} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={mockDataProviders} timelineId="test" /> </DroppableWrapper> </ManageGlobalTimeline> </TestProviders> @@ -434,21 +338,15 @@ describe('Providers', () => { test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => { const dataProviders = mockDataProviders.slice(0, 1); dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnToggleDataProviderEnabled = jest.fn(); + const mockOnToggleDataProviderEnabled = jest.spyOn( + timelineActions, + 'updateDataProviderEnabled' + ); const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={dataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={dataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -470,6 +368,7 @@ describe('Providers', () => { expect(mockOnToggleDataProviderEnabled.mock.calls[0][0]).toEqual({ andProviderId: 'id-Provider 2', enabled: false, + id: 'test', providerId: 'id-Provider 1', }); }); @@ -477,22 +376,16 @@ describe('Providers', () => { test('while loading data, it does NOT invoke the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => { const dataProviders = mockDataProviders.slice(0, 1); dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnToggleDataProviderEnabled = jest.fn(); + const mockOnToggleDataProviderEnabled = jest.spyOn( + timelineActions, + 'updateDataProviderEnabled' + ); const wrapper = mount( <TestProviders> <ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={dataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled} - onToggleDataProviderExcluded={jest.fn()} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={dataProviders} timelineId="test" /> </DroppableWrapper> </ManageGlobalTimeline> </TestProviders> @@ -518,21 +411,15 @@ describe('Providers', () => { test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => { const dataProviders = mockDataProviders.slice(0, 1); dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnToggleDataProviderExcluded = jest.fn(); + const mockOnToggleDataProviderExcluded = jest.spyOn( + timelineActions, + 'updateDataProviderExcluded' + ); const wrapper = mount( <TestProviders> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={dataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={mockOnToggleDataProviderExcluded} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={dataProviders} timelineId="test" /> </DroppableWrapper> </TestProviders> ); @@ -554,6 +441,7 @@ describe('Providers', () => { expect(mockOnToggleDataProviderExcluded.mock.calls[0][0]).toEqual({ andProviderId: 'id-Provider 2', excluded: true, + id: 'test', providerId: 'id-Provider 1', }); }); @@ -561,22 +449,16 @@ describe('Providers', () => { test('while loading data, it does NOT invoke the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => { const dataProviders = mockDataProviders.slice(0, 1); dataProviders[0].and = mockDataProviders.slice(1, 3); - const mockOnToggleDataProviderExcluded = jest.fn(); + const mockOnToggleDataProviderExcluded = jest.spyOn( + timelineActions, + 'updateDataProviderExcluded' + ); const wrapper = mount( <TestProviders> <ManageGlobalTimeline manageTimelineForTesting={manageTimelineForTesting}> <DroppableWrapper droppableId="unitTest"> - <Providers - browserFields={{}} - dataProviders={dataProviders} - timelineId="foo" - onDataProviderEdited={jest.fn()} - onDataProviderRemoved={jest.fn()} - onToggleDataProviderEnabled={jest.fn()} - onToggleDataProviderExcluded={mockOnToggleDataProviderExcluded} - onToggleDataProviderType={jest.fn()} - /> + <Providers browserFields={{}} dataProviders={dataProviders} timelineId="test" /> </DroppableWrapper> </ManageGlobalTimeline> </TestProviders> diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx index 1142bbc214d74e..4b6f3c6701794b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx @@ -6,9 +6,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiFormHelpText, EuiSpacer } from '@elastic/eui'; import { rgba } from 'polished'; -import React, { Fragment, useMemo } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { Draggable, DraggingStyle, Droppable, NotDraggingStyle } from 'react-beautiful-dnd'; -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; +import { useDispatch } from 'react-redux'; + +import { timelineActions } from '../../../store/timeline'; import { AndOrBadge } from '../../../../common/components/and_or_badge'; import { AddDataProviderPopover } from './add_data_provider_popover'; @@ -18,13 +21,6 @@ import { IS_DRAGGING_CLASS_NAME, getTimelineProviderDraggableId, } from '../../../../common/components/drag_and_drop/helpers'; -import { - OnDataProviderEdited, - OnDataProviderRemoved, - OnToggleDataProviderEnabled, - OnToggleDataProviderExcluded, - OnToggleDataProviderType, -} from '../events'; import { DataProvider, DataProviderType, DataProvidersAnd, IS_OPERATOR } from './data_provider'; import { EMPTY_GROUP, flattenIntoAndGroups } from './helpers'; @@ -36,11 +32,6 @@ interface Props { browserFields: BrowserFields; timelineId: string; dataProviders: DataProvider[]; - onDataProviderEdited: OnDataProviderEdited; - onDataProviderRemoved: OnDataProviderRemoved; - onToggleDataProviderEnabled: OnToggleDataProviderEnabled; - onToggleDataProviderExcluded: OnToggleDataProviderExcluded; - onToggleDataProviderType: OnToggleDataProviderType; } /** @@ -74,12 +65,10 @@ const DroppableContainer = styled.div` `; const Parens = styled.span` - ${({ theme }) => css` - color: ${theme.eui.euiColorMediumShade}; - font-size: 32px; - padding: 2px; - user-select: none; - `} + color: ${({ theme }) => theme.eui.euiColorMediumShade}; + font-size: 32px; + padding: 2px; + user-select: none; `; const AndOrBadgeContainer = styled.div<{ hideBadge: boolean }>` @@ -123,182 +112,269 @@ const getDataProviderValue = (dataProvider: DataProvidersAnd) => * 2) temporarily disabling a data provider * 3) applying boolean negation to the data provider */ -export const Providers = React.memo<Props>( - ({ - browserFields, - timelineId, - dataProviders, - onDataProviderEdited, - onDataProviderRemoved, - onToggleDataProviderEnabled, - onToggleDataProviderExcluded, - onToggleDataProviderType, - }) => { - // Transform the dataProviders into flattened groups, and append an empty group - const dataProviderGroups: DataProvidersAnd[][] = useMemo( - () => [...flattenIntoAndGroups(dataProviders), ...EMPTY_GROUP], - [dataProviders] +export const Providers = React.memo<Props>(({ browserFields, timelineId, dataProviders }) => { + // Transform the dataProviders into flattened groups, and append an empty group + const dataProviderGroups: DataProvidersAnd[][] = useMemo( + () => [...flattenIntoAndGroups(dataProviders), ...EMPTY_GROUP], + [dataProviders] + ); + + const content = useMemo( + () => + dataProviderGroups.map((group, groupIndex) => ( + <DataProvidersGroup + key={`droppable-${groupIndex}`} + group={group} + groupIndex={groupIndex} + isLastGroup={groupIndex === dataProviderGroups.length - 1} + browserFields={browserFields} + timelineId={timelineId} + dataProviders={dataProviders} + /> + )), + [browserFields, dataProviderGroups, dataProviders, timelineId] + ); + + return <div>{content}</div>; +}); + +Providers.displayName = 'Providers'; + +interface DataProvidersGroupItem extends Omit<Props, 'dataProviders'> { + index: number; + dataProvider: DataProvidersAnd; + group: DataProvidersAnd[]; + groupIndex: number; +} + +export const DataProvidersGroupItem = React.memo<DataProvidersGroupItem>( + ({ browserFields, group, groupIndex, dataProvider, index, timelineId }) => { + const dispatch = useDispatch(); + const draggableId = useMemo( + () => + getTimelineProviderDraggableId({ + dataProviderId: dataProvider.id, + groupIndex, + timelineId, + }), + [dataProvider.id, groupIndex, timelineId] + ); + + const handleDeleteProvider = useCallback(() => { + const payload = { + id: timelineId, + providerId: index > 0 ? group[0].id : dataProvider.id, + andProviderId: index > 0 ? dataProvider.id : undefined, + }; + + dispatch(timelineActions.removeProvider(payload)); + }, [dataProvider.id, dispatch, group, index, timelineId]); + + const handleToggleEnabledProvider = useCallback(() => { + const payload = { + id: timelineId, + providerId: index > 0 ? group[0].id : dataProvider.id, + enabled: !dataProvider.enabled, + andProviderId: index > 0 ? dataProvider.id : undefined, + }; + + dispatch(timelineActions.updateDataProviderEnabled(payload)); + }, [dataProvider.enabled, dataProvider.id, dispatch, group, index, timelineId]); + + const handleToggleExcludedProvider = useCallback(() => { + const payload = { + id: timelineId, + providerId: index > 0 ? group[0].id : dataProvider.id, + excluded: !dataProvider.excluded, + andProviderId: index > 0 ? dataProvider.id : undefined, + }; + + dispatch(timelineActions.updateDataProviderExcluded(payload)); + }, [dataProvider.excluded, dataProvider.id, dispatch, group, index, timelineId]); + + const handleToggleTypeProvider = useCallback(() => { + const payload = { + id: timelineId, + providerId: index > 0 ? group[0].id : dataProvider.id, + type: + dataProvider.type === DataProviderType.template + ? DataProviderType.default + : DataProviderType.template, + andProviderId: index > 0 ? dataProvider.id : undefined, + }; + + dispatch(timelineActions.updateDataProviderType(payload)); + }, [dataProvider.id, dataProvider.type, dispatch, group, index, timelineId]); + + const handleDataProviderEdited = useCallback( + ({ andProviderId, excluded, field, operator, providerId, value }) => + dispatch( + timelineActions.dataProviderEdited({ + andProviderId, + excluded, + field, + id: timelineId, + operator, + providerId, + value, + }) + ), + [dispatch, timelineId] + ); + + const DraggableContent = useCallback( + (provided, snapshot) => ( + <div + ref={provided.innerRef} + {...provided.draggableProps} + {...provided.dragHandleProps} + style={getItemStyle(provided.draggableProps.style)} + data-test-subj="providerContainer" + > + <EuiFlexGroup alignItems="center" gutterSize="none"> + <EuiFlexItem grow={false}> + <ProviderItemBadge + andProviderId={index > 0 ? dataProvider.id : undefined} + browserFields={browserFields} + deleteProvider={handleDeleteProvider} + field={ + index > 0 + ? dataProvider.queryMatch.displayField ?? dataProvider.queryMatch.field + : group[0].queryMatch.displayField ?? group[0].queryMatch.field + } + kqlQuery={index > 0 ? dataProvider.kqlQuery : group[0].kqlQuery} + isEnabled={index > 0 ? dataProvider.enabled : group[0].enabled} + isExcluded={index > 0 ? dataProvider.excluded : group[0].excluded} + onDataProviderEdited={handleDataProviderEdited} + operator={ + index > 0 + ? dataProvider.queryMatch.operator ?? IS_OPERATOR + : group[0].queryMatch.operator ?? IS_OPERATOR + } + register={dataProvider} + providerId={index > 0 ? group[0].id : dataProvider.id} + timelineId={timelineId} + toggleEnabledProvider={handleToggleEnabledProvider} + toggleExcludedProvider={handleToggleExcludedProvider} + toggleTypeProvider={handleToggleTypeProvider} + val={getDataProviderValue(dataProvider)} + type={dataProvider.type} + /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + {!snapshot.isDragging && + (index < group.length - 1 ? ( + <AndOrBadge type="and" /> + ) : ( + <LastAndOrBadgeInGroup> + <AndOrBadge type="and" /> + </LastAndOrBadgeInGroup> + ))} + </EuiFlexItem> + </EuiFlexGroup> + </div> + ), + [ + browserFields, + dataProvider, + group, + handleDataProviderEdited, + handleDeleteProvider, + handleToggleEnabledProvider, + handleToggleExcludedProvider, + handleToggleTypeProvider, + index, + timelineId, + ] ); return ( - <div> - {dataProviderGroups.map((group, groupIndex) => ( - <Fragment key={`droppable-${groupIndex}`}> - {groupIndex !== 0 && <EuiSpacer size="xs" />} - - <EuiFlexGroup alignItems="center" gutterSize="none"> - <OrFlexItem grow={false}> - <AndOrBadgeContainer hideBadge={groupIndex === 0}> - <AndOrBadge type="or" /> - </AndOrBadgeContainer> - </OrFlexItem> - <ParensContainer grow={false}> - <Parens>{'('}</Parens> - </ParensContainer> - <EuiFlexItem grow={false}> - <Droppable - droppableId={getTimelineProviderDroppableId({ groupIndex, timelineId })} - direction="horizontal" - > - {(droppableProvided) => ( - <DroppableContainer - className={ - groupIndex === dataProviderGroups.length - 1 - ? EMPTY_PROVIDERS_GROUP_CLASS_NAME - : '' - } - ref={droppableProvided.innerRef} - style={listStyle} - {...droppableProvided.droppableProps} - > - {group.map((dataProvider, index) => ( - <Draggable - disableInteractiveElementBlocking={true} - draggableId={getTimelineProviderDraggableId({ - dataProviderId: dataProvider.id, - groupIndex, - timelineId, - })} - index={index} - key={dataProvider.id} - > - {(provided, snapshot) => ( - <div - ref={provided.innerRef} - {...provided.draggableProps} - {...provided.dragHandleProps} - style={getItemStyle(provided.draggableProps.style)} - data-test-subj="providerContainer" - > - <EuiFlexGroup alignItems="center" gutterSize="none"> - <EuiFlexItem grow={false}> - <ProviderItemBadge - andProviderId={index > 0 ? dataProvider.id : undefined} - browserFields={browserFields} - deleteProvider={() => - index > 0 - ? onDataProviderRemoved(group[0].id, dataProvider.id) - : onDataProviderRemoved(dataProvider.id) - } - field={ - index > 0 - ? dataProvider.queryMatch.displayField ?? - dataProvider.queryMatch.field - : group[0].queryMatch.displayField ?? - group[0].queryMatch.field - } - kqlQuery={index > 0 ? dataProvider.kqlQuery : group[0].kqlQuery} - isEnabled={index > 0 ? dataProvider.enabled : group[0].enabled} - isExcluded={ - index > 0 ? dataProvider.excluded : group[0].excluded - } - onDataProviderEdited={onDataProviderEdited} - operator={ - index > 0 - ? dataProvider.queryMatch.operator ?? IS_OPERATOR - : group[0].queryMatch.operator ?? IS_OPERATOR - } - register={dataProvider} - providerId={index > 0 ? group[0].id : dataProvider.id} - timelineId={timelineId} - toggleEnabledProvider={() => - index > 0 - ? onToggleDataProviderEnabled({ - providerId: group[0].id, - enabled: !dataProvider.enabled, - andProviderId: dataProvider.id, - }) - : onToggleDataProviderEnabled({ - providerId: dataProvider.id, - enabled: !dataProvider.enabled, - }) - } - toggleExcludedProvider={() => - index > 0 - ? onToggleDataProviderExcluded({ - providerId: group[0].id, - excluded: !dataProvider.excluded, - andProviderId: dataProvider.id, - }) - : onToggleDataProviderExcluded({ - providerId: dataProvider.id, - excluded: !dataProvider.excluded, - }) - } - toggleTypeProvider={() => - index > 0 - ? onToggleDataProviderType({ - providerId: group[0].id, - type: - dataProvider.type === DataProviderType.template - ? DataProviderType.default - : DataProviderType.template, - andProviderId: dataProvider.id, - }) - : onToggleDataProviderType({ - providerId: dataProvider.id, - type: - dataProvider.type === DataProviderType.template - ? DataProviderType.default - : DataProviderType.template, - }) - } - val={getDataProviderValue(dataProvider)} - type={dataProvider.type} - /> - </EuiFlexItem> - <EuiFlexItem grow={false}> - {!snapshot.isDragging && - (index < group.length - 1 ? ( - <AndOrBadge type="and" /> - ) : ( - <LastAndOrBadgeInGroup> - <AndOrBadge type="and" /> - </LastAndOrBadgeInGroup> - ))} - </EuiFlexItem> - </EuiFlexGroup> - </div> - )} - </Draggable> - ))} - {droppableProvided.placeholder} - </DroppableContainer> - )} - </Droppable> - </EuiFlexItem> - <ParensContainer grow={false}> - <Parens>{')'}</Parens> - </ParensContainer> - {groupIndex === dataProviderGroups.length - 1 && ( - <AddDataProviderPopover browserFields={browserFields} timelineId={timelineId} /> - )} - </EuiFlexGroup> - </Fragment> - ))} - </div> + <Draggable + disableInteractiveElementBlocking={true} + draggableId={draggableId} + index={index} + key={dataProvider.id} + > + {DraggableContent} + </Draggable> ); } ); -Providers.displayName = 'Providers'; +DataProvidersGroupItem.displayName = 'DataProvidersGroupItem'; + +interface DataProvidersGroup extends Props { + group: DataProvidersAnd[]; + groupIndex: number; + isLastGroup: boolean; +} + +const DataProvidersGroup = React.memo<DataProvidersGroup>( + ({ browserFields, timelineId, group, groupIndex, isLastGroup }) => { + const droppableId = useMemo(() => getTimelineProviderDroppableId({ groupIndex, timelineId }), [ + groupIndex, + timelineId, + ]); + + const GroupDataProviders = useMemo( + () => + group.map((dataProvider, index) => ( + <DataProvidersGroupItem + key={dataProvider.id} + browserFields={browserFields} + dataProvider={dataProvider} + index={index} + timelineId={timelineId} + group={group} + groupIndex={groupIndex} + /> + )), + [browserFields, group, groupIndex, timelineId] + ); + + const DroppableContent = useCallback( + (droppableProvided) => ( + <DroppableContainer + className={isLastGroup ? EMPTY_PROVIDERS_GROUP_CLASS_NAME : ''} + ref={droppableProvided.innerRef} + style={listStyle} + {...droppableProvided.droppableProps} + > + {GroupDataProviders} + {droppableProvided.placeholder} + </DroppableContainer> + ), + [GroupDataProviders, isLastGroup] + ); + + return ( + <> + {groupIndex !== 0 && <EuiSpacer size="xs" />} + + <EuiFlexGroup alignItems="center" gutterSize="none"> + <OrFlexItem grow={false}> + <AndOrBadgeContainer hideBadge={groupIndex === 0}> + <AndOrBadge type="or" /> + </AndOrBadgeContainer> + </OrFlexItem> + <ParensContainer grow={false}> + <Parens>{'('}</Parens> + </ParensContainer> + <EuiFlexItem grow={false}> + <Droppable droppableId={droppableId} direction="horizontal"> + {DroppableContent} + </Droppable> + </EuiFlexItem> + <ParensContainer grow={false}> + <Parens>{')'}</Parens> + </ParensContainer> + {isLastGroup && ( + <AddDataProviderPopover browserFields={browserFields} timelineId={timelineId} /> + )} + </EuiFlexGroup> + </> + ); + } +); + +DataProvidersGroup.displayName = 'DataProvidersGroup'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts index f894ac4e73939d..8ab3a71604bf12 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/events.ts @@ -7,33 +7,8 @@ import { ColumnHeaderOptions } from '../../../timelines/store/timeline/model'; import { ColumnId } from './body/column_id'; import { SortDirection } from './body/sort'; -import { DataProvider, DataProviderType, QueryOperator } from './data_providers/data_provider'; +import { DataProvider, QueryOperator } from './data_providers/data_provider'; -/** Invoked when a user clicks the close button to remove a data provider */ -export type OnDataProviderRemoved = (providerId: string, andProviderId?: string) => void; - -/** Invoked when a user temporarily disables or re-enables a data provider */ -export type OnToggleDataProviderEnabled = (toggled: { - providerId: string; - enabled: boolean; - andProviderId?: string; -}) => void; - -/** Invoked when a user toggles negation ("boolean NOT") of a data provider */ -export type OnToggleDataProviderExcluded = (excluded: { - providerId: string; - excluded: boolean; - andProviderId?: string; -}) => void; - -/** Invoked when a user toggles type (can "default" or "template") of a data provider */ -export type OnToggleDataProviderType = (type: { - providerId: string; - type: DataProviderType; - andProviderId?: string; -}) => void; - -/** Invoked when a user edits the properties of a data provider */ export type OnDataProviderEdited = ({ andProviderId, excluded, @@ -54,9 +29,6 @@ export type OnDataProviderEdited = ({ type: DataProvider['type']; }) => void; -/** Invoked when a user change the kql query of our data provider */ -export type OnChangeDataProviderKqlQuery = (edit: { providerId: string; kqlQuery: string }) => void; - /** Invoked when a user selects a new minimap time range */ export type OnRangeSelected = (range: string) => void; @@ -76,8 +48,6 @@ export type OnChangeItemsPerPage = (itemsPerPage: number) => void; /** Invoked when a user clicks to load more item */ export type OnChangePage = (nextPage: number) => void; -export type OnChangeDroppableAndProvider = (providerId: string) => void; - /** Invoked when a user pins an event */ export type OnPinEvent = (eventId: string) => void; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap index f81934f9a1d91a..5b14edf818fdcc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/__snapshots__/index.test.tsx.snap @@ -77,7 +77,7 @@ exports[`Footer Timeline Component rendering it renders the default timeline foo data-test-subj="paging-control" isLoading={false} onPageClick={[Function]} - totalPages={2} + totalPages={5} /> </EuiFlexItem> <EuiFlexItem diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.test.tsx index f823b717e7f4c0..8ce109409c5fd9 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.test.tsx @@ -15,8 +15,9 @@ describe('Footer Timeline Component', () => { const loadMore = jest.fn(); const onChangeItemsPerPage = jest.fn(); const updatedAt = 1546878704036; - const totalCount = 15546; + const serverSideEventCount = 15546; const itemsCount = 2; + const totalCount = 10; describe('rendering', () => { test('it renders the default timeline footer', () => { @@ -33,8 +34,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> ); @@ -55,8 +56,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> ); @@ -78,8 +79,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> </TestProviders> ); @@ -129,8 +130,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> ); @@ -152,8 +153,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> </TestProviders> ); @@ -179,8 +180,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> </TestProviders> ); @@ -204,8 +205,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> </TestProviders> ); @@ -231,8 +232,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> </TestProviders> ); @@ -256,8 +257,8 @@ describe('Footer Timeline Component', () => { itemsPerPageOptions={[1, 5, 10, 20]} onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadMore} - serverSideEventCount={totalCount} - totalPages={2} + serverSideEventCount={serverSideEventCount} + totalCount={totalCount} /> </TestProviders> ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx index 7174e9b2121e55..7c10168da3c627 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx @@ -174,30 +174,36 @@ export const EventsCount = React.memo(EventsCountComponent); EventsCount.displayName = 'EventsCount'; -export const PagingControlComponent = ({ - activePage, - isLoading, - onPageClick, - totalPages, -}: { +interface PagingControlProps { activePage: number; isLoading: boolean; onPageClick: OnChangePage; totalPages: number; -}) => ( - <> - {isLoading ? ( - `${i18n.LOADING}...` - ) : ( - <EuiPagination - data-test-subj="timeline-pagination" - pageCount={totalPages} - activePage={activePage} - onPageClick={onPageClick} - /> - )} - </> -); +} + +export const PagingControlComponent: React.FC<PagingControlProps> = ({ + activePage, + isLoading, + onPageClick, + totalPages, +}) => { + if (isLoading) { + return <>{`${i18n.LOADING}...`}</>; + } + + if (!totalPages) { + return null; + } + + return ( + <EuiPagination + data-test-subj="timeline-pagination" + pageCount={totalPages} + activePage={activePage} + onPageClick={onPageClick} + /> + ); +}; PagingControlComponent.displayName = 'PagingControlComponent'; @@ -217,7 +223,7 @@ interface FooterProps { onChangeItemsPerPage: OnChangeItemsPerPage; onChangePage: OnChangePage; serverSideEventCount: number; - totalPages: number; + totalCount: number; } /** Renders a loading indicator and paging controls */ @@ -234,7 +240,7 @@ export const FooterComponent = ({ onChangeItemsPerPage, onChangePage, serverSideEventCount, - totalPages, + totalCount, }: FooterProps) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [paginationLoading, setPaginationLoading] = useState(false); @@ -259,6 +265,30 @@ export const FooterComponent = ({ ]); const closePopover = useCallback(() => setIsPopoverOpen(false), [setIsPopoverOpen]); + const rowItems = useMemo( + () => + itemsPerPageOptions && + itemsPerPageOptions.map((item) => ( + <EuiContextMenuItem + key={item} + icon={itemsPerPage === item ? 'check' : 'empty'} + data-test-subj={`items-per-page-option-${item}`} + onClick={() => { + closePopover(); + onChangeItemsPerPage(item); + }} + > + {`${item} ${i18n.ROWS}`} + </EuiContextMenuItem> + )), + [closePopover, itemsPerPage, itemsPerPageOptions, onChangeItemsPerPage] + ); + + const totalPages = useMemo(() => Math.ceil(totalCount / itemsPerPage), [ + itemsPerPage, + totalCount, + ]); + useEffect(() => { if (paginationLoading && !isLoading) { setPaginationLoading(false); @@ -279,22 +309,6 @@ export const FooterComponent = ({ ); } - const rowItems = - itemsPerPageOptions && - itemsPerPageOptions.map((item) => ( - <EuiContextMenuItem - key={item} - icon={itemsPerPage === item ? 'check' : 'empty'} - data-test-subj={`items-per-page-option-${item}`} - onClick={() => { - closePopover(); - onChangeItemsPerPage(item); - }} - > - {`${item} ${i18n.ROWS}`} - </EuiContextMenuItem> - )); - return ( <FooterContainer data-test-subj="timeline-footer" 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 f94c30c5a102d9..66758268fb39eb 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 @@ -138,11 +138,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = ` }, ] } - onDataProviderEdited={[MockFunction]} - onDataProviderRemoved={[MockFunction]} - onToggleDataProviderEnabled={[MockFunction]} - onToggleDataProviderExcluded={[MockFunction]} - onToggleDataProviderType={[MockFunction]} timelineId="foo" /> <Connect(StatefulSearchOrFilterComponent) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx index e50a6ed1e45fe0..22d28737e5d61e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/header/index.tsx @@ -11,13 +11,6 @@ import deepEqual from 'fast-deep-equal'; import { DataProviders } from '../data_providers'; import { DataProvider } from '../data_providers/data_provider'; -import { - OnDataProviderEdited, - OnDataProviderRemoved, - OnToggleDataProviderEnabled, - OnToggleDataProviderExcluded, - OnToggleDataProviderType, -} from '../events'; import { StatefulSearchOrFilter } from '../search_or_filter'; import { BrowserFields } from '../../../../common/containers/source'; @@ -33,11 +26,6 @@ interface Props { filterManager: FilterManager; graphEventId?: string; indexPattern: IIndexPattern; - onDataProviderEdited: OnDataProviderEdited; - onDataProviderRemoved: OnDataProviderRemoved; - onToggleDataProviderEnabled: OnToggleDataProviderEnabled; - onToggleDataProviderExcluded: OnToggleDataProviderExcluded; - onToggleDataProviderType: OnToggleDataProviderType; show: boolean; showCallOutUnauthorizedMsg: boolean; status: TimelineStatusLiteralWithNull; @@ -50,11 +38,6 @@ const TimelineHeaderComponent: React.FC<Props> = ({ dataProviders, filterManager, graphEventId, - onDataProviderEdited, - onDataProviderRemoved, - onToggleDataProviderEnabled, - onToggleDataProviderExcluded, - onToggleDataProviderType, show, showCallOutUnauthorizedMsg, status, @@ -85,11 +68,6 @@ const TimelineHeaderComponent: React.FC<Props> = ({ browserFields={browserFields} timelineId={timelineId} dataProviders={dataProviders} - onDataProviderEdited={onDataProviderEdited} - onDataProviderRemoved={onDataProviderRemoved} - onToggleDataProviderEnabled={onToggleDataProviderEnabled} - onToggleDataProviderExcluded={onToggleDataProviderExcluded} - onToggleDataProviderType={onToggleDataProviderType} /> <StatefulSearchOrFilter @@ -111,11 +89,6 @@ export const TimelineHeader = React.memo( deepEqual(prevProps.dataProviders, nextProps.dataProviders) && prevProps.filterManager === nextProps.filterManager && prevProps.graphEventId === nextProps.graphEventId && - prevProps.onDataProviderEdited === nextProps.onDataProviderEdited && - prevProps.onDataProviderRemoved === nextProps.onDataProviderRemoved && - prevProps.onToggleDataProviderEnabled === nextProps.onToggleDataProviderEnabled && - prevProps.onToggleDataProviderExcluded === nextProps.onToggleDataProviderExcluded && - prevProps.onToggleDataProviderType === nextProps.onToggleDataProviderType && prevProps.show === nextProps.show && prevProps.showCallOutUnauthorizedMsg === nextProps.showCallOutUnauthorizedMsg && prevProps.status === nextProps.status && 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 ccdb0793bc03f0..0c7b1e0cdecd5e 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 @@ -14,14 +14,7 @@ import { timelineActions, timelineSelectors } from '../../store/timeline'; import { ColumnHeaderOptions, TimelineModel } from '../../../timelines/store/timeline/model'; import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; import { defaultHeaders } from './body/column_headers/default_headers'; -import { - OnChangeItemsPerPage, - OnDataProviderRemoved, - OnDataProviderEdited, - OnToggleDataProviderEnabled, - OnToggleDataProviderExcluded, - OnToggleDataProviderType, -} from './events'; +import { OnChangeItemsPerPage } from './events'; import { Timeline } from './timeline'; import { useSourcererScope } from '../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; @@ -51,18 +44,13 @@ const StatefulTimelineComponent = React.memo<Props>( kqlMode, kqlQueryExpression, onClose, - onDataProviderEdited, removeColumn, - removeProvider, show, showCallOutUnauthorizedMsg, sort, start, status, timelineType, - updateDataProviderEnabled, - updateDataProviderExcluded, - updateDataProviderType, updateItemsPerPage, upsertColumn, usersViewing, @@ -75,59 +63,6 @@ const StatefulTimelineComponent = React.memo<Props>( selectedPatterns, } = useSourcererScope(SourcererScopeName.timeline); - const onDataProviderRemoved: OnDataProviderRemoved = useCallback( - (providerId: string, andProviderId?: string) => - removeProvider!({ id, providerId, andProviderId }), - [id, removeProvider] - ); - - const onToggleDataProviderEnabled: OnToggleDataProviderEnabled = useCallback( - ({ providerId, enabled, andProviderId }) => - updateDataProviderEnabled!({ - id, - enabled, - providerId, - andProviderId, - }), - [id, updateDataProviderEnabled] - ); - - const onToggleDataProviderExcluded: OnToggleDataProviderExcluded = useCallback( - ({ providerId, excluded, andProviderId }) => - updateDataProviderExcluded!({ - id, - excluded, - providerId, - andProviderId, - }), - [id, updateDataProviderExcluded] - ); - - const onToggleDataProviderType: OnToggleDataProviderType = useCallback( - ({ providerId, type, andProviderId }) => - updateDataProviderType!({ - id, - type, - providerId, - andProviderId, - }), - [id, updateDataProviderType] - ); - - const onDataProviderEditedLocal: OnDataProviderEdited = useCallback( - ({ andProviderId, excluded, field, operator, providerId, value }) => - onDataProviderEdited!({ - andProviderId, - excluded, - field, - id, - operator, - providerId, - value, - }), - [id, onDataProviderEdited] - ); - const onChangeItemsPerPage: OnChangeItemsPerPage = useCallback( (itemsChangedPerPage) => updateItemsPerPage!({ id, itemsPerPage: itemsChangedPerPage }), [id, updateItemsPerPage] @@ -183,11 +118,6 @@ const StatefulTimelineComponent = React.memo<Props>( loadingSourcerer={loading} onChangeItemsPerPage={onChangeItemsPerPage} onClose={onClose} - onDataProviderEdited={onDataProviderEditedLocal} - onDataProviderRemoved={onDataProviderRemoved} - onToggleDataProviderEnabled={onToggleDataProviderEnabled} - onToggleDataProviderExcluded={onToggleDataProviderExcluded} - onToggleDataProviderType={onToggleDataProviderType} show={show!} showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg} sort={sort!} @@ -287,14 +217,8 @@ const makeMapStateToProps = () => { const mapDispatchToProps = { addProvider: timelineActions.addProvider, createTimeline: timelineActions.createTimeline, - onDataProviderEdited: timelineActions.dataProviderEdited, removeColumn: timelineActions.removeColumn, - removeProvider: timelineActions.removeProvider, updateColumns: timelineActions.updateColumns, - updateDataProviderEnabled: timelineActions.updateDataProviderEnabled, - updateDataProviderExcluded: timelineActions.updateDataProviderExcluded, - updateDataProviderKqlQuery: timelineActions.updateDataProviderKqlQuery, - updateDataProviderType: timelineActions.updateDataProviderType, updateHighlightedDropAndProviderId: timelineActions.updateHighlightedDropAndProviderId, updateItemsPerPage: timelineActions.updateItemsPerPage, updateItemsPerPageOptions: timelineActions.updateItemsPerPageOptions, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx index 43ab8ab203e116..a28f4240d3a2f4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/properties/helpers.tsx @@ -73,23 +73,31 @@ export const StarIcon = React.memo<{ isFavorite: boolean; timelineId: string; updateIsFavorite: UpdateIsFavorite; -}>(({ isFavorite, timelineId: id, updateIsFavorite }) => ( - // TODO: 1 error is: Visible, non-interactive elements with click handlers must have at least one keyboard listener - // TODO: 2 error is: Elements with the 'button' interactive role must be focusable - // TODO: Investigate this error - // eslint-disable-next-line - <div role="button" onClick={() => updateIsFavorite({ id, isFavorite: !isFavorite })}> - {isFavorite ? ( - <EuiToolTip data-test-subj="timeline-favorite-filled-star-tool-tip" content={i18n.FAVORITE}> - <StyledStar data-test-subj="timeline-favorite-filled-star" type="starFilled" size="l" /> - </EuiToolTip> - ) : ( - <EuiToolTip content={i18n.NOT_A_FAVORITE}> - <StyledStar data-test-subj="timeline-favorite-empty-star" type="starEmpty" size="l" /> - </EuiToolTip> - )} - </div> -)); +}>(({ isFavorite, timelineId: id, updateIsFavorite }) => { + const handleClick = useCallback(() => updateIsFavorite({ id, isFavorite: !isFavorite }), [ + id, + isFavorite, + updateIsFavorite, + ]); + + return ( + // TODO: 1 error is: Visible, non-interactive elements with click handlers must have at least one keyboard listener + // TODO: 2 error is: Elements with the 'button' interactive role must be focusable + // TODO: Investigate this error + // eslint-disable-next-line + <div role="button" onClick={handleClick}> + {isFavorite ? ( + <EuiToolTip data-test-subj="timeline-favorite-filled-star-tool-tip" content={i18n.FAVORITE}> + <StyledStar data-test-subj="timeline-favorite-filled-star" type="starFilled" size="l" /> + </EuiToolTip> + ) : ( + <EuiToolTip content={i18n.NOT_A_FAVORITE}> + <StyledStar data-test-subj="timeline-favorite-empty-star" type="starEmpty" size="l" /> + </EuiToolTip> + )} + </div> + ); +}); StarIcon.displayName = 'StarIcon'; interface DescriptionProps { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx index bde1e7bf5829ad..630a71693d182c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.test.tsx @@ -19,11 +19,6 @@ import { import '../../../common/mock/match_media'; import { TestProviders } from '../../../common/mock/test_providers'; -import { - DELETE_CLASS_NAME, - ENABLE_CLASS_NAME, - EXCLUDE_CLASS_NAME, -} from './data_providers/provider_item_actions'; import { TimelineComponent, Props as TimelineComponentProps } from './timeline'; import { Sort } from './body/sort'; import { mockDataProviders } from './data_providers/mock/mock_data_providers'; @@ -115,11 +110,6 @@ describe('Timeline', () => { loadingSourcerer: false, onChangeItemsPerPage: jest.fn(), onClose: jest.fn(), - onDataProviderEdited: jest.fn(), - onDataProviderRemoved: jest.fn(), - onToggleDataProviderEnabled: jest.fn(), - onToggleDataProviderExcluded: jest.fn(), - onToggleDataProviderType: jest.fn(), show: true, showCallOutUnauthorizedMsg: false, sort, @@ -238,204 +228,4 @@ describe('Timeline', () => { }); }); }); - - describe('event wire up', () => { - describe('onDataProviderRemoved', () => { - test('it invokes the onDataProviderRemoved callback when the delete button on a provider is clicked', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} /> - </TestProviders> - ); - - wrapper - .find('[data-test-subj="providerBadge"] [data-euiicon-type]') - .first() - .simulate('click'); - - expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual( - 'id-Provider 1' - ); - }); - - test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the provider menu', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} /> - </TestProviders> - ); - wrapper.find('button[data-test-subj="providerBadge"]').first().simulate('click'); - - wrapper.update(); - - wrapper - .find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`) - .first() - .simulate('click'); - - expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0][0]).toEqual( - 'id-Provider 1' - ); - }); - }); - - describe('onToggleDataProviderEnabled', () => { - test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the provider menu', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} /> - </TestProviders> - ); - - wrapper.find('button[data-test-subj="providerBadge"]').first().simulate('click'); - - wrapper.update(); - - wrapper - .find(`[data-test-subj="providerActions"] .${ENABLE_CLASS_NAME}`) - .first() - .simulate('click'); - - expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({ - providerId: 'id-Provider 1', - enabled: false, - }); - }); - }); - - describe('onToggleDataProviderExcluded', () => { - test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the provider menu', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} /> - </TestProviders> - ); - - wrapper - .find('[data-test-subj="providerBadge"]') - .first() - .find('button') - .first() - .simulate('click'); - - wrapper.update(); - - wrapper - .find(`[data-test-subj="providerActions"] .${EXCLUDE_CLASS_NAME}`) - .first() - .simulate('click'); - - expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({ - providerId: 'id-Provider 1', - excluded: true, - }); - }); - }); - - describe('#ProviderWithAndProvider', () => { - const dataProviders = mockDataProviders.slice(0, 1); - dataProviders[0].and = mockDataProviders.slice(1, 3); - - test('Rendering And Provider', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} dataProviders={dataProviders} /> - </TestProviders> - ); - - const andProviderBadges = wrapper.find( - '[data-test-subj="providerBadge"] .euiBadge__content span.field-value' - ); - - const andProviderBadgesText = andProviderBadges.map((node) => node.text()).join(' '); - expect(andProviderBadges.length).toEqual(3); - expect(andProviderBadgesText).toEqual( - 'name: "Provider 1" name: "Provider 2" name: "Provider 3"' - ); - }); - - test('it invokes the onDataProviderRemoved callback when you click on the option "Delete" in the accordion menu', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} dataProviders={dataProviders} /> - </TestProviders> - ); - - wrapper - .find('[data-test-subj="providerBadge"]') - .at(3) - .find('button') - .first() - .simulate('click'); - - wrapper.update(); - - wrapper - .find(`[data-test-subj="providerActions"] .${DELETE_CLASS_NAME}`) - .first() - .simulate('click'); - - expect((props.onDataProviderRemoved as jest.Mock).mock.calls[0]).toEqual([ - 'id-Provider 1', - 'id-Provider 2', - ]); - }); - - test('it invokes the onToggleDataProviderEnabled callback when you click on the option "Temporary disable" in the accordion menu', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} dataProviders={dataProviders} /> - </TestProviders> - ); - - wrapper - .find('[data-test-subj="providerBadge"]') - .at(3) - .find('button') - .first() - .simulate('click'); - - wrapper.update(); - - wrapper - .find(`[data-test-subj="providerActions"] .${ENABLE_CLASS_NAME}`) - .first() - .simulate('click'); - - expect((props.onToggleDataProviderEnabled as jest.Mock).mock.calls[0][0]).toEqual({ - andProviderId: 'id-Provider 2', - enabled: false, - providerId: 'id-Provider 1', - }); - }); - - test('it invokes the onToggleDataProviderExcluded callback when you click on the option "Exclude results" in the accordion menu', () => { - const wrapper = mount( - <TestProviders> - <TimelineComponent {...props} dataProviders={dataProviders} /> - </TestProviders> - ); - - wrapper - .find('[data-test-subj="providerBadge"]') - .at(3) - .find('button') - .first() - .simulate('click'); - - wrapper.update(); - - wrapper - .find(`[data-test-subj="providerActions"] .${EXCLUDE_CLASS_NAME}`) - .first() - .simulate('click'); - - expect((props.onToggleDataProviderExcluded as jest.Mock).mock.calls[0][0]).toEqual({ - andProviderId: 'id-Provider 2', - excluded: true, - providerId: 'id-Provider 1', - }); - }); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx index d1a25e6f3e1a48..1097d58b227a86 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/timeline.tsx @@ -19,14 +19,7 @@ import { defaultHeaders } from './body/column_headers/default_headers'; import { Sort } from './body/sort'; import { StatefulBody } from './body/stateful_body'; import { DataProvider } from './data_providers/data_provider'; -import { - OnChangeItemsPerPage, - OnDataProviderRemoved, - OnDataProviderEdited, - OnToggleDataProviderEnabled, - OnToggleDataProviderExcluded, - OnToggleDataProviderType, -} from './events'; +import { OnChangeItemsPerPage } from './events'; import { TimelineKqlFetch } from './fetch_kql_timeline'; import { Footer, footerHeight } from './footer'; import { TimelineHeader } from './header'; @@ -113,11 +106,6 @@ export interface Props { loadingSourcerer: boolean; onChangeItemsPerPage: OnChangeItemsPerPage; onClose: () => void; - onDataProviderEdited: OnDataProviderEdited; - onDataProviderRemoved: OnDataProviderRemoved; - onToggleDataProviderEnabled: OnToggleDataProviderEnabled; - onToggleDataProviderExcluded: OnToggleDataProviderExcluded; - onToggleDataProviderType: OnToggleDataProviderType; show: boolean; showCallOutUnauthorizedMsg: boolean; sort: Sort; @@ -149,11 +137,6 @@ export const TimelineComponent: React.FC<Props> = ({ kqlQueryExpression, onChangeItemsPerPage, onClose, - onDataProviderEdited, - onDataProviderRemoved, - onToggleDataProviderEnabled, - onToggleDataProviderExcluded, - onToggleDataProviderType, show, showCallOutUnauthorizedMsg, start, @@ -270,11 +253,6 @@ export const TimelineComponent: React.FC<Props> = ({ dataProviders={dataProviders} filterManager={filterManager} graphEventId={graphEventId} - onDataProviderEdited={onDataProviderEdited} - onDataProviderRemoved={onDataProviderRemoved} - onToggleDataProviderEnabled={onToggleDataProviderEnabled} - onToggleDataProviderExcluded={onToggleDataProviderExcluded} - onToggleDataProviderType={onToggleDataProviderType} show={show} showCallOutUnauthorizedMsg={showCallOutUnauthorizedMsg} timelineId={id} @@ -324,7 +302,7 @@ export const TimelineComponent: React.FC<Props> = ({ onChangeItemsPerPage={onChangeItemsPerPage} onChangePage={loadPage} serverSideEventCount={totalCount} - totalPages={pageInfo.totalPages} + totalCount={pageInfo.fakeTotalCount} /> </StyledEuiFlyoutFooter> ) diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index 54db52b985c314..53944fd29a6877 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -15,11 +15,13 @@ import { inputsModel } from '../../common/store'; import { useKibana } from '../../common/lib/kibana'; import { createFilter } from '../../common/containers/helpers'; import { DocValueFields } from '../../common/containers/query_template'; +import { generateTablePaginationOptions } from '../../common/components/paginated_table/helpers'; import { timelineActions } from '../../timelines/store/timeline'; import { detectionsTimelineIds, skipQueryForDetectionsPage } from './helpers'; import { getInspectResponse } from '../../helpers'; import { Direction, + PageInfoPaginated, TimelineEventsQueries, TimelineEventsAllStrategyResponse, TimelineEventsAllRequestOptions, @@ -35,10 +37,7 @@ export interface TimelineArgs { id: string; inspect: InspectResponse; loadPage: LoadPage; - pageInfo: { - activePage: number; - totalPages: number; - }; + pageInfo: PageInfoPaginated; refetch: inputsModel.Refetch; totalCount: number; updatedAt: number; @@ -97,10 +96,7 @@ export const useTimelineEvents = ({ from: startDate, to: endDate, }, - pagination: { - activePage, - querySize: limit, - }, + pagination: generateTablePaginationOptions(activePage, limit), sort, defaultIndex: indexNames, docValueFields: docValueFields ?? [], @@ -134,7 +130,8 @@ export const useTimelineEvents = ({ totalCount: -1, pageInfo: { activePage: 0, - totalPages: 0, + fakeTotalCount: 0, + showMorePagesIndicator: false, }, events: [], loadPage: wrappedLoadPage, @@ -218,10 +215,7 @@ export const useTimelineEvents = ({ defaultIndex: indexNames, docValueFields: docValueFields ?? [], filterQuery: createFilter(filterQuery), - pagination: { - activePage, - querySize: limit, - }, + pagination: generateTablePaginationOptions(activePage, limit), timerange: { interval: '12h', from: startDate, diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx index 1f79b26394a69e..1992b1f88f0641 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_local_storage.test.tsx @@ -96,11 +96,6 @@ describe('epicLocalStorage', () => { loadingSourcerer: false, onChangeItemsPerPage: jest.fn(), onClose: jest.fn(), - onDataProviderEdited: jest.fn(), - onDataProviderRemoved: jest.fn(), - onToggleDataProviderEnabled: jest.fn(), - onToggleDataProviderExcluded: jest.fn(), - onToggleDataProviderType: jest.fn(), show: true, showCallOutUnauthorizedMsg: false, start: startDate, 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 fc178df86362b2..30d0796443ab50 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 @@ -1209,17 +1209,20 @@ export const updateTimelinePerPageOptions = ({ const removeAndProvider = (andProviderId: string, providerId: string, timeline: TimelineModel) => { const providerIndex = timeline.dataProviders.findIndex((p) => p.id === providerId); - const providerAndIndex = timeline.dataProviders[providerIndex].and.findIndex( + const providerAndIndex = timeline.dataProviders[providerIndex]?.and.findIndex( (p) => p.id === andProviderId ); + return [ ...timeline.dataProviders.slice(0, providerIndex), { ...timeline.dataProviders[providerIndex], - and: [ - ...timeline.dataProviders[providerIndex].and.slice(0, providerAndIndex), - ...timeline.dataProviders[providerIndex].and.slice(providerAndIndex + 1), - ], + and: timeline.dataProviders[providerIndex]?.and + ? [ + ...timeline.dataProviders[providerIndex]?.and.slice(0, providerAndIndex), + ...timeline.dataProviders[providerIndex]?.and.slice(providerAndIndex + 1), + ] + : [], }, ...timeline.dataProviders.slice(providerIndex + 1), ]; @@ -1229,7 +1232,7 @@ const removeProvider = (providerId: string, timeline: TimelineModel) => { const providerIndex = timeline.dataProviders.findIndex((p) => p.id === providerId); return [ ...timeline.dataProviders.slice(0, providerIndex), - ...(timeline.dataProviders[providerIndex].and.length + ...(timeline.dataProviders[providerIndex]?.and.length ? [ { ...timeline.dataProviders[providerIndex].and.slice(0, 1)[0], diff --git a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts index 12729eb88a6669..6b28fc2598d410 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/timeline/factory/events/all/index.ts @@ -42,17 +42,19 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory<TimelineEventsQu ...fieldRequested, ...reduceFields(TIMELINE_EVENTS_FIELDS, eventFieldsMap), ]); - const { activePage, querySize } = options.pagination; + const { activePage, cursorStart, fakePossibleCount, querySize } = options.pagination; const totalCount = getOr(0, 'hits.total.value', response.rawResponse); const hits = response.rawResponse.hits.hits; - const edges: TimelineEdges[] = hits.map((hit) => + const edges: TimelineEdges[] = hits.splice(cursorStart, querySize - cursorStart).map((hit) => // @ts-expect-error formatTimelineData(options.fieldRequested, TIMELINE_EVENTS_FIELDS, hit, eventFieldsMap) ); const inspect = { dsl: [inspectStringifyObject(buildTimelineEventsAllQuery(queryOptions))], }; + const fakeTotalCount = fakePossibleCount <= totalCount ? fakePossibleCount : totalCount; + const showMorePagesIndicator = totalCount > fakeTotalCount; return { ...response, @@ -61,7 +63,8 @@ export const timelineEventsAll: SecuritySolutionTimelineFactory<TimelineEventsQu totalCount, pageInfo: { activePage: activePage ?? 0, - totalPages: Math.ceil(totalCount / querySize), + fakeTotalCount, + showMorePagesIndicator, }, }; }, From d772c8ca9545ade37f474023840b2219c28db141 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm <matthias.wilhelm@elastic.co> Date: Fri, 2 Oct 2020 11:22:41 +0200 Subject: [PATCH 070/128] [Discover] Change context query to prevent duplicates (#77014) --- .../application/angular/context/api/_stubs.js | 2 +- .../context/api/context.predecessors.test.js | 8 ++++++-- .../context/api/context.successors.test.js | 8 ++++++-- .../angular/context/api/context.ts | 4 +++- .../api/utils/fetch_hits_in_interval.ts | 20 ++++++++++++++----- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/plugins/discover/public/application/angular/context/api/_stubs.js b/src/plugins/discover/public/application/angular/context/api/_stubs.js index 35ddf396c2dbad..d82189db609356 100644 --- a/src/plugins/discover/public/application/angular/context/api/_stubs.js +++ b/src/plugins/discover/public/application/angular/context/api/_stubs.js @@ -74,7 +74,7 @@ export function createContextSearchSourceStub(hits, timeField = '@timestamp') { searchSourceStub.fetch = sinon.spy(() => { const timeField = searchSourceStub._stubTimeField; const lastQuery = searchSourceStub.setField.withArgs('query').lastCall.args[1]; - const timeRange = lastQuery.query.constant_score.filter.range[timeField]; + const timeRange = lastQuery.query.bool.must.constant_score.filter.range[timeField]; const lastSort = searchSourceStub.setField.withArgs('sort').lastCall.args[1]; const sortDirection = lastSort[0][timeField]; const sortFunction = diff --git a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js index 4987c77f4bf256..4c0515906a4947 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.predecessors.test.js @@ -124,7 +124,9 @@ describe('context app', function () { ).then((hits) => { const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') - .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp'])); + .map(([, { query }]) => + get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) + ); expect( intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true)) @@ -160,7 +162,9 @@ describe('context app', function () { ).then((hits) => { const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') - .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp'])); + .map(([, { query }]) => + get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) + ); // should have started at the given time expect(intervals[0].gte).toEqual(moment(MS_PER_DAY * 1000).toISOString()); diff --git a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js index ebf6e78585962d..285d39cd4d8a44 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.successors.test.js +++ b/src/plugins/discover/public/application/angular/context/api/context.successors.test.js @@ -125,7 +125,9 @@ describe('context app', function () { ).then((hits) => { const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') - .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp'])); + .map(([, { query }]) => + get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) + ); expect( intervals.every(({ gte, lte }) => (gte && lte ? moment(gte).isBefore(lte) : true)) @@ -163,7 +165,9 @@ describe('context app', function () { ).then((hits) => { const intervals = mockSearchSource.setField.args .filter(([property]) => property === 'query') - .map(([, { query }]) => get(query, ['constant_score', 'filter', 'range', '@timestamp'])); + .map(([, { query }]) => + get(query, ['bool', 'must', 'constant_score', 'filter', 'range', '@timestamp']) + ); // should have started at the given time expect(intervals[0].lte).toEqual(moment(MS_PER_DAY * 3000).toISOString()); diff --git a/src/plugins/discover/public/application/angular/context/api/context.ts b/src/plugins/discover/public/application/angular/context/api/context.ts index e244176914a9b7..ba8cffd1d7558f 100644 --- a/src/plugins/discover/public/application/angular/context/api/context.ts +++ b/src/plugins/discover/public/application/angular/context/api/context.ts @@ -31,6 +31,7 @@ export interface EsHitRecord { fields: Record<string, any>; sort: number[]; _source: Record<string, any>; + _id: string; } export type EsHitRecordList = EsHitRecord[]; @@ -100,7 +101,8 @@ function fetchContextProvider(indexPatterns: IndexPatternsContract) { interval, searchAfter, remainingSize, - nanos + nanos, + anchor._id ); documents = diff --git a/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts index 9a199ea4a62fc6..5ac41641916339 100644 --- a/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts +++ b/src/plugins/discover/public/application/angular/context/api/utils/fetch_hits_in_interval.ts @@ -43,7 +43,8 @@ export async function fetchHitsInInterval( interval: IntervalValue[], searchAfter: EsQuerySearchAfter, maxCount: number, - nanosValue: string + nanosValue: string, + anchorId: string ): Promise<EsHitRecordList> { const range: RangeQuery = { format: 'strict_date_optional_time', @@ -61,10 +62,19 @@ export async function fetchHitsInInterval( .setField('size', maxCount) .setField('query', { query: { - constant_score: { - filter: { - range: { - [timeField]: range, + bool: { + must: { + constant_score: { + filter: { + range: { + [timeField]: range, + }, + }, + }, + }, + must_not: { + ids: { + values: [anchorId], }, }, }, From e52884cfa285ee0aeae6ba1ad7655efefddb67c3 Mon Sep 17 00:00:00 2001 From: Pete Hampton <pjhampton@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:28:37 +0100 Subject: [PATCH 071/128] [7.10][Telemetry] Display collected security event sample (#78963) * Add security example to usage data opt in panel. * Update translations. * Fix docs. * Fix broken type. Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- docs/developer/plugin-list.asciidoc | 2 +- .../telemetry_management_section/README.md | 2 +- ...t_in_security_example_flyout.test.tsx.snap | 134 ++++++++++ ...telemetry_management_section.test.tsx.snap | 35 ++- .../opt_in_security_example_flyout.test.tsx | 27 ++ .../opt_in_security_example_flyout.tsx | 235 ++++++++++++++++++ .../telemetry_management_section.test.tsx | 38 ++- .../telemetry_management_section.tsx | 37 ++- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 10 files changed, 491 insertions(+), 21 deletions(-) create mode 100644 src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap create mode 100644 src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx create mode 100644 src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index ed58e77427d47d..bf11f87b96ce9f 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -168,7 +168,7 @@ It also provides a stateful version of it on the start contract. |{kib-repo}blob/{branch}/src/plugins/telemetry_management_section/README.md[telemetryManagementSection] -|This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry). +|This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry). |{kib-repo}blob/{branch}/src/plugins/tile_map[tileMap] diff --git a/src/plugins/telemetry_management_section/README.md b/src/plugins/telemetry_management_section/README.md index 0f795786720c93..c23a8591f6794a 100644 --- a/src/plugins/telemetry_management_section/README.md +++ b/src/plugins/telemetry_management_section/README.md @@ -1,5 +1,5 @@ # Telemetry Management Section -This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry). +This plugin adds the Advanced Settings section for the Usage and Security Data collection (aka Telemetry). The reason for having it separated from the `telemetry` plugin is to avoid circular dependencies. The plugin `advancedSettings` depends on the `home` app that depends on the `telemetry` plugin because of the telemetry banner in the welcome screen. diff --git a/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap b/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap new file mode 100644 index 00000000000000..0b9d426008ca43 --- /dev/null +++ b/src/plugins/telemetry_management_section/public/components/__snapshots__/opt_in_security_example_flyout.test.tsx.snap @@ -0,0 +1,134 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`security flyout renders as expected renders as expected 1`] = ` +<EuiPortal> + <EuiFlyout + maxWidth={true} + onClose={[MockFunction]} + ownFocus={true} + > + <EuiFlyoutHeader> + <EuiTitle> + <h2> + Endpoint security data + </h2> + </EuiTitle> + <EuiTextColor + color="subdued" + > + <EuiText> + This is a representative sample of the endpoint security alert event that we collect. Endpoint security data is collected only when the Elastic Endpoint is enabled. It includes information about the endpoint configuration and detection events. + </EuiText> + </EuiTextColor> + </EuiFlyoutHeader> + <EuiFlyoutBody> + <EuiCodeBlock + language="js" + > + { + "@timestamp": "2020-09-22T14:34:56.82202300Z", + "agent": { + "build": { + "original": "version: 7.9.1, compiled: Thu Aug 27 14:50:21 2020, branch: 7.9, commit: b594beb958817dee9b9d908191ed766d483df3ea" + }, + "id": "22dd8544-bcac-46cb-b970-5e681bb99e0b", + "type": "endpoint", + "version": "7.9.1" + }, + "Endpoint": { + "policy": { + "applied": { + "artifacts": { + "global": { + "identifiers": [ + { + "sha256": "6a546aade5563d3e8dffc1fe2d93d33edda8f9ca3e17ac3cc9ac707620cb9ecd", + "name": "endpointpe-v4-blocklist" + }, + { + "sha256": "04f9f87accc5d5aea433427bd1bd4ec6908f8528c78ceed26f70df7875a99385", + "name": "endpointpe-v4-exceptionlist" + }, + { + "sha256": "1471838597fcd79a54ea4a3ec9a9beee1a86feaedab6c98e61102559ced822a8", + "name": "endpointpe-v4-model" + }, + { + "sha256": "824859b0c6749cc31951d92a73bbdddfcfe9f38abfe432087934d4dab9766ce8", + "name": "global-exceptionlist-windows" + } + ], + "version": "1.0.0" + }, + "user": { + "identifiers": [ + { + "sha256": "d801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658", + "name": "endpoint-exceptionlist-windows-v1" + } + ], + "version": "1.0.0" + } + } + } + } + }, + "ecs": { + "version": "1.5.0" + }, + "elastic": { + "agent": { + "id": "b2e88aea-2671-402a-828a-957526bac315" + } + }, + "file": { + "path": "C:\\\\Windows\\\\Temp\\\\mimikatz.exe", + "size": 1263880, + "created": "2020-05-19T07:50:06.0Z", + "accessed": "2020-09-22T14:29:19.93531400Z", + "mtime": "2020-09-22T14:29:03.6040000Z", + "directory": "C:\\\\Windows\\\\Temp", + "hash": { + "sha1": "c9fb7f8a4c6b7b12b493a99a8dc6901d17867388", + "sha256": "cb1553a3c88817e4cc774a5a93f9158f6785bd3815447d04b6c3f4c2c4b21ed7", + "md5": "465d5d850f54d9cde767bda90743df30" + }, + "Ext": { + "code_signature": { + "trusted": true, + "subject_name": "Open Source Developer, Benjamin Delpy", + "exists": true, + "status": "trusted" + }, + "malware_classification": { + "identifier": "endpointpe-v4-model", + "score": 0.99956864118576, + "threshold": 0.71, + "version": "0.0.0" + } + } + }, + "host": { + "os": { + "Ext": { + "variant": "Windows 10 Enterprise Evaluation" + }, + "kernel": "2004 (10.0.19041.388)", + "name": "Windows", + "family": "windows", + "version": "2004 (10.0.19041.388)", + "platform": "windows", + "full": "Windows 10 Enterprise Evaluation 2004 (10.0.19041.388)" + } + }, + "event": { + "kind": "alert" + }, + "cluster_uuid": "kLbKvSMcRiiFAR0t8LebDA", + "cluster_name": "elasticsearch" +} + </EuiCodeBlock> + </EuiFlyoutBody> + </EuiFlyout> +</EuiPortal> +`; 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 bed1bbeabb0449..7357598c8495fa 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 @@ -80,15 +80,32 @@ exports[`TelemetryManagementSectionComponent renders as expected 1`] = ` /> </p> <p> - <EuiLink - onClick={[Function]} - > - <FormattedMessage - defaultMessage="See an example of what we collect" - id="telemetry.seeExampleOfWhatWeCollectLinkText" - values={Object {}} - /> - </EuiLink> + <FormattedMessage + defaultMessage="See examples of the {clusterData} and {endpointSecurityData} that we collect." + id="telemetry.seeExamplesOfWhatWeCollect" + values={ + Object { + "clusterData": <EuiLink + onClick={[Function]} + > + <FormattedMessage + defaultMessage="cluster data" + id="telemetry.clusterData" + values={Object {}} + /> + </EuiLink>, + "endpointSecurityData": <EuiLink + onClick={[Function]} + > + <FormattedMessage + defaultMessage="endpoint security data" + id="telemetry.securityData" + values={Object {}} + /> + </EuiLink>, + } + } + /> </p> </React.Fragment>, "displayName": "Provide usage statistics", diff --git a/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx new file mode 100644 index 00000000000000..c80d0daf5a6955 --- /dev/null +++ b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.test.tsx @@ -0,0 +1,27 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from 'react'; +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { OptInSecurityExampleFlyout } from './opt_in_security_example_flyout'; + +describe('security flyout renders as expected', () => { + it('renders as expected', () => { + expect(shallowWithIntl(<OptInSecurityExampleFlyout onClose={jest.fn()} />)).toMatchSnapshot(); + }); +}); diff --git a/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx new file mode 100644 index 00000000000000..af0de5b268ddc5 --- /dev/null +++ b/src/plugins/telemetry_management_section/public/components/opt_in_security_example_flyout.tsx @@ -0,0 +1,235 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; + +import { + EuiCallOut, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiFlyout, + EuiFlyoutHeader, + EuiFlyoutBody, + EuiLoadingSpinner, + EuiPortal, // EuiPortal is a temporary requirement to use EuiFlyout with "ownFocus" + EuiText, + EuiTextColor, + EuiTitle, +} from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n/react'; + +interface Props { + onClose: () => void; +} + +interface State { + isLoading: boolean; + hasPrivilegeToRead: boolean; +} + +/** + * React component for displaying the example data associated with the Telemetry opt-in banner. + */ +export class OptInSecurityExampleFlyout extends React.PureComponent<Props, State> { + public readonly state: State = { + isLoading: true, + hasPrivilegeToRead: false, + }; + + async componentDidMount() { + try { + this.setState({ + isLoading: false, + hasPrivilegeToRead: true, + }); + } catch (err) { + this.setState({ + isLoading: false, + hasPrivilegeToRead: err.status !== 403, + }); + } + } + + renderBody({ isLoading, hasPrivilegeToRead }: State) { + if (isLoading) { + return ( + <EuiFlexGroup justifyContent="spaceAround"> + <EuiFlexItem grow={false}> + <EuiLoadingSpinner size="xl" /> + </EuiFlexItem> + </EuiFlexGroup> + ); + } + + if (!hasPrivilegeToRead) { + return ( + <EuiCallOut + title={ + <FormattedMessage + id="telemetry.callout.errorUnprivilegedUserTitle" + defaultMessage="Error displaying cluster statistics" + /> + } + color="danger" + iconType="cross" + > + <FormattedMessage + id="telemetry.callout.errorUnprivilegedUserDescription" + defaultMessage="You do not have access to see unencrypted cluster statistics." + /> + </EuiCallOut> + ); + } + + return ( + <EuiCodeBlock language="js"> + {JSON.stringify(this.exampleSecurityPayload, null, 2)} + </EuiCodeBlock> + ); + } + + render() { + return ( + <EuiPortal> + <EuiFlyout ownFocus onClose={this.props.onClose} maxWidth={true}> + <EuiFlyoutHeader> + <EuiTitle> + <h2>Endpoint security data</h2> + </EuiTitle> + <EuiTextColor color="subdued"> + <EuiText> + This is a representative sample of the endpoint security alert event that we + collect. Endpoint security data is collected only when the Elastic Endpoint is + enabled. It includes information about the endpoint configuration and detection + events. + </EuiText> + </EuiTextColor> + </EuiFlyoutHeader> + <EuiFlyoutBody>{this.renderBody(this.state)}</EuiFlyoutBody> + </EuiFlyout> + </EuiPortal> + ); + } + + exampleSecurityPayload = { + '@timestamp': '2020-09-22T14:34:56.82202300Z', + agent: { + build: { + original: + 'version: 7.9.1, compiled: Thu Aug 27 14:50:21 2020, branch: 7.9, commit: b594beb958817dee9b9d908191ed766d483df3ea', + }, + id: '22dd8544-bcac-46cb-b970-5e681bb99e0b', + type: 'endpoint', + version: '7.9.1', + }, + Endpoint: { + policy: { + applied: { + artifacts: { + global: { + identifiers: [ + { + sha256: '6a546aade5563d3e8dffc1fe2d93d33edda8f9ca3e17ac3cc9ac707620cb9ecd', + name: 'endpointpe-v4-blocklist', + }, + { + sha256: '04f9f87accc5d5aea433427bd1bd4ec6908f8528c78ceed26f70df7875a99385', + name: 'endpointpe-v4-exceptionlist', + }, + { + sha256: '1471838597fcd79a54ea4a3ec9a9beee1a86feaedab6c98e61102559ced822a8', + name: 'endpointpe-v4-model', + }, + { + sha256: '824859b0c6749cc31951d92a73bbdddfcfe9f38abfe432087934d4dab9766ce8', + name: 'global-exceptionlist-windows', + }, + ], + version: '1.0.0', + }, + user: { + identifiers: [ + { + sha256: 'd801aa1fb7ddcc330a5e3173372ea6af4a3d08ec58074478e85aa5603e926658', + name: 'endpoint-exceptionlist-windows-v1', + }, + ], + version: '1.0.0', + }, + }, + }, + }, + }, + ecs: { + version: '1.5.0', + }, + elastic: { + agent: { + id: 'b2e88aea-2671-402a-828a-957526bac315', + }, + }, + file: { + path: 'C:\\Windows\\Temp\\mimikatz.exe', + size: 1263880, + created: '2020-05-19T07:50:06.0Z', + accessed: '2020-09-22T14:29:19.93531400Z', + mtime: '2020-09-22T14:29:03.6040000Z', + directory: 'C:\\Windows\\Temp', + hash: { + sha1: 'c9fb7f8a4c6b7b12b493a99a8dc6901d17867388', + sha256: 'cb1553a3c88817e4cc774a5a93f9158f6785bd3815447d04b6c3f4c2c4b21ed7', + md5: '465d5d850f54d9cde767bda90743df30', + }, + Ext: { + code_signature: { + trusted: true, + subject_name: 'Open Source Developer, Benjamin Delpy', + exists: true, + status: 'trusted', + }, + malware_classification: { + identifier: 'endpointpe-v4-model', + score: 0.99956864118576, + threshold: 0.71, + version: '0.0.0', + }, + }, + }, + host: { + os: { + Ext: { + variant: 'Windows 10 Enterprise Evaluation', + }, + kernel: '2004 (10.0.19041.388)', + name: 'Windows', + family: 'windows', + version: '2004 (10.0.19041.388)', + platform: 'windows', + full: 'Windows 10 Enterprise Evaluation 2004 (10.0.19041.388)', + }, + }, + event: { + kind: 'alert', + }, + cluster_uuid: 'kLbKvSMcRiiFAR0t8LebDA', + cluster_name: 'elasticsearch', + }; +} diff --git a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx index 0e2855f055540a..993295746ea5ba 100644 --- a/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx +++ b/src/plugins/telemetry_management_section/public/components/telemetry_management_section.test.tsx @@ -212,7 +212,7 @@ describe('TelemetryManagementSectionComponent', () => { /> ); try { - const toggleExampleComponent = component.find('p > EuiLink[onClick]'); + const toggleExampleComponent = component.find('FormattedMessage > EuiLink[onClick]').at(0); const updatedView = toggleExampleComponent.simulate('click'); updatedView.find('OptInExampleFlyout'); updatedView.simulate('close'); @@ -221,6 +221,42 @@ describe('TelemetryManagementSectionComponent', () => { } }); + it('shows the OptInSecurityExampleFlyout', () => { + const onQueryMatchChange = jest.fn(); + const telemetryService = new TelemetryService({ + config: { + enabled: true, + url: '', + banner: true, + allowChangingOptInStatus: true, + optIn: false, + optInStatusUrl: '', + sendUsageFrom: 'browser', + }, + reportOptInStatusChange: false, + notifications: coreStart.notifications, + http: coreSetup.http, + }); + + const component = mountWithIntl( + <TelemetryManagementSection + telemetryService={telemetryService} + onQueryMatchChange={onQueryMatchChange} + showAppliesSettingMessage={false} + enableSaving={true} + toasts={coreStart.notifications.toasts} + /> + ); + try { + const toggleExampleComponent = component.find('FormattedMessage > EuiLink[onClick]').at(1); + const updatedView = toggleExampleComponent.simulate('click'); + updatedView.find('OptInSecurityExampleFlyout'); + updatedView.simulate('close'); + } finally { + component.unmount(); + } + }); + it('toggles the OptIn button', async () => { const onQueryMatchChange = jest.fn(); const telemetryService = new TelemetryService({ 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 9ae0a3d12fbb52..822d8b49661c1d 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 @@ -34,6 +34,7 @@ import { i18n } from '@kbn/i18n'; import { TelemetryPluginSetup } from 'src/plugins/telemetry/public'; import { PRIVACY_STATEMENT_URL } from '../../../telemetry/common/constants'; import { OptInExampleFlyout } from './opt_in_example_flyout'; +import { OptInSecurityExampleFlyout } from './opt_in_security_example_flyout'; import { LazyField } from '../../../advanced_settings/public'; import { ToastsStart } from '../../../../core/public'; @@ -53,6 +54,7 @@ interface Props { interface State { processing: boolean; showExample: boolean; + showSecurityExample: boolean; queryMatches: boolean | null; enabled: boolean; } @@ -61,6 +63,7 @@ export class TelemetryManagementSection extends Component<Props, State> { state: State = { processing: false, showExample: false, + showSecurityExample: false, queryMatches: null, enabled: this.props.telemetryService.getIsOptedIn() || false, }; @@ -87,7 +90,7 @@ export class TelemetryManagementSection extends Component<Props, State> { render() { const { telemetryService } = this.props; - const { showExample, queryMatches, enabled, processing } = this.state; + const { showExample, showSecurityExample, queryMatches, enabled, processing } = this.state; if (!telemetryService.getCanChangeOptInStatus()) { return null; @@ -105,6 +108,7 @@ export class TelemetryManagementSection extends Component<Props, State> { onClose={this.toggleExample} /> )} + {showSecurityExample && <OptInSecurityExampleFlyout onClose={this.toggleSecurityExample} />} <EuiPanel paddingSize="l"> <EuiForm> <EuiText> @@ -197,12 +201,25 @@ export class TelemetryManagementSection extends Component<Props, State> { /> </p> <p> - <EuiLink onClick={this.toggleExample}> - <FormattedMessage - id="telemetry.seeExampleOfWhatWeCollectLinkText" - defaultMessage="See an example of what we collect" - /> - </EuiLink> + <FormattedMessage + id="telemetry.seeExamplesOfWhatWeCollect" + defaultMessage="See examples of the {clusterData} and {endpointSecurityData} that we collect." + values={{ + clusterData: ( + <EuiLink onClick={this.toggleExample}> + <FormattedMessage id="telemetry.clusterData" defaultMessage="cluster data" /> + </EuiLink> + ), + endpointSecurityData: ( + <EuiLink onClick={this.toggleSecurityExample}> + <FormattedMessage + id="telemetry.securityData" + defaultMessage="endpoint security data" + /> + </EuiLink> + ), + }} + /> </p> </Fragment> ); @@ -245,6 +262,12 @@ export class TelemetryManagementSection extends Component<Props, State> { showExample: !this.state.showExample, }); }; + + toggleSecurityExample = () => { + this.setState({ + showSecurityExample: !this.state.showSecurityExample, + }); + }; } // required for lazy loading diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index bd9a66b48f6332..e344d18213ae59 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -2962,7 +2962,6 @@ "telemetry.provideUsageStatisticsAriaName": "使用統計を提供", "telemetry.provideUsageStatisticsTitle": "使用統計を提供", "telemetry.readOurUsageDataPrivacyStatementLinkText": "プライバシーポリシー", - "telemetry.seeExampleOfWhatWeCollectLinkText": "収集されるデータの例を見る", "telemetry.telemetryBannerDescription": "Elastic Stackの改善にご協力ください使用状況データの収集は現在無効です。使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。", "telemetry.telemetryConfigAndLinkDescription": "使用状況データの収集を有効にすると、製品とサービスを管理して改善することができます。詳細については、{privacyStatementLink}をご覧ください。", "telemetry.telemetryConfigDescription": "基本的な機能の利用状況に関する統計情報を提供して、Elastic Stack の改善にご協力ください。このデータは Elastic 社外と共有されません。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 3ce92782340058..ab7b558afbbf21 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -2963,7 +2963,6 @@ "telemetry.provideUsageStatisticsAriaName": "提供使用情况统计", "telemetry.provideUsageStatisticsTitle": "提供使用情况统计", "telemetry.readOurUsageDataPrivacyStatementLinkText": "隐私声明", - "telemetry.seeExampleOfWhatWeCollectLinkText": "查看我们收集的内容示例", "telemetry.telemetryBannerDescription": "想帮助我们改进 Elastic Stack?数据使用情况收集当前已禁用。启用数据使用情况收集可帮助我们管理并改善产品和服务。有关详情,请参阅我们的{privacyStatementLink}。", "telemetry.telemetryConfigAndLinkDescription": "启用使用情况数据收集可帮助我们管理并改善产品和服务。有关更多详情,请参阅我们的{privacyStatementLink}。", "telemetry.telemetryConfigDescription": "通过提供基本功能的使用情况统计信息,来帮助我们改进 Elastic Stack。我们不会在 Elastic 之外共享此数据。", From 94ef651d7b03f0d31c1b704a66cdb9191e094d88 Mon Sep 17 00:00:00 2001 From: PavithraCP <31021423+PavithraCP@users.noreply.github.com> Date: Fri, 2 Oct 2020 07:16:46 -0400 Subject: [PATCH 072/128] [Lens]Do not enable histogram mode for multiple un-stacked bar series (#78525) --- .../xy_visualization/expression.test.tsx | 50 ++++++++++++++++++- .../public/xy_visualization/expression.tsx | 14 +++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index 3bd6cc73d6320a..5fc89d831a961c 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -896,7 +896,12 @@ describe('xy_expression', () => { test('it applies histogram mode to the series for single series', () => { const { data, args } = sampleArgs(); - const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; + const firstLayer: LayerArgs = { + ...args.layers[0], + accessors: ['b'], + seriesType: 'bar', + isHistogram: true, + }; delete firstLayer.splitAccessor; const component = shallow( <XYChart @@ -911,7 +916,48 @@ describe('xy_expression', () => { /> ); expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(true); - expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(true); + }); + + test('it does not apply histogram mode to more than one bar series for unstacked bar chart', () => { + const { data, args } = sampleArgs(); + const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'bar', isHistogram: true }; + delete firstLayer.splitAccessor; + const component = shallow( + <XYChart + data={data} + args={{ ...args, layers: [firstLayer] }} + formatFactory={getFormatSpy} + timeZone="UTC" + chartsThemeService={chartsThemeService} + histogramBarTarget={50} + onClickValue={onClickValue} + onSelectRange={onSelectRange} + /> + ); + expect(component.find(BarSeries).at(0).prop('enableHistogramMode')).toEqual(false); + expect(component.find(BarSeries).at(1).prop('enableHistogramMode')).toEqual(false); + }); + + test('it applies histogram mode to more than one the series for unstacked line/area chart', () => { + const { data, args } = sampleArgs(); + const firstLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true }; + delete firstLayer.splitAccessor; + const secondLayer: LayerArgs = { ...args.layers[0], seriesType: 'line', isHistogram: true }; + delete secondLayer.splitAccessor; + const component = shallow( + <XYChart + data={data} + args={{ ...args, layers: [firstLayer, secondLayer] }} + formatFactory={getFormatSpy} + timeZone="UTC" + chartsThemeService={chartsThemeService} + histogramBarTarget={50} + onClickValue={onClickValue} + onSelectRange={onSelectRange} + /> + ); + expect(component.find(LineSeries).at(0).prop('enableHistogramMode')).toEqual(true); + expect(component.find(LineSeries).at(1).prop('enableHistogramMode')).toEqual(true); }); test('it applies histogram mode to the series for stacked series', () => { diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.tsx index f36525a9a0b146..a59739ec78f7fb 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.tsx @@ -299,6 +299,13 @@ export function XYChart({ yRight: true, }; + const filteredBarLayers = filteredLayers.filter((layer) => layer.seriesType.includes('bar')); + + const chartHasMoreThanOneBarSeries = + filteredBarLayers.length > 1 || + filteredBarLayers.some((layer) => layer.accessors.length > 1) || + filteredBarLayers.some((layer) => layer.splitAccessor); + function calculateMinInterval() { // check all the tables to see if all of the rows have the same timestamp // that would mean that chart will draw a single bar @@ -599,7 +606,12 @@ export function XYChart({ groupId: yAxesConfiguration.find((axisConfiguration) => axisConfiguration.series.find((currentSeries) => currentSeries.accessor === accessor) )?.groupId, - enableHistogramMode: isHistogram && (seriesType.includes('stacked') || !splitAccessor), + enableHistogramMode: + isHistogram && + (seriesType.includes('stacked') || !splitAccessor) && + (seriesType.includes('stacked') || + !seriesType.includes('bar') || + !chartHasMoreThanOneBarSeries), stackMode: seriesType.includes('percentage') ? StackMode.Percentage : undefined, timeZone, areaSeriesStyle: { From 70dac72ad311d595b0f9f6bc871f718656296da6 Mon Sep 17 00:00:00 2001 From: Tim Roes <tim.roes@elastic.co> Date: Fri, 2 Oct 2020 13:39:38 +0200 Subject: [PATCH 073/128] Move legacy plugins to appropriate teams (#79078) * Move legacy plugins to appropriate teams * More cleanup --- .github/CODEOWNERS | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fc9c55e7868f42..d1cf0300b9e17b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -17,6 +17,7 @@ /src/plugins/input_control_vis/ @elastic/kibana-app /src/plugins/management/ @elastic/kibana-app /src/plugins/kibana_legacy/ @elastic/kibana-app +/src/plugins/timelion/ @elastic/kibana-app /src/plugins/vis_default_editor/ @elastic/kibana-app /src/plugins/vis_type_markdown/ @elastic/kibana-app /src/plugins/vis_type_metric/ @elastic/kibana-app @@ -30,32 +31,23 @@ /src/plugins/visualize/ @elastic/kibana-app /src/plugins/visualizations/ @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app -#CC# /src/plugins/vis_type @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/ @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/common/utils @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/migrations @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/public @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-app -#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app #CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app -#CC# /src/legacy/core_plugins/console_legacy @elastic/kibana-app #CC# /src/legacy/core_plugins/input_control_vis @elastic/kibana-app #CC# /src/legacy/core_plugins/timelion @elastic/kibana-app #CC# /src/legacy/core_plugins/vis_type_tagcloud @elastic/kibana-app #CC# /src/legacy/core_plugins/vis_type_vega @elastic/kibana-app #CC# /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app -#CC# /src/legacy/server/sample_data/ @elastic/kibana-app #CC# /src/legacy/server/url_shortening/ @elastic/kibana-app #CC# /src/legacy/ui/public/state_management @elastic/kibana-app -#CC# /src/plugins/charts/public/static/color_maps @elastic/kibana-app #CC# /src/plugins/index_pattern_management/public @elastic/kibana-app -#CC# /src/plugins/input_control_vis/ @elastic/kibana-app -#CC# /src/plugins/kibana_legacy/ @elastic/kibana-app -#CC# /src/plugins/timelion @elastic/kibana-app #CC# /x-pack/legacy/plugins/dashboard_mode/ @elastic/kibana-app #CC# /x-pack/plugins/dashboard_mode @elastic/kibana-app -#CC# /x-pack/plugins/lens/ @elastic/kibana-app # App Architecture /examples/bfetch_explorer/ @elastic/kibana-app-arch @@ -147,6 +139,7 @@ /src/plugins/home/server/services/ @elastic/kibana-core-ui /x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui #CC# /src/legacy/core_plugins/newsfeed @elastic/kibana-core-ui +#CC# /src/legacy/server/sample_data/ @elastic/kibana-core-ui #CC# /src/plugins/newsfeed @elastic/kibana-core-ui #CC# /src/plugins/home/public @elastic/kibana-core-ui #CC# /src/plugins/home/server/services/ @elastic/kibana-core-ui @@ -351,6 +344,8 @@ x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kib /x-pack/plugins/ingest_pipelines/ @elastic/es-ui /packages/kbn-ace/ @elastic/es-ui /packages/kbn-monaco/ @elastic/es-ui +#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/es-ui +#CC# /src/legacy/core_plugins/console_legacy @elastic/es-ui #CC# /x-pack/legacy/plugins/rollup/ @elastic/es-ui #CC# /x-pack/legacy/server/lib/create_router/ @elastic/es-ui #CC# /x-pack/legacy/server/lib/check_license/ @elastic/es-ui From 09b0b6630abd6fe03c0be504a8e7a9ee5fc83e24 Mon Sep 17 00:00:00 2001 From: ymao1 <ying.mao@elastic.co> Date: Fri, 2 Oct 2020 07:40:47 -0400 Subject: [PATCH 074/128] Rearranged PagerDuty action params so non-optional params are at the top (#79026) --- .../pagerduty/pagerduty_params.tsx | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx index 39800865ed761b..32f16760dd461e 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/pagerduty/pagerduty_params.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import React, { Fragment } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiSelect, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ActionParamsProps } from '../../../../types'; import { PagerDutyActionParams } from '.././types'; @@ -143,6 +143,29 @@ const PagerDutyParamsFields: React.FunctionComponent<ActionParamsProps<PagerDuty </EuiFormRow> </EuiFlexItem> </EuiFlexGroup> + <EuiSpacer size="m" /> + <EuiFormRow + id="pagerDutySummary" + fullWidth + error={errors.summary} + isInvalid={errors.summary.length > 0 && summary !== undefined} + label={i18n.translate( + 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel', + { + defaultMessage: 'Summary', + } + )} + > + <TextFieldWithMessageVariables + index={index} + editAction={editAction} + messageVariables={messageVariables} + paramsProperty={'summary'} + inputTargetValue={summary} + errors={errors.summary as string[]} + /> + </EuiFormRow> + <EuiSpacer size="m" /> <EuiFlexGroup> <EuiFlexItem> <EuiFormRow @@ -197,6 +220,7 @@ const PagerDutyParamsFields: React.FunctionComponent<ActionParamsProps<PagerDuty </EuiFormRow> </EuiFlexItem> </EuiFlexGroup> + <EuiSpacer size="m" /> <EuiFormRow fullWidth label={i18n.translate( @@ -248,27 +272,6 @@ const PagerDutyParamsFields: React.FunctionComponent<ActionParamsProps<PagerDuty inputTargetValue={source} /> </EuiFormRow> - <EuiFormRow - id="pagerDutySummary" - fullWidth - error={errors.summary} - isInvalid={errors.summary.length > 0 && summary !== undefined} - label={i18n.translate( - 'xpack.triggersActionsUI.components.builtinActionTypes.pagerDutyAction.summaryFieldLabel', - { - defaultMessage: 'Summary', - } - )} - > - <TextFieldWithMessageVariables - index={index} - editAction={editAction} - messageVariables={messageVariables} - paramsProperty={'summary'} - inputTargetValue={summary} - errors={errors.summary as string[]} - /> - </EuiFormRow> <EuiFormRow id="pagerDutyClass" fullWidth From b01140fc2e38848f522fe317725718671b0a759d Mon Sep 17 00:00:00 2001 From: MadameSheema <snootchie.boochies@gmail.com> Date: Fri, 2 Oct 2020 13:52:03 +0200 Subject: [PATCH 075/128] improves eql test (#79014) Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../alerts_detection_rules_eql.spec.ts | 25 +++++++++++++++++++ .../security_solution/cypress/objects/rule.ts | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts index e2ff51dd544a2d..ca7832603f13d2 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_eql.spec.ts @@ -6,6 +6,14 @@ import { eqlRule, indexPatterns } from '../objects/rule'; +import { + ALERT_RULE_METHOD, + ALERT_RULE_NAME, + ALERT_RULE_RISK_SCORE, + ALERT_RULE_SEVERITY, + ALERT_RULE_VERSION, + NUMBER_OF_ALERTS, +} from '../screens/alerts'; import { CUSTOM_RULES_BTN, RISK_SCORE, @@ -59,9 +67,11 @@ import { fillDefineEqlRuleAndContinue, fillScheduleRuleAndContinue, selectEqlRuleType, + waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; +import { refreshPage } from '../tasks/security_header'; import { DETECTIONS_URL } from '../urls/navigation'; @@ -74,6 +84,7 @@ const expectedMitre = eqlRule.mitre }) .join(''); const expectedNumberOfRules = 1; +const expectedNumberOfAlerts = 7; describe('Detection rules, EQL', () => { before(() => { @@ -146,5 +157,19 @@ describe('Detection rules, EQL', () => { `${eqlRule.lookBack.interval}${eqlRule.lookBack.type}` ); }); + + refreshPage(); + waitForTheRuleToBeExecuted(); + + cy.get(NUMBER_OF_ALERTS) + .invoke('text') + .then((numberOfAlertsText) => { + cy.wrap(parseInt(numberOfAlertsText, 10)).should('eql', expectedNumberOfAlerts); + }); + cy.get(ALERT_RULE_NAME).first().should('have.text', eqlRule.name); + cy.get(ALERT_RULE_VERSION).first().should('have.text', '1'); + cy.get(ALERT_RULE_METHOD).first().should('have.text', 'eql'); + cy.get(ALERT_RULE_SEVERITY).first().should('have.text', eqlRule.severity.toLowerCase()); + cy.get(ALERT_RULE_RISK_SCORE).first().should('have.text', eqlRule.riskScore); }); }); diff --git a/x-pack/plugins/security_solution/cypress/objects/rule.ts b/x-pack/plugins/security_solution/cypress/objects/rule.ts index e84e2b7b1669f1..f375eccd902c48 100644 --- a/x-pack/plugins/security_solution/cypress/objects/rule.ts +++ b/x-pack/plugins/security_solution/cypress/objects/rule.ts @@ -215,7 +215,7 @@ export const machineLearningRule: MachineLearningRule = { }; export const eqlRule: CustomRule = { - customQuery: 'process where process_name == "explorer.exe"', + customQuery: 'any where process.name == "which"', name: 'New EQL Rule', description: 'New EQL rule description.', severity: 'High', From ea6bec6c9bd6ef8912e8eed9d9e8ba77a3505789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= <sorenlouv@gmail.com> Date: Fri, 2 Oct 2020 14:33:33 +0200 Subject: [PATCH 076/128] [APM] Use `history.replace` to preserve back-button functionality (#78978) --- x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts b/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts index cd3e02d1556029..a5096a314388ca 100644 --- a/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts +++ b/x-pack/plugins/apm/public/hooks/useTransactionDistribution.ts @@ -75,7 +75,7 @@ export function useTransactionDistribution(urlParams: IUrlParams) { const preferredSample = maybe(bucketsSortedByCount[0]?.samples[0]); - history.push({ + history.replace({ ...history.location, search: fromQuery({ ...omit(toQuery(history.location.search), [ From b9a79836f8c15652da834be23db1b208180322da Mon Sep 17 00:00:00 2001 From: Larry Gregory <larry.gregory@elastic.co> Date: Fri, 2 Oct 2020 08:45:28 -0400 Subject: [PATCH 077/128] Grouped features for role management (#78152) * Grouped features for role management * address PR feedback Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../security/authorization/index.asciidoc | 38 +- .../roles/__fixtures__/kibana_features.ts | 5 +- .../feature_table/__fixtures__/index.ts | 68 ++- .../feature_table/change_all_privileges.tsx | 20 +- .../kibana/feature_table/feature_table.scss | 5 + .../feature_table/feature_table.test.tsx | 134 +++-- .../kibana/feature_table/feature_table.tsx | 487 ++++++++++-------- .../feature_table_cell.scss | 4 - .../feature_table_cell.test.tsx | 13 +- .../feature_table_cell/feature_table_cell.tsx | 7 +- .../privilege_summary/privilege_summary.tsx | 48 +- .../privilege_summary_table.tsx | 122 ++++- .../space_column_header.test.tsx | 5 +- .../privilege_summary/space_column_header.tsx | 12 +- .../privilege_space_form.test.tsx | 23 +- .../privilege_space_form.tsx | 123 ++--- .../privilege_space_table.tsx | 15 +- .../space_aware_privilege_section.tsx | 4 +- .../customize_space/customize_space.tsx | 2 +- .../enabled_features.test.tsx.snap | 2 - .../enabled_features/enabled_features.tsx | 2 - .../enabled_features/feature_table.scss | 2 +- .../__snapshots__/section_panel.test.tsx.snap | 11 - .../section_panel/section_panel.test.tsx | 23 +- .../section_panel/section_panel.tsx | 66 +-- .../translations/translations/ja-JP.json | 17 - .../translations/translations/zh-CN.json | 17 - .../functional/page_objects/security_page.ts | 5 +- 28 files changed, 630 insertions(+), 650 deletions(-) create mode 100644 x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.scss delete mode 100644 x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss diff --git a/docs/user/security/authorization/index.asciidoc b/docs/user/security/authorization/index.asciidoc index 44ca96e4aece59..3af49753db6640 100644 --- a/docs/user/security/authorization/index.asciidoc +++ b/docs/user/security/authorization/index.asciidoc @@ -2,11 +2,11 @@ [[xpack-security-authorization]] === Granting access to {kib} -The Elastic Stack comes with the `kibana_admin` {ref}/built-in-roles.html[built-in role], which you can use to grant access to all Kibana features in all spaces. To grant users access to a subset of spaces or features, you can create a custom role that grants the desired Kibana privileges. +The Elastic Stack comes with the `kibana_admin` {ref}/built-in-roles.html[built-in role], which you can use to grant access to all {kib} features in all spaces. To grant users access to a subset of spaces or features, you can create a custom role that grants the desired {kib} privileges. -When you assign a user multiple roles, the user receives a union of the roles’ privileges. Therefore, assigning the `kibana_admin` role in addition to a custom role that grants Kibana privileges is ineffective because `kibana_admin` has access to all the features in all spaces. +When you assign a user multiple roles, the user receives a union of the roles’ privileges. Therefore, assigning the `kibana_admin` role in addition to a custom role that grants {kib} privileges is ineffective because `kibana_admin` has access to all the features in all spaces. -NOTE: When running multiple tenants of Kibana by changing the `kibana.index` in your `kibana.yml`, you cannot use `kibana_admin` to grant access. You must create custom roles that authorize the user for that specific tenant. Although multi-tenant installations are supported, the recommended approach to securing access to Kibana segments is to grant users access to specific spaces. +NOTE: When running multiple tenants of {kib} by changing the `kibana.index` in your `kibana.yml`, you cannot use `kibana_admin` to grant access. You must create custom roles that authorize the user for that specific tenant. Although multi-tenant installations are supported, the recommended approach to securing access to {kib} segments is to grant users access to specific spaces. [role="xpack"] [[xpack-kibana-role-management]] @@ -17,26 +17,26 @@ To create a role that grants {kib} privileges, open the menu, go to *Stack Manag [[adding_kibana_privileges]] ==== Adding {kib} privileges -To assign {kib} privileges to the role, click **Add space privilege** in the Kibana section. +To assign {kib} privileges to the role, click **Add {kib} privilege** in the {kib} section. [role="screenshot"] -image::user/security/images/add-space-privileges.png[Add space privileges] +image::user/security/images/add-space-privileges.png[Add {kib} privileges] Open the **Spaces** selection control to specify whether to grant the role access to all spaces *** Global (all spaces)** or one or more individual spaces. If you select *** Global (all spaces)**, you can’t select individual spaces until you clear your selection. Use the **Privilege** menu to grant access to features. The default is **Custom**, which you can use to grant access to individual features. Otherwise, you can grant read and write access to all current and future features by selecting **All**, or grant read access to all current and future features by selecting **Read**. -When using the **Customize by feature** option, you can choose either **All**, **Read** or **None** for access to each feature. As new features are added to Kibana, roles that use the custom option do not automatically get access to the new features. You must manually update the roles. +When using the **Customize by feature** option, you can choose either **All**, **Read** or **None** for access to each feature. As new features are added to {kib}, roles that use the custom option do not automatically get access to the new features. You must manually update the roles. NOTE: *{stack-monitor-app}* relies on built-in roles to grant access. When a user is assigned the appropriate roles, the *{stack-monitor-app}* application is available; otherwise, it is not visible. -To apply your changes, click **Create space privilege**. The space privilege shows up under the Kibana privileges section of the role. +To apply your changes, click **Add {kib} privilege**. The privilege shows up under the {kib} privileges section of the role. [role="screenshot"] -image::user/security/images/create-space-privilege.png[Create space privilege] +image::user/security/images/create-space-privilege.png[Add {kib} privilege] ==== Feature availability @@ -64,9 +64,9 @@ Features are available to users when their roles grant access to the features, * ==== Assigning different privileges to different spaces -Using the same role, it’s possible to assign different privileges to different spaces. After you’ve added space privileges, click **Add space privilege**. If you’ve already added privileges for either *** Global (all spaces)** or an individual space, you will not be able to select these in the **Spaces** selection control. +Using the same role, it’s possible to assign different privileges to different spaces. After you’ve added privileges, click **Add {kib} privilege**. If you’ve already added privileges for either *** Global (all spaces)** or an individual space, you will not be able to select these in the **Spaces** selection control. -Additionally, if you’ve already assigned privileges at *** Global (all spaces)**, you are only able to assign additional privileges to individual spaces. Similar to the behavior of multiple roles granting the union of all privileges, space privileges are also a union. If you’ve already granted the user the **All** privilege at *** Global (all spaces)**, you’re not able to restrict the role to only the **Read** privilege at an individual space. +Additionally, if you’ve already assigned privileges at *** Global (all spaces)**, you are only able to assign additional privileges to individual spaces. Similar to the behavior of multiple roles granting the union of all privileges, {kib} privileges are also a union. If you’ve already granted the user the **All** privilege at *** Global (all spaces)**, you’re not able to restrict the role to only the **Read** privilege at an individual space. ==== Privilege summary @@ -78,39 +78,37 @@ image::user/security/images/view-privilege-summary.png[View privilege summary] ==== Example 1: Grant all access to Dashboard at an individual space -. Click **Add space privilege**. +. Click **Add {kib} privilege**. . For **Spaces**, select an individual space. . For **Privilege**, leave the default selection of **Custom**. . For the Dashboard feature, select **All** -. Click **Create space privilege**. +. Click **Add {kib} privilege**. [role="screenshot"] image::user/security/images/privilege-example-1.png[Privilege example 1] ==== Example 2: Grant all access to one space and read access to another -. Click **Add space privilege**. +. Click **Add {kib} privilege**. . For **Spaces**, select the first space. . For **Privilege**, select **All**. -. Click **Create space privilege**. -. Click **Add space privilege**. +. Click **Add {kib} privilege**. . For **Spaces**, select the second space. . For **Privilege**, select **Read**. -. Click **Create space privilege**. +. Click **Add {kib} privilege**. [role="screenshot"] image::user/security/images/privilege-example-2.png[Privilege example 2] ==== Example 3: Grant read access to all spaces and write access to an individual space -. Click **Add space privilege**. +. Click **Add {kib} privilege**. . For **Spaces**, select *** Global (all spaces)**. . For **Privilege**, select **Read**. -. Click **Create space privilege**. -. Click **Add space privilege**. +. Click **Add {kib} privilege**. . For **Spaces**, select the individual space. . For **Privilege**, select **All**. -. Click **Create space privilege**. +. Click **Add {kib} privilege**. [role="screenshot"] image::user/security/images/privilege-example-3.png[Privilege example 3] diff --git a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts index 1bab51e70a4947..c501ad82954a3d 100644 --- a/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts +++ b/x-pack/plugins/security/public/management/roles/__fixtures__/kibana_features.ts @@ -14,14 +14,15 @@ export const createFeature = ( excludeFromBaseAll?: boolean; excludeFromBaseRead?: boolean; privileges?: KibanaFeatureConfig['privileges']; + category?: KibanaFeatureConfig['category']; } ) => { - const { excludeFromBaseAll, excludeFromBaseRead, privileges, ...rest } = config; + const { excludeFromBaseAll, excludeFromBaseRead, privileges, category, ...rest } = config; return new KibanaFeature({ icon: 'discoverApp', navLinkId: 'discover', app: [], - category: { id: 'foo', label: 'foo' }, + category: category ?? { id: 'foo', label: 'foo' }, catalogue: [], privileges: privileges === null diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts index 9df50b198bde07..7cfa50f6204fb2 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/__fixtures__/index.ts @@ -6,30 +6,37 @@ import { ReactWrapper } from 'enzyme'; -import { - EuiTableRow, - EuiCheckbox, - EuiCheckboxProps, - EuiButtonGroup, - EuiButtonGroupProps, -} from '@elastic/eui'; +import { EuiCheckbox, EuiCheckboxProps, EuiButtonGroup, EuiButtonGroupProps } from '@elastic/eui'; import { findTestSubject } from 'test_utils/find_test_subject'; +import { EuiAccordion } from '@elastic/eui'; import { SubFeatureForm } from '../sub_feature_form'; export function getDisplayedFeaturePrivileges(wrapper: ReactWrapper<any>) { - const allExpanderButtons = findTestSubject(wrapper, 'expandFeaturePrivilegeRow'); + const categoryExpander = findTestSubject(wrapper, 'featureCategoryButton_foo'); + categoryExpander.simulate('click'); + + const allExpanderButtons = findTestSubject(wrapper, 'featureTableCell'); allExpanderButtons.forEach((button) => button.simulate('click')); - // each expanded row renders its own `EuiTableRow`, so there are 2 rows - // for each feature: one for the primary feature privilege, and one for the sub privilege form - const rows = wrapper.find(EuiTableRow); + const featurePrivilegeControls = wrapper + .find(EuiAccordion) + .filter('[data-test-subj="featurePrivilegeControls"]'); + + return featurePrivilegeControls.reduce((acc, featureControls) => { + const buttonGroup = featureControls + .find(EuiButtonGroup) + .filter('[data-test-subj="primaryFeaturePrivilegeControl"]'); + const { name, idSelected } = buttonGroup.props(); + expect(name).toBeDefined(); + expect(idSelected).toBeDefined(); - return rows.reduce((acc, row) => { + const featureId = name!.substr(`featurePrivilege_`.length); + const primaryFeaturePrivilege = idSelected!.substr(`${featureId}_`.length); const subFeaturePrivileges = []; - const subFeatureForm = row.find(SubFeatureForm); + + const subFeatureForm = featureControls.find(SubFeatureForm); if (subFeatureForm.length > 0) { - const { featureId } = subFeatureForm.props(); const independentPrivileges = (subFeatureForm.find(EuiCheckbox) as ReactWrapper< EuiCheckboxProps >).reduce((acc2, checkbox) => { @@ -47,30 +54,15 @@ export function getDisplayedFeaturePrivileges(wrapper: ReactWrapper<any>) { }, [] as string[]); subFeaturePrivileges.push(...independentPrivileges, ...mutuallyExclusivePrivileges); - - return { - ...acc, - [featureId]: { - ...acc[featureId], - subFeaturePrivileges, - }, - }; - } else { - const buttonGroup = row.find(EuiButtonGroup); - const { name, idSelected } = buttonGroup.props(); - expect(name).toBeDefined(); - expect(idSelected).toBeDefined(); - - const featureId = name!.substr(`featurePrivilege_`.length); - const primaryFeaturePrivilege = idSelected!.substr(`${featureId}_`.length); - - return { - ...acc, - [featureId]: { - ...acc[featureId], - primaryFeaturePrivilege, - }, - }; } + + return { + ...acc, + [featureId]: { + ...acc[featureId], + primaryFeaturePrivilege, + subFeaturePrivileges, + }, + }; }, {} as Record<string, { primaryFeaturePrivilege: string; subFeaturePrivileges: string[] }>); } diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx index 14375587c84976..426d9d7bf336ba 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/change_all_privileges.tsx @@ -6,7 +6,14 @@ import './change_all_privileges.scss'; -import { EuiContextMenuItem, EuiContextMenuPanel, EuiLink, EuiPopover } from '@elastic/eui'; +import { + EuiContextMenuItem, + EuiContextMenuPanel, + EuiLink, + EuiPopover, + EuiIcon, + EuiText, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; import React, { Component } from 'react'; @@ -34,10 +41,13 @@ export class ChangeAllPrivilegesControl extends Component<Props, State> { className={'secPrivilegeFeatureChangeAllLink'} data-test-subj="changeAllPrivilegesButton" > - <FormattedMessage - id="xpack.security.management.editRole.changeAllPrivilegesLink" - defaultMessage="(change all)" - /> + <EuiText size="xs"> + <FormattedMessage + id="xpack.security.management.editRole.changeAllPrivilegesLink" + defaultMessage="Bulk actions" + />{' '} + <EuiIcon size="s" type="arrowDown" /> + </EuiText> </EuiLink> ); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.scss new file mode 100644 index 00000000000000..e5c026d317034e --- /dev/null +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.scss @@ -0,0 +1,5 @@ +.subFeaturePrivilegeExpandedRegion { + background-color: $euiColorLightestShade; + padding-left: $euiSizeXXL; + padding-top: $euiSizeS; +} \ No newline at end of file diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx index 02d692bf9f5070..002b13609005a5 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.test.tsx @@ -13,7 +13,7 @@ import { createKibanaPrivileges } from '../../../../__fixtures__/kibana_privileg import { PrivilegeFormCalculator } from '../privilege_form_calculator'; import { getDisplayedFeaturePrivileges } from './__fixtures__'; import { findTestSubject } from 'test_utils/find_test_subject'; -import { FeatureTableExpandedRow } from './feature_table_expanded_row'; +import { EuiAccordion } from '@elastic/eui'; const createRole = (kibana: Role['kibana'] = []): Role => { return { @@ -86,18 +86,19 @@ describe('FeatureTable', () => { expect(displayedPrivileges).toEqual({ excluded_from_base: { primaryFeaturePrivilege: 'none', - ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}), + subFeaturePrivileges: [], }, no_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'none', - ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}), + subFeaturePrivileges: [], }, with_sub_features: { primaryFeaturePrivilege: 'none', - ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}), + subFeaturePrivileges: [], }, }); }); @@ -125,14 +126,15 @@ describe('FeatureTable', () => { expect(displayedPrivileges).toEqual({ excluded_from_base: { primaryFeaturePrivilege: 'none', - ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}), + subFeaturePrivileges: [], }, no_sub_features: { primaryFeaturePrivilege: 'all', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'all', - ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}), + subFeaturePrivileges: [], }, with_sub_features: { primaryFeaturePrivilege: 'all', @@ -144,7 +146,7 @@ describe('FeatureTable', () => { 'cool_all', ], } - : {}), + : { subFeaturePrivileges: [] }), }, }); }); @@ -175,14 +177,15 @@ describe('FeatureTable', () => { expect(displayedPrivileges).toEqual({ excluded_from_base: { primaryFeaturePrivilege: 'none', - ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}), + subFeaturePrivileges: [], }, no_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'none', - ...(canCustomizeSubFeaturePrivileges ? { subFeaturePrivileges: [] } : {}), + subFeaturePrivileges: [], }, with_sub_features: { primaryFeaturePrivilege: 'all', @@ -194,7 +197,7 @@ describe('FeatureTable', () => { 'cool_all', ], } - : {}), + : { subFeaturePrivileges: [] }), }, }); }); @@ -279,6 +282,7 @@ describe('FeatureTable', () => { }, no_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'none', @@ -313,43 +317,18 @@ describe('FeatureTable', () => { }); kibanaFeatures.forEach((feature) => { - const rowExpander = findTestSubject(wrapper, `expandFeaturePrivilegeRow-${feature.id}`); + const { arrowDisplay } = wrapper + .find(EuiAccordion) + .filter(`#featurePrivilegeControls_${feature.id}`) + .props(); if (!feature.subFeatures || feature.subFeatures.length === 0) { - expect(rowExpander).toHaveLength(0); + expect(arrowDisplay).toEqual('none'); } else { - expect(rowExpander).toHaveLength(1); + expect(arrowDisplay).toEqual('left'); } }); }); - it('renders the <FeatureTableExpandedRow> when the row is expanded', () => { - const role = createRole([ - { - spaces: ['*'], - base: ['read'], - feature: {}, - }, - { - spaces: ['foo'], - base: [], - feature: {}, - }, - ]); - const { wrapper } = setup({ - role, - features: kibanaFeatures, - privilegeIndex: 1, - calculateDisplayedPrivileges: false, - canCustomizeSubFeaturePrivileges: true, - }); - - expect(wrapper.find(FeatureTableExpandedRow)).toHaveLength(0); - - findTestSubject(wrapper, 'expandFeaturePrivilegeRow').first().simulate('click'); - - expect(wrapper.find(FeatureTableExpandedRow)).toHaveLength(1); - }); - it('renders with sub-feature privileges granted when primary feature privilege is "all"', () => { const role = createRole([ { @@ -679,6 +658,7 @@ describe('FeatureTable', () => { }, no_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'none', @@ -716,15 +696,19 @@ describe('FeatureTable', () => { expect(displayedPrivileges).toEqual({ excluded_from_base: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, no_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, }); }); @@ -750,15 +734,19 @@ describe('FeatureTable', () => { expect(displayedPrivileges).toEqual({ excluded_from_base: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, no_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, }); }); @@ -843,6 +831,7 @@ describe('FeatureTable', () => { expect(displayedPrivileges).toEqual({ reserved_feature: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, }); }); @@ -873,16 +862,79 @@ describe('FeatureTable', () => { expect(displayedPrivileges).toEqual({ excluded_from_base: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, no_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_excluded_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, with_sub_features: { primaryFeaturePrivilege: 'none', + subFeaturePrivileges: [], }, }); }); + + it('renders features by category, indicating how many features are granted within', async () => { + const role = createRole([ + { + spaces: ['foo'], + base: [], + feature: { + feature_1: ['all'], + feature_3: ['all'], + feature_4: ['all'], + }, + }, + ]); + + const features = [ + createFeature({ + id: 'feature_1', + name: 'Feature1', + category: { id: 'foo', label: 'foo' }, + }), + createFeature({ + id: 'feature_2', + name: 'Feature2', + category: { id: 'foo', label: 'foo' }, + }), + createFeature({ + id: 'feature_3', + name: 'Feature3', + category: { id: 'bar', label: 'bar' }, + }), + createFeature({ + id: 'feature_4', + name: 'Feature4', + category: { id: 'bar', label: 'bar' }, + }), + ]; + + const { wrapper } = setup({ + role, + features, + privilegeIndex: 0, + calculateDisplayedPrivileges: false, + canCustomizeSubFeaturePrivileges: false, + }); + + const fooCategory = findTestSubject(wrapper, 'featureCategory_foo'); + const barCategory = findTestSubject(wrapper, 'featureCategory_bar'); + + expect(fooCategory).toHaveLength(1); + expect(barCategory).toHaveLength(1); + + expect(findTestSubject(fooCategory, 'categoryLabel').text()).toMatchInlineSnapshot( + `"1 / 2 features granted"` + ); + + expect(findTestSubject(barCategory, 'categoryLabel').text()).toMatchInlineSnapshot( + `"2 / 2 features granted"` + ); + }); }); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx index 57e24f28382262..a07c2e1c14ac43 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table/feature_table.tsx @@ -5,24 +5,31 @@ */ import { + EuiAccordionProps, EuiButtonGroup, EuiIconTip, - EuiInMemoryTable, EuiText, - EuiButtonIcon, EuiFlexGroup, EuiFlexItem, + EuiSpacer, + EuiCallOut, + EuiHorizontalRule, + EuiAccordion, + EuiIcon, + EuiTitle, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import React, { Component } from 'react'; +import React, { Component, ReactElement } from 'react'; +import { AppCategory } from 'kibana/public'; import { Role } from '../../../../../../../common/model'; import { ChangeAllPrivilegesControl } from './change_all_privileges'; import { FeatureTableExpandedRow } from './feature_table_expanded_row'; import { NO_PRIVILEGE_VALUE } from '../constants'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; import { FeatureTableCell } from '../feature_table_cell'; -import { KibanaPrivileges, SecuredFeature, KibanaPrivilege } from '../../../../model'; +import { KibanaPrivileges, SecuredFeature } from '../../../../model'; +import './feature_table.scss'; interface Props { role: Role; @@ -35,250 +42,296 @@ interface Props { disabled?: boolean; } -interface State { - expandedFeatures: string[]; -} - -interface TableRow { - featureId: string; - feature: SecuredFeature; - inherited: KibanaPrivilege[]; - effective: KibanaPrivilege[]; - role: Role; -} - -export class FeatureTable extends Component<Props, State> { +export class FeatureTable extends Component<Props, {}> { public static defaultProps = { privilegeIndex: -1, showLocks: true, }; + private featureCategories: Map<string, SecuredFeature[]> = new Map(); + constructor(props: Props) { super(props); - this.state = { - expandedFeatures: [], - }; + + // features are static for the lifetime of the page, so this is safe to do here in a non-reactive manner + props.kibanaPrivileges + .getSecuredFeatures() + .filter((feature) => feature.privileges != null || feature.reserved != null) + .forEach((feature) => { + if (!this.featureCategories.has(feature.category.id)) { + this.featureCategories.set(feature.category.id, []); + } + this.featureCategories.get(feature.category.id)!.push(feature); + }); } public render() { - const { role, kibanaPrivileges } = this.props; + const basePrivileges = this.props.kibanaPrivileges.getBasePrivileges( + this.props.role.kibana[this.props.privilegeIndex] + ); - const featurePrivileges = kibanaPrivileges - .getSecuredFeatures() - .filter((feature) => feature.privileges != null || feature.reserved != null); + const accordions: Array<{ order: number; element: ReactElement }> = []; + this.featureCategories.forEach((featuresInCategory) => { + const { category } = featuresInCategory[0]; - const items: TableRow[] = featurePrivileges - .sort((feature1, feature2) => { - if (feature1.reserved && !feature2.reserved) { - return 1; - } + const featureCount = featuresInCategory.length; + const grantedCount = featuresInCategory.filter( + (feature) => + this.props.privilegeCalculator.getEffectivePrimaryFeaturePrivilege( + feature.id, + this.props.privilegeIndex + ) != null + ).length; + + const canExpandCategory = true; // featuresInCategory.length > 1; + + const buttonContent = ( + <EuiFlexGroup + data-test-subj={`featureCategoryButton_${category.id}`} + alignItems={'center'} + responsive={false} + gutterSize="m" + > + {category.euiIconType ? ( + <EuiFlexItem grow={false}> + <EuiIcon size="m" type={category.euiIconType} /> + </EuiFlexItem> + ) : null} + <EuiFlexItem grow={1}> + <EuiTitle size="xs"> + <h4 className="eui-displayInlineBlock">{category.label}</h4> + </EuiTitle> + </EuiFlexItem> + </EuiFlexGroup> + ); - if (feature2.reserved && !feature1.reserved) { - return -1; + const label: string = i18n.translate( + 'xpack.security.management.editRole.featureTable.featureAccordionSwitchLabel', + { + defaultMessage: + '{grantedCount} / {featureCount} {featureCount, plural, one {feature} other {features}} granted', + values: { + grantedCount, + featureCount, + }, } + ); + const extraAction = ( + <EuiText size="s" aria-hidden="true" color={'subdued'} data-test-subj="categoryLabel"> + {label} + </EuiText> + ); - return 0; - }) - .map((feature) => { - return { - featureId: feature.id, - feature, - inherited: [], - effective: [], - role, - }; + const helpText = this.getCategoryHelpText(category); + + const accordion = ( + <EuiAccordion + id={`featureCategory_${category.id}`} + data-test-subj={`featureCategory_${category.id}`} + key={category.id} + arrowDisplay={canExpandCategory ? 'left' : 'none'} + forceState={canExpandCategory ? undefined : 'closed'} + buttonContent={buttonContent} + extraAction={canExpandCategory ? extraAction : undefined} + > + <div> + <EuiSpacer size="s" /> + {helpText && ( + <> + <EuiCallOut iconType="iInCircle" size="s"> + {helpText} + </EuiCallOut> + <EuiSpacer size="s" /> + </> + )} + <EuiFlexGroup direction="column" gutterSize="s"> + {featuresInCategory.map((feature) => ( + <EuiFlexItem key={feature.id}> + {this.renderPrivilegeControlsForFeature(feature)} + </EuiFlexItem> + ))} + </EuiFlexGroup> + </div> + </EuiAccordion> + ); + + accordions.push({ + order: category.order ?? Number.MAX_SAFE_INTEGER, + element: accordion, }); + }); + + accordions.sort((a1, a2) => a1.order - a2.order); return ( - <EuiInMemoryTable - responsive={false} - columns={this.getColumns()} - itemId={'featureId'} - itemIdToExpandedRowMap={this.state.expandedFeatures.reduce((acc, featureId) => { - return { - ...acc, - [featureId]: ( - <FeatureTableExpandedRow - feature={featurePrivileges.find((f) => f.id === featureId)!} - privilegeIndex={this.props.privilegeIndex} - onChange={this.props.onChange} - privilegeCalculator={this.props.privilegeCalculator} - selectedFeaturePrivileges={ - this.props.role.kibana[this.props.privilegeIndex].feature[featureId] ?? [] - } - disabled={this.props.disabled} + <div> + <EuiFlexGroup alignItems={'flexEnd'}> + <EuiFlexItem> + <EuiText size="xs"> + <b> + {i18n.translate( + 'xpack.security.management.editRole.featureTable.featureVisibilityTitle', + { + defaultMessage: 'Customize feature privileges', + } + )} + </b> + </EuiText> + </EuiFlexItem> + {!this.props.disabled && ( + <EuiFlexItem grow={false}> + <ChangeAllPrivilegesControl + privileges={basePrivileges} + onChange={this.onChangeAllFeaturePrivileges} /> - ), - }; - }, {})} - items={items} - /> + </EuiFlexItem> + )} + </EuiFlexGroup> + <EuiHorizontalRule margin={'m'} /> + {accordions.flatMap((a, idx) => [ + a.element, + <EuiHorizontalRule key={`accordion-hr-${idx}`} margin={'m'} />, + ])} + </div> ); } - public onChange = (featureId: string) => (featurePrivilegeId: string) => { - const privilege = featurePrivilegeId.substr(`${featureId}_`.length); - if (privilege === NO_PRIVILEGE_VALUE) { - this.props.onChange(featureId, []); - } else { - this.props.onChange(featureId, [privilege]); + private renderPrivilegeControlsForFeature = (feature: SecuredFeature) => { + const renderFeatureMarkup = ( + buttonContent: EuiAccordionProps['buttonContent'], + extraAction: EuiAccordionProps['extraAction'], + warningIcon: JSX.Element + ) => { + const { canCustomizeSubFeaturePrivileges } = this.props; + const hasSubFeaturePrivileges = feature.getSubFeaturePrivileges().length > 0; + + return ( + <EuiFlexGroup gutterSize="s" alignItems="center"> + <EuiFlexItem grow={false}>{warningIcon}</EuiFlexItem> + <EuiFlexItem> + <EuiAccordion + id={`featurePrivilegeControls_${feature.id}`} + data-test-subj="featurePrivilegeControls" + buttonContent={buttonContent} + extraAction={extraAction} + forceState={ + canCustomizeSubFeaturePrivileges && hasSubFeaturePrivileges ? undefined : 'closed' + } + arrowDisplay={ + canCustomizeSubFeaturePrivileges && hasSubFeaturePrivileges ? 'left' : 'none' + } + > + <div className="subFeaturePrivilegeExpandedRegion"> + <FeatureTableExpandedRow + feature={feature} + privilegeIndex={this.props.privilegeIndex} + onChange={this.props.onChange} + privilegeCalculator={this.props.privilegeCalculator} + selectedFeaturePrivileges={ + this.props.role.kibana[this.props.privilegeIndex].feature[feature.id] ?? [] + } + disabled={this.props.disabled} + /> + </div> + </EuiAccordion> + </EuiFlexItem> + </EuiFlexGroup> + ); + }; + + const primaryFeaturePrivileges = feature.getPrimaryFeaturePrivileges(); + + if (feature.reserved && primaryFeaturePrivileges.length === 0) { + const buttonContent = ( + <> + {<EuiIcon type="empty" size="l" />} <FeatureTableCell feature={feature} /> + </> + ); + + const extraAction = ( + <EuiText size={'s'} data-test-subj="reservedFeatureDescription"> + {feature.reserved.description} + </EuiText> + ); + + return renderFeatureMarkup(buttonContent, extraAction, <EuiIcon type="empty" />); } - }; - private getColumns = () => { - const basePrivileges = this.props.kibanaPrivileges.getBasePrivileges( - this.props.role.kibana[this.props.privilegeIndex] + if (primaryFeaturePrivileges.length === 0) { + return null; + } + + const selectedPrivilegeId = this.props.privilegeCalculator.getDisplayedPrimaryFeaturePrivilegeId( + feature.id, + this.props.privilegeIndex ); - const columns = []; - - if (this.props.canCustomizeSubFeaturePrivileges) { - columns.push({ - width: '30px', - isExpander: true, - field: 'featureId', - name: '', - render: (featureId: string, record: TableRow) => { - const { feature } = record; - const hasSubFeaturePrivileges = feature.getSubFeaturePrivileges().length > 0; - if (!hasSubFeaturePrivileges) { - return null; - } - return ( - <EuiButtonIcon - onClick={() => this.toggleExpandedFeature(featureId)} - data-test-subj={`expandFeaturePrivilegeRow expandFeaturePrivilegeRow-${featureId}`} - aria-label={this.state.expandedFeatures.includes(featureId) ? 'Collapse' : 'Expand'} - iconType={this.state.expandedFeatures.includes(featureId) ? 'arrowUp' : 'arrowDown'} - /> - ); - }, - }); - } + const options = primaryFeaturePrivileges.map((privilege) => { + return { + id: `${feature.id}_${privilege.id}`, + label: privilege.name, + isDisabled: this.props.disabled, + }; + }); - columns.push( - { - field: 'feature', - width: '200px', - name: i18n.translate( - 'xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle', - { - defaultMessage: 'Feature', - } - ), - render: (feature: SecuredFeature) => { - return <FeatureTableCell feature={feature} />; - }, - }, - { - field: 'privilege', - width: '200px', - name: ( - <span> + options.push({ + id: `${feature.id}_${NO_PRIVILEGE_VALUE}`, + label: 'None', + isDisabled: this.props.disabled, + }); + + let warningIcon = <EuiIconTip type="empty" content={null} />; + if ( + this.props.privilegeCalculator.hasCustomizedSubFeaturePrivileges( + feature.id, + this.props.privilegeIndex + ) + ) { + warningIcon = ( + <EuiIconTip + type="alert" + content={ <FormattedMessage - id="xpack.security.management.editRole.featureTable.enabledRoleFeaturesEnabledColumnTitle" - defaultMessage="Privilege" + id="xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip" + defaultMessage="Feature has customized sub-feature privileges. Expand this row for more information." /> - {!this.props.disabled && ( - <ChangeAllPrivilegesControl - privileges={basePrivileges} - onChange={this.onChangeAllFeaturePrivileges} - /> - )} - </span> - ), - mobileOptions: { - // Table isn't responsive, so skip rendering this for mobile. <ChangeAllPrivilegesControl /> isn't free... - header: false, - }, - render: (roleEntry: Role, record: TableRow) => { - const { feature } = record; - - const primaryFeaturePrivileges = feature.getPrimaryFeaturePrivileges(); - - if (feature.reserved && primaryFeaturePrivileges.length === 0) { - return ( - <EuiText size={'s'} data-test-subj="reservedFeatureDescription"> - {feature.reserved.description} - </EuiText> - ); } + /> + ); + } - if (primaryFeaturePrivileges.length === 0) { - return null; - } + const { canCustomizeSubFeaturePrivileges } = this.props; + const hasSubFeaturePrivileges = feature.getSubFeaturePrivileges().length > 0; - const selectedPrivilegeId = this.props.privilegeCalculator.getDisplayedPrimaryFeaturePrivilegeId( - feature.id, - this.props.privilegeIndex - ); - - const options = primaryFeaturePrivileges.map((privilege) => { - return { - id: `${feature.id}_${privilege.id}`, - label: privilege.name, - isDisabled: this.props.disabled, - }; - }); - - options.push({ - id: `${feature.id}_${NO_PRIVILEGE_VALUE}`, - label: 'None', - isDisabled: this.props.disabled, - }); - - let warningIcon = <EuiIconTip type="empty" content={null} />; - if ( - this.props.privilegeCalculator.hasCustomizedSubFeaturePrivileges( - feature.id, - this.props.privilegeIndex - ) - ) { - warningIcon = ( - <EuiIconTip - type="iInCircle" - content={ - <FormattedMessage - id="xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip" - defaultMessage="Feature has customized sub-feature privileges. Expand this row for more information." - /> - } - /> - ); - } + const showAccordionArrow = canCustomizeSubFeaturePrivileges && hasSubFeaturePrivileges; - return ( - <EuiFlexGroup alignItems="center" gutterSize="xs"> - <EuiFlexItem grow={false}>{warningIcon}</EuiFlexItem> - <EuiFlexItem> - <EuiButtonGroup - name={`featurePrivilege_${feature.id}`} - data-test-subj={`primaryFeaturePrivilegeControl`} - buttonSize="compressed" - color={'primary'} - isFullWidth={true} - options={options} - idSelected={`${feature.id}_${selectedPrivilegeId ?? NO_PRIVILEGE_VALUE}`} - onChange={this.onChange(feature.id)} - /> - </EuiFlexItem> - </EuiFlexGroup> - ); - }, - } + const buttonContent = ( + <> + {!showAccordionArrow && <EuiIcon type="empty" size="l" />}{' '} + <FeatureTableCell feature={feature} /> + </> ); - return columns; + + const extraAction = ( + <EuiButtonGroup + name={`featurePrivilege_${feature.id}`} + data-test-subj={`primaryFeaturePrivilegeControl`} + isFullWidth={true} + options={options} + idSelected={`${feature.id}_${selectedPrivilegeId ?? NO_PRIVILEGE_VALUE}`} + onChange={this.onChange(feature.id)} + /> + ); + + return renderFeatureMarkup(buttonContent, extraAction, warningIcon); }; - private toggleExpandedFeature = (featureId: string) => { - if (this.state.expandedFeatures.includes(featureId)) { - this.setState({ - expandedFeatures: this.state.expandedFeatures.filter((ef) => ef !== featureId), - }); + private onChange = (featureId: string) => (featurePrivilegeId: string) => { + const privilege = featurePrivilegeId.substr(`${featureId}_`.length); + if (privilege === NO_PRIVILEGE_VALUE) { + this.props.onChange(featureId, []); } else { - this.setState({ - expandedFeatures: [...this.state.expandedFeatures, featureId], - }); + this.props.onChange(featureId, [privilege]); } }; @@ -289,4 +342,16 @@ export class FeatureTable extends Component<Props, State> { this.props.onChangeAll([privilege]); } }; + + private getCategoryHelpText = (category: AppCategory) => { + if (category.id === 'management') { + return i18n.translate( + 'xpack.security.management.editRole.featureTable.managementCategoryHelpText', + { + defaultMessage: + 'Access to Stack Management is determined by both Elasticsearch and Kibana privileges, and cannot be explicitly disabled.', + } + ); + } + }; } diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss deleted file mode 100644 index a7f24c96a28216..00000000000000 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.scss +++ /dev/null @@ -1,4 +0,0 @@ -.secPrivilegeFeatureIcon { - flex-shrink: 0; - margin-right: $euiSizeS; -} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx index 155e41baeba1e2..1514677c82457d 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.test.tsx @@ -9,10 +9,10 @@ import { createFeature } from '../../../../__fixtures__/kibana_features'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { FeatureTableCell } from '.'; import { SecuredFeature } from '../../../../model'; -import { EuiIcon, EuiIconTip } from '@elastic/eui'; +import { EuiIconTip } from '@elastic/eui'; describe('FeatureTableCell', () => { - it('renders an icon and feature name', () => { + it('renders the feature name', () => { const feature = createFeature({ id: 'test-feature', name: 'Test Feature', @@ -23,13 +23,10 @@ describe('FeatureTableCell', () => { ); expect(wrapper.text()).toMatchInlineSnapshot(`"Test Feature "`); - expect(wrapper.find(EuiIcon).props()).toMatchObject({ - type: feature.icon, - }); expect(wrapper.find(EuiIconTip)).toHaveLength(0); }); - it('renders an icon and feature name with tooltip when configured', () => { + it('renders a feature name with tooltip when configured', () => { const feature = createFeature({ id: 'test-feature', name: 'Test Feature', @@ -41,9 +38,7 @@ describe('FeatureTableCell', () => { ); expect(wrapper.text()).toMatchInlineSnapshot(`"Test Feature "`); - expect(wrapper.find(EuiIcon).first().props()).toMatchObject({ - type: feature.icon, - }); + expect(wrapper.find(EuiIconTip).props().content).toMatchInlineSnapshot(` <EuiText> <p> diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx index 77445952f3d692..869be7f6a583a7 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/feature_table_cell/feature_table_cell.tsx @@ -4,10 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import './feature_table_cell.scss'; - import React from 'react'; -import { EuiText, EuiIconTip, EuiIcon, IconType } from '@elastic/eui'; +import { EuiText, EuiIconTip } from '@elastic/eui'; import { SecuredFeature } from '../../../../model'; interface Props { @@ -35,8 +33,7 @@ export const FeatureTableCell = ({ feature }: Props) => { } return ( - <span> - <EuiIcon size="m" type={feature.icon as IconType} className="secPrivilegeFeatureIcon" /> + <span data-test-subj={`featureTableCell`}> {feature.name} {tooltipElement} </span> ); diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx index e0889d91d759a9..aa37b95ba3f2a0 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary.tsx @@ -6,16 +6,9 @@ import React, { useState, Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - EuiModal, - EuiButtonEmpty, - EuiOverlayMask, - EuiModalHeader, - EuiModalHeaderTitle, - EuiModalBody, - EuiModalFooter, - EuiButton, -} from '@elastic/eui'; +import { EuiButtonEmpty, EuiOverlayMask, EuiButton } from '@elastic/eui'; +import { EuiFlyout } from '@elastic/eui'; +import { EuiFlyoutHeader, EuiTitle, EuiFlyoutBody, EuiFlyoutFooter } from '@elastic/eui'; import { Space } from '../../../../../../../../spaces/common/model/space'; import { Role } from '../../../../../../../common/model'; import { PrivilegeSummaryTable } from './privilege_summary_table'; @@ -30,6 +23,9 @@ interface Props { export const PrivilegeSummary = (props: Props) => { const [isOpen, setIsOpen] = useState(false); + const numberOfPrivilegeDefinitions = props.role.kibana.length; + const flyoutSize = numberOfPrivilegeDefinitions > 5 ? 'l' : 'm'; + return ( <Fragment> <EuiButtonEmpty onClick={() => setIsOpen(true)} data-test-subj="viewPrivilegeSummaryButton"> @@ -39,33 +35,35 @@ export const PrivilegeSummary = (props: Props) => { /> </EuiButtonEmpty> {isOpen && ( - <EuiOverlayMask> - <EuiModal onClose={() => setIsOpen(false)} maxWidth={false}> - <EuiModalHeader> - <EuiModalHeaderTitle> - <FormattedMessage - id="xpack.security.management.editRole.privilegeSummary.modalHeaderTitle" - defaultMessage="Privilege summary" - /> - </EuiModalHeaderTitle> - </EuiModalHeader> - <EuiModalBody> + <EuiOverlayMask headerZindexLocation="below"> + <EuiFlyout onClose={() => setIsOpen(false)} size={flyoutSize}> + <EuiFlyoutHeader> + <EuiTitle size="m"> + <h2> + <FormattedMessage + id="xpack.security.management.editRole.privilegeSummary.modalHeaderTitle" + defaultMessage="Privilege summary" + /> + </h2> + </EuiTitle> + </EuiFlyoutHeader> + <EuiFlyoutBody> <PrivilegeSummaryTable role={props.role} spaces={props.spaces} kibanaPrivileges={props.kibanaPrivileges} canCustomizeSubFeaturePrivileges={props.canCustomizeSubFeaturePrivileges} /> - </EuiModalBody> - <EuiModalFooter> + </EuiFlyoutBody> + <EuiFlyoutFooter> <EuiButton onClick={() => setIsOpen(false)}> <FormattedMessage id="xpack.security.management.editRole.privilegeSummary.closeSummaryButtonText" defaultMessage="Close" /> </EuiButton> - </EuiModalFooter> - </EuiModal> + </EuiFlyoutFooter> + </EuiFlyout> </EuiOverlayMask> )} </Fragment> diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx index 4b5169de3dfc3d..8d24c0b2208634 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/privilege_summary_table.tsx @@ -4,14 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React, { useMemo, useState, Fragment } from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { + EuiFlexGroup, + EuiFlexItem, EuiInMemoryTable, EuiBasicTableColumn, EuiButtonIcon, EuiIcon, EuiIconTip, + EuiSpacer, + EuiAccordion, + EuiTitle, } from '@elastic/eui'; import { Space } from '../../../../../../../../spaces/common/model/space'; import { Role, RoleKibanaPrivilege } from '../../../../../../../common/model'; @@ -39,6 +44,22 @@ function getColumnKey(entry: RoleKibanaPrivilege) { export const PrivilegeSummaryTable = (props: Props) => { const [expandedFeatures, setExpandedFeatures] = useState<string[]>([]); + const featureCategories = useMemo(() => { + const featureCategoryMap = new Map<string, SecuredFeature[]>(); + + props.kibanaPrivileges + .getSecuredFeatures() + .filter((feature) => feature.privileges != null || feature.reserved != null) + .forEach((feature) => { + if (!featureCategoryMap.has(feature.category.id)) { + featureCategoryMap.set(feature.category.id, []); + } + featureCategoryMap.get(feature.category.id)!.push(feature); + }); + + return featureCategoryMap; + }, [props.kibanaPrivileges]); + const calculator = new PrivilegeSummaryCalculator(props.kibanaPrivileges, props.role); const toggleExpandedFeature = (featureId: string) => { @@ -140,35 +161,80 @@ export const PrivilegeSummaryTable = (props: Props) => { }; }, {} as Record<string, EffectiveFeaturePrivileges>); - const items = props.kibanaPrivileges.getSecuredFeatures().map((feature) => { - return { - feature, - featureId: feature.id, - ...privileges, - }; + const accordions: any[] = []; + + featureCategories.forEach((featuresInCategory) => { + const { category } = featuresInCategory[0]; + + const buttonContent = ( + <EuiFlexGroup + data-test-subj={`featureCategoryButton_${category.id}`} + alignItems={'center'} + responsive={false} + gutterSize="m" + > + {category.euiIconType ? ( + <EuiFlexItem grow={false}> + <EuiIcon size="m" type={category.euiIconType} /> + </EuiFlexItem> + ) : null} + <EuiFlexItem grow={1}> + <EuiTitle size="xs"> + <h4 className="eui-displayInlineBlock">{category.label}</h4> + </EuiTitle> + </EuiFlexItem> + </EuiFlexGroup> + ); + + const categoryItems = featuresInCategory.map((feature) => { + return { + feature, + featureId: feature.id, + ...privileges, + }; + }); + + accordions.push( + <EuiAccordion + id={`privilegeSummaryFeatureCategory_${category.id}`} + data-test-subj={`privilegeSummaryFeatureCategory_${category.id}`} + key={category.id} + buttonContent={buttonContent} + initialIsOpen={true} + > + <EuiInMemoryTable + columns={columns} + items={categoryItems} + itemId="featureId" + rowProps={(record) => { + return { + 'data-test-subj': `summaryTableRow-${record.featureId}`, + }; + }} + itemIdToExpandedRowMap={expandedFeatures.reduce((acc, featureId) => { + return { + ...acc, + [featureId]: ( + <PrivilegeSummaryExpandedRow + feature={props.kibanaPrivileges.getSecuredFeature(featureId)} + effectiveFeaturePrivileges={Object.values(privileges).map((p) => p[featureId])} + /> + ), + }; + }, {})} + /> + </EuiAccordion> + ); }); return ( - <EuiInMemoryTable - columns={columns} - items={items} - itemId="featureId" - rowProps={(record) => { - return { - 'data-test-subj': `summaryTableRow-${record.featureId}`, - }; - }} - itemIdToExpandedRowMap={expandedFeatures.reduce((acc, featureId) => { - return { - ...acc, - [featureId]: ( - <PrivilegeSummaryExpandedRow - feature={props.kibanaPrivileges.getSecuredFeature(featureId)} - effectiveFeaturePrivileges={Object.values(privileges).map((p) => p[featureId])} - /> - ), - }; - }, {})} - /> + <> + {accordions.map((a, idx) => ( + <Fragment key={idx}> + {a} + <EuiSpacer /> + </Fragment> + ))} + </> ); }; diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx index b6910565284983..3c9d1789fa5ab2 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.test.tsx @@ -43,7 +43,7 @@ const spaces = [ ]; describe('SpaceColumnHeader', () => { - it('renders the Global privilege definition with a special label and popover control', () => { + it('renders the Global privilege definition with a special label', () => { const wrapper = mountWithIntl( <SpaceColumnHeader spaces={spaces} @@ -55,10 +55,9 @@ describe('SpaceColumnHeader', () => { /> ); - expect(wrapper.find(SpacesPopoverList)).toHaveLength(1); // Snapshot includes space avatar (The first "G"), followed by the "Global" label, // followed by the (all spaces) text as part of the SpacesPopoverList - expect(wrapper.text()).toMatchInlineSnapshot(`"G Global(all spaces)"`); + expect(wrapper.text()).toMatchInlineSnapshot(`"G All Spaces"`); }); it('renders a placeholder space when the requested space no longer exists', () => { diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx index 24ac0022b12af6..a1641577dbb2fb 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/privilege_summary/space_column_header.tsx @@ -39,17 +39,7 @@ export const SpaceColumnHeader = (props: Props) => { <span> <FormattedMessage id="xpack.security.management.editRole.spacePrivilegeMatrix.globalSpaceName" - defaultMessage="Global" - /> - <br /> - <SpacesPopoverList - spaces={props.spaces.filter((s) => s.id !== '*')} - buttonText={i18n.translate( - 'xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink', - { - defaultMessage: '(all spaces)', - } - )} + defaultMessage="All Spaces" /> </span> )} diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx index 32eed6c878016c..e4dc0606ebf67f 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.test.tsx @@ -11,11 +11,11 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; import { PrivilegeSpaceForm } from './privilege_space_form'; import React from 'react'; import { Space } from '../../../../../../../../spaces/public'; -import { EuiSuperSelect } from '@elastic/eui'; import { FeatureTable } from '../feature_table'; import { getDisplayedFeaturePrivileges } from '../feature_table/__fixtures__'; import { findTestSubject } from 'test_utils/find_test_subject'; import { SpaceSelector } from './space_selector'; +import { EuiButtonGroup } from '@elastic/eui'; const createRole = (kibana: Role['kibana'] = []): Role => { return { @@ -59,7 +59,9 @@ describe('PrivilegeSpaceForm', () => { /> ); - expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_custom`); + expect( + wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected + ).toEqual(`basePrivilege_custom`); expect(wrapper.find(FeatureTable).props().disabled).toEqual(true); expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(` Object { @@ -69,6 +71,7 @@ describe('PrivilegeSpaceForm', () => { }, "no_sub_features": Object { "primaryFeaturePrivilege": "none", + "subFeaturePrivileges": Array [], }, "with_excluded_sub_features": Object { "primaryFeaturePrivilege": "none", @@ -106,7 +109,9 @@ describe('PrivilegeSpaceForm', () => { /> ); - expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_all`); + expect( + wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected + ).toEqual(`basePrivilege_all`); expect(wrapper.find(FeatureTable).props().disabled).toEqual(true); expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(` Object { @@ -116,6 +121,7 @@ describe('PrivilegeSpaceForm', () => { }, "no_sub_features": Object { "primaryFeaturePrivilege": "all", + "subFeaturePrivileges": Array [], }, "with_excluded_sub_features": Object { "primaryFeaturePrivilege": "all", @@ -159,7 +165,9 @@ describe('PrivilegeSpaceForm', () => { /> ); - expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_custom`); + expect( + wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected + ).toEqual(`basePrivilege_custom`); expect(wrapper.find(FeatureTable).props().disabled).toEqual(false); expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(` Object { @@ -169,6 +177,7 @@ describe('PrivilegeSpaceForm', () => { }, "no_sub_features": Object { "primaryFeaturePrivilege": "none", + "subFeaturePrivileges": Array [], }, "with_excluded_sub_features": Object { "primaryFeaturePrivilege": "none", @@ -256,7 +265,10 @@ describe('PrivilegeSpaceForm', () => { /> ); - expect(wrapper.find(EuiSuperSelect).props().valueOfSelected).toEqual(`basePrivilege_custom`); + expect( + wrapper.find(EuiButtonGroup).filter('[name="basePrivilegeButtonGroup"]').props().idSelected + ).toEqual(`basePrivilege_custom`); + expect(wrapper.find(FeatureTable).props().disabled).toEqual(false); expect(getDisplayedFeaturePrivileges(wrapper)).toMatchInlineSnapshot(` Object { @@ -266,6 +278,7 @@ describe('PrivilegeSpaceForm', () => { }, "no_sub_features": Object { "primaryFeaturePrivilege": "none", + "subFeaturePrivileges": Array [], }, "with_excluded_sub_features": Object { "primaryFeaturePrivilege": "none", diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx index 6c43f2f7ea7343..28bbd55c7d5447 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_form.tsx @@ -18,7 +18,6 @@ import { EuiFormRow, EuiOverlayMask, EuiSpacer, - EuiSuperSelect, EuiText, EuiTitle, EuiErrorBoundary, @@ -26,6 +25,7 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component, Fragment } from 'react'; +import { EuiButtonGroup } from '@elastic/eui'; import { Space } from '../../../../../../../../spaces/public'; import { Role, copyRole } from '../../../../../../../common/model'; import { SpaceSelector } from './space_selector'; @@ -95,7 +95,7 @@ export class PrivilegeSpaceForm extends Component<Props, State> { <h2> <FormattedMessage id="xpack.security.management.editRole.spacePrivilegeForm.modalTitle" - defaultMessage="Space privileges" + defaultMessage="Kibana privileges" /> </h2> </EuiTitle> @@ -164,6 +164,13 @@ export class PrivilegeSpaceForm extends Component<Props, State> { defaultMessage: 'Spaces', } )} + helpText={i18n.translate( + 'xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormHelpText', + { + defaultMessage: + 'Select one or more Kibana spaces to which you wish to assign privileges.', + } + )} > <SpaceSelector selectedSpaceIds={this.state.selectedSpaceIds} @@ -179,104 +186,46 @@ export class PrivilegeSpaceForm extends Component<Props, State> { label={i18n.translate( 'xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel', { - defaultMessage: 'Privilege', + defaultMessage: 'Privileges for all features', + } + )} + helpText={i18n.translate( + 'xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormHelpText', + { + defaultMessage: + 'Assign the privilege level you wish to grant to all present and future features across this space.', } )} > - <EuiSuperSelect - data-test-subj={'basePrivilegeComboBox'} - fullWidth - onChange={this.onSpaceBasePrivilegeChange} + <EuiButtonGroup + name={`basePrivilegeButtonGroup`} + data-test-subj={`basePrivilegeButtonGroup`} + isFullWidth={true} + color={'primary'} options={[ { - value: 'basePrivilege_custom', - inputDisplay: ( - <EuiText> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDisplay" - defaultMessage="Custom" - /> - </EuiText> - ), - dropdownDisplay: ( - <EuiText> - <strong> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDropdownDisplay" - defaultMessage="Custom" - /> - </strong> - <p> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDetails" - defaultMessage="Customize access by feature in selected spaces." - /> - </p> - </EuiText> - ), + id: 'basePrivilege_all', + label: 'All', + ['data-test-subj']: 'basePrivilege_all', }, { - value: 'basePrivilege_read', - inputDisplay: ( - <EuiText> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDisplay" - defaultMessage="Read" - /> - </EuiText> - ), - dropdownDisplay: ( - <EuiText> - <strong> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDropdownDisplay" - defaultMessage="Read" - /> - </strong> - <p> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDetails" - defaultMessage="Grant read-only access to all features in selected spaces." - /> - </p> - </EuiText> - ), + id: 'basePrivilege_read', + label: 'Read', + ['data-test-subj']: 'basePrivilege_read', }, { - value: 'basePrivilege_all', - inputDisplay: ( - <EuiText> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDisplay" - defaultMessage="All" - /> - </EuiText> - ), - dropdownDisplay: ( - <EuiText> - <strong> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDropdownDisplay" - defaultMessage="All" - /> - </strong> - <p> - <FormattedMessage - id="xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDetails" - defaultMessage="Grant full access to all features in selected spaces." - /> - </p> - </EuiText> - ), + id: 'basePrivilege_custom', + label: 'Customize', + ['data-test-subj']: 'basePrivilege_custom', }, ]} - hasDividers - valueOfSelected={this.getDisplayedBasePrivilege()} - disabled={!hasSelectedSpaces} + idSelected={this.getDisplayedBasePrivilege()} + isDisabled={!hasSelectedSpaces} + onChange={this.onSpaceBasePrivilegeChange} /> </EuiFormRow> - <EuiSpacer size="s" /> + <EuiSpacer /> <EuiTitle size="xxs"> <h3>{this.getFeatureListLabel(this.state.selectedBasePrivilege.length > 0)}</h3> @@ -338,7 +287,7 @@ export class PrivilegeSpaceForm extends Component<Props, State> { buttonText = ( <FormattedMessage id="xpack.security.management.editRolespacePrivilegeForm.createPrivilegeButton" - defaultMessage="Create space privilege" + defaultMessage="Add Kibana privilege" /> ); } diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx index 64b7fe3e2e3a90..6bb9840fd343c5 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/privilege_space_table.tsx @@ -23,7 +23,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { Component } from 'react'; import { Space, getSpaceColor } from '../../../../../../../../spaces/public'; import { FeaturesPrivileges, Role, copyRole } from '../../../../../../../common/model'; -import { SpacesPopoverList } from '../../../spaces_popover_list'; import { PrivilegeDisplay } from './privilege_display'; import { isGlobalPrivilegeDefinition } from '../../../privilege_utils'; import { PrivilegeFormCalculator } from '../privilege_form_calculator'; @@ -118,19 +117,7 @@ export class PrivilegeSpaceTable extends Component<Props, State> { const displayedSpaces = isExpanded ? spaces : spaces.slice(0, SPACES_DISPLAY_COUNT); let button = null; - if (record.isGlobal) { - button = ( - <SpacesPopoverList - spaces={this.props.displaySpaces.filter((s) => s.id !== '*')} - buttonText={i18n.translate( - 'xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink', - { - defaultMessage: 'show spaces', - } - )} - /> - ); - } else if (spaces.length > displayedSpaces.length) { + if (spaces.length > displayedSpaces.length) { button = ( <EuiButtonEmpty size="xs" diff --git a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx index 734f7b1826723b..442e95016c7506 100644 --- a/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx +++ b/x-pack/plugins/security/public/management/roles/edit_role/privileges/kibana/space_aware_privilege_section/space_aware_privilege_section.tsx @@ -50,7 +50,7 @@ export class SpaceAwarePrivilegeSection extends Component<Props, State> { name: i18n.translate( 'xpack.security.management.editRole.spaceAwarePrivilegeForm.globalSpacesName', { - defaultMessage: '* Global (all spaces)', + defaultMessage: '* All Spaces', } ), color: '#D3DAE6', @@ -198,7 +198,7 @@ export class SpaceAwarePrivilegeSection extends Component<Props, State> { > <FormattedMessage id="xpack.security.management.editRole.spacePrivilegeSection.addSpacePrivilegeButton" - defaultMessage="Add space privilege" + defaultMessage="Add Kibana privilege" /> </EuiButton> ); diff --git a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx index 911a6b78a49449..95295b8e64732a 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/customize_space/customize_space.tsx @@ -58,7 +58,7 @@ export class CustomizeSpace extends Component<Props, State> { }; return ( - <SectionPanel collapsible={false} title={panelTitle} description={panelTitle}> + <SectionPanel title={panelTitle} description={panelTitle}> <EuiDescribedFormGroup title={ <EuiTitle size="xs"> diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap index ee1eb7c5e9aba7..063a34091c4e74 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/__snapshots__/enabled_features.test.tsx.snap @@ -2,10 +2,8 @@ exports[`EnabledFeatures renders as expected 1`] = ` <SectionPanel - collapsible={false} data-test-subj="enabled-features-panel" description="Customize visible features" - initiallyCollapsed={false} title={ <span> <FormattedMessage diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx index 5e7629c29bbdd0..7d8516f396e798 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/enabled_features.tsx @@ -34,8 +34,6 @@ export class EnabledFeatures extends Component<Props, {}> { return ( <SectionPanel - collapsible={false} - initiallyCollapsed={false} title={this.getPanelTitle()} description={description} data-test-subj="enabled-features-panel" diff --git a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.scss b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.scss index 4f73349edac205..35b9dc1d45661b 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.scss +++ b/x-pack/plugins/spaces/public/management/edit_space/enabled_features/feature_table.scss @@ -1,4 +1,4 @@ .spcFeatureTableAccordionContent { // Align accordion content with the feature category logo in the accordion's buttonContent padding-left: $euiSizeXL; -} \ No newline at end of file +} diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap b/x-pack/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap index 8d54413cda28c1..b9d054d6309d0c 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap +++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/__snapshots__/section_panel.test.tsx.snap @@ -24,17 +24,6 @@ exports[`it renders without blowing up 1`] = ` </h2> </EuiTitle> </EuiFlexItem> - <EuiFlexItem - grow={false} - > - <EuiLink - aria-label="hide desc" - data-test-subj="show-hide-section-link" - onClick={[Function]} - > - hide - </EuiLink> - </EuiFlexItem> </EuiFlexGroup> <EuiSpacer /> <p> diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx index 0b8085ff1ad168..576881398a63c7 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.test.tsx @@ -4,14 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiLink } from '@elastic/eui'; import React from 'react'; import { mountWithIntl, shallowWithIntl } from 'test_utils/enzyme_helpers'; import { SectionPanel } from './section_panel'; test('it renders without blowing up', () => { const wrapper = shallowWithIntl( - <SectionPanel collapsible iconType="logoElasticsearch" title="Elasticsearch" description="desc"> + <SectionPanel iconType="logoElasticsearch" title="Elasticsearch" description="desc"> <p>child</p> </SectionPanel> ); @@ -19,9 +18,9 @@ test('it renders without blowing up', () => { expect(wrapper).toMatchSnapshot(); }); -test('it renders children by default', () => { +test('it renders children', () => { const wrapper = mountWithIntl( - <SectionPanel collapsible iconType="logoElasticsearch" title="Elasticsearch" description="desc"> + <SectionPanel iconType="logoElasticsearch" title="Elasticsearch" description="desc"> <p className="child">child 1</p> <p className="child">child 2</p> </SectionPanel> @@ -30,19 +29,3 @@ test('it renders children by default', () => { expect(wrapper.find(SectionPanel)).toHaveLength(1); expect(wrapper.find('.child')).toHaveLength(2); }); - -test('it hides children when the "hide" link is clicked', () => { - const wrapper = mountWithIntl( - <SectionPanel collapsible iconType="logoElasticsearch" title="Elasticsearch" description="desc"> - <p className="child">child 1</p> - <p className="child">child 2</p> - </SectionPanel> - ); - - expect(wrapper.find(SectionPanel)).toHaveLength(1); - expect(wrapper.find('.child')).toHaveLength(2); - - wrapper.find(EuiLink).simulate('click'); - - expect(wrapper.find('.child')).toHaveLength(0); -}); diff --git a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx index a6d25511acba67..492932aa957410 100644 --- a/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx +++ b/x-pack/plugins/spaces/public/management/edit_space/section_panel/section_panel.tsx @@ -8,39 +8,20 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, - EuiLink, EuiPanel, EuiSpacer, EuiTitle, IconType, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; import React, { Component, Fragment, ReactNode } from 'react'; interface Props { iconType?: IconType; title: string | ReactNode; description: string; - collapsible: boolean; - initiallyCollapsed?: boolean; } -interface State { - collapsed: boolean; -} - -export class SectionPanel extends Component<Props, State> { - public state = { - collapsed: false, - }; - - constructor(props: Props) { - super(props); - this.state = { - collapsed: props.initiallyCollapsed || false, - }; - } - +export class SectionPanel extends Component<Props, {}> { public render() { return ( <EuiPanel> @@ -51,30 +32,6 @@ export class SectionPanel extends Component<Props, State> { } public getTitle = () => { - const showLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.showLinkText', { - defaultMessage: 'show', - }); - - const hideLinkText = i18n.translate('xpack.spaces.management.collapsiblePanel.hideLinkText', { - defaultMessage: 'hide', - }); - - const showLinkDescription = i18n.translate( - 'xpack.spaces.management.collapsiblePanel.showLinkDescription', - { - defaultMessage: 'show {title}', - values: { title: this.props.description }, - } - ); - - const hideLinkDescription = i18n.translate( - 'xpack.spaces.management.collapsiblePanel.hideLinkDescription', - { - defaultMessage: 'hide {title}', - values: { title: this.props.description }, - } - ); - return ( <EuiFlexGroup alignItems={'baseline'} gutterSize="s" responsive={false}> <EuiFlexItem grow={false}> @@ -93,26 +50,11 @@ export class SectionPanel extends Component<Props, State> { </h2> </EuiTitle> </EuiFlexItem> - {this.props.collapsible && ( - <EuiFlexItem grow={false}> - <EuiLink - data-test-subj="show-hide-section-link" - onClick={this.toggleCollapsed} - aria-label={this.state.collapsed ? showLinkDescription : hideLinkDescription} - > - {this.state.collapsed ? showLinkText : hideLinkText} - </EuiLink> - </EuiFlexItem> - )} </EuiFlexGroup> ); }; public getForm = () => { - if (this.state.collapsed) { - return null; - } - return ( <Fragment> <EuiSpacer /> @@ -120,10 +62,4 @@ export class SectionPanel extends Component<Props, State> { </Fragment> ); }; - - public toggleCollapsed = () => { - this.setState({ - collapsed: !this.state.collapsed, - }); - }; } diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index e344d18213ae59..f144cbd3873f76 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -14302,8 +14302,6 @@ "xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription": "このロールがクラスターに対して実行できる操作を管理します。 ", "xpack.security.management.editRole.elasticSearchPrivileges.runAsPrivilegesTitle": "権限として実行", "xpack.security.management.editRole.featureTable.customizeSubFeaturePrivilegesSwitchLabel": "サブ機能権限をカスタマイズする", - "xpack.security.management.editRole.featureTable.enabledRoleFeaturesEnabledColumnTitle": "権限", - "xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle": "機能", "xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "機能でサブ機能の権限がカスタマイズされています。この行を展開すると詳細が表示されます。", "xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "インデックスの権限を削除", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "提供されたドキュメントのクエリ", @@ -14345,35 +14343,24 @@ "xpack.security.management.editRole.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription": "利用可能なすべてのスペースを表示する権限がありません。", "xpack.security.management.editRole.spaceAwarePrivilegeForm.insufficientPrivilegesDescription": "権限が不十分です", "xpack.security.management.editRole.spaceAwarePrivilegeForm.kibanaAdminTitle": "kibana_admin", - "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDetails": "選択されたスペースの全機能への完全アクセスを許可します。", - "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDisplay": "すべて", - "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDropdownDisplay": "すべて", "xpack.security.management.editRole.spacePrivilegeForm.cancelButton": "キャンセル", "xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivilegeDescription": "機能ごとに権限のレベルを上げます。機能によってはスペースごとに非表示になっているか、グローバルスペース権限による影響を受けているものもあります。", "xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivileges": "機能ごとにカスタマイズ", - "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDetails": "選択されたスペースの機能ごとにアクセスをカスタマイズします", - "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDisplay": "カスタム", - "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDropdownDisplay": "カスタム", "xpack.security.management.editRole.spacePrivilegeForm.featurePrivilegeSummaryDescription": "機能によってはスペースごとに非表示になっているか、グローバルスペース権限による影響を受けているものもあります。", "xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeNotice": "これらの権限はすべての現在および未来のスペースに適用されます。", "xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeWarning": "グローバル権限の作成は他のスペース権限に影響を与える可能性があります。", "xpack.security.management.editRole.spacePrivilegeForm.modalTitle": "スペース権限", "xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel": "権限", - "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDetails": "選択されたスペースの全機能への読み込み専用アクセスを許可します。", - "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDisplay": "読み込み", - "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDropdownDisplay": "読み込み", "xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormLabel": "スペース", "xpack.security.management.editRole.spacePrivilegeForm.summaryOfFeaturePrivileges": "機能権限のサマリー", "xpack.security.management.editRole.spacePrivilegeForm.supersededWarning": "宣言された権限は、構成済みグローバル権限よりも許容度が低くなります。権限サマリーを表示すると有効な権限がわかります。", "xpack.security.management.editRole.spacePrivilegeForm.supersededWarningTitle": "グローバル権限に置き換え", "xpack.security.management.editRole.spacePrivilegeMatrix.globalSpaceName": "グローバル", - "xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink": "(すべてのスペース)", "xpack.security.management.editRole.spacePrivilegeMatrix.showNMoreSpacesLink": "他 {count} 件", "xpack.security.management.editRole.spacePrivilegeSection.addSpacePrivilegeButton": "スペース権限を追加", "xpack.security.management.editRole.spacePrivilegeSection.noAccessToKibanaTitle": "このロールは Kibana へのアクセスを許可しません", "xpack.security.management.editRole.spacePrivilegeTable.deletePrivilegesLabel": "次のスペースの権限を削除: {spaceNames}", "xpack.security.management.editRole.spacePrivilegeTable.editPrivilegesLabel": "次のスペースの権限を編集: {spaceNames}", - "xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink": "スペースを表示", "xpack.security.management.editRole.spacePrivilegeTable.showLessSpacesLink": "縮小表示", "xpack.security.management.editRole.spacePrivilegeTable.showNMoreSpacesLink": "他 {count} 件", "xpack.security.management.editRole.spacePrivilegeTable.supersededPrivilegeWarning": "権限は、構成されたグローバル権限に置き換わります。権限サマリーを表示すると有効な権限がわかります。", @@ -17360,10 +17347,6 @@ "xpack.spaces.management.advancedSettingsSubtitle.applyingSettingsOnPageToSpaceDescription": "このページの設定は、別途指定されていない限り {spaceName}’スペースに適用されます。’", "xpack.spaces.management.advancedSettingsTitle.settingsTitle": "設定", "xpack.spaces.management.breadcrumb": "スペース", - "xpack.spaces.management.collapsiblePanel.hideLinkDescription": "{title} を非表示", - "xpack.spaces.management.collapsiblePanel.hideLinkText": "非表示", - "xpack.spaces.management.collapsiblePanel.showLinkDescription": "{title} を表示", - "xpack.spaces.management.collapsiblePanel.showLinkText": "表示", "xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton": "キャンセル", "xpack.spaces.management.confirmAlterActiveSpaceModal.reloadWarningMessage": "このスペースで表示される機能を更新しました。保存後にページが更新されます。", "xpack.spaces.management.confirmAlterActiveSpaceModal.title": "スペースの更新の確認", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index ab7b558afbbf21..c4a599fa35e7fa 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -14311,8 +14311,6 @@ "xpack.security.management.editRole.elasticSearchPrivileges.manageRoleActionsDescription": "管理此角色可以对您的集群执行的操作。 ", "xpack.security.management.editRole.elasticSearchPrivileges.runAsPrivilegesTitle": "运行身份权限", "xpack.security.management.editRole.featureTable.customizeSubFeaturePrivilegesSwitchLabel": "定制子功能权限", - "xpack.security.management.editRole.featureTable.enabledRoleFeaturesEnabledColumnTitle": "权限", - "xpack.security.management.editRole.featureTable.enabledRoleFeaturesFeatureColumnTitle": "功能", "xpack.security.management.editRole.featureTable.privilegeCustomizationTooltip": "功能已定制子功能权限。展开此行以了解更多信息。", "xpack.security.management.editRole.indexPrivilegeForm.deleteSpacePrivilegeAriaLabel": "删除索引权限", "xpack.security.management.editRole.indexPrivilegeForm.grantedDocumentsQueryFormRowLabel": "已授权文档查询", @@ -14354,35 +14352,24 @@ "xpack.security.management.editRole.spaceAwarePrivilegeForm.howToViewAllAvailableSpacesDescription": "您无权查看所有可用工作区。", "xpack.security.management.editRole.spaceAwarePrivilegeForm.insufficientPrivilegesDescription": "权限不足", "xpack.security.management.editRole.spaceAwarePrivilegeForm.kibanaAdminTitle": "kibana_admin", - "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDetails": "授予对选定工作区所有功能的完全访问权限。", - "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDisplay": "全部", - "xpack.security.management.editRole.spacePrivilegeForm.allPrivilegeDropdownDisplay": "全部", "xpack.security.management.editRole.spacePrivilegeForm.cancelButton": "取消", "xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivilegeDescription": "按功能提高权限级别。某些功能可能被工作区隐藏或受全局工作区权限影响。", "xpack.security.management.editRole.spacePrivilegeForm.customizeFeaturePrivileges": "按功能定制", - "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDetails": "在选定工作区中按功能定制访问权限。", - "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDisplay": "定制", - "xpack.security.management.editRole.spacePrivilegeForm.customPrivilegeDropdownDisplay": "定制", "xpack.security.management.editRole.spacePrivilegeForm.featurePrivilegeSummaryDescription": "某些功能可能被工作区隐藏或受全局工作区权限影响。", "xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeNotice": "这些权限将应用到所有当前和未来工作区。", "xpack.security.management.editRole.spacePrivilegeForm.globalPrivilegeWarning": "创建全局权限可能会影响您的其他工作区权限。", "xpack.security.management.editRole.spacePrivilegeForm.modalTitle": "工作区权限", "xpack.security.management.editRole.spacePrivilegeForm.privilegeSelectorFormLabel": "权限", - "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDetails": "授予对选定工作区所有功能的只读访问权限。", - "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDisplay": "读取", - "xpack.security.management.editRole.spacePrivilegeForm.readPrivilegeDropdownDisplay": "读取", "xpack.security.management.editRole.spacePrivilegeForm.spaceSelectorFormLabel": "工作区", "xpack.security.management.editRole.spacePrivilegeForm.summaryOfFeaturePrivileges": "功能权限的摘要", "xpack.security.management.editRole.spacePrivilegeForm.supersededWarning": "声明的权限相对配置的全局权限有较小的宽容度。查看权限摘要以查看有效的权限。", "xpack.security.management.editRole.spacePrivilegeForm.supersededWarningTitle": "已由全局权限取代", "xpack.security.management.editRole.spacePrivilegeMatrix.globalSpaceName": "全局", - "xpack.security.management.editRole.spacePrivilegeMatrix.showAllSpacesLink": "(所有工作区)", "xpack.security.management.editRole.spacePrivilegeMatrix.showNMoreSpacesLink": "另外 {count} 个", "xpack.security.management.editRole.spacePrivilegeSection.addSpacePrivilegeButton": "添加工作区权限", "xpack.security.management.editRole.spacePrivilegeSection.noAccessToKibanaTitle": "此角色未授予对 Kibana 的访问权限", "xpack.security.management.editRole.spacePrivilegeTable.deletePrivilegesLabel": "删除以下工作区的权限:{spaceNames}。", "xpack.security.management.editRole.spacePrivilegeTable.editPrivilegesLabel": "编辑以下工作区的权限:{spaceNames}。", - "xpack.security.management.editRole.spacePrivilegeTable.showAllSpacesLink": "显示工作区", "xpack.security.management.editRole.spacePrivilegeTable.showLessSpacesLink": "显示更少", "xpack.security.management.editRole.spacePrivilegeTable.showNMoreSpacesLink": "另外 {count} 个", "xpack.security.management.editRole.spacePrivilegeTable.supersededPrivilegeWarning": "权限已由配置的全局权限取代。查看权限摘要以查看有效的权限。", @@ -17370,10 +17357,6 @@ "xpack.spaces.management.advancedSettingsSubtitle.applyingSettingsOnPageToSpaceDescription": "除非已指定,否则此页面上的设置适用于 {spaceName} 空间。", "xpack.spaces.management.advancedSettingsTitle.settingsTitle": "设置", "xpack.spaces.management.breadcrumb": "工作区", - "xpack.spaces.management.collapsiblePanel.hideLinkDescription": "隐藏 {title}", - "xpack.spaces.management.collapsiblePanel.hideLinkText": "隐藏", - "xpack.spaces.management.collapsiblePanel.showLinkDescription": "显示 {title}", - "xpack.spaces.management.collapsiblePanel.showLinkText": "显示", "xpack.spaces.management.confirmAlterActiveSpaceModal.cancelButton": "取消", "xpack.spaces.management.confirmAlterActiveSpaceModal.reloadWarningMessage": "您已更新此工作区中的可见功能。保存后,您的页面将重新加载。", "xpack.spaces.management.confirmAlterActiveSpaceModal.title": "确认更新工作区", diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts index 3ce8a0e681d69d..77457ad94cf88c 100644 --- a/x-pack/test/functional/page_objects/security_page.ts +++ b/x-pack/test/functional/page_objects/security_page.ts @@ -429,10 +429,7 @@ export function SecurityPageProvider({ getService, getPageObjects }: FtrProvider const globalSpaceOption = await find.byCssSelector(`#spaceOption_\\*`); await globalSpaceOption.click(); - await testSubjects.click('basePrivilegeComboBox'); - - const privilegeOption = await find.byCssSelector(`#basePrivilege_${privilegeName}`); - await privilegeOption.click(); + await testSubjects.click(`basePrivilege_${privilegeName}`); await testSubjects.click('createSpacePrivilegeButton'); } From 79eb9b7b7a47c4859020b0b84db911143fbffbb4 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Fri, 2 Oct 2020 08:53:55 -0400 Subject: [PATCH 078/128] Use `process.executable` instead of `process.path` (#79216) --- .../common/endpoint/schema/trusted_apps.test.ts | 4 ++-- .../common/endpoint/schema/trusted_apps.ts | 2 +- .../common/endpoint/types/trusted_apps.ts | 2 +- .../components/condition_entry.tsx | 2 +- .../components/trusted_app_card/index.stories.tsx | 4 ++-- .../routes/trusted_apps/trusted_apps.test.ts | 14 +++++++------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts index 13a3fb96e10f7e..ef1d9a99b0aebe 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts @@ -76,7 +76,7 @@ describe('When invoking Trusted Apps Schema', () => { os: 'windows', entries: [ { - field: 'process.path.text', + field: 'process.executable.text', type: 'match', operator: 'included', value: 'c:/programs files/Anti-Virus', @@ -204,7 +204,7 @@ describe('When invoking Trusted Apps Schema', () => { field: 'process.hash.*', value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476', }, - { field: 'process.path.text', value: '/tmp/dir1' }, + { field: 'process.executable.text', value: '/tmp/dir1' }, ].forEach((partialEntry) => { const bodyMsg3 = { ...getCreateTrustedAppItem(), diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index 912468b52adc04..25456115b37133 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -35,7 +35,7 @@ export const PostTrustedAppCreateRequestSchema = { schema.object({ field: schema.oneOf([ schema.literal('process.hash.*'), - schema.literal('process.path.text'), + schema.literal('process.executable.text'), ]), type: schema.literal('match'), operator: schema.literal('included'), diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts index c0afe3b612d826..75e0347b100780 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts @@ -33,7 +33,7 @@ export interface PostTrustedAppCreateResponse { } export interface MacosLinuxConditionEntry { - field: 'process.hash.*' | 'process.path.text'; + field: 'process.hash.*' | 'process.executable.text'; type: 'match'; operator: 'included'; value: string; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx index 7f7eae18b08163..7d30e81898cf22 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx @@ -83,7 +83,7 @@ export const ConditionEntry = memo<ConditionEntryProps>( 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.path', { defaultMessage: 'Path' } ), - value: 'process.path.text', + value: 'process.executable.text', }, ]; }, []); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx index 713e5e7095e12a..4b64030a702c55 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx @@ -30,7 +30,7 @@ storiesOf('TrustedApps|TrustedAppCard', module) trustedApp.created_at = '2020-09-17T14:52:33.899Z'; trustedApp.entries = [ { - field: 'process.path.text', + field: 'process.executable.text', operator: 'included', type: 'match', value: '/some/path/on/file/system', @@ -44,7 +44,7 @@ storiesOf('TrustedApps|TrustedAppCard', module) trustedApp.created_at = '2020-09-17T14:52:33.899Z'; trustedApp.entries = [ { - field: 'process.path.text', + field: 'process.executable.text', operator: 'included', type: 'match', value: '/some/path/on/file/system', diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts index 98c9b79f32d6b3..9e9a35ea353185 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts @@ -240,7 +240,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { os: 'windows', entries: [ { - field: 'process.path.text', + field: 'process.executable.text', type: 'match', operator: 'included', value: 'c:/programs files/Anti-Virus', @@ -293,7 +293,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { description: 'this one is ok', entries: [ { - field: 'process.path.text', + field: 'process.executable.text', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', @@ -320,7 +320,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { description: 'this one is ok', entries: [ { - field: 'process.path.text', + field: 'process.executable.text', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', @@ -357,7 +357,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { it('should trim condition entry values', async () => { const newTrustedApp = createNewTrustedAppBody(); newTrustedApp.entries.push({ - field: 'process.path.text', + field: 'process.executable.text', value: '\n some value \r\n ', operator: 'included', type: 'match', @@ -366,13 +366,13 @@ describe('when invoking endpoint trusted apps route handlers', () => { await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([ { - field: 'process.path.text', + field: 'process.executable.text', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', }, { - field: 'process.path.text', + field: 'process.executable.text', value: 'some value', operator: 'included', type: 'match', @@ -392,7 +392,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([ { - field: 'process.path.text', + field: 'process.executable.text', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', From 0628cfecf4ef2d0bab6525702d5a98404cd37509 Mon Sep 17 00:00:00 2001 From: Tiago Costa <tiagoffcc@hotmail.com> Date: Fri, 2 Oct 2020 14:30:32 +0100 Subject: [PATCH 079/128] skip flaky suite (#79249) --- .../spaces_only/tests/alerting/execution_status.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts index ac63fe8faadc7c..1c2e51637fb41a 100644 --- a/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts +++ b/x-pack/test/alerting_api_integration/spaces_only/tests/alerting/execution_status.ts @@ -19,7 +19,8 @@ import { FtrProviderContext } from '../../../common/ftr_provider_context'; export default function executionStatusAlertTests({ getService }: FtrProviderContext) { const supertest = getService('supertest'); - describe('executionStatus', () => { + // FLAKY: https://github.com/elastic/kibana/issues/79249 + describe.skip('executionStatus', () => { const objectRemover = new ObjectRemover(supertest); after(async () => await objectRemover.removeAll()); From a7d9e2f481c0d15b8fe6d8cedef881a310c23be7 Mon Sep 17 00:00:00 2001 From: Ryan Keairns <contactryank@gmail.com> Date: Fri, 2 Oct 2020 08:39:37 -0500 Subject: [PATCH 080/128] Improved empty state for nav search (#79123) * Improved empty state for nav search * Updates tests to include required props * Update empty state text --- ...tration_product_no_search_results_dark.svg | 1 + ...ration_product_no_search_results_light.svg | 1 + .../public/components/search_bar.test.tsx | 21 +++++++- .../public/components/search_bar.tsx | 51 ++++++++++++------- .../global_search_bar/public/plugin.tsx | 20 ++++++-- 5 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg create mode 100644 x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg diff --git a/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg new file mode 100644 index 00000000000000..3a87f06b7bcc85 --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_dark.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 140"><defs><style>.cls-1{fill:#20377d;}.cls-2{fill:#294492;}.cls-3{fill:#0a89db;}.cls-4{fill:#1c1e23;}.cls-5{fill:#00bfb3;}.cls-6{fill:#fec514;}.cls-7{fill:#ff957d;}.cls-8{fill:#f04e98;}.cls-9{fill:#7de2d1;}</style></defs><g id="Layer_1" data-name="Layer 1"><path class="cls-1" d="M80.64,19.05a.66.66,0,0,0-.13.76.61.61,0,0,0,.72.08.46.46,0,0,0,.13-.53C81.17,19.05,80.84,18.91,80.64,19.05Z"/><path class="cls-1" d="M80.8,21.08a.36.36,0,0,0-.07.49.72.72,0,0,0,.33.29l.5-.22a1,1,0,0,0,0-.56C81.53,20.87,81,20.85,80.8,21.08Z"/><path class="cls-1" d="M80.31,18c.15.15.39.05.51,0s.11-.24,0-.37-.32-.2-.41-.07S80.16,17.81,80.31,18Z"/><path class="cls-1" d="M81.86,17.28a.51.51,0,0,0,.67.2.77.77,0,0,0,.18-.93.52.52,0,0,0-.66-.07A.54.54,0,0,0,81.86,17.28Z"/><path class="cls-1" d="M85,18.35a.59.59,0,0,0,.2-.88.63.63,0,0,0-.81-.25c-.27.14-.32.63-.11,1A.48.48,0,0,0,85,18.35Z"/><path class="cls-1" d="M82.43,19.18a.8.8,0,0,0,1,.31.83.83,0,0,0,.25-1c-.17-.32-.62-.38-1-.14A.62.62,0,0,0,82.43,19.18Z"/><path class="cls-1" d="M84.52,20.42s0,0,0,0c-.12-.16-.27-.15-.43,0a.38.38,0,0,0-.18.31Z"/><path class="cls-1" d="M76.75,23.79a.25.25,0,0,0,.06-.41.46.46,0,0,0-.34-.14c-.1,0-.28.33-.19.47A.33.33,0,0,0,76.75,23.79Z"/><path class="cls-1" d="M74.9,21.3c.16-.07.08-.3,0-.37a.21.21,0,0,0-.28-.1.23.23,0,0,0-.07.29C74.61,21.19,74.75,21.37,74.9,21.3Z"/><path class="cls-1" d="M74.53,24.76c.1,0,.17-.18.17-.24a.24.24,0,0,0-.17-.19c-.08,0-.16.11-.18.18A.28.28,0,0,0,74.53,24.76Z"/><path class="cls-1" d="M74.7,23.78a.38.38,0,0,0,.41.27.3.3,0,0,0,.31-.26c0-.28-.14-.4-.38-.52C74.85,23.42,74.62,23.51,74.7,23.78Z"/><path class="cls-1" d="M79.91,18.26a.42.42,0,0,0,.06-.34.3.3,0,0,0-.46-.06.64.64,0,0,0-.07.28C79.59,18.34,79.81,18.38,79.91,18.26Z"/><path class="cls-1" d="M75.8,23.28a.56.56,0,0,0,.13-.41c0-.14-.33-.19-.45-.07s-.09.22-.12.29C75.5,23.32,75.71,23.4,75.8,23.28Z"/><path class="cls-1" d="M73.4,21.57c.23.26.64,0,.63-.2s-.34-.43-.51-.34S73.17,21.32,73.4,21.57Z"/><path class="cls-1" d="M73.37,19.17c.12-.09,0-.23,0-.29a.24.24,0,0,0-.23,0,.27.27,0,0,0,0,.24S73.24,19.27,73.37,19.17Z"/><path class="cls-1" d="M77.34,22.59a.66.66,0,0,0,.74.65l.1,0,.24-.12c.16-.17.18-.4.41-.33a.27.27,0,0,1,.12.07l1-.46c-.09-.06-.17-.09-.17-.14s.09-.45-.19-.51c-.58-.14-.56.42-.75.43S78.3,22,78,22,77.3,22.19,77.34,22.59Z"/><path class="cls-1" d="M77.42,20.93c.1.18.51.22.75.08a.45.45,0,0,0,.2-.62.6.6,0,0,0-.76-.25A.64.64,0,0,0,77.42,20.93Z"/><path class="cls-1" d="M79.38,20c.13-.11.08-.42,0-.52a.29.29,0,0,0-.44,0,.23.23,0,0,0,0,.38C79,19.87,79.26,20.08,79.38,20Z"/><path class="cls-1" d="M77.06,19.1c.08-.15.37-.32.28-.5s-.39-.12-.56-.18l-.08.09a.35.35,0,0,0,.09.41A.44.44,0,0,0,77.06,19.1Z"/><path class="cls-1" d="M77.07,19.39s0,.09,0,.1.09.09.14.07,0-.08,0-.13S77.1,19.38,77.07,19.39Z"/><path class="cls-1" d="M73.53,24.37c.08-.14-.18-.58-.39-.53a.37.37,0,0,0-.26.41C72.94,24.46,73.45,24.52,73.53,24.37Z"/><path class="cls-1" d="M65.58,31.11c.13-.09.09-.3-.06-.42s-.32-.13-.41,0-.23.38,0,.52S65.46,31.19,65.58,31.11Z"/><path class="cls-1" d="M60.51,36a.67.67,0,0,0,.06-.81.65.65,0,0,0-.85,0,.67.67,0,0,0,.15.88A.45.45,0,0,0,60.51,36Z"/><path class="cls-1" d="M82.63,21a.32.32,0,0,0,.14-.46.35.35,0,0,0-.5-.14.39.39,0,0,0-.12.52A.36.36,0,0,0,82.63,21Z"/><path class="cls-1" d="M58.44,38.1a.36.36,0,0,0-.36-.26c-.18,0-.22.16-.13.54A.33.33,0,0,0,58.44,38.1Z"/><path class="cls-1" d="M56.15,42.72a.27.27,0,0,0-.42.06.53.53,0,0,0-.08.6l0,0c0,.07,0,.17,0,.24a.08.08,0,0,0,.08,0c.17-.28.36-.56.53-.84A.68.68,0,0,0,56.15,42.72Z"/><path class="cls-1" d="M56.74,41.7a.37.37,0,0,0,0,.39c.11-.18.22-.37.34-.55A.55.55,0,0,0,56.74,41.7Z"/><path class="cls-1" d="M49.84,57.68a.49.49,0,0,0-.13.42c0,.09.13.18.23.22.06-.24.1-.48.16-.72A.37.37,0,0,0,49.84,57.68Z"/><path class="cls-1" d="M73.46,24.64s0,.18,0,.2.19.14.28.05,0-.23,0-.24A.29.29,0,0,0,73.46,24.64Z"/><path class="cls-1" d="M106.1,30.08a.39.39,0,0,0,.2,0s.08-.16.06-.19a.31.31,0,0,0-.23-.08s0,0-.07,0Z"/><path class="cls-1" d="M50.19,53.75a.26.26,0,0,0-.09-.41.44.44,0,0,0-.37,0c-.09.05-.15.4,0,.5A.35.35,0,0,0,50.19,53.75Z"/><path class="cls-1" d="M52,46.87c.12-.13,0-.27-.11-.36a.26.26,0,0,0-.41.07c0,.12-.14.37.05.46S51.91,47,52,46.87Z"/><path class="cls-1" d="M51.56,51.51c-.17.07-.54,0-.79.09s-.62.45-.44.82a.66.66,0,0,0,.92.35c.49-.21.19-.68.54-.69.15-.41.28-.81.44-1.21a.35.35,0,0,0-.11,0C51.52,50.92,51.73,51.43,51.56,51.51Z"/><path class="cls-1" d="M50.75,47.23a.5.5,0,0,0,0,.28c.21.14.43.11.48,0a.45.45,0,0,0,0-.34A.31.31,0,0,0,50.75,47.23Z"/><path class="cls-1" d="M50.76,48.87a.23.23,0,0,0,.11.36c.1.06.4.17.48,0s-.07-.42-.17-.5S50.87,48.71,50.76,48.87Z"/><path class="cls-1" d="M53.05,48.55A.46.46,0,0,0,53,48c-.29-.22-.65-.25-.79,0a.65.65,0,0,0,.14.76A.6.6,0,0,0,53.05,48.55Z"/><path class="cls-1" d="M71.39,25.93a.37.37,0,0,0-.49.06.41.41,0,0,0,0,.53.19.19,0,0,0,.1.05.77.77,0,0,0-.11.83.2.2,0,0,0,.08.08l1-.71a1.34,1.34,0,0,0-.07-.18.62.62,0,0,0-.5-.28A.48.48,0,0,0,71.39,25.93Z"/><path class="cls-1" d="M65.08,26.93c0-.06.12-.21,0-.26a.26.26,0,0,0-.26,0c0,.05-.11.25,0,.3A.22.22,0,0,0,65.08,26.93Z"/><path class="cls-1" d="M65.31,24.29c.11-.11,0-.23-.09-.29a.23.23,0,0,0-.2,0,.29.29,0,0,0,0,.24A.22.22,0,0,0,65.31,24.29Z"/><path class="cls-1" d="M63.56,28.38c-.29.26-.3.45,0,.73s.43.43.64.64c-.37.42-.36.49.06.71.27-.12.3-.32.19-.58.55-.42.46-.81-.21-1.1a.71.71,0,0,1,0-.14c0-.21-.09-.3-.26-.37A.37.37,0,0,0,63.56,28.38Z"/><path class="cls-1" d="M65.42,25.67a.2.2,0,0,0,.3-.08.22.22,0,0,0-.08-.27.19.19,0,0,0-.27,0A.21.21,0,0,0,65.42,25.67Z"/><path class="cls-1" d="M64.13,33.09a.91.91,0,0,0-.91.3.56.56,0,0,0,0,.64Z"/><path class="cls-1" d="M63.75,27.92c.17-.22.1-.36,0-.45s-.3-.14-.38,0a.41.41,0,0,0,0,.39C63.47,28,63.65,27.91,63.75,27.92Z"/><path class="cls-1" d="M53.45,45.83a.76.76,0,0,0-.15-.93.56.56,0,0,0-.55,1A.51.51,0,0,0,53.45,45.83Z"/><path class="cls-1" d="M61.92,32.22a.48.48,0,0,0,.72-.15.63.63,0,0,0-.17-.94.65.65,0,0,0-.84.16A.72.72,0,0,0,61.92,32.22Z"/><path class="cls-1" d="M71.26,24.25a.56.56,0,0,0,.22-.61.37.37,0,0,0-.08-.18.24.24,0,0,0,0-.22c-.07-.08-.27-.27-.45-.14a.19.19,0,0,0-.08.19c-.19,0-.44,0-.53.15s.11.33.15.45C70.6,24.22,71,24.41,71.26,24.25Z"/><path class="cls-1" d="M69,27.19c.09.06.3-.11.32-.17a.27.27,0,0,0-.12-.26.29.29,0,0,0-.27.1A.3.3,0,0,0,69,27.19Z"/><path class="cls-1" d="M66.92,30.25a.8.8,0,0,0,.27.12l.66-.55a.75.75,0,0,0-.29-.67.76.76,0,0,0-.89.17C66.46,29.6,66.57,30,66.92,30.25Z"/><path class="cls-1" d="M68.92,24.41a.38.38,0,0,0,0-.49.33.33,0,0,0-.48,0,.39.39,0,0,0,0,.5A.32.32,0,0,0,68.92,24.41Z"/><path class="cls-1" d="M68.26,27.16a.63.63,0,0,0,0-.84.56.56,0,0,0-.69.19.52.52,0,0,0,.15.68C67.93,27.36,68.12,27.34,68.26,27.16Z"/><path class="cls-1" d="M69.44,28.55l0,0,.57-.44a.49.49,0,0,0-.32-.31.41.41,0,0,0-.3.73Z"/><path class="cls-1" d="M54.93,41.6A.33.33,0,0,0,55,42c.21.24.34.25.67.08.25.07.46.16.68.2a.24.24,0,0,0,.26-.37c-.16-.21-.27-.48-.58-.55l-.33.18C55.36,41.42,55,41.43,54.93,41.6Z"/><path class="cls-1" d="M54.8,40.5a.33.33,0,0,0,.45-.31c0-.11-.25-.2-.4-.28a.14.14,0,0,0-.22.05A.45.45,0,0,0,54.8,40.5Z"/><path class="cls-1" d="M56.34,36.18a.36.36,0,0,0-.36-.26.32.32,0,0,0-.14.54A.33.33,0,0,0,56.34,36.18Z"/><path class="cls-1" d="M54.2,46.47l.1-.17a1,1,0,0,0-.43.3.59.59,0,0,0-.06.69Z"/><path class="cls-1" d="M56.87,39c.07-.09,0-.23-.06-.25s-.16-.11-.25,0a.14.14,0,0,0,0,.21C56.64,39,56.8,39.07,56.87,39Z"/><path class="cls-1" d="M55.08,43.77c.08-.17-.26-.68-.48-.67s-.31.52-.13.68S55,43.93,55.08,43.77Z"/><path class="cls-1" d="M58.91,31.58a.44.44,0,0,0,0-.54.53.53,0,0,0-.67,0,.59.59,0,0,0,0,.72C58.34,31.91,58.68,31.83,58.91,31.58Z"/><path class="cls-1" d="M61.24,29.65c0-.24,0-.47-.23-.56a.32.32,0,0,0-.42.17.45.45,0,0,0,0,.36C60.76,29.86,61,29.8,61.24,29.65Z"/><path class="cls-1" d="M60.46,33.57a.65.65,0,0,0-.06-.81.59.59,0,0,0-.75.1c-.19.21-.14.44.14.67S60.34,33.71,60.46,33.57Z"/><path class="cls-1" d="M57.47,40.76a.33.33,0,0,0,0-.47.29.29,0,0,0-.43.07.27.27,0,0,0,0,.39A.28.28,0,0,0,57.47,40.76Z"/><path class="cls-1" d="M61.84,35.46l.45-.51A.78.78,0,0,0,62,34.5a.37.37,0,0,0-.51.12.52.52,0,0,0,0,.74A.67.67,0,0,0,61.84,35.46Z"/><path class="cls-1" d="M57.47,35.79c0,.07,0,.15-.07.23a.74.74,0,0,0,.28.77.87.87,0,0,0,.84,0,.44.44,0,0,0,.19-.27.68.68,0,0,0-.48-.83L58,35.6a4.28,4.28,0,0,0,.14-.43c.07-.27,0-.39-.25-.5a.55.55,0,0,0-.63.13.48.48,0,0,0-.07.59A2.43,2.43,0,0,0,57.47,35.79Z"/><path class="cls-1" d="M51.91,83.2a.29.29,0,0,0,.33-.27.33.33,0,0,0-.33-.34.3.3,0,0,0-.28.33A.27.27,0,0,0,51.91,83.2Z"/><path class="cls-1" d="M54.58,89.62a.83.83,0,0,0-.74-.72c-.37,0-.64.34-.65.82a.61.61,0,0,0,.61.63A.8.8,0,0,0,54.58,89.62Z"/><path class="cls-1" d="M54.25,85.16a2.81,2.81,0,0,0,0-.58c0-.16-.1-.22-.28-.21s-.11.09-.11.13a.84.84,0,0,1-.23.47.4.4,0,0,0,.08.34.44.44,0,0,0,.35.1C54.14,85.4,54.23,85.25,54.25,85.16Z"/><path class="cls-1" d="M53.73,87.07a.63.63,0,0,0-.62.58c0,.3.38.59.78.58a.48.48,0,0,0,.5-.54A.59.59,0,0,0,53.73,87.07Z"/><path class="cls-1" d="M52.59,86.13c.22,0,.51-.26.5-.44s-.29-.42-.62-.41-.28.14-.29.32S52.39,86.14,52.59,86.13Z"/><path class="cls-1" d="M52.7,84.44a.43.43,0,0,0,.39-.37c0-.17-.26-.44-.4-.43a.53.53,0,0,0-.41.48A.37.37,0,0,0,52.7,84.44Z"/><path class="cls-1" d="M53.32,83.24c0,.28.21.43.55.41s.42-.21.39-.55-.15-.4-.34-.4A.81.81,0,0,0,53.32,83.24Z"/><path class="cls-1" d="M52.24,93.46a.23.23,0,0,0,.32.21c.11,0,.41-.12.38-.29s-.32-.27-.44-.27S52.22,93.26,52.24,93.46Z"/><path class="cls-1" d="M88.41,130.26a1,1,0,0,0,0-.2l-.7-.29a.36.36,0,0,0,0,.36C87.94,130.37,88.18,130.3,88.41,130.26Z"/><path class="cls-1" d="M53.41,98.18c0,.11.15.19.19.25.27,0,.45-.14.39-.29a.59.59,0,0,0-.29-.31C53.56,97.77,53.37,98,53.41,98.18Z"/><path class="cls-1" d="M55.14,91.46a.33.33,0,0,0,.31-.37.31.31,0,0,0-.32-.35.36.36,0,0,0-.38.36A.39.39,0,0,0,55.14,91.46Z"/><path class="cls-1" d="M53.41,91.37c-.37,0-.65.21-.64.46a.69.69,0,0,0,.6.5.62.62,0,0,0,.43-.58A.46.46,0,0,0,53.41,91.37Z"/><path class="cls-1" d="M55,93c.26,0,.56-.17.58-.37,0-.4-.36-.55-.56-.61s-.47.34-.4.67A.37.37,0,0,0,55,93Z"/><path class="cls-1" d="M51.38,92.41c.26,0,.4-.2.34-.34a.42.42,0,0,0-.26-.23c-.15,0-.33.21-.27.37S51.34,92.37,51.38,92.41Z"/><path class="cls-1" d="M51.44,98.71c-.14-.08-.54.07-.55.26s.07.46.41.39S51.59,98.79,51.44,98.71Z"/><path class="cls-1" d="M50.72,94.45c-.2,0-.3.28-.43.4l0,.11a.32.32,0,0,0,.39.13A.42.42,0,0,0,51,95C50.93,94.81,50.92,94.47,50.72,94.45Z"/><path class="cls-1" d="M51.72,102c-.1,0-.36.09-.34.31s.27.21.36.19a.35.35,0,0,0,.24-.23A.23.23,0,0,0,51.72,102Z"/><path class="cls-1" d="M50.69,91.09a.45.45,0,0,0-.36-.5.46.46,0,0,0-.43.4c0,.18.27.36.61.36C50.55,91.3,50.68,91.2,50.69,91.09Z"/><path class="cls-1" d="M53.79,100.66c-.08-.15-.6-.14-.66.07a.38.38,0,0,0,.22.43C53.57,101.21,53.87,100.8,53.79,100.66Z"/><path class="cls-1" d="M53.67,105.59a.27.27,0,0,0-.05.28.33.33,0,0,0,.32.1c.1,0,.06-.31,0-.36A.26.26,0,0,0,53.67,105.59Z"/><path class="cls-1" d="M52.79,94.47c-.32,0-.63.28-.6.53a.64.64,0,0,0,.58.56c.21,0,.45-.33.45-.61A.44.44,0,0,0,52.79,94.47Z"/><path class="cls-1" d="M55.1,96.16c.25-.49-.28-.65,0-.89s.37.11.72-.25.24-.88-.1-1-.54.18-.61.14-.35-.3-.55-.09c-.4.43.08.69,0,.86s-.41.35-.55.58a.6.6,0,0,0,.18.91A.66.66,0,0,0,55.1,96.16Z"/><path class="cls-1" d="M50.16,83.91c-.07-.08-.32,0-.48,0a.15.15,0,0,0-.14.18.46.46,0,0,0,.48.31A.33.33,0,0,0,50.16,83.91Z"/><path class="cls-1" d="M54.58,97.32a.49.49,0,0,0-.29.23c0,.1.14.41.31.4a.35.35,0,0,0,.31-.37A.26.26,0,0,0,54.58,97.32Z"/><path class="cls-1" d="M51.61,90.92c-.18,0-.34.17-.27.32s.12.38.33.32.24-.3.25-.43S51.76,90.91,51.61,90.92Z"/><path class="cls-1" d="M51.51,98.11c.07,0,.29,0,.31-.19s-.22-.22-.3-.21-.23.05-.23.19S51.43,98.1,51.51,98.11Z"/><path class="cls-1" d="M51.86,89.88c.3,0,.54-.25.52-.47a.76.76,0,0,0-.71-.63.54.54,0,0,0-.4.54A.53.53,0,0,0,51.86,89.88Z"/><path class="cls-1" d="M49,98c-.06,0-.15.14-.13.19a.23.23,0,0,0,.18.16c.06,0,.22,0,.2-.21S49,98,49,98Z"/><path class="cls-1" d="M49.11,88.12a.19.19,0,0,0-.15.15c0,.07.12.14.2.15s.2,0,.2-.16S49.18,88.12,49.11,88.12Z"/><path class="cls-1" d="M48.38,92.64A.35.35,0,0,0,48,93c0,.22.15.35.34.34s.51-.23.49-.4S48.57,92.64,48.38,92.64Z"/><path class="cls-1" d="M48.4,88.57c0-.1-.12-.16-.18-.17s-.26.07-.26.16.19.18.25.18S48.4,88.66,48.4,88.57Z"/><path class="cls-1" d="M51.46,95.09c0-.06-.06-.06-.09-.1s-.09.06-.1.1,0,.09.07.08S51.46,95.14,51.46,95.09Z"/><path class="cls-1" d="M64.28,115.12a.5.5,0,0,0,.09.44.41.41,0,0,0,.39.06.43.43,0,0,0,.13-.65A.38.38,0,0,0,64.28,115.12Z"/><path class="cls-1" d="M62.66,111.52a.26.26,0,0,0-.27-.32.43.43,0,0,0-.33.18c-.05.09.06.42.23.44A.33.33,0,0,0,62.66,111.52Z"/><path class="cls-1" d="M63.25,109.28c.2-.12.28,0,.43,0-.28-.48-.55-1-.81-1.42a1.24,1.24,0,0,0-.27,0,.9.9,0,0,0-.6,1c.1.29.49.21.63,0s.1-.11.15-.11a.16.16,0,0,1,0,.12c-.11.14-.47.26-.65.47s-.32.69,0,.92a.65.65,0,0,0,1-.14C63.44,109.72,62.94,109.47,63.25,109.28Z"/><path class="cls-1" d="M64.67,111c-.2,0-.44.25-.46.5a.47.47,0,0,0,.47.56c.3,0,.54-.11.55-.35a.31.31,0,0,0,0-.08c-.13-.19-.27-.38-.39-.58A.44.44,0,0,0,64.67,111Z"/><path class="cls-1" d="M60,101.92c-.46-1.12-.88-2.25-1.26-3.36-.23,0-.46.23-.46.41a.69.69,0,0,0,.56.59l.11,0a.82.82,0,0,0-.11.3,1.18,1.18,0,0,0,.21.78.63.63,0,0,1,.07.3c0,.39.15.59.54.59a.39.39,0,0,0,0,.14C59.66,101.79,59.83,101.9,60,101.92Z"/><path class="cls-1" d="M68.57,116.84c0,.3.12.59.34.61a.71.71,0,0,0,.57-.36c-.21-.23-.4-.46-.6-.7A.48.48,0,0,0,68.57,116.84Z"/><path class="cls-1" d="M60.82,109.17s0,0,.09,0h0c.21,0,.51-.24.56-.52a.46.46,0,0,0-.35-.56c-.3,0-.66.17-.68.42a.68.68,0,0,0,.19.49.05.05,0,0,0,0,0C60.62,109.09,60.74,109.15,60.82,109.17Z"/><path class="cls-1" d="M62.13,115.56c.09.12.67-.16.7-.32s-.28-.33-.56-.26S62.05,115.44,62.13,115.56Z"/><path class="cls-1" d="M73.13,122.42a.35.35,0,0,0,.44-.28.37.37,0,0,0-.29-.43.45.45,0,0,0-.46.29A.36.36,0,0,0,73.13,122.42Z"/><path class="cls-1" d="M79.4,127.62a.37.37,0,0,0,0,.45c.17.11.56-.14.53-.3S79.52,127.46,79.4,127.62Z"/><path class="cls-1" d="M79.89,126.24a.51.51,0,0,0-.37.11c-.13.18,0,.32.15.45s.39-.09.39-.33C80.06,126.39,80,126.24,79.89,126.24Z"/><path class="cls-1" d="M72.54,120.31a.47.47,0,0,0-.41.4.4.4,0,0,0,.37.39.32.32,0,0,0,.23-.07.1.1,0,0,0,0,0c0,.06.16.13.24.08a.2.2,0,0,0,.06-.24.35.35,0,0,0-.17-.13,0,0,0,0,0,0,0A.37.37,0,0,0,72.54,120.31Z"/><path class="cls-1" d="M59.7,106.31c0-.09-.11-.16-.18-.17s-.26.08-.25.17.18.18.24.17S59.7,106.41,59.7,106.31Z"/><path class="cls-1" d="M69.06,118.39a.35.35,0,0,0,.27.4.34.34,0,0,0,.43-.28.39.39,0,0,0-.32-.47A.36.36,0,0,0,69.06,118.39Z"/><path class="cls-1" d="M73.8,121.8a.24.24,0,0,0-.07.4.93.93,0,0,0,.32.15.38.38,0,0,0,.09-.53C74,121.69,73.92,121.73,73.8,121.8Z"/><path class="cls-1" d="M59.11,107.69c-.2-.06-.35.22-.5.31l0,.12a.34.34,0,0,0,.36.2.45.45,0,0,0,.31-.09C59.25,108.07,59.3,107.74,59.11,107.69Z"/><path class="cls-1" d="M57.67,101.61a.21.21,0,0,0,.21-.13c0-.09-.08-.18-.15-.2s-.26,0-.28.12S57.6,101.61,57.67,101.61Z"/><path class="cls-1" d="M56.88,94.55a.66.66,0,0,0,.61.6.27.27,0,0,0,.19-.11c-.08-.32-.18-.64-.26-1A.61.61,0,0,0,56.88,94.55Z"/><path class="cls-1" d="M57.71,101.24a.44.44,0,0,0,0-.67.38.38,0,0,0-.57.27.49.49,0,0,0,.17.41A.42.42,0,0,0,57.71,101.24Z"/><path class="cls-1" d="M57.47,97.34a.73.73,0,0,0-.67-.6.56.56,0,0,0-.37.57.47.47,0,0,0,.56.47C57.3,97.75,57.5,97.57,57.47,97.34Z"/><path class="cls-1" d="M55,99.17a.47.47,0,0,0,.39.77,2.92,2.92,0,0,0,.78-.09.74.74,0,0,0,.45-.74c0-.17-.11-.29-.27-.3a2.1,2.1,0,0,0-1.21.22A.6.6,0,0,0,55,99.17Z"/><path class="cls-1" d="M61.94,113.84c.08-.08,0-.25-.06-.28a.27.27,0,0,0-.26,0c-.07.06,0,.2,0,.26A.27.27,0,0,0,61.94,113.84Z"/><path class="cls-1" d="M61.66,111.91a.62.62,0,0,0-.23-.37c-.13-.07-.36.12-.35.3s.11.21.14.27C61.49,112.16,61.69,112.06,61.66,111.91Z"/><path class="cls-1" d="M60.52,105.52a.44.44,0,0,0-.21-.27c-.15-.06-.37.15-.34.31a.76.76,0,0,0,.15.24C60.38,105.81,60.55,105.68,60.52,105.52Z"/><path class="cls-1" d="M55.09,84.12c0,.29.24.44.61.4h0c0-.35-.07-.7-.1-1a.21.21,0,0,0-.1,0C55.27,83.46,55.07,83.8,55.09,84.12Z"/><path class="cls-1" d="M60.55,105c.21,0,.3-.26.33-.39s-.11-.24-.27-.26-.35.11-.32.27S60.35,105,60.55,105Z"/><path class="cls-1" d="M61,106.19a1.26,1.26,0,0,0,.28-.2.38.38,0,0,0-.41-.35c-.17,0-.19.15-.18.29S60.82,106.25,61,106.19Z"/><path class="cls-1" d="M56,89.89a.33.33,0,0,0,.29-.38.39.39,0,0,0-.42-.33.29.29,0,0,0-.26.35A.37.37,0,0,0,56,89.89Z"/><path class="cls-1" d="M60.9,104.25a.25.25,0,0,0,.25.27c-.08-.15-.14-.31-.21-.46A.38.38,0,0,0,60.9,104.25Z"/><path class="cls-1" d="M56.53,92.85c.08.12.39.08.6,0-.12-.52-.23-1-.34-1.54-.4,0-.7.43-.54.69s.36.24.34.35S56.4,92.69,56.53,92.85Z"/><path class="cls-1" d="M55,86.25a.61.61,0,0,0,.37.52c.14,0,.41-.19.45-.37a.29.29,0,0,0-.28-.33C55.21,86,55,86.11,55,86.25Z"/><path class="cls-1" d="M58.66,101.17a.18.18,0,0,0-.17.12c0,.07.09.15.16.19s.21,0,.23-.12S58.73,101.19,58.66,101.17Z"/><path class="cls-1" d="M61.05,107.25c.12,0,.43,0,.43-.21s-.27-.34-.39-.36-.29.11-.31.3A.23.23,0,0,0,61.05,107.25Z"/><path class="cls-1" d="M81.66,128.84a.52.52,0,1,0-.55-.87c-.25.17-.38.53-.25.72A.72.72,0,0,0,81.66,128.84Z"/><path class="cls-1" d="M85.2,129.3a.64.64,0,0,0,.29-.53l-.92-.43h0a.58.58,0,0,0-.14.75A.62.62,0,0,0,85.2,129.3Z"/><path class="cls-1" d="M76.73,127.17a.38.38,0,0,0-.12.51.35.35,0,0,0,.5.15.37.37,0,0,0,.14-.5A.45.45,0,0,0,76.73,127.17Z"/><path class="cls-1" d="M80.08,125.89a.61.61,0,0,0,.32.43.69.69,0,0,0,.53.06Z"/><path class="cls-1" d="M77.55,125.74a.27.27,0,0,0-.21-.16.29.29,0,0,0-.27.13.4.4,0,0,0,0,.34l.08,0a.41.41,0,0,0,.06.24.36.36,0,0,0,.52.07.37.37,0,0,0,.11-.54A.4.4,0,0,0,77.55,125.74Z"/><path class="cls-1" d="M54.62,100c.06-.08-.06-.24-.12-.26a.23.23,0,0,0-.25,0c0,.06,0,.2.07.25S54.56,100.07,54.62,100Z"/><path class="cls-1" d="M75.58,125.91c-.16.1-.07.35,0,.46a.24.24,0,0,0,.34.16c.17-.08.28-.32.17-.43S75.73,125.8,75.58,125.91Z"/><path class="cls-1" d="M87.16,129.51l-.49-.2a.22.22,0,0,0-.11.05.35.35,0,0,0,0,.51c.12.13.24.28.4.18S87.24,129.68,87.16,129.51Z"/><path class="cls-1" d="M59.64,108.33s-.1,0-.11.08,0,.1,0,.1.13,0,.14-.07S59.67,108.38,59.64,108.33Z"/><path class="cls-1" d="M59.25,103a.51.51,0,0,0-.49.55.41.41,0,0,0,.33.38.45.45,0,0,0-.17.24c0,.18.2.4.53.46.06,0,.2-.12.23-.22a.48.48,0,0,0-.15-.5.31.31,0,0,0,.21-.32C59.73,103.34,59.43,103,59.25,103Z"/><path class="cls-1" d="M58.46,112.17c-.06.26,0,.45.33.45s.39-.51.26-.62S58.5,112,58.46,112.17Z"/><path class="cls-1" d="M60,119.17a.32.32,0,0,0-.1.27.33.33,0,0,0,.3.16c.1,0,.11-.29.08-.36A.31.31,0,0,0,60,119.17Z"/><path class="cls-1" d="M59.31,111c-.08,0-.24,0-.27.15s.1.21.18.24.3,0,.34-.13S59.38,111.05,59.31,111Z"/><path class="cls-1" d="M60.34,114.29a.4.4,0,0,0,.14.47c.21.09.58-.26.52-.42S60.44,114.1,60.34,114.29Z"/><path class="cls-1" d="M63.87,113a2.1,2.1,0,0,0-1.24,0,.85.85,0,0,0-.16.11.47.47,0,0,0,.25.82,2.9,2.9,0,0,0,.78.06.72.72,0,0,0,.57-.65C64.08,113.16,64,113,63.87,113Z"/><path class="cls-1" d="M62.26,117.77a.67.67,0,0,0-.59-.8.54.54,0,0,0-.65.43c-.06.38.26.88.58.9A.62.62,0,0,0,62.26,117.77Z"/><path class="cls-1" d="M56.73,110.89a.25.25,0,0,0-.17.16c0,.07.09.19.15.2s.23,0,.24-.17S56.8,110.9,56.73,110.89Z"/><path class="cls-1" d="M56.61,106.77a.8.8,0,0,0-.67.72.72.72,0,0,0,.67.71,1,1,0,0,0,.75-.81A.7.7,0,0,0,56.61,106.77Z"/><path class="cls-1" d="M55.15,101.06c-.26.12-.14.49,0,.59s.62-.28.63-.44S55.41,100.93,55.15,101.06Z"/><path class="cls-1" d="M64,117.68c0,.21-.05.47.25.55a.35.35,0,0,0,.44-.21.43.43,0,0,0,0-.35C64.5,117.48,64.33,117.5,64,117.68Z"/><path class="cls-1" d="M54.92,103.12a.54.54,0,0,0-.57.54c0,.39.42.82.73.78a.62.62,0,0,0,.56-.64A.67.67,0,0,0,54.92,103.12Z"/><path class="cls-1" d="M58.49,101.82a.38.38,0,0,0-.35-.39.39.39,0,0,0-.39.36.42.42,0,0,0,.39.42A.38.38,0,0,0,58.49,101.82Z"/><path class="cls-1" d="M58.74,115.24c-.1,0-.37,0-.4.24s.24.26.33.26a.32.32,0,0,0,.27-.18A.23.23,0,0,0,58.74,115.24Z"/><path class="cls-1" d="M57.13,105.48a.34.34,0,0,0-.4.33.31.31,0,0,0,.27.39c.23,0,.54-.13.55-.3S57.32,105.51,57.13,105.48Z"/><path class="cls-1" d="M57.3,103.41c.05.21,0,.47.34.49a.35.35,0,0,0,.4-.28.46.46,0,0,0-.1-.34C57.79,103.12,57.62,103.16,57.3,103.41Z"/><path class="cls-1" d="M71.09,123.3c-.06,0,0,.17,0,.24s.15.15.24.1,0-.22,0-.29A.19.19,0,0,0,71.09,123.3Z"/><path class="cls-1" d="M71.57,121.82c-.14,0-.45.06-.46.25s.22.27.33.31a.25.25,0,0,0,.35-.15C71.84,122.05,71.72,121.8,71.57,121.82Z"/><path class="cls-1" d="M75.59,123.88c-.18-.1-.37-.23-.57,0s-.32.29-.49.43a.3.3,0,0,0-.2.45.39.39,0,0,0,.53.19.94.94,0,0,1,.4-.07.56.56,0,0,0,.59-.24C76,124.46,75.78,124,75.59,123.88Z"/><path class="cls-1" d="M73.11,124.43a.89.89,0,0,0-1.19,0c-.2.23.07.53.29.56s.14,0,.22.26a.38.38,0,0,0,.56.23s.05,0,.07,0,.29-.32.27-.37l-.06-.16A.92.92,0,0,0,73.11,124.43Z"/><path class="cls-1" d="M71,124.39a.24.24,0,0,0-.21.11s0,.25.11.27.18-.15.21-.22S71.07,124.39,71,124.39Z"/><path class="cls-1" d="M74.4,124.05a.73.73,0,0,0,.71-.57c0-.22-.23-.48-.51-.5a.5.5,0,0,0-.57.48C74,123.77,74.16,124,74.4,124.05Z"/><path class="cls-1" d="M71.15,119.08a.6.6,0,0,0,.19-.06c-.28-.27-.54-.56-.81-.84a.59.59,0,0,0-.06.16s0,0,0,.07a.62.62,0,0,0-.39.07.7.7,0,0,0-.27.93,1,1,0,0,0,1.07.25A.62.62,0,0,0,71.15,119.08Z"/><path class="cls-1" d="M67.93,119.33a.38.38,0,0,0-.34-.42c-.18,0-.22.12-.24.25a.25.25,0,0,0,.26.32A1.19,1.19,0,0,0,67.93,119.33Z"/><path class="cls-1" d="M66.34,118.28a.77.77,0,0,0-.38-.67.5.5,0,0,0-.58.45c0,.23.15.44.48.5A.37.37,0,0,0,66.34,118.28Z"/><path class="cls-1" d="M65.66,120.77c-.07,0-.27,0-.28.12s.14.21.21.21.2,0,.21-.13S65.72,120.79,65.66,120.77Z"/><path class="cls-1" d="M67.51,116.4a.36.36,0,0,0-.36-.39h0a.7.7,0,0,0,.07-.37.64.64,0,0,1,.09-.5.6.6,0,0,0,.06-.55L67,114.1a.66.66,0,0,0-.33-.07h-.07a.63.63,0,0,0,.18-.22c-.16-.22-.34-.43-.5-.65-.24,0-.5.14-.53.32a.7.7,0,0,0,.45.69h.11a.56.56,0,0,0-.16.27,1.19,1.19,0,0,0,0,.81.44.44,0,0,1,0,.3.48.48,0,0,0,.43.68.27.27,0,0,0,0,.14c0,.16.18.33.4.36A.44.44,0,0,0,67.51,116.4Z"/><path class="cls-1" d="M65.14,115.89a.38.38,0,0,0-.44.28.43.43,0,0,0,.31.49.37.37,0,0,0,.41-.33A.37.37,0,0,0,65.14,115.89Z"/><path class="cls-1" d="M68.38,123a.89.89,0,0,0-.77.9c0,.31.44.29.61.15s.13-.08.34,0,.56,0,.56-.36A.76.76,0,0,0,68.38,123Z"/><path class="cls-1" d="M69.57,120.75c-.2.07-.41.13-.38.42s0,.43,0,.64a.31.31,0,0,0,.21.46.42.42,0,0,0,.49-.29,1.1,1.1,0,0,1,.2-.35.55.55,0,0,0,.2-.61C70.24,120.84,69.78,120.68,69.57,120.75Z"/><path class="cls-1" d="M67.07,120.52c-.18,0-.22.11-.24.25s.09.34.26.31a1.12,1.12,0,0,0,.31-.15A.36.36,0,0,0,67.07,120.52Z"/><path class="cls-1" d="M124.22,137.55c-.06,0-.23,0-.23.18s.19.13.26.14.15-.12.13-.15A.27.27,0,0,0,124.22,137.55Z"/><path class="cls-1" d="M122.15,137.46c-.13,0-.23.23-.2.44a.21.21,0,0,0,.27.2c.17,0,.31-.11.3-.32A.39.39,0,0,0,122.15,137.46Z"/><path class="cls-1" d="M123.87,135.36a.61.61,0,0,0-.57.7.75.75,0,0,0,.79.64.81.81,0,0,0,.61-.78C124.68,135.56,124.33,135.33,123.87,135.36Z"/><path class="cls-1" d="M122.67,134.54a.17.17,0,0,0-.27,0,.39.39,0,0,0,0,.32.51.51,0,0,0,.1.29c.11.1.25,0,.29-.16A.56.56,0,0,0,122.67,134.54Z"/><path class="cls-1" d="M126.64,132.93c-.38-.05-.75-.12-1.12-.18.14.14.24.35.5.39A.68.68,0,0,0,126.64,132.93Z"/><path class="cls-1" d="M126.87,135.77c-.29,0-.42.28-.35.64a.5.5,0,0,0,.5.37c.23,0,.6-.45.57-.64A.64.64,0,0,0,126.87,135.77Z"/><path class="cls-1" d="M124.3,133.24c-.21.17-.11.59.12.9a.68.68,0,0,0,.87,0,.69.69,0,0,0-.15-.94A.68.68,0,0,0,124.3,133.24Z"/><path class="cls-1" d="M122.12,136.22a.41.41,0,0,0,.18-.32.3.3,0,0,0-.34-.32.33.33,0,0,0-.29.33C121.66,136.06,122,136.3,122.12,136.22Z"/><path class="cls-1" d="M126.86,134.27c0-.24.15-.46-.1-.6a.37.37,0,0,0-.48.09.3.3,0,0,0,0,.4C126.39,134.39,126.61,134.35,126.86,134.27Z"/><path class="cls-1" d="M127.2,133a.67.67,0,0,0,0,.2c0,.32.17.46.4.43s.52-.25.48-.51h0Z"/><path class="cls-1" d="M119.07,137.61a.6.6,0,0,0-.5-.4.39.39,0,0,0-.29.45.37.37,0,0,0,.45.28C119,137.91,119.11,137.77,119.07,137.61Z"/><path class="cls-1" d="M118.77,135.48c.19,0,.37-.41.33-.7a.44.44,0,0,0-.48-.32.51.51,0,0,0-.44.5A.67.67,0,0,0,118.77,135.48Z"/><path class="cls-1" d="M117.34,131.51a.71.71,0,0,0-.47.66.64.64,0,0,0,.6.41.47.47,0,0,0,.39-.42C117.82,131.76,117.59,131.48,117.34,131.51Z"/><path class="cls-1" d="M120.7,135.15a.26.26,0,0,0-.24.34c.05.11.11.24.23.26s.35-.25.31-.43S120.83,135.14,120.7,135.15Z"/><path class="cls-1" d="M120.89,132.07a.38.38,0,0,0-.43-.15.43.43,0,0,0-.32.43c.06.29.31.33.53.39C121,132.43,121,132.26,120.89,132.07Z"/><path class="cls-1" d="M119.69,137.33c-.09,0-.14.17-.13.22s.06.24.15.22a.26.26,0,0,0,.17-.2C119.88,137.51,119.78,137.31,119.69,137.33Z"/><path class="cls-1" d="M119.41,131.92c.27,0,.42-.18.45-.35l-.73-.18a.45.45,0,0,0,0,.2C119.18,131.76,119.23,131.94,119.41,131.92Z"/><path class="cls-1" d="M119.87,133.09a.52.52,0,0,0-.44,0c-.25.15-.09.36,0,.59.28,0,.52,0,.59-.27A.35.35,0,0,0,119.87,133.09Z"/><path class="cls-1" d="M130.54,134.65a.21.21,0,0,0-.2.06c0,.07,0,.18.1.23s.2.05.25,0S130.61,134.69,130.54,134.65Z"/><path class="cls-1" d="M138.53,136.68a.49.49,0,0,0-.69.26c-.13.29-.07.59.16.68a.71.71,0,0,0,.85-.3C139,137.12,138.79,136.79,138.53,136.68Z"/><path class="cls-1" d="M137.83,135.55a.38.38,0,0,0-.13-.51.45.45,0,0,0-.53.13.36.36,0,0,0,.16.5A.35.35,0,0,0,137.83,135.55Z"/><path class="cls-1" d="M136.32,137.83a.27.27,0,0,0-.15-.22.38.38,0,0,0-.48.1.27.27,0,0,0,0,.39c.13.2.3.2.51.15,0,.14,0,.28.09.33s.38-.21.38-.26,0-.38-.15-.48A.16.16,0,0,0,136.32,137.83Z"/><path class="cls-1" d="M137.63,134a.36.36,0,0,0-.06-.45l-.81,0a.65.65,0,0,0,.18.15.39.39,0,0,0,.22.47A.34.34,0,0,0,137.63,134Z"/><path class="cls-1" d="M135.2,134c-.12.05-.42.22-.4.35s.35.22.48.24.27-.2.23-.36S135.37,133.92,135.2,134Z"/><path class="cls-1" d="M139.14,134.6c-.31-.15-.76,0-.81.29a.54.54,0,0,0,.26.59.42.42,0,0,0,.58-.07c.13-.13.47-.33.4-.59S139.26,134.66,139.14,134.6Z"/><path class="cls-1" d="M141.42,135.42a.85.85,0,0,0-.11-.16.48.48,0,0,0-.83.23,2.94,2.94,0,0,0-.06.78.72.72,0,0,0,.63.59c.18,0,.54-.1.48-.44C141.17,136,141.57,135.83,141.42,135.42Z"/><path class="cls-1" d="M142.26,134.09a.52.52,0,1,0-.92-.46.59.59,0,0,0,.15.74A.72.72,0,0,0,142.26,134.09Z"/><path class="cls-1" d="M140,137.94s0-.1,0-.12a.11.11,0,0,0-.15,0,.12.12,0,0,0,.07.14S140,138,140,137.94Z"/><path class="cls-1" d="M139.7,134.2a.35.35,0,0,0,.27.35c.2,0,.41-.41.3-.52S139.72,134,139.7,134.2Z"/><path class="cls-1" d="M128.73,137.17c-.12.05-.41.22-.4.37s.37.19.5.18.25-.13.2-.31S128.9,137.12,128.73,137.17Z"/><path class="cls-1" d="M135.05,133.59h-1.17c0,.08,0,.15-.07.23a.31.31,0,0,0,.05.5.42.42,0,0,0,.56-.11,1,1,0,0,1,.3-.27A.58.58,0,0,0,135.05,133.59Z"/><path class="cls-1" d="M130.09,138.85c-.09,0-.16.2-.15.26s.09.18.18.17.15-.12.15-.19S130.18,138.84,130.09,138.85Z"/><path class="cls-1" d="M128.66,135.61a.41.41,0,0,0-.33.46.34.34,0,0,0,.38.29.37.37,0,0,0,.37-.42A.41.41,0,0,0,128.66,135.61Z"/><path class="cls-1" d="M130.16,133.35l-1.08-.1a.8.8,0,0,0,.43.65C129.8,134,129.94,133.89,130.16,133.35Z"/><path class="cls-1" d="M116.29,133.77a.42.42,0,0,0-.33.47.34.34,0,0,0,.38.28.37.37,0,0,0,.37-.42A.41.41,0,0,0,116.29,133.77Z"/><path class="cls-1" d="M130,136.43a.19.19,0,0,0,.12.07.12.12,0,0,0,.09-.1c0-.05-.06-.11-.12-.1A.13.13,0,0,0,130,136.43Z"/><path class="cls-1" d="M133.13,135.52a.77.77,0,0,0-.48-.86.89.89,0,0,0-1,.6c-.05.3.33.42.54.33s.14,0,.32.11l.06,0a.66.66,0,0,0-.11.45c0,.32.31.47.67.41a.51.51,0,0,0,.46-.63A.54.54,0,0,0,133.13,135.52Z"/><path class="cls-1" d="M134.43,136.87s-.06.24,0,.29.22-.08.27-.14a.15.15,0,0,0-.05-.19A.26.26,0,0,0,134.43,136.87Z"/><path class="cls-1" d="M132.54,133.52l-1.23-.07a.71.71,0,0,0,.68.45A.76.76,0,0,0,132.54,133.52Z"/><path class="cls-1" d="M131.15,134.68a.62.62,0,0,0-.53.56.43.43,0,0,0,.43.38.52.52,0,0,0,.45-.55A.36.36,0,0,0,131.15,134.68Z"/><path class="cls-1" d="M162.93,127.71c.1-.07.25-.25.21-.33a.37.37,0,0,0-.31-.16c-.09,0-.29.22-.27.32S162.88,127.74,162.93,127.71Z"/><path class="cls-1" d="M168.18,123.32a.64.64,0,0,0,.62.52.71.71,0,0,0,.48-.67.55.55,0,0,0-.62-.34C168.3,122.89,168.15,123.05,168.18,123.32Z"/><path class="cls-1" d="M171,120.17c.19,0,.55-.47.53-.63a.39.39,0,0,0-.12-.24l-.76.7A.37.37,0,0,0,171,120.17Z"/><path class="cls-1" d="M127.29,135.35c.1-.07.26-.24.22-.32a.39.39,0,0,0-.3-.18c-.09,0-.3.21-.28.31S127.24,135.38,127.29,135.35Z"/><path class="cls-1" d="M115.08,132.78a.49.49,0,0,0,.57.47c.27,0,.51-.27.47-.47a.74.74,0,0,0-.73-.53A.45.45,0,0,0,115.08,132.78Z"/><path class="cls-1" d="M143.87,134a.31.31,0,0,0,.17.44.42.42,0,0,0,.5-.18.39.39,0,0,0-.2-.49A.36.36,0,0,0,143.87,134Z"/><path class="cls-1" d="M136.29,136.3a.37.37,0,0,0,.18-.17s-.09-.15-.13-.15-.23,0-.24.16S136.26,136.31,136.29,136.3Z"/><path class="cls-1" d="M136,134.59c-.13,0-.44-.09-.51.08s.12.33.21.41a.24.24,0,0,0,.38,0C136.22,134.9,136.2,134.63,136,134.59Z"/><path class="cls-1" d="M151.69,131.15a.25.25,0,0,0,.19.15c.06,0,.15-.12.15-.18a.31.31,0,0,0,0-.08l-.32.1Z"/><path class="cls-1" d="M142.45,139a.38.38,0,0,0-.54.15.4.4,0,0,0,.2.53.38.38,0,0,0,.53-.18A.4.4,0,0,0,142.45,139Z"/><path class="cls-1" d="M176.93,115a.59.59,0,0,0-.67-.26.46.46,0,0,0-.26.48c.11.35.39.57.62.48A.64.64,0,0,0,176.93,115Z"/><path class="cls-1" d="M177,113.69a.37.37,0,0,0,.18-.46.08.08,0,0,0,0,0c-.15.19-.31.38-.47.56A.54.54,0,0,0,177,113.69Z"/><path class="cls-1" d="M166.68,123.34l-.25.19A.33.33,0,0,0,166.68,123.34Z"/><path class="cls-1" d="M177.56,113.1c.25,0,.5-.27.47-.47a.56.56,0,0,0-.17-.34l-.58.71A.44.44,0,0,0,177.56,113.1Z"/><path class="cls-1" d="M115.4,135c-.17,0-.36.35-.29.46a.42.42,0,0,0,.36.13.22.22,0,0,0,.24-.28C115.67,135.18,115.61,135,115.4,135Z"/><path class="cls-1" d="M114.17,130.1c-.09,0-.33.12-.33.24s.25.23.35.25a.27.27,0,0,0,.27-.33A.37.37,0,0,0,114.17,130.1Z"/><path class="cls-1" d="M113.82,132.36a.29.29,0,0,0-.32.34.35.35,0,0,0,.37.36.36.36,0,0,0,.34-.35A.37.37,0,0,0,113.82,132.36Z"/><path class="cls-1" d="M115.67,130.8c0-.26-.26-.4-.6-.35s-.43.19-.41.4.39.58.6.56S115.71,131.14,115.67,130.8Z"/><path class="cls-1" d="M123.75,134.71a.32.32,0,0,0,.19-.23.41.41,0,0,0-.15-.32c-.23-.12-.34.07-.46.23C123.4,134.61,123.51,134.76,123.75,134.71Z"/><path class="cls-1" d="M122.72,133.38a1.21,1.21,0,0,1,.32-.11.54.54,0,0,0,.5-.5c0-.28-.13-.43-.54-.38a5.55,5.55,0,0,0-.68.4.34.34,0,0,0-.07.49A.34.34,0,0,0,122.72,133.38Z"/><path class="cls-1" d="M121.88,132.36a.7.7,0,0,0,.44-.21l-.87-.2a.3.3,0,0,0,0,.13A.34.34,0,0,0,121.88,132.36Z"/><path class="cls-1" d="M151.48,131.64c.1,0,.18.11.3.08s.13-.21,0-.33a.6.6,0,0,0-.39-.17l-.13,0a.18.18,0,0,0-.06.18A.43.43,0,0,0,151.48,131.64Z"/><path class="cls-1" d="M145.54,132.69h0l-.83.16s0,0,.06,0A.62.62,0,0,0,145.54,132.69Z"/><path class="cls-1" d="M171.35,121.34a.41.41,0,0,0-.48-.3.4.4,0,0,0-.27.45.41.41,0,0,0,.45.27A.36.36,0,0,0,171.35,121.34Z"/><path class="cls-1" d="M170.57,122.92a.33.33,0,0,0,.23-.42.43.43,0,0,0-.54-.26,1.18,1.18,0,0,0-.17.39C170.08,122.84,170.35,123,170.57,122.92Z"/><path class="cls-1" d="M172.58,119.48a.55.55,0,0,0-.49.66.67.67,0,1,0,1.33-.2C173.37,119.6,173,119.41,172.58,119.48Z"/><path class="cls-1" d="M173.29,117.48l-.4.39v0c0,.15.15.27.34.24s.31-.14.24-.33A.78.78,0,0,0,173.29,117.48Z"/><path class="cls-1" d="M170.31,120.43l-.06-.05-.4.37.09.15a.14.14,0,0,0,.2.08A.46.46,0,0,0,170.31,120.43Z"/><path class="cls-1" d="M149,132.79c-.07-.07-.27-.12-.33-.07a.6.6,0,0,0-.26.37c0,.28.25.27.48.34C149,133.21,149.15,133,149,132.79Z"/><path class="cls-1" d="M151.86,132.38a.33.33,0,0,0,.11.43c.12.1.5-.05.51-.19a.41.41,0,0,0-.17-.34A.3.3,0,0,0,151.86,132.38Z"/><path class="cls-1" d="M155.06,132a.3.3,0,0,0-.12-.13l.1-.12s0-.18,0-.19a.28.28,0,0,0-.24,0,.21.21,0,0,0,0,.29c-.06,0-.11.1-.1.13s.19.13.27.13S155.07,132,155.06,132Z"/><path class="cls-1" d="M152.2,136.94a.12.12,0,0,0,.06.14s.11,0,.12-.06a.11.11,0,0,0,0-.13A.12.12,0,0,0,152.2,136.94Z"/><path class="cls-1" d="M151.32,136.11a.36.36,0,0,0,.21.48.35.35,0,0,0,.48-.21c.1-.21.07-.4-.08-.47A.61.61,0,0,0,151.32,136.11Z"/><path class="cls-1" d="M151.11,133c-.17-.06-.24,0-.31.16s0,.35.14.38.27.05.35,0S151.28,133.1,151.11,133Z"/><path class="cls-1" d="M153.18,136.76s0,.26,0,.29.22-.08.26-.14,0-.19,0-.19A.27.27,0,0,0,153.18,136.76Z"/><path class="cls-1" d="M154.15,131.17c-.06,0-.08.09-.07.13s.09.07.12.06.09-.05.08-.1A.11.11,0,0,0,154.15,131.17Z"/><path class="cls-1" d="M154.82,130.34a.52.52,0,0,0-.19-.24l-.6.22v.1a.37.37,0,0,0,.46.26C154.73,130.64,154.86,130.5,154.82,130.34Z"/><path class="cls-1" d="M154,131.25a.79.79,0,0,0-.2-.84c-.36.13-.71.27-1.07.39a.59.59,0,0,0,.29.74A.76.76,0,0,0,154,131.25Z"/><path class="cls-1" d="M154,133.16a.39.39,0,0,0-.48.11c-.06.12.07.32.26.42a.2.2,0,0,0,.31-.11C154.16,133.42,154.18,133.26,154,133.16Z"/><path class="cls-1" d="M150.6,138.64a.26.26,0,0,0,.12.27.24.24,0,0,0,.23-.06.24.24,0,0,0,0-.23C150.84,138.59,150.64,138.54,150.6,138.64Z"/><path class="cls-1" d="M152,135.24c0,.09.07.22.11.24s.23.08.26,0a.25.25,0,0,0-.06-.25C152.29,135.19,152.08,135.15,152,135.24Z"/><path class="cls-1" d="M145.81,139.12a.33.33,0,0,0-.46.14.41.41,0,0,0,.14.55.41.41,0,0,0,.5-.18A.45.45,0,0,0,145.81,139.12Z"/><path class="cls-1" d="M146.28,135.66a.72.72,0,0,0-.85.31c-.1.17,0,.45.26.56a.5.5,0,0,0,.71-.21C146.52,136.07,146.46,135.74,146.28,135.66Z"/><path class="cls-1" d="M145.9,133.94a.71.71,0,0,0,.29.76.65.65,0,0,0,.68-.27.47.47,0,0,0-.14-.56C146.38,133.68,146,133.71,145.9,133.94Z"/><path class="cls-1" d="M145.15,137.21a.37.37,0,0,0-.51.12.28.28,0,0,0,.1.45.35.35,0,1,0,.41-.57Z"/><path class="cls-1" d="M149.6,134.31a.43.43,0,0,0-.53.21.5.5,0,0,0,.17.65A.68.68,0,0,0,150,135C150.07,134.8,149.86,134.44,149.6,134.31Z"/><path class="cls-1" d="M144.38,134.93c-.24-.12-.47,0-.63.3a.4.4,0,0,0,.11.56.75.75,0,0,0,.79-.18A.48.48,0,0,0,144.38,134.93Z"/><path class="cls-1" d="M143.26,135.63a.36.36,0,0,0-.3.15c-.05.08-.08.34,0,.41s.32-.08.4-.16A.26.26,0,0,0,143.26,135.63Z"/><path class="cls-1" d="M148,138c.12,0,.23-.11.31-.22a.22.22,0,0,0-.1-.36c-.16-.07-.32-.1-.43.08S147.83,138,148,138Z"/><path class="cls-1" d="M147.7,135.91a.42.42,0,0,0-.51.16.43.43,0,0,0,.2.54.34.34,0,0,0,.45-.16A.37.37,0,0,0,147.7,135.91Z"/><path class="cls-1" d="M165.88,131.08c-.09,0-.15.21-.14.27s.1.18.19.17.15-.14.15-.21S166,131.07,165.88,131.08Z"/><path class="cls-1" d="M164.31,127.91a.42.42,0,0,0-.31.48.33.33,0,0,0,.39.27.37.37,0,0,0,.35-.44A.4.4,0,0,0,164.31,127.91Z"/><path class="cls-1" d="M169,124.3c-.14,0-.17.36-.14.46s.19.15.3.15a.19.19,0,0,0,.18-.27C169.27,124.53,169.11,124.27,169,124.3Z"/><path class="cls-1" d="M164.45,129.47c-.12.05-.4.24-.38.38s.37.17.51.17a.23.23,0,0,0,.18-.33C164.71,129.54,164.61,129.4,164.45,129.47Z"/><path class="cls-1" d="M163.08,132.52c-.11,0-.38.21-.35.31s.29.22.41.24.26-.13.25-.34A.23.23,0,0,0,163.08,132.52Z"/><path class="cls-1" d="M162.92,125.94a.29.29,0,0,0,.25,0c.3,0,.48-.26.44-.5Z"/><path class="cls-1" d="M165.85,128.63a.11.11,0,0,0-.13-.09.12.12,0,0,0-.07.13s.09.07.12.06S165.86,128.68,165.85,128.63Z"/><path class="cls-1" d="M168.15,125.34c0-.38-.35-.7-.65-.67a.91.91,0,0,0-.7.82.72.72,0,0,0,.76.56A.69.69,0,0,0,168.15,125.34Z"/><path class="cls-1" d="M166.76,126.87a.63.63,0,0,0-.51.58.43.43,0,0,0,.45.37.55.55,0,0,0,.43-.57C167.1,127,167,126.86,166.76,126.87Z"/><path class="cls-1" d="M166.32,125.38a.38.38,0,0,0-.13-.48c-.06,0-.17-.08-.22-.05a4.32,4.32,0,0,0-.48.31c-.44-.12-.79,0-.87.23a.76.76,0,0,0,.47.77c.3.1.43,0,.64-.62C166,125.6,166.21,125.69,166.32,125.38Z"/><path class="cls-1" d="M162.21,126.72l.25-.06a1.32,1.32,0,0,0,0-.44l-.63.37h0a.33.33,0,0,0,.09.21s.21,0,.26,0S162.21,126.74,162.21,126.72Z"/><path class="cls-1" d="M157.34,134.59c0,.1,0,.29,0,.34a.34.34,0,0,0,.32-.07c.06-.07.08-.35,0-.4S157.36,134.53,157.34,134.59Z"/><path class="cls-1" d="M157.89,130.05c-.13,0-.22.24-.18.45a.21.21,0,0,0,.27.19c.17,0,.31-.13.29-.33A.38.38,0,0,0,157.89,130.05Z"/><path class="cls-1" d="M159,130.46a.22.22,0,0,0-.36.12c0,.11,0,.43,0,.47s.35-.11.44-.19S159.13,130.57,159,130.46Z"/><path class="cls-1" d="M157.68,132.7a.35.35,0,1,0-.68.11.3.3,0,0,0,.36.3A.36.36,0,0,0,157.68,132.7Z"/><path class="cls-1" d="M155.43,130a.26.26,0,0,0-.13.23c0,.08.08.23.16.2a.23.23,0,0,0,.16-.2C155.62,130.2,155.52,130,155.43,130Z"/><path class="cls-1" d="M159.1,133.9c-.09,0-.25.14-.25.22s.16.21.25.22.33-.15.32-.25S159.16,133.88,159.1,133.9Z"/><path class="cls-1" d="M163.26,128.49a.65.65,0,0,0-.74-.34c-.28.06-.4.3-.32.65a.52.52,0,0,0,.52.36C163,129.11,163.31,128.67,163.26,128.49Z"/><path class="cls-1" d="M155.53,135a.35.35,0,1,0-.31.62.29.29,0,0,0,.45-.12A.36.36,0,0,0,155.53,135Z"/><path class="cls-1" d="M159.52,127.88l-.06,0-.29.14a.63.63,0,0,0-.2.56.75.75,0,0,0,.82.6.78.78,0,0,0,.57-.8C160.33,128,160,127.82,159.52,127.88Z"/><path class="cls-1" d="M160,130c-.06,0-.22,0-.22.19s.19.13.27.13.13-.12.12-.15A.31.31,0,0,0,160,130Z"/><path class="cls-1" d="M190.54,38.34l-.07,0,.35.72a.62.62,0,0,0,.16-.54A.33.33,0,0,0,190.54,38.34Z"/><path class="cls-1" d="M189.52,36.57c.17.31.34.63.5,1a.6.6,0,0,0,.13-.56A.63.63,0,0,0,189.52,36.57Z"/><path class="cls-1" d="M195.63,80a.34.34,0,0,0-.34-.38.36.36,0,0,0-.38.36.45.45,0,0,0,.36.4A.36.36,0,0,0,195.63,80Z"/><path class="cls-1" d="M194.73,83a.75.75,0,0,0-.14.76c.1.19.19.39.48.31s.42-.08.63-.12a.31.31,0,0,0,.41-.28.41.41,0,0,0-.36-.44.91.91,0,0,1-.38-.14A.57.57,0,0,0,194.73,83Z"/><path class="cls-1" d="M189.94,89.92a.49.49,0,0,0,.41-.17.39.39,0,0,0-.67-.41A.38.38,0,0,0,189.94,89.92Z"/><path class="cls-1" d="M196.76,48.1a.78.78,0,0,0-.43.63.5.5,0,0,0,.67.32c.22-.06.32-.33.23-.65A.37.37,0,0,0,196.76,48.1Z"/><path class="cls-1" d="M195.39,43.54a.69.69,0,0,0-.79-.39.74.74,0,0,0-.51.56.52.52,0,0,0-.4-.17.82.82,0,0,0-.76.37c.19.49.37,1,.55,1.48h.07a.36.36,0,0,0,.25-.45.09.09,0,0,0,0-.07.68.68,0,0,0,.48-.55.43.43,0,0,0,0-.05.61.61,0,0,0,.7.12.74.74,0,0,0,.28-.14.32.32,0,0,0,.13.23.5.5,0,0,0,.67-.19.62.62,0,0,0-.06-.68A.49.49,0,0,0,195.39,43.54Z"/><path class="cls-1" d="M197.67,50a.35.35,0,0,0-.38-.3.46.46,0,0,0-.3.19c-.11.19,0,.34.31.58C197.48,50.38,197.73,50.32,197.67,50Z"/><path class="cls-1" d="M195.67,81.32c-.18,0-.41.13-.37.29s.14.42.33.41.23-.27.25-.38A.24.24,0,0,0,195.67,81.32Z"/><path class="cls-1" d="M193.48,42.68a.32.32,0,0,0,.42.23.36.36,0,0,0,.29-.44.4.4,0,0,0-.47-.27A.41.41,0,0,0,193.48,42.68Z"/><path class="cls-1" d="M194.27,40.77c.17-.14.06-.31,0-.46s-.41,0-.46.22a.31.31,0,0,0,.11.27A.49.49,0,0,0,194.27,40.77Z"/><path class="cls-1" d="M198.91,52.32a.63.63,0,0,0-.77-.36.68.68,0,0,0-.45.89.55.55,0,0,0,.68.4A.86.86,0,0,0,198.91,52.32Z"/><path class="cls-1" d="M198.63,55a.7.7,0,0,0-1,0,.5.5,0,0,0,0,.74.64.64,0,0,0,1,.07A.65.65,0,0,0,198.63,55Z"/><path class="cls-1" d="M194.68,77a.73.73,0,0,0,.2-.84.48.48,0,0,0-.33-.26c-.12.45-.26.9-.4,1.35A3.65,3.65,0,0,0,194.68,77Z"/><path class="cls-1" d="M196.29,50.52a.54.54,0,0,0,0-.64.48.48,0,0,0-.55-.21,2.14,2.14,0,0,0-.46.17,2.12,2.12,0,0,0-.2-.12.89.89,0,0,0-.32-.08c.13.5.26,1,.37,1.5l.05-.06a.43.43,0,0,0-.09-.35.69.69,0,0,0,.11-.13l.1-.14a.36.36,0,0,0,.21,0l.2.14A.37.37,0,0,0,196.29,50.52Z"/><path class="cls-1" d="M198.25,58.9c.17-.12.21-.28.06-.4s-.31-.33-.5-.19,0,.41,0,.54S198.1,59,198.25,58.9Z"/><path class="cls-1" d="M199.24,61.26a.75.75,0,0,0,0-.9.66.66,0,0,0-1,0,.55.55,0,0,0-.11.81A.75.75,0,0,0,199.24,61.26Z"/><path class="cls-1" d="M196.87,73.83a.38.38,0,0,0-.38-.36c.13-.09.14-.24,0-.37s-.27-.18-.37,0-.24.4-.15.52.13,0,.23,0a.4.4,0,0,0-.1.26.38.38,0,0,0,.39.4A.41.41,0,0,0,196.87,73.83Z"/><path class="cls-1" d="M196.73,76.84c.16,0,.17-.24.05-.41a.22.22,0,0,0-.37,0c-.08.09-.22.37-.16.44S196.62,76.88,196.73,76.84Z"/><path class="cls-1" d="M195.67,72.38a.38.38,0,0,0,.09-.56.4.4,0,0,0-.26-.12c-.05.24-.09.48-.14.72A.35.35,0,0,0,195.67,72.38Z"/><path class="cls-1" d="M197.25,65.2a.53.53,0,0,0-.7,0,.64.64,0,0,0-.25.32s0,.06,0,.1a.38.38,0,0,0,.08.24.64.64,0,0,0,.79.14A.67.67,0,0,0,197.25,65.2Z"/><path class="cls-1" d="M198.11,63.11a.37.37,0,0,0,.06-.52.39.39,0,0,0-.59.5A.42.42,0,0,0,198.11,63.11Z"/><path class="cls-1" d="M197.3,61.73a1,1,0,0,0,.16-.81.53.53,0,0,0-.78,0c-.2.17-.19.42,0,.7S197.14,61.87,197.3,61.73Z"/><path class="cls-1" d="M191.57,36.79a.31.31,0,0,0,.18-.44.41.41,0,0,0-.48-.22.4.4,0,0,0-.2.49A.38.38,0,0,0,191.57,36.79Z"/><path class="cls-1" d="M200.69,53.33s.09-.26,0-.34-.32,0-.36.09a.29.29,0,0,0,.06.28A.34.34,0,0,0,200.69,53.33Z"/><path class="cls-1" d="M199.29,47.59c0,.06.15.22.23.2s.12-.23.1-.29-.12-.16-.21-.13S199.28,47.52,199.29,47.59Z"/><path class="cls-1" d="M201.1,61.89a.37.37,0,0,0-.11.55.64.64,0,0,0,.82.19.56.56,0,0,0,0-.71A.52.52,0,0,0,201.1,61.89Z"/><path class="cls-1" d="M200.51,54.4c-.27.1-.27.33-.18.62.23,0,.44.15.59-.09a.31.31,0,0,0-.06-.44A.42.42,0,0,0,200.51,54.4Z"/><path class="cls-1" d="M199,52.28a.43.43,0,0,0,.53.09.52.52,0,0,0,.18-.65.59.59,0,0,0-.7-.19C198.85,51.65,198.84,52,199,52.28Z"/><path class="cls-1" d="M197.33,67.64a.7.7,0,0,0-.89-.26.89.89,0,0,0-.21,1.06.72.72,0,0,0,.93.12A.71.71,0,0,0,197.33,67.64Z"/><path class="cls-1" d="M198.76,46.54c.18-.06.27-.23.17-.37a1.13,1.13,0,0,0-.28-.22.37.37,0,0,0-.21.49C198.51,46.6,198.64,46.58,198.76,46.54Z"/><path class="cls-1" d="M195.62,52.59c-.07,0-.11.11-.12.21a2.38,2.38,0,0,1,.05.26.75.75,0,0,0,.09.15c.19.21.51,0,.58-.14S195.78,52.55,195.62,52.59Z"/><path class="cls-1" d="M203.14,60.05a.2.2,0,0,0,0,.31.22.22,0,0,0,.29,0,.19.19,0,0,0,0-.26A.21.21,0,0,0,203.14,60.05Z"/><path class="cls-1" d="M195.64,45.62a.35.35,0,0,0,.23-.42.33.33,0,0,0-.44-.26.39.39,0,0,0-.28.49A.37.37,0,0,0,195.64,45.62Z"/><path class="cls-1" d="M197.85,56.49c.21-.08.13-.32.09-.4a.34.34,0,0,0-.29-.16.23.23,0,0,0-.19.32C197.5,56.35,197.65,56.57,197.85,56.49Z"/><path class="cls-1" d="M196.86,69.66a.62.62,0,0,0-.16.75.42.42,0,0,0,.57.11.55.55,0,0,0,.1-.71A.36.36,0,0,0,196.86,69.66Z"/><path class="cls-1" d="M196.21,57.05a.87.87,0,0,0,0-1,.55.55,0,0,0-.23-.17c.06.42.12.84.16,1.25Z"/><path class="cls-1" d="M195.81,54.6c0,.22.06.43.09.64a.36.36,0,0,0,.34-.33C196.24,54.75,196,54.62,195.81,54.6Z"/><path class="cls-1" d="M196.56,58.23c-.05-.07-.19-.09-.34-.09,0,.31.05.62.07.93l.1,0A.63.63,0,0,0,196.56,58.23Z"/><path class="cls-1" d="M194,45.84a.4.4,0,0,0-.28.37l.16.45a.46.46,0,0,0,.55.09c.29-.1.48-.38.4-.58A.75.75,0,0,0,194,45.84Z"/><path class="cls-1" d="M195.06,48.1a0,0,0,0,0,0,0,.41.41,0,0,0-.5-.23.31.31,0,0,0-.19.2c.11.39.24.78.34,1.18a.47.47,0,0,0,.11-.53.24.24,0,0,0,.27-.17.42.42,0,0,0,.13.07.35.35,0,0,0,.34-.29A.32.32,0,0,0,195.06,48.1Z"/><path class="cls-1" d="M191.45,85a.43.43,0,0,0-.13-.26l-.3.67C191.27,85.33,191.47,85.15,191.45,85Z"/><path class="cls-1" d="M189.68,88l.06,0a.53.53,0,0,1,.3-.07c.39,0,.6-.14.6-.53a.23.23,0,0,0,.14,0,.42.42,0,0,0,.29-.46.44.44,0,0,0-.43-.45.37.37,0,0,0-.32.42h-.07C190.07,87.31,189.87,87.68,189.68,88Z"/><path class="cls-1" d="M193.61,79.53c-.07.08-.15.25-.11.32s.25.11.33.07.21-.29.15-.37S193.66,79.49,193.61,79.53Z"/><path class="cls-1" d="M191.48,40.46c.11.23.21.47.31.7l.05,0a1.29,1.29,0,0,0,.67.14l.08.15a.67.67,0,0,0,.86.14.7.7,0,0,0,0-1,.77.77,0,0,0-.53-.18.3.3,0,0,0,0-.1c-.1-.38-.4-.44-.73-.42,0-.21,0-.46-.29-.5a.34.34,0,0,0-.39.23c-.11.28.12.39.32.51l-.11.38Z"/><path class="cls-1" d="M197.52,53.43a.6.6,0,0,0,.08-.76c-.15-.22-.38-.23-.68,0a.6.6,0,0,0-.2.65A.66.66,0,0,0,197.52,53.43Z"/><path class="cls-1" d="M196.91,71.77s.05-.09,0-.13a.12.12,0,0,0-.16,0,.13.13,0,0,0,0,.16A.17.17,0,0,0,196.91,71.77Z"/><path class="cls-1" d="M198.07,81.6s-.15.06-.14.13a.25.25,0,0,0,.15.19c.06,0,.24-.06.24-.15S198.15,81.61,198.07,81.6Z"/><path class="cls-1" d="M198.15,86.15c-.1,0-.14.17-.14.25s.08.15.15.15.13-.13.15-.2S198.26,86.15,198.15,86.15Z"/><path class="cls-1" d="M198,84.19c0-.22-.05-.55-.45-.49a.76.76,0,0,0-.54.85.89.89,0,0,0,1,.61c.29-.1.21-.49,0-.63S198,84.42,198,84.19Z"/><path class="cls-1" d="M198.16,73.76c-.08.06,0,.26,0,.31a.23.23,0,0,0,.25.05.23.23,0,0,0,0-.25C198.4,73.82,198.23,73.71,198.16,73.76Z"/><path class="cls-1" d="M197.54,66.33c-.11.09,0,.4.1.48s.25,0,.34,0a.18.18,0,0,0,0-.31C197.91,66.4,197.64,66.24,197.54,66.33Z"/><path class="cls-1" d="M198,75.29a.32.32,0,0,0,.17.24.43.43,0,0,0,.34-.06.35.35,0,0,0-.12-.5C198.31,74.91,198,75.05,198,75.29Z"/><path class="cls-1" d="M198.29,79.3c-.13.09-.33.17-.35.36s.22.21.29.19.47-.2.47-.34S198.34,79.27,198.29,79.3Z"/><path class="cls-1" d="M194.67,93.17a.28.28,0,0,0,0,.28.31.31,0,0,0,.28.06.32.32,0,0,0,.11-.32C195,93.09,194.73,93.12,194.67,93.17Z"/><path class="cls-1" d="M194.44,80.81a.36.36,0,0,0-.41-.32.38.38,0,0,0-.38.41.45.45,0,0,0,.47.33A.39.39,0,0,0,194.44,80.81Z"/><path class="cls-1" d="M193.11,86c0,.17.15.19.29.19a.24.24,0,0,0,.26-.31.88.88,0,0,0-.2-.28A.37.37,0,0,0,193.11,86Z"/><path class="cls-1" d="M196.49,89.78a.71.71,0,0,0-.63.75.81.81,0,0,0,.71.68.71.71,0,0,0,.72-.67A1.05,1.05,0,0,0,196.49,89.78Z"/><path class="cls-1" d="M195.33,86.15a.93.93,0,0,0-.2-.29.37.37,0,0,0-.35.4c0,.18.15.19.29.19S195.39,86.31,195.33,86.15Z"/><path class="cls-1" d="M197,78.44a.73.73,0,0,0-.68-.61c-.22,0-.44.31-.41.59a.49.49,0,0,0,.57.48C196.82,78.88,197.05,78.68,197,78.44Z"/><path class="cls-1" d="M195.44,87.43c-.09,0-.16.11-.17.18s.07.26.16.26.18-.19.18-.25S195.53,87.43,195.44,87.43Z"/><path class="cls-1" d="M202,59.15c-.07.06,0,.22,0,.26s.18.16.24.1a.22.22,0,0,0,0-.25C202.25,59.2,202.06,59.08,202,59.15Z"/><path class="cls-1" d="M202,68s-.17.14-.12.26.23.06.25,0a.34.34,0,0,0,.06-.24S202,68,202,68Z"/><path class="cls-1" d="M202.67,69.51a.37.37,0,0,0-.36.33.3.3,0,0,0,.17.36c.26.12.42,0,.6-.23C203,69.74,203,69.49,202.67,69.51Z"/><path class="cls-1" d="M193.26,82.83a.48.48,0,0,0-.54-.23.77.77,0,0,0-.29-.47c-.16.38-.31.76-.47,1.13l.1,0a.75.75,0,0,0,.31-.06.33.33,0,0,0,.06.26.49.49,0,0,0,.7,0A.62.62,0,0,0,193.26,82.83Z"/><path class="cls-1" d="M201.89,57.6c-.09-.08-.31-.13-.38-.07s-.1.25-.13.34c.16.22.32.19.43.1S202,57.72,201.89,57.6Z"/><path class="cls-1" d="M203.61,65.39c-.34,0-.64.4-.54.67a.55.55,0,0,0,.53.37.43.43,0,0,0,.46-.37c0-.18.23-.53,0-.71S203.74,65.37,203.61,65.39Z"/><path class="cls-1" d="M203.59,63.29a.34.34,0,0,0-.1.47.38.38,0,0,0,.49.07.32.32,0,0,0,.1-.46A.38.38,0,0,0,203.59,63.29Z"/><path class="cls-1" d="M200.32,67c-.12-.12-.28-.17-.38,0s-.25.41-.16.51.41,0,.53,0S200.44,67.08,200.32,67Z"/><path class="cls-1" d="M200,63.7a.38.38,0,0,0-.14-.48.37.37,0,0,0-.5,0,.56.56,0,0,0,0,.68A.53.53,0,0,0,200,63.7Z"/><path class="cls-1" d="M198.26,63.87a.45.45,0,0,0-.61,0,1.07,1.07,0,0,0,0,.42c.09.19.39.18.56,0A.34.34,0,0,0,198.26,63.87Z"/><path class="cls-1" d="M198.74,69.46a.62.62,0,0,0,0,.81.58.58,0,0,0,.84-.79A.61.61,0,0,0,198.74,69.46Z"/><path class="cls-1" d="M201,74.07c-.05.05,0,.18,0,.25s.16.13.24.07,0-.23,0-.29A.19.19,0,0,0,201,74.07Z"/><path class="cls-1" d="M200.57,58.15c.19.08.31,0,.41-.16a.34.34,0,0,0,0-.41c-.19-.35-.36-.4-.71-.19s-.52.3-.78.45c-.31-.45-.38-.46-.7-.11.05.29.24.37.52.33.26.63.66.65,1.12.07Z"/><path class="cls-1" d="M201.47,65.28a.4.4,0,0,0-.52-.1.38.38,0,0,0-.07.48.46.46,0,0,0,.53.11A.37.37,0,0,0,201.47,65.28Z"/><path class="cls-1" d="M190.07,35.41a.69.69,0,0,0,.32-.74.63.63,0,0,0-.67-.28.47.47,0,0,0-.29.5C189.55,35.27,189.83,35.5,190.07,35.41Z"/><path class="cls-1" d="M196.77,46.69c.07.16.2.14.32.1a.24.24,0,0,0,.17-.37,1,1,0,0,0-.27-.21A.36.36,0,0,0,196.77,46.69Z"/><path class="cls-1" d="M191.26,95.11a.23.23,0,0,0-.28.25c0,.1.09.36.31.35s.21-.28.2-.37A.33.33,0,0,0,191.26,95.11Z"/><path class="cls-1" d="M190.94,88.59a.38.38,0,0,0-.4.35.39.39,0,0,0,.36.39.42.42,0,0,0,.42-.39A.37.37,0,0,0,190.94,88.59Z"/><path class="cls-1" d="M190.29,91.32c-.16,0-.28.34-.16.6s.49.14.59,0S190.45,91.33,190.29,91.32Z"/><path class="cls-1" d="M192.89,91.46a.68.68,0,0,0-.7.72.54.54,0,0,0,.54.57c.38,0,.82-.41.79-.72A.62.62,0,0,0,192.89,91.46Z"/><path class="cls-1" d="M189.72,93.28c-.15.08-.15.59.06.66a.38.38,0,0,0,.43-.22C190.27,93.5,189.86,93.2,189.72,93.28Z"/><path class="cls-1" d="M193,89.46a.35.35,0,0,0-.28-.4.45.45,0,0,0-.33.1c-.17.15-.12.31.12.64C192.72,89.75,193,89.77,193,89.46Z"/><path class="cls-1" d="M192.69,87.36c-.22,0-.58.3-.59.48a.49.49,0,0,0,.54.5c.24,0,.41-.23.41-.56A.36.36,0,0,0,192.69,87.36Z"/><path class="cls-1" d="M192.35,84a.41.41,0,0,0-.42.39.38.38,0,0,0,.42.32.36.36,0,0,0,.35-.33A.34.34,0,0,0,192.35,84Z"/><path class="cls-1" d="M188.35,90.44c-.15.25-.29.52-.45.77a1.73,1.73,0,0,0,.21.69.46.46,0,0,0,.14.14.47.47,0,0,0,.76-.38,2.91,2.91,0,0,0-.07-.78A.68.68,0,0,0,188.35,90.44Z"/><path class="cls-1" d="M187.21,93a.63.63,0,0,0-.33.29c0,.14.18.34.35.3s.2-.15.25-.19C187.48,93.16,187.35,93,187.21,93Z"/><path class="cls-1" d="M188.79,92.55a.28.28,0,0,0,0,.25c.07.06.2,0,.25-.06s.06-.24,0-.3S188.82,92.5,188.79,92.55Z"/><path class="cls-1" d="M146.46,9.19a.5.5,0,0,0,0-.78.49.49,0,0,0-.76-.12.58.58,0,0,0-.09.83A.62.62,0,0,0,146.46,9.19Z"/><path class="cls-1" d="M162,16.48c.18,0,.21-.13.23-.26s-.11-.34-.28-.3a1.06,1.06,0,0,0-.3.16A.37.37,0,0,0,162,16.48Z"/><path class="cls-1" d="M165.52,23.85a2.15,2.15,0,0,0,1.23-.06.56.56,0,0,0,.16-.13.47.47,0,0,0-.29-.8,2.56,2.56,0,0,0-.78,0,.74.74,0,0,0-.55.67C165.29,23.68,165.35,23.82,165.52,23.85Z"/><path class="cls-1" d="M159.45,16.37c.19-.08.4-.15.36-.44s0-.43,0-.64a.32.32,0,0,0-.23-.45.42.42,0,0,0-.48.31,1,1,0,0,1-.18.36.55.55,0,0,0-.17.62A.77.77,0,0,0,159.45,16.37Z"/><path class="cls-1" d="M157.85,12.8a.26.26,0,0,0,.21-.12c0-.06,0-.25-.13-.26s-.17.15-.2.22S157.77,12.8,157.85,12.8Z"/><path class="cls-1" d="M157.6,18.28c.33.25.68.49,1,.74a.59.59,0,0,0,.07-.2.82.82,0,0,0,0-.32.33.33,0,0,0,.27,0,.48.48,0,0,0,.09-.68.61.61,0,0,0-.64-.22.51.51,0,0,0-.3.51A.83.83,0,0,0,157.6,18.28Z"/><path class="cls-1" d="M162.49,13.29s.16-.06.16-.13-.1-.15-.17-.18-.21,0-.22.14S162.42,13.28,162.49,13.29Z"/><path class="cls-1" d="M164.11,21a.39.39,0,0,0,.43-.3.43.43,0,0,0-.34-.47.36.36,0,0,0-.39.34A.37.37,0,0,0,164.11,21Z"/><path class="cls-1" d="M163.36,16.16c.06,0,.26,0,.27-.13s-.15-.2-.22-.21a.22.22,0,0,0-.21.15C163.19,16.06,163.3,16.14,163.36,16.16Z"/><path class="cls-1" d="M165.2,19.16c0-.21,0-.46-.28-.53a.35.35,0,0,0-.43.23.48.48,0,0,0,.06.35C164.67,19.39,164.84,19.36,165.2,19.16Z"/><path class="cls-1" d="M161.72,20.62a.36.36,0,0,0,.37.37h0a.69.69,0,0,0,0,.36.71.71,0,0,1-.07.48c.56.5,1.1,1,1.63,1.51a.69.69,0,0,0-.47-.57H163a.58.58,0,0,0,.15-.28,1.22,1.22,0,0,0-.09-.8.52.52,0,0,1,0-.3.47.47,0,0,0-.45-.66.37.37,0,0,0,0-.14c0-.16-.2-.32-.42-.34A.44.44,0,0,0,161.72,20.62Z"/><path class="cls-1" d="M163.2,19.33a.49.49,0,0,0,.56-.48c0-.23-.17-.43-.5-.47a.36.36,0,0,0-.46.3A.78.78,0,0,0,163.2,19.33Z"/><path class="cls-1" d="M157.41,15.39c.13,0,.43-.08.44-.27s-.23-.26-.35-.3a.24.24,0,0,0-.33.18C157.12,15.17,157.25,15.42,157.41,15.39Z"/><path class="cls-1" d="M161.52,18.1c.17,0,.21-.12.22-.25s-.1-.34-.27-.31a1.14,1.14,0,0,0-.31.17A.36.36,0,0,0,161.52,18.1Z"/><path class="cls-1" d="M149.56,12.19c.12-.08,0-.41-.07-.53a.21.21,0,0,0-.36,0c-.12.12-.2.26-.08.38S149.44,12.27,149.56,12.19Z"/><path class="cls-1" d="M147.67,12.47l.66.3a.33.33,0,0,0,0-.37.37.37,0,0,0-.54-.13A.34.34,0,0,0,147.67,12.47Z"/><path class="cls-1" d="M156.5,17a.45.45,0,0,0,.39-.43.4.4,0,0,0-.38-.37.37.37,0,0,0-.37.37A.38.38,0,0,0,156.5,17Z"/><path class="cls-1" d="M150.21,10a.2.2,0,0,0,.07-.24.2.2,0,0,0-.24-.05c-.06,0-.18.2-.13.28S150.16,10.08,150.21,10Z"/><path class="cls-1" d="M146.31,5.59a.28.28,0,0,0,.37-.14c.13-.25,0-.43-.19-.61-.23.07-.48.08-.49.37A.37.37,0,0,0,146.31,5.59Z"/><path class="cls-1" d="M147.82,11.26s0-.11,0-.14-.09-.06-.13,0a.1.1,0,1,0,.12.17Z"/><path class="cls-1" d="M149.46,13.27s0,0,0,.06l.59.28a.33.33,0,0,0,.2-.29.41.41,0,0,0-.38-.42A.38.38,0,0,0,149.46,13.27Z"/><path class="cls-1" d="M155.45,15.07s-.06,0-.06.1a.36.36,0,0,0,.31.41.44.44,0,0,0,.44-.31.35.35,0,0,0-.33-.4s-.05,0-.07,0,0-.06,0-.08-.27-.24-.36-.18-.09.31,0,.36A.41.41,0,0,0,155.45,15.07Z"/><path class="cls-1" d="M154.32,14.37a.48.48,0,0,0,.54-.5c0-.31-.15-.57-.39-.57a.71.71,0,0,0-.68.6C153.76,14.12,154,14.37,154.32,14.37Z"/><path class="cls-1" d="M150.68,7.22s.14-.16.09-.25-.23,0-.29,0a.16.16,0,0,0,0,.2C150.47,7.25,150.61,7.24,150.68,7.22Z"/><path class="cls-1" d="M155.79,12.54c.18,0,.24-.19.23-.27s-.14-.48-.28-.5-.28.33-.26.38S155.61,12.5,155.79,12.54Z"/><path class="cls-1" d="M151.46,11.91a.27.27,0,0,0,.26-.14.4.4,0,0,0,0-.34.35.35,0,0,0-.51.06C151.12,11.6,151.22,11.88,151.46,11.91Z"/><path class="cls-1" d="M152.83,12.15c.08-.06.07-.36,0-.48s-.23-.18-.41-.08a.22.22,0,0,0-.06.37C152.48,12.05,152.75,12.21,152.83,12.15Z"/><path class="cls-1" d="M160.53,14.07a.88.88,0,0,0,.73-.94c-.06-.3-.46-.27-.62-.11s-.12.08-.34,0-.56,0-.54.39A.76.76,0,0,0,160.53,14.07Z"/><path class="cls-1" d="M166.79,25.25a.26.26,0,0,0,.29.31.51.51,0,0,0,.32-.19c0-.09-.08-.42-.25-.44A.35.35,0,0,0,166.79,25.25Z"/><path class="cls-1" d="M160.08,18.7a.36.36,0,0,0-.29-.39.34.34,0,0,0-.42.3.4.4,0,0,0,.34.46A.37.37,0,0,0,160.08,18.7Z"/><path class="cls-1" d="M160.64,20.22c0-.3-.15-.59-.37-.59a.66.66,0,0,0-.51.32l.76.61A.62.62,0,0,0,160.64,20.22Z"/><path class="cls-1" d="M165,21.73a.53.53,0,0,0-.12-.43.41.41,0,0,0-.39,0,.44.44,0,0,0-.1.66A.39.39,0,0,0,165,21.73Z"/><path class="cls-1" d="M169.15,28.18a.67.67,0,0,0-.51-.64c-.21,0-.49.27-.53.55,0,0,0,.05,0,.07l.37.46C168.8,28.65,169.14,28.42,169.15,28.18Z"/><path class="cls-1" d="M167.13,21.2c-.09-.12-.66.19-.69.35s.3.32.58.23S167.21,21.31,167.13,21.2Z"/><path class="cls-1" d="M130.1,6.93s-.1,0-.1,0-.08.1-.05.14.08,0,.14,0S130.12,7,130.1,6.93Z"/><path class="cls-1" d="M121.93,6a.49.49,0,0,0-.38-.21.38.38,0,0,0-.31.24.43.43,0,0,0,.4.53A.38.38,0,0,0,121.93,6Z"/><path class="cls-1" d="M167.4,22.91c-.07.07,0,.24.08.27a.23.23,0,0,0,.25,0c.07-.06,0-.2,0-.26S167.47,22.83,167.4,22.91Z"/><path class="cls-1" d="M152.73,13.74a.13.13,0,0,0-.11-.11s-.09.07-.09.1a.12.12,0,0,0,.07.11C152.65,13.84,152.73,13.81,152.73,13.74Z"/><path class="cls-1" d="M112.56,8.76a.44.44,0,0,0,0-.57.4.4,0,0,0-.54,0,.37.37,0,0,0,0,.52.4.4,0,0,0,.39.13h0A.32.32,0,0,0,112.56,8.76Z"/><path class="cls-1" d="M142.94,10c.05,0,0-.24,0-.33a.18.18,0,0,0-.32,0c-.08.09-.25.34-.17.45S142.85,10.09,142.94,10Z"/><path class="cls-1" d="M166.33,15.92a.79.79,0,0,0,.76-.62.71.71,0,0,0-.57-.79,1,1,0,0,0-.86.7A.71.71,0,0,0,166.33,15.92Z"/><path class="cls-1" d="M146.46,11.23a.44.44,0,0,0,.15-.56.54.54,0,0,0-.7-.15.37.37,0,0,0-.19.49A.61.61,0,0,0,146.46,11.23Z"/><path class="cls-1" d="M170.17,25.58c.08,0,.24,0,.25-.16a.23.23,0,0,0-.19-.24c-.07,0-.29,0-.33.15S170.09,25.57,170.17,25.58Z"/><path class="cls-1" d="M169,22.37a.36.36,0,0,0-.17-.45c-.21-.08-.56.28-.5.44S168.88,22.57,169,22.37Z"/><path class="cls-1" d="M170,28.29s.09-.05.11-.08,0-.1-.06-.09-.12,0-.13.07S169.93,28.25,170,28.29Z"/><path class="cls-1" d="M169.08,17.49a.27.27,0,0,0,.09-.27c0-.06-.2-.19-.3-.15s-.1.3-.06.36S169,17.52,169.08,17.49Z"/><path class="cls-1" d="M167.77,24.82a.65.65,0,0,0,.25.35c.13.07.36-.13.34-.31a.77.77,0,0,0-.16-.27C167.93,24.56,167.74,24.67,167.77,24.82Z"/><path class="cls-1" d="M167.52,19.77a.54.54,0,0,0,.63-.47c0-.38-.3-.86-.62-.87a.63.63,0,0,0-.64.56A.69.69,0,0,0,167.52,19.77Z"/><path class="cls-1" d="M172.75,25.6a.25.25,0,0,0,.16-.17c0-.07-.1-.18-.16-.18a.21.21,0,0,0-.24.18C172.51,25.58,172.68,25.6,172.75,25.6Z"/><path class="cls-1" d="M167.43,27.38c.24-.3.29-.71-.06-.92a.65.65,0,0,0-.76,0C166.89,26.75,167.16,27.07,167.43,27.38Z"/><path class="cls-1" d="M170.61,28.28a.44.44,0,0,0-.31.1c.07.16,0,.49.22.54s.34-.24.49-.34l0-.12A.34.34,0,0,0,170.61,28.28Z"/><path class="cls-1" d="M170.37,24.6c.14.1.55,0,.59-.19s0-.46-.36-.44S170.24,24.5,170.37,24.6Z"/><path class="cls-1" d="M170.53,21.35c.1,0,.37,0,.38-.26s-.24-.24-.33-.24a.35.35,0,0,0-.27.2A.23.23,0,0,0,170.53,21.35Z"/><path class="cls-1" d="M120.27,6.07a.37.37,0,1,0,.6-.43.43.43,0,0,0-.57-.09A.37.37,0,0,0,120.27,6.07Z"/><path class="cls-1" d="M144.73,6c.12,0,.08-.22.05-.24a.32.32,0,0,0-.23-.08s-.09.15-.07.18S144.61,6,144.73,6Z"/><path class="cls-1" d="M117.78,8.07c-.2-.21-.51-.28-.66-.13a.39.39,0,0,0-.12.23Z"/><path class="cls-1" d="M124.7,5.27a.24.24,0,0,0,.17-.19c0-.08-.13-.15-.21-.15s-.22.1-.21.2S124.64,5.28,124.7,5.27Z"/><path class="cls-1" d="M123.41,7.06a.29.29,0,0,0,.39.08,2.11,2.11,0,0,0,.82-.93.5.5,0,0,0,0-.2.47.47,0,0,0-.78-.35,3.18,3.18,0,0,0-.56.54A.75.75,0,0,0,123.41,7.06Z"/><path class="cls-1" d="M110.52,8.31a.37.37,0,0,0,.51.07.44.44,0,0,0,.08-.54.36.36,0,0,0-.51,0A.34.34,0,0,0,110.52,8.31Z"/><path class="cls-1" d="M115.84,7.37a.33.33,0,0,0-.08.5.39.39,0,0,0,.56.08.36.36,0,0,0,0-.52A.35.35,0,0,0,115.84,7.37Z"/><path class="cls-1" d="M114.48,8.43s0,.06-.06.08l.85-.12-.09-.08a.37.37,0,0,0,.17-.21c.07-.24-.17-.51-.43-.55a.62.62,0,0,0-.6.31A.5.5,0,0,0,114.48,8.43Z"/><path class="cls-1" d="M116.9,6c.12-.13.06-.23,0-.34a.25.25,0,0,0-.41,0,1.09,1.09,0,0,0-.09.33A.37.37,0,0,0,116.9,6Z"/><path class="cls-1" d="M118.84,7.59a.36.36,0,0,0,.52,0h0a.87.87,0,0,0,.23.29l0,0,1.52-.1a1.21,1.21,0,0,0-.59-.43.57.57,0,0,1-.24-.19.48.48,0,0,0-.79-.14.19.19,0,0,0-.07-.12.41.41,0,0,0-.53.06A.43.43,0,0,0,118.84,7.59Z"/><path class="cls-1" d="M138.82,6.36c.28-.19.41-.6.26-.8a.57.57,0,0,0-.72,0,.5.5,0,0,0-.07.69C138.44,6.44,138.63,6.49,138.82,6.36Z"/><path class="cls-1" d="M138.77,9.19a.38.38,0,0,0-.12.16l.69.18a.36.36,0,0,0-.57-.34Z"/><path class="cls-1" d="M136.58,8.87l.79.16a.75.75,0,0,0,.15-1,.75.75,0,0,0-.9-.12.66.66,0,0,0-.05,1Z"/><path class="cls-1" d="M140.17,8.11a.54.54,0,0,0-.16-.66.37.37,0,0,0-.49.11.36.36,0,0,0,0,.49A.57.57,0,0,0,140.17,8.11Z"/><path class="cls-1" d="M143.42,10.77l1.26.47a.72.72,0,0,0,0-.61.69.69,0,0,0-.9-.23A.81.81,0,0,0,143.42,10.77Z"/><path class="cls-1" d="M143.72,7.94c.11-.09,0-.41,0-.54s-.3-.15-.43,0-.18.26-.05.38S143.62,8,143.72,7.94Z"/><path class="cls-1" d="M142,6.71a.46.46,0,0,0,.15-.52.36.36,0,0,0-.48-.1.41.41,0,0,0-.15.52A.4.4,0,0,0,142,6.71Z"/><path class="cls-1" d="M140.05,9.19a.44.44,0,0,0-.07.5l.38.11.11,0a.36.36,0,0,0,.06-.55A.33.33,0,0,0,140.05,9.19Z"/><path class="cls-1" d="M128.05,6.81a.59.59,0,0,0-.25.68c.1.21.45.24.75.07a.43.43,0,0,0,.13-.52A.52.52,0,0,0,128.05,6.81Z"/><path class="cls-1" d="M128.52,7.79l1,.06a.42.42,0,0,0-.08-.28.65.65,0,0,0-.81-.09A.38.38,0,0,0,128.52,7.79Z"/><path class="cls-1" d="M126.5,6.27c.14,0,.15-.35,0-.45a.65.65,0,0,0-.31-.08c-.21.17-.27.38-.13.46A.57.57,0,0,0,126.5,6.27Z"/><path class="cls-1" d="M130.93,7.1c.17-.11.06-.41.09-.58l-.1-.07a.36.36,0,0,0-.39.14.48.48,0,0,0-.14.29C130.55,7,130.76,7.21,130.93,7.1Z"/><path class="cls-1" d="M131.68,8.06h0Z"/><path class="cls-1" d="M133.89,7.08l0,0,.22.45c-.48.29-.49.35-.16.7.29,0,.38-.21.36-.5.65-.21.7-.61.15-1.11,0,0,0-.09,0-.13s0-.31-.13-.43A.36.36,0,0,0,134,6c-.26.12-.36.24-.34.43a.26.26,0,0,0-.2.07c-.18.15-.26.49-.14.61S133.74,7.2,133.89,7.08Z"/><path class="cls-1" d="M131.37,6.47c.07-.22.19-.43,0-.6a.32.32,0,0,0-.45,0,.44.44,0,0,0-.11.34C130.85,6.52,131.08,6.54,131.37,6.47Z"/><path class="cls-1" d="M134.43,5.21c-.08-.13-.24-.23-.36-.1a.42.42,0,0,0-.11.37c.06.1.25.12.34.16C134.53,5.49,134.51,5.33,134.43,5.21Z"/><path class="cls-1" d="M125.7,7.21a.26.26,0,0,0,.42,0,.5.5,0,0,0,.09-.36c0-.09-.36-.23-.49-.12A.33.33,0,0,0,125.7,7.21Z"/><path class="cls-1" d="M57,62.89h0a1.8,1.8,0,0,1,1.15,2.37A13.41,13.41,0,0,0,56.9,70.6a2.12,2.12,0,0,1-2.14,1.85h0a2,2,0,0,1-2.14-2,15.92,15.92,0,0,1,1.48-6.31A2.35,2.35,0,0,1,57,62.89Z"/><ellipse class="cls-1" cx="53.97" cy="79.3" rx="2.08" ry="1.7"/><path class="cls-1" d="M94.5,122.3h0a2.72,2.72,0,0,1,3.11-.16,21.07,21.07,0,0,0,5.8,2.62A1.86,1.86,0,0,1,104.9,127h0c-.31.93-1.68,1.45-3,1a24.75,24.75,0,0,1-6.87-3.1C93.9,124.24,93.7,123,94.5,122.3Z"/><ellipse class="cls-1" cx="112.61" cy="129.73" rx="2.08" ry="1.7"/><path class="cls-1" d="M171.93,108.66h0a1.71,1.71,0,0,1,.77-2.47,18.73,18.73,0,0,0,4.84-3.72,2.68,2.68,0,0,1,3.06-.47h0a1.62,1.62,0,0,1,.28,2.66,21.85,21.85,0,0,1-5.72,4.4A2.54,2.54,0,0,1,171.93,108.66Z"/><ellipse class="cls-1" cx="186.15" cy="96.84" rx="2.08" ry="1.7"/><path class="cls-1" d="M181.72,44.92h0a2.38,2.38,0,0,1-2.63-1.36,15,15,0,0,0-2.82-4.92,1.68,1.68,0,0,1,.4-2.53h0a2.53,2.53,0,0,1,3.18.6,17.64,17.64,0,0,1,3.33,5.82A1.78,1.78,0,0,1,181.72,44.92Z"/><ellipse class="cls-1" cx="172.4" cy="30.19" rx="2.08" ry="1.7"/><path class="cls-2" d="M185,50.16c-.75-1.6-3.26-2.48-3.91.38a50,50,0,0,1-2.73,9.86c-2.77,6.48-9.49,4.55-8.77-4.63a2.44,2.44,0,0,0,0-.28c1.55-14.35-10.23-30.15-26.93-36.31s-26-5.82-27.92-5.72c-2.08.11-3.85,1.82-.71,3.17a79.21,79.21,0,0,1,10.69,5.05c6.39,3.89,4.34,8.18-3.6,6.57-16-4.49-37-1.08-47.43,8.93s-14.82,18.54-15.29,20,.91,3.56,3.46,1.54A69.85,69.85,0,0,1,71.07,52c6-3.49,11,1.23,6.41,6.49a30.72,30.72,0,0,0-2.33,2.07l-.2.15a.39.39,0,0,0-.19.23c-10.83,10.91-13.69,28.17-4.93,40.37s16.92,17.37,18.51,18.2c1.76.92,4.42.4,2.86-2.22a55,55,0,0,1-5-9.24C84,102.19,89.8,99.49,95,105c8.49,12.21,28.44,19.6,44.79,16.39s25.48-7.58,26.95-8.56c1.62-1.07,1.86-3.31-1.66-2.92a86.84,86.84,0,0,1-12.28,1c-7.56-.13-7.62-5.46.41-7.68.87-.16,1.73-.34,2.59-.55l.32,0a.82.82,0,0,0,.3-.11c15.47-4,29-16.4,30.2-28.73S185.71,51.6,185,50.16ZM159.52,71.77l0,0,9.62.73Z"/><path class="cls-1" d="M107.68,14.09h0a2.25,2.25,0,0,1-2.39,1.63,23.75,23.75,0,0,0-6.58.67A2.48,2.48,0,0,1,95.9,15.3h0c-.42-.91.32-2,1.68-2.29a27.79,27.79,0,0,1,7.78-.8C106.78,12.23,107.81,13.12,107.68,14.09Z"/><ellipse class="cls-1" cx="87.71" cy="16.8" rx="2.08" ry="1.7"/><path class="cls-3" d="M127.18,44l8.18-1.47c8.8-1.58,17.32,3.49,18.21,10.85l.82,6.82a10.82,10.82,0,0,0,2,5l4.23,5.92c4.56,6.37,1.31,14.58-7,17.54L146,91.41a15.11,15.11,0,0,0-5.22,3.11l-5.56,5.12c-6,5.53-16.52,5.53-22.51,0l-5.56-5.12a15,15,0,0,0-5.22-3.11l-7.67-2.75c-8.26-3-11.51-11.17-7-17.54l4.23-5.92a10.94,10.94,0,0,0,2-5l.81-6.82c.89-7.36,9.41-12.43,18.22-10.85L120.73,44A17.93,17.93,0,0,0,127.18,44Z"/><path class="cls-3" d="M141.48,98.29c.09-.06.07-.31.08-.48a.14.14,0,0,0-.13-.18A.44.44,0,0,0,141,98,.33.33,0,0,0,141.48,98.29Z"/><path class="cls-3" d="M141.18,99.49a.73.73,0,0,0,.71-.56c0-.2-.18-.42-.42-.45a.5.5,0,0,0-.62.42C140.82,99.17,141,99.46,141.18,99.49Z"/><path class="cls-3" d="M136.31,98.63l-.31.28A.7.7,0,0,0,136.31,98.63Z"/><path class="cls-3" d="M123.66,103.77l-.47,0A.69.69,0,0,0,123.66,103.77Z"/><path class="cls-3" d="M134.27,100.56a.23.23,0,0,0,0-.13,2.31,2.31,0,0,1-.24.2l.05,0A.31.31,0,0,0,134.27,100.56Z"/><path class="cls-3" d="M134.18,102.39a.33.33,0,0,0,.3.38.39.39,0,0,0,.43-.31c0-.2-.09-.3-.28-.34A.38.38,0,0,0,134.18,102.39Z"/><path class="cls-3" d="M162.65,56.92a.42.42,0,0,0,.44-.31.44.44,0,0,0-.36-.45.34.34,0,0,0-.38.29A.37.37,0,0,0,162.65,56.92Z"/><path class="cls-3" d="M165.37,57.72a.74.74,0,0,0-.88.18c-.19.26-.12.42.38.82-.13.2-.29.4,0,.61a.38.38,0,0,0,.49,0,.27.27,0,0,0,.12-.2,4.45,4.45,0,0,0-.14-.55C165.56,58.24,165.6,57.87,165.37,57.72Z"/><path class="cls-3" d="M121.75,103.76l0,0a.72.72,0,0,0,1-.06c-.39,0-.78-.07-1.17-.12A.38.38,0,0,0,121.75,103.76Z"/><path class="cls-3" d="M162.81,59.49a.36.36,0,0,0,.47-.23.61.61,0,0,0-.39-.66.43.43,0,0,0-.49.3A.54.54,0,0,0,162.81,59.49Z"/><path class="cls-3" d="M163.29,61.54c.16.05.36-.26.3-.54s-.43-.26-.56-.19S163.14,61.49,163.29,61.54Z"/><path class="cls-3" d="M163.13,57.35a.33.33,0,0,0,.16.28.23.23,0,0,0,.33-.17c0-.11,0-.38-.21-.42S163.14,57.26,163.13,57.35Z"/><path class="cls-3" d="M145.87,98.22a.5.5,0,0,0,.87-.35,3.42,3.42,0,0,0-.17-.77.76.76,0,0,0-.79-.36c-.17.05-.48.26-.32.57C145.94,97.64,145.6,97.88,145.87,98.22Z"/><path class="cls-3" d="M162.91,54.33a.52.52,0,0,0-.5.37c0,.24.27.72.46.73s.51-.27.55-.59S163.28,54.37,162.91,54.33Z"/><path class="cls-3" d="M163.1,77.42c.09-.35-.08-.6-.46-.72a9.61,9.61,0,0,1,0,1.2A.72.72,0,0,0,163.1,77.42Z"/><path class="cls-3" d="M88,53.66c-.1-.13-.54-.06-.57.14a.33.33,0,0,0,.25.36C87.87,54.19,88.1,53.78,88,53.66Z"/><path class="cls-3" d="M119.26,103.71a.33.33,0,0,0,.19.26.48.48,0,0,0,.44-.1.5.5,0,0,0,.32.11c.21,0,.29-.16.37-.32l.1-.18c-.3,0-.6-.12-.89-.19,0,0,0,0,0,0a.38.38,0,0,0-.52.21.09.09,0,0,0,0,0,.25.25,0,0,0-.2-.17c-.18,0-.35.06-.34.23a1,1,0,0,0,.13.33A.37.37,0,0,0,119.26,103.71Z"/><path class="cls-3" d="M90.15,89.75a1.26,1.26,0,0,0,.29.27c.16.07.28,0,.32-.2s0-.3-.14-.36A.41.41,0,0,0,90.15,89.75Z"/><path class="cls-3" d="M88.41,90.68a.73.73,0,0,0,.94-.21.41.41,0,0,0,.17.2.32.32,0,0,0,.45-.15.37.37,0,0,0-.17-.46.31.31,0,0,0-.39.07.81.81,0,0,0-.43-.67.66.66,0,0,0-.83.29A.63.63,0,0,0,88.41,90.68Z"/><path class="cls-3" d="M89.79,86.23a.18.18,0,0,0-.13.2.27.27,0,0,0,.23.18c.09,0,.13-.15.12-.24h0l-.14-.11S89.82,86.22,89.79,86.23Z"/><path class="cls-3" d="M85.21,76.58a.5.5,0,0,0,0,.17s0,0,.06.08c0-.14,0-.28,0-.42a.54.54,0,0,0,0,.11Z"/><path class="cls-3" d="M88.31,87.48a.82.82,0,0,0,.71-.41c0-.26-.1-.47-.41-.56s-.46,0-.55.38S88.08,87.43,88.31,87.48Z"/><path class="cls-3" d="M84.83,78.72a1.18,1.18,0,0,0,.41.4l.15,0a12.36,12.36,0,0,1-.14-1.35h0l0,0c0-.15,0-.31,0-.46a.4.4,0,0,0-.15-.14A15.24,15.24,0,0,0,84.83,78.72Z"/><path class="cls-3" d="M92.14,88.24a.22.22,0,0,0,.14.24c0,.13,0,.24.12.37s.36.41.59.21.11-.38.05-.44-.08-.26,0-.21.17,0,.25-.14c-.29-.13-.58-.25-.86-.39a.48.48,0,0,0-.06.21A.2.2,0,0,0,92.14,88.24Z"/><path class="cls-3" d="M89,85.71a.29.29,0,0,0,.15,0c-.21-.2-.41-.41-.61-.61a.05.05,0,0,0,0,0A.65.65,0,0,0,89,85.71Z"/><path class="cls-3" d="M90.66,87.89a.14.14,0,0,0-.09-.21c-.1,0-.16,0-.2.09a.13.13,0,0,0,.08.2C90.56,88,90.62,88,90.66,87.89Z"/><path class="cls-3" d="M96.64,96.57a.52.52,0,0,0,.62-.26.6.6,0,0,0-.29-.67c-.21-.08-.49.14-.59.46A.42.42,0,0,0,96.64,96.57Z"/><path class="cls-3" d="M95.41,94c0-.07-.1-.16-.16-.19s-.21,0-.23.11.14.18.21.2S95.4,94.05,95.41,94Z"/><path class="cls-3" d="M97.54,100.14c0-.21.06-.47-.24-.55a.33.33,0,0,0-.44.2.43.43,0,0,0,0,.35C97,100.32,97.17,100.31,97.54,100.14Z"/><path class="cls-3" d="M93,89.65s.18,0,.19-.07a.3.3,0,0,0-.06-.23.21.21,0,0,0-.29.05C92.79,89.53,93,89.61,93,89.65Z"/><path class="cls-3" d="M94.56,90.05c.15-.11.13-.23-.1-.54a.32.32,0,0,0-.33.45A.33.33,0,0,0,94.56,90.05Z"/><path class="cls-3" d="M92.67,92.23a.48.48,0,0,0,.19-.34c0-.27-.28-.33-.54-.32-.14.17-.27.35-.12.56A.32.32,0,0,0,92.67,92.23Z"/><path class="cls-3" d="M93.37,93.49a.59.59,0,1,0,.27-1.13.46.46,0,0,0-.54.37C93,93,93.16,93.43,93.37,93.49Z"/><path class="cls-3" d="M165.29,65.2a.52.52,0,0,0-.33.58.36.36,0,0,0,.42.26.35.35,0,0,0,.36-.33C165.77,65.41,165.41,65.2,165.29,65.2Z"/><path class="cls-3" d="M159.23,67.72a.35.35,0,0,0,.25.46.41.41,0,0,0,.5-.28.37.37,0,0,0-.33-.41A.36.36,0,0,0,159.23,67.72Z"/><path class="cls-3" d="M161.9,57.84s.13,0,.13-.09-.06-.11-.1-.12-.09.07-.1.1A.12.12,0,0,0,161.9,57.84Z"/><path class="cls-3" d="M160.73,63.12c.19-.11.19-.28,0-.66-.21,0-.46-.08-.57.21a.36.36,0,0,0,.18.46A.47.47,0,0,0,160.73,63.12Z"/><path class="cls-3" d="M161.25,56.46c.16,0,.32,0,.31-.22s-.09-.45-.24-.48-.28.3-.32.43S161.06,56.46,161.25,56.46Z"/><path class="cls-3" d="M160.81,60.76h.11a.48.48,0,0,0,.46.45.6.6,0,0,0,.64-.54.56.56,0,0,0-.35-.54.53.53,0,0,0-.38-.59.85.85,0,0,0-.95.51A.62.62,0,0,0,160.81,60.76Z"/><path class="cls-3" d="M160,64.78a.75.75,0,0,0,.69-.31.47.47,0,0,0-.19-.52s.1,0,.1-.07,0-.23-.08-.27-.21.11-.21.14a.33.33,0,0,0,0,.13l-.06,0c-.23-.07-.45.12-.54.44A.36.36,0,0,0,160,64.78Z"/><path class="cls-3" d="M147,44.37a14.18,14.18,0,0,1,1.45.91c.09-.07.17-.13.22-.12s.3.6.75.64a.68.68,0,0,0,.45-.12,21.94,21.94,0,0,0-1.83-2h0a.36.36,0,0,0-.14,0,.22.22,0,0,0-.18,0A1.2,1.2,0,0,0,147,44.37Z"/><path class="cls-3" d="M150.33,46.32h.08l-.42-.55A.44.44,0,0,0,150.33,46.32Z"/><path class="cls-3" d="M145.84,43.72v0q.32.14.63.3s0,0,0,0a.42.42,0,0,0,0-.63,1.23,1.23,0,0,1,.41.1.43.43,0,0,0,.35-.09,2.82,2.82,0,0,1,.36-.24l-.67-.59-.07.06-.25-.14a.33.33,0,0,0-.21-.05.23.23,0,0,0,.19-.15l-.43-.35a.63.63,0,0,0-.12.23c0,.17.09.27.26.27h.06a.4.4,0,0,0-.2.09.54.54,0,0,0-.12.56.33.33,0,0,0,.19.26A.51.51,0,0,0,145.84,43.72Z"/><path class="cls-3" d="M145,41.65a.31.31,0,0,0,.44-.18s0,0,0,0l-.54-.38a.37.37,0,0,0-.1.16A.32.32,0,0,0,145,41.65Z"/><path class="cls-3" d="M152.08,49.11l0,0c-.17-.33-.35-.67-.55-1l-.22,0A13.06,13.06,0,0,1,152.08,49.11Z"/><path class="cls-3" d="M165.4,53.77a.67.67,0,0,0,.51.24l-.21-1.07h0A.68.68,0,0,0,165.4,53.77Z"/><path class="cls-3" d="M165.26,56.14a.47.47,0,0,0,.36.6.83.83,0,0,0,.57-.47c0-.21-.1-.35-.39-.4S165.3,55.91,165.26,56.14Z"/><path class="cls-3" d="M164,59.23c-.1.19.17.53.33.54a.76.76,0,0,0-.49.51.7.7,0,0,0,1.33.39.9.9,0,0,0-.56-.92.74.74,0,0,0-.21,0c.15-.09.25-.52.08-.63A.39.39,0,0,0,164,59.23Z"/><path class="cls-3" d="M165.48,52.39h.1c0-.17-.07-.34-.11-.51a.27.27,0,0,0-.16.25A.31.31,0,0,0,165.48,52.39Z"/><path class="cls-3" d="M165.22,50.83a.18.18,0,0,0,.05.23C165.25,51,165.24,50.91,165.22,50.83Z"/><path class="cls-3" d="M164,55.07c-.11,0-.3.24-.29.3s.15.32.25.3a.36.36,0,0,0,.25-.24C164.23,55.34,164.1,55.08,164,55.07Z"/><path class="cls-3" d="M161.7,63.59a.4.4,0,0,0,.76.2.44.44,0,0,0,0-.23l.08,0a.26.26,0,0,0,.26-.1.45.45,0,0,0,.61.13.39.39,0,0,0-.11-.63.57.57,0,0,0-.44.06.44.44,0,0,0-.06.12c0-.14-.1-.28-.19-.29s-.3.27-.36.39a.16.16,0,0,0,0,.07h0A.44.44,0,0,0,161.7,63.59Z"/><path class="cls-3" d="M87.33,59.76a.32.32,0,0,0-.3,0c-.15.12,0,.3.06.34s.46.2.57.11,0,0,0-.09l.07,0a.33.33,0,0,0-.07-.5.3.3,0,0,0-.29,0A.35.35,0,0,0,87.33,59.76Z"/><path class="cls-3" d="M166.5,62.36c.06-.36-.05-.56-.32-.61a.66.66,0,0,0-.58.23,1.21,1.21,0,0,0,0-.46.5.5,0,0,0-.1-.17.47.47,0,0,0-.84.18,3.15,3.15,0,0,0-.09.42.46.46,0,0,0-.14.2.19.19,0,0,0,.14.25.77.77,0,0,0,.57.53c.17,0,.32,0,.37-.18l.09-.33a.67.67,0,0,0,.43.41C166.19,62.87,166.46,62.63,166.5,62.36Z"/><path class="cls-3" d="M166.46,58c0-.21,0-.42-.06-.63-.09,0-.16.12-.17.31A.32.32,0,0,0,166.46,58Z"/><path class="cls-3" d="M165.47,55.32a.38.38,0,0,0,.05-.49.3.3,0,0,0-.37-.15c-.27.09-.29.31-.29.58C165.09,55.36,165.27,55.53,165.47,55.32Z"/><path class="cls-3" d="M162.14,51.75s.07.17.1.17a.32.32,0,0,0,.22-.12.22.22,0,0,0-.11-.27C162.2,51.49,162.16,51.68,162.14,51.75Z"/><path class="cls-3" d="M163.12,46s.13,0,.13-.09-.06-.11-.1-.12a.17.17,0,0,0-.1.1S163.07,46,163.12,46Z"/><path class="cls-3" d="M162.7,50.1a.38.38,0,0,0,.41-.25c0-.14-.15-.29-.36-.32s-.26,0-.27.2S162.5,50.06,162.7,50.1Z"/><path class="cls-3" d="M162.16,46.54a.32.32,0,0,0,.22-.12c0-.05,0-.25-.11-.26s-.19.14-.21.21S162.13,46.54,162.16,46.54Z"/><path class="cls-3" d="M163.64,47.63c.06,0,.28,0,.28-.11a.25.25,0,0,0-.18-.19c-.08,0-.24,0-.24.08A.24.24,0,0,0,163.64,47.63Z"/><path class="cls-3" d="M163.82,46.84a.39.39,0,0,0,.2-.05c-.07-.23-.15-.45-.22-.67a.38.38,0,0,0-.2.3C163.57,46.66,163.66,46.83,163.82,46.84Z"/><path class="cls-3" d="M163.88,52.72c.36.08.68-.19.77-.64a.61.61,0,0,0-.51-.75.78.78,0,0,0-.84.58A.82.82,0,0,0,163.88,52.72Z"/><path class="cls-3" d="M164.57,50.42a.3.3,0,0,0,.4-.24.32.32,0,0,0-.23-.37c-.15-.05-.47.2-.43.34A.41.41,0,0,0,164.57,50.42Z"/><path class="cls-3" d="M95.18,95.23c.08-.26-.06-.45-.4-.55s-.58.07-.63.25a.65.65,0,0,0,.38.71A.6.6,0,0,0,95.18,95.23Z"/><path class="cls-3" d="M96.56,92.49a.5.5,0,0,0-.18-.57,4.23,4.23,0,0,0-.42-.25,2,2,0,0,0,0-.24.75.75,0,0,0-.58-.59.86.86,0,0,0-.75.37.45.45,0,0,0-.07.32.69.69,0,0,0,.78.56l.25,0a3.14,3.14,0,0,0,0,.45c.05.27.16.36.43.35A.55.55,0,0,0,96.56,92.49Z"/><path class="cls-3" d="M95.76,96.81c0,.09.09.19.15.2s.26,0,.28-.1-.14-.22-.21-.23S95.78,96.72,95.76,96.81Z"/><path class="cls-3" d="M126.88,104.32a.75.75,0,0,0,.66-.29.35.35,0,0,0,.41.15c.17-.08.17-.21,0-.55a.27.27,0,0,0-.25,0,.63.63,0,0,0,0-.27c-.43.09-.87.17-1.31.22A.84.84,0,0,0,126.88,104.32Z"/><path class="cls-3" d="M133.18,101.54a.32.32,0,0,0,.42-.22.41.41,0,0,0-.09-.36l-.57.38A.33.33,0,0,0,133.18,101.54Z"/><path class="cls-3" d="M136.24,100.6a.37.37,0,0,0-.35-.39.4.4,0,0,0-.19,0,.85.85,0,0,0-.59-.52c-.26.24-.53.46-.81.68a.8.8,0,0,0,.52.68.78.78,0,0,0,.7-.26.25.25,0,0,0,.2.16A.62.62,0,0,0,136.24,100.6Z"/><path class="cls-3" d="M129.66,102.86l-.37.11A.5.5,0,0,0,129.66,102.86Z"/><path class="cls-3" d="M135,104c0-.13-.16-.29-.37-.32a.21.21,0,0,0-.27.2.81.81,0,0,0,0,.16.31.31,0,0,0-.28.23.31.31,0,0,0,.23.41.36.36,0,0,0,.47-.23.28.28,0,0,0,0-.22A.4.4,0,0,0,135,104Z"/><path class="cls-3" d="M129.65,103.56c-.07.21,0,.34.19.35s.33,0,.37-.14-.1-.31-.2-.4S129.69,103.4,129.65,103.56Z"/><path class="cls-3" d="M143.65,43a16.35,16.35,0,0,1,1.58.55c.72-.17.71-.74.51-1.22a.77.77,0,0,0-.33-.38.8.8,0,0,0-.68,0c-.25.08-.5,0-.7.13A1.18,1.18,0,0,0,143.65,43Z"/><path class="cls-3" d="M85.76,61.52c.07-.06,0-.23-.05-.29a.15.15,0,0,0-.2,0,.23.23,0,0,0,0,.24S85.69,61.59,85.76,61.52Z"/><path class="cls-3" d="M86.53,82.51a.34.34,0,0,0,.19.44.65.65,0,0,0,.22.08c-.14-.22-.29-.44-.41-.66A.65.65,0,0,1,86.53,82.51Z"/><path class="cls-3" d="M86.55,57.49a.49.49,0,0,0,0,.74c.23.22.54.25.69.06a.68.68,0,0,0,.1-.69.14.14,0,0,0,.1-.05s0-.1,0-.13a.12.12,0,0,0-.15,0h0a.56.56,0,0,1,0-.08A.53.53,0,0,0,86.55,57.49Z"/><path class="cls-3" d="M87.69,87.12a.42.42,0,0,0,.1-.79.57.57,0,0,0-.6.33A.54.54,0,0,0,87.69,87.12Z"/><path class="cls-3" d="M87,85.76c.15,0,.38-.24.4-.43a.26.26,0,0,0,0-.12.37.37,0,0,0,.38-.22.42.42,0,0,0-.26-.51.41.41,0,0,0-.47.24.41.41,0,0,0,.08.33c-.3,0-.5.1-.51.25a.31.31,0,0,0,0,.08.54.54,0,0,0-.26-.2c-.09.06-.27.12-.35.25s0,.43.25.5a.34.34,0,0,0,.42-.24.34.34,0,0,0,0-.22A.63.63,0,0,0,87,85.76Z"/><path class="cls-3" d="M91.73,92c.12-.28-.19-.54-.18-.87,0-.78-.57-.89-1.09-.81a.84.84,0,0,0-.44.25.8.8,0,0,0-.18.65c0,.27-.15.48,0,.72a1.12,1.12,0,0,0,1.21.58A.92.92,0,0,0,91.73,92Z"/><path class="cls-3" d="M90.53,93.13a.17.17,0,0,0,.11.16.24.24,0,0,0,.21-.11c0-.05,0-.25-.1-.27S90.56,93.05,90.53,93.13Z"/><path class="cls-3" d="M85.81,84.28c-.26,0-.3.24-.28.46a.42.42,0,0,0,.65,0C86.14,84.47,86.08,84.24,85.81,84.28Z"/><path class="cls-3" d="M94.48,96.67a1.12,1.12,0,0,0-.31.15.36.36,0,0,0,.32.42c.18,0,.22-.11.24-.24A.25.25,0,0,0,94.48,96.67Z"/><path class="cls-3" d="M166.33,58.4a.57.57,0,0,0,.21.51c0-.24,0-.47-.06-.71A.29.29,0,0,0,166.33,58.4Z"/><path class="cls-3" d="M163.78,71.73a.32.32,0,0,0,.22-.12s0-.22-.11-.26-.18.14-.21.21S163.75,71.73,163.78,71.73Z"/><path class="cls-3" d="M163.36,70.83a.28.28,0,0,0-.27.17,1.11,1.11,0,0,0-.79-.28.77.77,0,0,0-.48.16.78.78,0,0,0-.29.62c0,.26-.24.44-.15.69a1.53,1.53,0,0,0,.14.29l0,.08v0a3.13,3.13,0,0,1,.15.32.86.86,0,0,0,.36,0,1,1,0,0,0,.47.09.86.86,0,0,0,.77-.38c.18-.26-.08-.57,0-.89a1.22,1.22,0,0,0,0-.17.33.33,0,0,0,.36,0c.15-.1.23-.26.12-.41S163.49,70.83,163.36,70.83Z"/><path class="cls-3" d="M89.37,58.87a.41.41,0,0,0,.17.54.37.37,0,0,0,.53-.16.39.39,0,0,0-.15-.5A.38.38,0,0,0,89.37,58.87Z"/><path class="cls-3" d="M89.07,55.11a.5.5,0,0,0-.1-.17.47.47,0,0,0-.84.19,3.41,3.41,0,0,0-.11.78.74.74,0,0,0,.61.61c.17,0,.54-.06.5-.41C88.8,55.63,89.2,55.53,89.07,55.11Z"/><path class="cls-3" d="M164.57,65.65c.23,0,.43-.14.51-.47a.55.55,0,0,0-.28-.61.7.7,0,0,0-.72.39.41.41,0,0,0,0,.11.66.66,0,0,0-.26-.18,1.19,1.19,0,0,0-.81,0,.51.51,0,0,1-.3,0,.48.48,0,0,0-.71.37.19.19,0,0,0-.13-.06c-.17,0-.35.15-.39.37a.43.43,0,0,0,.29.54.35.35,0,0,0,.42-.32h0a.66.66,0,0,0,.36.09A.72.72,0,0,1,163,66a.77.77,0,0,0,.75,0,.66.66,0,0,0,.41-.61v-.07A.57.57,0,0,0,164.57,65.65Z"/><path class="cls-3" d="M148.33,99.6c-.15.18-.2.36,0,.52s.25.07.29,0a.47.47,0,0,0,.16-.35C148.72,99.57,148.51,99.59,148.33,99.6Z"/><path class="cls-3" d="M135.81,101.65c0-.09-.12-.19-.17-.19s-.24,0-.25.09a.25.25,0,0,0,.14.21C135.59,101.78,135.81,101.74,135.81,101.65Z"/><path class="cls-3" d="M164.81,68.32a.51.51,0,0,0,.52-.4h0a.68.68,0,0,0,.62-.37c0-.2-.23-.54-.48-.61a.32.32,0,0,0-.41.24,1.49,1.49,0,0,0,0,.21h0a.48.48,0,0,0-.12,0,.41.41,0,0,0,0-.08.38.38,0,0,0-.29-.44.4.4,0,0,0-.47.31.43.43,0,0,0,.19.43.37.37,0,0,0-.08.18C164.29,68,164.54,68.29,164.81,68.32Z"/><path class="cls-3" d="M166.64,60.62c0-.26,0-.5,0-.76l0,0a1.43,1.43,0,0,0,0,.29h0C166.48,60.4,166.53,60.57,166.64,60.62Z"/><path class="cls-3" d="M139,100.66c.22,0,.48-.18.55-.5a.27.27,0,0,0-.23-.36.52.52,0,0,0-.62.25A.6.6,0,0,0,139,100.66Z"/><path class="cls-3" d="M147.52,98.6c-.19.05-.26.52-.12.59s.53-.13.49-.33A.36.36,0,0,0,147.52,98.6Z"/><path class="cls-3" d="M140.18,99.37a.43.43,0,0,0-.36-.45.35.35,0,0,0-.38.29.38.38,0,0,0,.31.47A.4.4,0,0,0,140.18,99.37Z"/><path class="cls-3" d="M135.19,103.86a.41.41,0,0,0,.64,0c0-.23-.09-.46-.37-.42S135.16,103.64,135.19,103.86Z"/><path class="cls-3" d="M136.34,100.77a.48.48,0,0,0,.39.62.59.59,0,0,0,.77-.48.63.63,0,0,0-.39-.75C136.82,100.07,136.43,100.38,136.34,100.77Z"/><path class="cls-3" d="M137.85,101.27c0,.19.29.48.58.52a.43.43,0,0,0,.44-.37.51.51,0,0,0-.36-.56A.66.66,0,0,0,137.85,101.27Z"/><path class="cls-3" d="M90.13,87.83a.52.52,0,0,0-.33-.63.35.35,0,0,0-.52.18.74.74,0,0,0,0,.65.41.41,0,0,0-.22,0,.61.61,0,0,0-.36.67A.52.52,0,0,0,89,89a.71.71,0,0,0,.94-.34,2.6,2.6,0,0,0-.19-.42.27.27,0,0,0-.08-.07A.67.67,0,0,0,90.13,87.83Z"/><path class="cls-3" d="M92.19,57.19a.29.29,0,0,0,.07.46.36.36,0,0,0,.52-.08.36.36,0,0,0-.08-.48A.37.37,0,0,0,92.19,57.19Z"/><path class="cls-3" d="M135.56,41.64c-.28,0-.49.15-.48.36a1,1,0,0,0,.31.5c.21,0,.41,0,.62-.08a.62.62,0,0,0,.11-.37C136.12,41.79,135.92,41.64,135.56,41.64Z"/><path class="cls-3" d="M135.06,41.06a.35.35,0,0,0-.46.24.33.33,0,0,0,.19.44c.23.07.56,0,.61-.19S135.24,41.13,135.06,41.06Z"/><path class="cls-3" d="M133.85,41.36a.43.43,0,0,0-.35.41.38.38,0,0,0,.37.37.42.42,0,0,0,.37-.43A.41.41,0,0,0,133.85,41.36Z"/><path class="cls-3" d="M134.63,39.28a.4.4,0,0,0-.45.22.45.45,0,0,0,.27.58.47.47,0,0,0,.48-.48A.3.3,0,0,0,134.63,39.28Z"/><path class="cls-3" d="M140.21,41.75c-.35-.13-.69,0-.77.19v0a.36.36,0,0,0-.36.26c.44,0,.88,0,1.31.08l0-.06A.47.47,0,0,0,140.21,41.75Z"/><path class="cls-3" d="M90.59,56c.09.07.33-.07.41-.14a.26.26,0,0,0-.1-.4.36.36,0,0,0-.31.13C90.54,55.62,90.49,55.88,90.59,56Z"/><path class="cls-3" d="M88.05,56.67a.13.13,0,0,0,.16,0,.17.17,0,0,0,0-.14s-.1,0-.13,0A.1.1,0,0,0,88.05,56.67Z"/><path class="cls-3" d="M90,54.47A.38.38,0,0,0,90,55a.4.4,0,0,0,.56,0,.37.37,0,0,0,0-.56A.39.39,0,0,0,90,54.47Z"/><path class="cls-3" d="M90.26,56.86a.31.31,0,0,0-.05.29c.05.09.18.21.27.2.25,0,.32-.3.28-.43A.34.34,0,0,0,90.26,56.86Z"/><path class="cls-3" d="M94.1,51.42a.34.34,0,0,0,0,.34c0,.07.3.18.38.12s0-.33,0-.37S94.17,51.38,94.1,51.42Z"/><path class="cls-3" d="M93,55.87a.46.46,0,0,0,.24.57.5.5,0,0,0,.71-.16l.06-.47a.36.36,0,0,0-.14-.21A.75.75,0,0,0,93,55.87Z"/><path class="cls-3" d="M92.05,54.78c-.23-.14-.47,0-.64.26a.4.4,0,0,0,.08.57.79.79,0,0,0,.8-.14A.48.48,0,0,0,92.05,54.78Z"/><path class="cls-3" d="M92.33,52a.56.56,0,0,0,.22.73.57.57,0,0,0,.64-.95A.83.83,0,0,0,92.33,52Z"/><path class="cls-3" d="M91.64,51.29a.44.44,0,0,0-.48,0,.38.38,0,0,0-.47.13.57.57,0,0,0,.19.69.33.33,0,0,0,.46-.12s0-.05,0-.07a.32.32,0,0,0,.28-.1A.41.41,0,0,0,91.64,51.29Z"/><path class="cls-3" d="M130.52,36.24a.51.51,0,0,0-.65-.25.76.76,0,0,0-.25.91.53.53,0,0,0,.63.13.68.68,0,1,0,1,.74.9.9,0,0,0-.69-.83.53.53,0,0,0-.24.06A.53.53,0,0,0,130.52,36.24Z"/><path class="cls-3" d="M130.17,39.2c-.09,0,0,.22,0,.29a.19.19,0,0,0,.2.07c.06,0,.06-.17,0-.25S130.27,39.15,130.17,39.2Z"/><path class="cls-3" d="M117.05,35.82c.06,0,.22-.14.17-.24s-.21-.07-.25,0-.16.1-.11.21A.13.13,0,0,0,117.05,35.82Z"/><path class="cls-3" d="M114.5,35.47a.33.33,0,0,0,0-.57.34.34,0,0,0-.38.22A.32.32,0,0,0,114.5,35.47Z"/><path class="cls-3" d="M94.56,52a.39.39,0,0,0,0,.24c0-.1,0-.21.07-.31Z"/><path class="cls-3" d="M123.92,37.86c.24-.14.29-.37.12-.67s-.37-.28-.67-.11a.4.4,0,0,0-.21.48A.81.81,0,0,0,123.92,37.86Z"/><path class="cls-3" d="M131.06,40.32c.08,0,.08-.24,0-.3a.2.2,0,0,0-.23-.09c-.08,0-.09.17-.07.24S131,40.36,131.06,40.32Z"/><path class="cls-3" d="M159.84,70a.57.57,0,0,0,.15-.12.52.52,0,0,0,.26.36.72.72,0,0,0,1-.08.83.83,0,0,0,0-.74.34.34,0,0,0,.37-.25.4.4,0,0,0-.3-.44.37.37,0,0,0-.45.26s0,0,0,.07a.68.68,0,0,0-.69.22l0,0a.62.62,0,0,0-.54-.46.89.89,0,0,0-.32,0,.29.29,0,0,0,0-.26.47.47,0,0,0-.61-.2Z"/><path class="cls-3" d="M159.25,66c0-.17-.1-.22-.23-.25s-.35.06-.33.23a1.19,1.19,0,0,0,.12.32A.37.37,0,0,0,159.25,66Z"/><path class="cls-3" d="M161.09,66.1a.54.54,0,0,0-.64.08.43.43,0,0,0-.1.58c.08.13.16.4.33.5a.28.28,0,0,0-.08.15.75.75,0,0,0,.54.72.43.43,0,0,0,.52-.38.47.47,0,0,0-.42-.63h-.17a.41.41,0,0,1,.07-.15A.6.6,0,0,0,161.09,66.1Z"/><path class="cls-3" d="M132.12,36.76a.4.4,0,1,0,.26.75c.16-.09.21-.4.06-.7A.54.54,0,0,0,132.12,36.76Z"/><path class="cls-3" d="M132.86,39.23c0-.37-.14-.55-.41-.56a.65.65,0,0,0-.61.53.69.69,0,0,0,.58.58C132.63,39.78,132.86,39.5,132.86,39.23Z"/><path class="cls-3" d="M112.7,99.64l-.15-.14-.07,0c.14.22.31.48.52.76a1,1,0,0,1,.37-.12C113.14,100,112.91,99.84,112.7,99.64Z"/><path class="cls-3" d="M113.53,101l.33.37A.61.61,0,0,0,113.53,101Z"/><path class="cls-3" d="M116.25,102.05h0c-.44-.22-.87-.45-1.29-.71-.19.12-.48.28-.53.6h0c.28.27.59.54.92.82A1.34,1.34,0,0,0,116.25,102.05Z"/><path class="cls-3" d="M118.71,103.16a.37.37,0,0,0,0-.18l-.62-.19-.37.07a.33.33,0,0,0-.18-.11.36.36,0,0,0-.18,0c-.14-.11-.21-.06-.46.15,0,.16.23.35,0,.47a2.91,2.91,0,0,0-.37-.33.49.49,0,0,0-.53,0l-.07.06c.27.21.56.41.87.61l0,0v.06a16.78,16.78,0,0,0,1.75,1l.15-.1c.27-.26.25-.5-.05-.88A2.55,2.55,0,0,0,118.71,103.16Z"/><path class="cls-3" d="M133.27,103.3c-.29-.06-.59.08-.63.3a.8.8,0,0,0,.58.79c.22,0,.46-.18.51-.48S133.65,103.37,133.27,103.3Z"/><path class="cls-3" d="M121.65,105a.51.51,0,0,0-.38-.24.66.66,0,0,0-.28,0c0-.06,0-.12-.08-.15-.2-.13-.41-.05-.54.23,0,0,0,.07,0,.1s-.19.18-.31,0a1.53,1.53,0,0,0-.11-.16.64.64,0,0,0-.72-.13.55.55,0,0,0-.29.21c.54.24,1.11.46,1.72.67a1.09,1.09,0,0,0,.08-.15.8.8,0,0,0,.31.27c.34.11.7.21,1.07.3a1.1,1.1,0,0,0,.1-.45C122.21,105.34,121.91,105.08,121.65,105Z"/><path class="cls-3" d="M133.43,105.92a.38.38,0,0,0-.09-.44.51.51,0,0,0-.58,0,.36.36,0,0,0-.24.21.5.5,0,0,0-.37-.32.31.31,0,0,0-.37.26.57.57,0,0,0,.12.43.4.4,0,0,0,.61-.12,1.2,1.2,0,0,1,0,.42.32.32,0,0,0,0,.16l1-.13-.18-.16Z"/><path class="cls-3" d="M136,105.47a.38.38,0,0,0-.21,0,.49.49,0,0,0-.25-.16.75.75,0,0,0-.75.25.85.85,0,0,0-.51.15.16.16,0,0,0-.08,0c-.14,0-.18.14-.21.21s0,.1.07.13,0,0,0,0a.62.62,0,0,0,0,.19c.78-.13,1.58-.28,2.41-.45A.57.57,0,0,0,136,105.47Z"/><path class="cls-3" d="M132,103.27c.17.07.44,0,.59-.35l0,0a.38.38,0,0,0,.41-.26.37.37,0,0,0-.31-.43.24.24,0,0,0-.17,0,.4.4,0,0,0-.43-.2c-.3.09-.32.29-.43.24s-.12-.1-.21-.15a3.88,3.88,0,0,1-.36.15,1.23,1.23,0,0,0,0,.62c0,.09.27.28.12.43a3.39,3.39,0,0,1-.34.23.37.37,0,0,0-.27.16.49.49,0,0,0,.08.33.65.65,0,0,0,.15.65c.28.21.41-.07.61-.12a.48.48,0,0,0,.39-.5c0-.27-.19-.52-.15-.65S131.76,103.19,132,103.27Z"/><path class="cls-3" d="M125.83,105.43a.45.45,0,0,0,.17-.2c0-.19-.23-.53-.48-.6a.31.31,0,0,0-.41.23.63.63,0,0,0,.17.69.3.3,0,0,0,0,.29.32.32,0,0,0,.45.18.46.46,0,0,0,.24-.31A.38.38,0,0,0,125.83,105.43Z"/><path class="cls-3" d="M130.06,105.34a.35.35,0,0,0,.41.16.32.32,0,0,0,0-.56C130.17,104.84,130,105.18,130.06,105.34Z"/><path class="cls-3" d="M124.82,104.6a.71.71,0,0,0-.69-.82h-.41a.77.77,0,0,0-.33.15.8.8,0,0,0-.28.61c0,.27-.23.45-.14.7a1.24,1.24,0,0,0,.34.51l-.07,0c-.21.09-.38.2-.62,0s-.34,0-.43.21c0,0,0,0,0,0,.62.15,1.27.28,2,.38a1,1,0,0,0-.06-.44.9.9,0,0,0,.73-.39.5.5,0,0,0,0-.4A.36.36,0,0,0,125,105,.39.39,0,0,0,124.82,104.6Z"/><path class="cls-3" d="M129.85,105.92a.36.36,0,0,0-.46,0l-.24.14-.09-.06a1,1,0,0,0,0-.24.65.65,0,0,0,0-.2.71.71,0,0,0,.7-.52.71.71,0,0,0-1.37-.32.79.79,0,0,0,0,.34.85.85,0,0,0-.56.23.55.55,0,0,0-.43-.48c-.45-.1-.85.09-.91.43a.58.58,0,0,0,0,.29.36.36,0,0,0-.32,0,.38.38,0,0,0-.23.44c0,.23.22.27.42.31a.56.56,0,0,0-.31.28s0,0,0,.08l1,0a.74.74,0,0,0-.36-.4.35.35,0,0,0-.11,0,.87.87,0,0,0,.18-.38.63.63,0,0,0,.26.15.79.79,0,0,0,.71-.3.67.67,0,0,0,.66.56l.24,0a.86.86,0,0,1,0,.16.43.43,0,0,0-.05.21v0c.44,0,.9,0,1.37,0v0a1.36,1.36,0,0,1,0-.24C130,106.19,130,106,129.85,105.92Z"/><path class="cls-3" d="M96.7,102.18a.43.43,0,0,0-.14.65.38.38,0,0,0,.61-.14.54.54,0,0,0-.08-.44A.42.42,0,0,0,96.7,102.18Z"/><path class="cls-3" d="M93.16,100.26l-.5-.19.77.91s0,0,0,0C93.61,100.57,93.54,100.4,93.16,100.26Z"/><path class="cls-3" d="M94.18,98.59c0-.18-.08-.35-.25-.32a1.06,1.06,0,0,0-.32.14.36.36,0,0,0,.33.42C94.11,98.84,94.15,98.72,94.18,98.59Z"/><path class="cls-3" d="M93.23,94.74a.89.89,0,0,0,.79-.89c0-.3-.44-.3-.61-.15s-.13.07-.34,0a.45.45,0,0,0-.5.11.35.35,0,0,0-.14-.06.59.59,0,0,0-.5.11.35.35,0,0,0-.33-.19c-.25,0-.32.22-.41.44.16.17.31.39.57.25l.06,0a.91.91,0,0,0,.53.42.36.36,0,0,0,.37-.21A.89.89,0,0,0,93.23,94.74Z"/><path class="cls-3" d="M92.39,96.54c0-.21,0-.42,0-.64a.32.32,0,0,0-.2-.46.41.41,0,0,0-.5.28s0,.07,0,.1a.64.64,0,0,0-.44-.31.65.65,0,0,0-.89.53.73.73,0,0,0,.58.84.62.62,0,0,0,.44-.14A.82.82,0,0,0,92,97C92.19,96.89,92.41,96.84,92.39,96.54Z"/><path class="cls-3" d="M90.48,95.61c0-.19-.21-.28-.32-.33h0a.77.77,0,0,0,.34-.45.81.81,0,0,0-.51-.85.68.68,0,0,0-.83.42,1.1,1.1,0,0,0,.57,1,1,1,0,0,0,.17,0,.43.43,0,0,0,0,.07c-.06.18,0,.43.21.42S90.46,95.79,90.48,95.61Z"/><path class="cls-3" d="M142.74,40.49a.69.69,0,0,0-.21.81c.11.24.55.26.9.12a.67.67,0,0,0,.26-.83A.69.69,0,0,0,142.74,40.49Z"/><path class="cls-3" d="M91.17,98.12l.36.49a.5.5,0,0,0-.07-.27A.66.66,0,0,0,91.17,98.12Z"/><path class="cls-3" d="M93.83,101.31s-.07,0-.1,0l.27.31C94,101.52,93.91,101.33,93.83,101.31Z"/><path class="cls-3" d="M92.77,97.19a.64.64,0,1,0,1.24.29c.07-.31-.23-.65-.64-.73A.5.5,0,0,0,92.77,97.19Z"/><path class="cls-3" d="M89.12,92.37a.66.66,0,0,0-.75-.43.69.69,0,0,0-.3.9.67.67,0,0,0,.74.38C89.08,93.17,89.19,92.75,89.12,92.37Z"/><path class="cls-3" d="M99.09,106a.32.32,0,0,0-.35.2l.43.36a.26.26,0,0,0,.14-.11C99.37,106.39,99.26,106.05,99.09,106Z"/><path class="cls-3" d="M96.77,101.63a.43.43,0,0,0-.3-.49.36.36,0,0,0-.41.32.37.37,0,0,0,.26.45A.39.39,0,0,0,96.77,101.63Z"/><path class="cls-3" d="M95.66,99.22h-.13a.3.3,0,0,0,.27-.3.45.45,0,0,0-.15-.33c-.25-.15-.43,0-.6.24.1.19.16.4.38.41a.31.31,0,0,0-.25.27.74.74,0,0,0,.36.67.5.5,0,0,0,.59-.44C96.18,99.5,96,99.29,95.66,99.22Z"/><path class="cls-3" d="M95.24,102.21a.48.48,0,0,0-.4-.69.21.21,0,0,0,0-.13c0-.17-.18-.34-.4-.37a.43.43,0,0,0-.52.33.37.37,0,0,0,.34.4h0a1,1,0,0,0,0,.18c.35.38.71.76,1.07,1.13a1.46,1.46,0,0,0-.09-.54A.66.66,0,0,1,95.24,102.21Z"/><path class="cls-3" d="M98.93,104.74a.46.46,0,0,0-.23-.82,2.91,2.91,0,0,0-.78-.07.72.72,0,0,0-.58.63.29.29,0,0,0,.2.35,2.06,2.06,0,0,0,1.23,0A.55.55,0,0,0,98.93,104.74Z"/><path class="cls-3" d="M136.84,104.19a.35.35,0,0,0-.21-.25c-.15,0-.46.2-.42.35a.6.6,0,0,0,.06.11,0,0,0,0,0,0,0,.63.63,0,0,0,.44.47c.14,0,.38-.24.39-.43S137,104.22,136.84,104.19Z"/><path class="cls-3" d="M162,80.89l.15-.23-.06,0C162.09,80.71,162.07,80.8,162,80.89Z"/><path class="cls-3" d="M162.64,78.5h0c0,.26-.09.52-.14.79.32,0,.52,0,.57-.18A.63.63,0,0,0,162.64,78.5Z"/><path class="cls-3" d="M162.39,79.7h0a.52.52,0,0,0,0-.27A2.8,2.8,0,0,1,162.39,79.7Z"/><path class="cls-3" d="M163,78.09a.35.35,0,0,0,.22.44.33.33,0,0,0,.14,0c.09-.18.18-.37.26-.56a.27.27,0,0,0-.13-.1A.39.39,0,0,0,163,78.09Z"/><path class="cls-3" d="M156.47,95.31s-.21,0-.23.11.15.18.22.2a.19.19,0,0,0,.17-.12C156.64,95.43,156.54,95.34,156.47,95.31Z"/><path class="cls-3" d="M164,73.79a.31.31,0,0,0-.26-.26.44.44,0,0,0-.46.28.34.34,0,0,0,.12.31c-.15.24-.23.55-.33.57s-.43-.25-.78-.25a10.12,10.12,0,0,1,.37,1.66.45.45,0,0,0,.23,0A.31.31,0,0,0,163,76s-.06.06-.07.09a.61.61,0,0,0,.35.64h0a.74.74,0,0,0,.45.84c.18-.42.36-.86.53-1.31a1,1,0,0,0-.46,0,.45.45,0,0,0-.22-.49.61.61,0,0,0-.53.14c.11-.16.15-.37.21-.39s.54.34,1,.21a.61.61,0,0,0,.31-.24c.1-.28.19-.56.28-.85a0,0,0,0,0,0,0,1.07,1.07,0,0,0-.38-.68A1,1,0,0,0,164,73.79Z"/><path class="cls-3" d="M153.41,97a.42.42,0,0,0-.5.28,1.1,1.1,0,0,1-.2.35.54.54,0,0,0-.22.6c0,.1.19.19.36.25l.76-.52v-.5A.31.31,0,0,0,153.41,97Z"/><path class="cls-3" d="M154.64,95.22c-.17.14-.13.07-.35,0s-.56,0-.57.36a.76.76,0,0,0,.73.69.88.88,0,0,0,.79-.89C155.2,95.07,154.8,95.07,154.64,95.22Z"/><path class="cls-3" d="M165.06,70.61a.2.2,0,0,0-.21-.24.23.23,0,0,0-.2.2.19.19,0,0,0,.15.23A.2.2,0,0,0,165.06,70.61Z"/><path class="cls-3" d="M166.31,65.77a.72.72,0,0,0,.17.59c0-.13,0-.26,0-.39l0-.5A.5.5,0,0,0,166.31,65.77Z"/><path class="cls-3" d="M165.93,68.32a.33.33,0,0,0,.28.27c0-.25.08-.5.11-.75a.34.34,0,0,0-.16,0A.38.38,0,0,0,165.93,68.32Z"/><path class="cls-3" d="M165.82,70.88c0-.11,0-.22.07-.33a.29.29,0,0,0-.09.15A.36.36,0,0,0,165.82,70.88Z"/><path class="cls-3" d="M166.29,63.63a.41.41,0,0,0,0,.19l-.05,0c-.19-.07-.41.13-.43.36a.34.34,0,0,0,.32.36.44.44,0,0,0,.4-.32l.06,0h0c0-.33,0-.66,0-1A.46.46,0,0,0,166.29,63.63Z"/><path class="cls-3" d="M165,72.8a.51.51,0,0,0-.69.35c-.07.27,0,.57.25.62a.71.71,0,0,0,.65-.26c0-.13.08-.27.11-.4A.52.52,0,0,0,165,72.8Z"/><path class="cls-3" d="M165.05,74.05c0-.13.07-.25.1-.38a.84.84,0,0,0-.16.24A1.14,1.14,0,0,0,165.05,74.05Z"/><path class="cls-3" d="M165.37,69.22c-.25-.07-.56.06-.6.26a.28.28,0,0,0,0,.09.74.74,0,0,0-.52-.38.65.65,0,0,0-.24,0,1,1,0,0,0,.06-.92.71.71,0,0,0-.85-.19c-.21.1-.38.21-.62,0a.2.2,0,0,0-.22,0l-.05,0a.39.39,0,0,0-.42.27.32.32,0,0,0,.24.41.46.46,0,0,0,.32-.13.31.31,0,0,0,.18,0,3.18,3.18,0,0,0,.38.59.81.81,0,0,0,.68.15.74.74,0,0,0-.19.44c-.07.52.06.71.48.81a.7.7,0,0,0,.84-.63s0-.06,0-.1a.72.72,0,0,0,.44.4c.2.06.45-.14.52-.4S165.72,69.32,165.37,69.22Z"/><path class="cls-3" d="M164.7,71.4a.35.35,0,0,0-.22-.43.34.34,0,0,0-.45.17c-.08.22,0,.56.16.61s.27,0,.4-.16l0,0a.56.56,0,0,0,.42.7,2,2,0,0,0,.51,0c.09-.35.17-.69.25-1.05a.67.67,0,0,0-.33-.24A.89.89,0,0,0,164.7,71.4Z"/><path class="cls-3" d="M151.38,96.8A.25.25,0,0,0,151,97c-.05.17.06.42.22.41s.44-.06.46-.24S151.49,96.85,151.38,96.8Z"/><path class="cls-3" d="M143.26,100.28a.38.38,0,0,0,.34.4.37.37,0,0,0,.38-.36.32.32,0,0,0-.3-.37A.42.42,0,0,0,143.26,100.28Z"/><path class="cls-3" d="M141.49,102.71c-.18,0-.37,0-.4.17s.08.52.28.55a.4.4,0,0,0,.48-.35A.34.34,0,0,0,141.49,102.71Z"/><path class="cls-3" d="M141.55,100.37a.62.62,0,0,0-.48.29.52.52,0,0,0-.35-.38.36.36,0,0,0-.41.32.42.42,0,0,0,.25.47.64.64,0,0,0,.44-.15.47.47,0,0,0,.29.41c.4.07.73-.07.77-.32A.7.7,0,0,0,141.55,100.37Z"/><path class="cls-3" d="M141.68,102.23a.82.82,0,0,0-.38-.72c-.27-.06-.46.09-.53.43s.1.45.43.51A.37.37,0,0,0,141.68,102.23Z"/><path class="cls-3" d="M142.05,100a.26.26,0,0,0-.33.19.3.3,0,0,0,.16.4.34.34,0,0,0,.42-.23A.3.3,0,0,0,142.05,100Z"/><path class="cls-3" d="M138,102.32a.63.63,0,0,0-.6.23.44.44,0,0,0,.09.36h-.09c-.12,0-.26,0-.31.15s.14.4.33.42.21-.11.23-.25a.26.26,0,0,0,0-.15h0a.28.28,0,0,0,.38-.18C138.12,102.61,138.11,102.39,138,102.32Z"/><path class="cls-3" d="M140.54,103.49a.68.68,0,0,0-.54-.5.35.35,0,0,0-.15,0,.62.62,0,0,0,.12-.42c-.07-.28-.32-.19-.56-.19-.1.27-.15.5.09.65a.31.31,0,0,0,.17,0,.65.65,0,0,0-.21.42.51.51,0,0,0,.34.52.45.45,0,0,0,.22,0c.18.28.32.36.52.29a.35.35,0,0,0,.25-.37A.41.41,0,0,0,140.54,103.49Z"/><path class="cls-3" d="M143.21,99.6c.26,0,.45-.14.5-.48a.41.41,0,0,0-.27-.51c-.22,0-.67.22-.7.42S142.87,99.54,143.21,99.6Z"/><path class="cls-3" d="M138.88,104c-.37-.07-.65,0-.69.28a.66.66,0,0,0,.48.61.65.65,0,0,0,.52-.45A.41.41,0,0,0,138.88,104Z"/><path class="cls-3" d="M140,101.85s0-.13-.1-.14-.27-.19-.39-.34a.39.39,0,0,0-.35,0,.45.45,0,0,0-.18.31c0,.08.11.2.2.24a2.49,2.49,0,0,0,.55.16C139.93,102.11,140,102,140,101.85Z"/><path class="cls-3" d="M144,102.87a.12.12,0,0,0-.16.11c0,.08,0,.23.12.27s.19-.14.18-.18S144.14,102.89,144,102.87Z"/><path class="cls-3" d="M148.2,100.47a.7.7,0,0,0-.41.38l-.1,0c-.38-.08-.57.15-.71.46-.21-.07-.4-.24-.58,0a.35.35,0,0,0,0,.45c.2.24.4.08.6,0l.13.13c.63-.33,1.25-.68,1.86-1A.68.68,0,0,0,148.2,100.47Z"/><path class="cls-3" d="M137.46,104.91a.41.41,0,0,0-.31,0c-.12.07-.07.24.07.32a.6.6,0,0,0,.45.05.18.18,0,0,0,.09-.26A.36.36,0,0,0,137.46,104.91Z"/><path class="cls-3" d="M150,97.16a.36.36,0,0,0-.3-.43.35.35,0,0,0-.44.27.37.37,0,0,0,.28.44A.44.44,0,0,0,150,97.16Z"/><path class="cls-3" d="M145.25,101.67a.33.33,0,0,0-.39.28.54.54,0,0,0,0,.31c-.19,0-.38.13-.39.28s.22.29.43.38l.36-.18a.75.75,0,0,0,0-.2.39.39,0,0,0,.41-.29A.57.57,0,0,0,145.25,101.67Z"/><path class="cls-3" d="M146.12,99.25a.7.7,0,0,0-.64.5.52.52,0,1,0,1,.15A.58.58,0,0,0,146.12,99.25Z"/><path class="cls-3" d="M143.46,101.66a.61.61,0,0,0-.67.43.62.62,0,0,0,.41.69.85.85,0,0,0,.73-.52A.57.57,0,0,0,143.46,101.66Z"/><path class="cls-3" d="M150.3,98.06a.4.4,0,0,0-.06.79.43.43,0,0,0,.41-.39A.4.4,0,0,0,150.3,98.06Z"/><path class="cls-3" d="M144.15,98c-.11,0-.28.18-.33.27s.09.37.24.34a.35.35,0,0,0,.24-.23C144.32,98.26,144.27,98,144.15,98Z"/><path class="cls-3" d="M92.2,98.92a.35.35,0,0,0-.36.11c.17.23.36.46.54.68a.77.77,0,0,0,0-.21.48.48,0,0,0,.07-.18A.34.34,0,0,0,92.2,98.92Z"/><path class="cls-3" d="M126.76,34.21a.44.44,0,0,0,.21-.08l-.47-.08A.2.2,0,0,0,126.76,34.21Z"/><path class="cls-3" d="M126.13,37.72a.28.28,0,0,0,.41.12c.24-.12.39-.42.3-.6a.59.59,0,0,0-.63-.25C126,37.09,126,37.44,126.13,37.72Z"/><path class="cls-3" d="M125.9,35.05a.63.63,0,0,0,.3-.56.53.53,0,0,0-.54-.23.27.27,0,0,0-.17.38C125.59,34.93,125.75,35.09,125.9,35.05Z"/><path class="cls-3" d="M88.25,91.5c.06-.27-.19-.29-.27-.4a.59.59,0,0,0-.85-.14h0c.18.42.38.86.6,1.29a.59.59,0,0,0,0-.08.43.43,0,0,0,0-.4C88,91.75,88.2,91.7,88.25,91.5Z"/><path class="cls-3" d="M128.41,34.37l-.5-.09a.36.36,0,0,0,.3.17A.32.32,0,0,0,128.41,34.37Z"/><path class="cls-3" d="M127.63,38.89a.59.59,0,0,0,.54-.63.61.61,0,0,0-.62-.52.58.58,0,0,0,.08,1.15Z"/><path class="cls-3" d="M128.76,37c.25,0,.42-.1.44-.3a.61.61,0,0,0-.49-.59.44.44,0,0,0-.44.38A.53.53,0,0,0,128.76,37Z"/><path class="cls-3" d="M128,36.11c.27-.12.36-.6.18-1a.49.49,0,0,0-.71-.21.59.59,0,0,0-.26.87A.63.63,0,0,0,128,36.11Z"/><path class="cls-3" d="M122.79,33.75a.15.15,0,0,0,0-.09l-.36,0a.37.37,0,0,0,.08.14C122.59,33.82,122.74,33.83,122.79,33.75Z"/><path class="cls-3" d="M121,35.71c.2.06.39.21.55.13a.36.36,0,0,0,.21-.25.57.57,0,0,0,.07-.34.78.78,0,0,0-.4-.55.58.58,0,0,0-.61.09.47.47,0,0,0-.15.47A.6.6,0,0,0,121,35.71Z"/><path class="cls-3" d="M129.79,35.12a.62.62,0,0,0,.33-.38l-1.38-.3a.79.79,0,0,0,0,.46C128.91,35.24,129.35,35.33,129.79,35.12Z"/><path class="cls-3" d="M122.75,37.11c0,.1.17.14.25.15s.15-.07.15-.14S123,37,123,37,122.76,37,122.75,37.11Z"/><path class="cls-3" d="M125.17,38.41a.38.38,0,0,0,.1-.52.41.41,0,0,0-.5-.18.52.52,0,0,0-.21.55A.54.54,0,0,0,125.17,38.41Z"/><path class="cls-3" d="M123.93,35.88c.25-.15.28-.4.09-.72a.52.52,0,0,0-.6-.16.87.87,0,0,0-.3.81A.64.64,0,0,0,123.93,35.88Z"/><path class="cls-3" d="M124.81,36.82a.77.77,0,0,1,.52,0c.08,0,.23-.12.27-.22a.43.43,0,0,0-.07-.36c0-.06-.21-.07-.3,0a2.71,2.71,0,0,0-.52.24c-.15.07-.16.19-.06.34S124.77,36.84,124.81,36.82Z"/><path class="cls-3" d="M124.14,34.43c.3-.18.43-.43.34-.62l-.85-.08a.69.69,0,0,0,0,.58A.42.42,0,0,0,124.14,34.43Z"/><path class="cls-3" d="M130.69,35.88c-.1.22-.23.45.05.61a.38.38,0,0,0,.5-.05s.11-.15.09-.21-.15-.34-.23-.52a.89.89,0,0,0,.08-.71l-.64-.16a.77.77,0,0,0-.36.3C130,35.42,130.13,35.57,130.69,35.88Z"/><path class="cls-3" d="M138.51,41.28c.21,0,.35-.19.4-.31s-.06-.26-.21-.31-.37,0-.37.2S138.31,41.26,138.51,41.28Z"/><path class="cls-3" d="M137.6,37.36c0-.09-.11-.19-.18-.22a.19.19,0,0,0-.19.08c0,.07.06.17.12.22S137.56,37.46,137.6,37.36Z"/><path class="cls-3" d="M137.92,37.27l-.28-.12a.33.33,0,0,0,.13.11A.4.4,0,0,0,137.92,37.27Z"/><path class="cls-3" d="M139.26,38.51a.75.75,0,0,1,.57,0,.42.42,0,0,0,.51-.17c-.61-.3-1.25-.59-1.92-.88a.79.79,0,0,0,0,.13c.17.25.4.58.64.89A.26.26,0,0,0,139.26,38.51Z"/><path class="cls-3" d="M137.83,41.7a.71.71,0,0,0,.1.26c.25.07.45,0,.45-.19a.41.41,0,0,0-.16-.31C138.09,41.37,137.83,41.53,137.83,41.7Z"/><path class="cls-3" d="M139.92,39.63s.05-.05.05-.08a.76.76,0,0,0-.42-.85.48.48,0,0,0-.43.15.32.32,0,0,0-.17-.06.55.55,0,0,0-.33.45.4.4,0,0,0,.42.38h0a.49.49,0,0,0,.26.18.47.47,0,0,0,.16,0,.51.51,0,0,0-.09.25.7.7,0,0,0,.46.52.68.68,0,0,0,.59-.55C140.44,39.81,140.23,39.67,139.92,39.63Z"/><path class="cls-3" d="M143.09,40a1,1,0,0,1,.24,0s0,0,.06,0c-.32-.2-.66-.4-1-.59a.32.32,0,0,1,.12.15A.48.48,0,0,0,143.09,40Z"/><path class="cls-3" d="M141.62,41.88a.47.47,0,0,0-.17,0,1.12,1.12,0,0,0-.73.51c.45.06.89.12,1.33.2a.29.29,0,0,1,0-.09.42.42,0,0,0,.18-.21.33.33,0,0,0-.18-.45A.38.38,0,0,0,141.62,41.88Z"/><path class="cls-3" d="M140.94,41a.8.8,0,0,0,1-.39.77.77,0,0,0-.13-.67.56.56,0,0,0,.21-.17c.06-.09.1-.19.19-.36-.21-.13-.42-.25-.65-.37a.57.57,0,0,0-.13,0c-.39.08-.5.34-.32.63a.91.91,0,0,0-.5.52A.62.62,0,0,0,140.94,41Z"/><path class="cls-3" d="M132.44,41.09a.34.34,0,0,0,.37.31.45.45,0,0,0,.36-.49,1.25,1.25,0,0,0-.36-.24C132.6,40.63,132.43,40.86,132.44,41.09Z"/><path class="cls-3" d="M132.6,35.91l.15,0a.23.23,0,0,0,.16-.06s0,0,0,0a.61.61,0,0,0,.15-.24l-1-.28s0,.05,0,.08A.63.63,0,0,0,132.6,35.91Z"/><path class="cls-3" d="M132,36.11c.08-.09.28-.28.15-.44s-.39-.08-.51,0-.12.24-.05.37S131.87,36.23,132,36.11Z"/><path class="cls-3" d="M132.54,36.77c0,.19,0,.41.25.45.09,0,.24-.09.3-.18a.28.28,0,0,0,0-.29C132.91,36.57,132.73,36.62,132.54,36.77Z"/><path class="cls-3" d="M130.92,39.07c-.07,0-.17.17-.19.27s.07.27.23.23.41-.14.4-.28S131,39.06,130.92,39.07Z"/><path class="cls-3" d="M118.78,34.08c.09,0,.22.14.12.18s-.16.14-.08.39a.38.38,0,0,0,.47.32.56.56,0,0,0,.43-.67c-.06-.31-.24-.32-.26-.53a.27.27,0,0,0-.23-.24h-.59a.22.22,0,0,0-.12.19C118.49,34,118.68,34.08,118.78,34.08Z"/><path class="cls-3" d="M136.72,36.78l-.4-.14a.41.41,0,0,0,.35.15Z"/><path class="cls-3" d="M136.32,38.82l-.14,0c-.34-.06-.5.07-.52.41s0,.56.42.69a1.36,1.36,0,0,1-.37.17.36.36,0,0,0-.34.35.5.5,0,0,0,.22.55c.67.39.89.35,1.36-.25l.05,0h0a.86.86,0,0,0-.12.27.24.24,0,0,0,.22.32.29.29,0,0,0,.35-.26.75.75,0,0,0-.15-.34.33.33,0,0,0,.12,0,.47.47,0,0,1,.1,0c.06,0,.21-.08.26-.18a.44.44,0,0,0-.15-.6.47.47,0,0,0-.45.09h-.07c-.25.07-.36-.07-.53-.19C136.9,39.31,136.79,39,136.32,38.82Z"/><path class="cls-3" d="M134.08,37.16a.9.9,0,0,0,.13,1,.76.76,0,0,0,.5.17.4.4,0,0,0,.32,0l.07-.06a1,1,0,0,0,.47-.38.37.37,0,0,1,.37-.19.44.44,0,0,0,.35-.33.1.1,0,0,0,.07,0,.23.23,0,0,0,.24-.08.22.22,0,0,0-.11-.23.4.4,0,0,0-.22,0c-.06-.15-.19-.18-.35-.16a.86.86,0,0,1-.42,0,9.2,9.2,0,0,1-.86-.39,2.42,2.42,0,0,0,0-.49l-.88-.28a.49.49,0,0,0-.27.71A4.61,4.61,0,0,0,134.08,37.16Z"/><path class="cls-3" d="M124.94,35.24c.06,0,.25-.06.26-.15s-.18-.19-.24-.19a.22.22,0,0,0-.2.16C124.76,35.15,124.87,35.23,124.94,35.24Z"/><path class="cls-3" d="M82.18,64.48a.2.2,0,0,0,0,.21s.19,0,.25,0,.11-.18,0-.25S82.24,64.43,82.18,64.48Z"/><path class="cls-3" d="M80.81,58.91a.53.53,0,0,0,.12,0,.64.64,0,0,0,.57-.35.47.47,0,0,0-.07-.43Z"/><path class="cls-3" d="M82.93,60.66a1,1,0,0,1-.16-.38.55.55,0,0,0-.37-.52c-.18-.06-.59.21-.65.42s-.15.41.1.56a6.28,6.28,0,0,1,.52.38.31.31,0,0,0,.49.1A.41.41,0,0,0,82.93,60.66Z"/><path class="cls-3" d="M82.83,62.47a.9.9,0,0,0,.25,1.17c.27.14.5-.19.48-.41s0-.15.21-.27a.38.38,0,0,0,0-.67A.76.76,0,0,0,82.83,62.47Z"/><path class="cls-3" d="M80.06,62.05c-.11.13,0,.24.06.33a.24.24,0,0,0,.4,0,1.21,1.21,0,0,0,.07-.34A.37.37,0,0,0,80.06,62.05Z"/><path class="cls-3" d="M77.7,65.77a.81.81,0,0,0,0,1,.73.73,0,0,0,1,.06,1,1,0,0,0,0-1.1A.71.71,0,0,0,77.7,65.77Z"/><path class="cls-3" d="M79.41,63.33s-.13.23-.07.29.26,0,.3,0,.09-.18,0-.25S79.47,63.29,79.41,63.33Z"/><path class="cls-3" d="M83.78,58.05a.21.21,0,0,0,0-.06.54.54,0,0,0,.31-.24c0-.16,0-.38-.11-.49s-.3.08-.32.15,0,.06,0,.09a.35.35,0,0,0-.5,0,.46.46,0,0,0,.07.57A.4.4,0,0,0,83.78,58.05Z"/><path class="cls-3" d="M79.55,61a1.21,1.21,0,0,0,.07-.34.41.41,0,0,0-.11-.07l-.33.45C79.31,61.13,79.48,61.13,79.55,61Z"/><path class="cls-3" d="M75.19,67s0,0,0,0h0Z"/><path class="cls-3" d="M87.24,88.91c.06-.54-.08-.63-.58-.8a.53.53,0,0,0-.57.22l.34,1,.12.06,0,0a.33.33,0,0,0,.24,0A.52.52,0,0,0,87.24,88.91Z"/><path class="cls-3" d="M86.07,83.68a.47.47,0,0,0-.26-.62.31.31,0,0,0-.41.16.39.39,0,0,0,0,.5A.43.43,0,0,0,86.07,83.68Z"/><path class="cls-3" d="M86.42,87a.76.76,0,0,0-.5-.81.8.8,0,0,0-.46,0c.11.43.23.86.37,1.3A.74.74,0,0,0,86.42,87Z"/><path class="cls-3" d="M87.24,90.27a.43.43,0,0,0-.48-.16c.11.28.23.56.36.85a1.45,1.45,0,0,0,.09-.22C87.25,90.59,87.35,90.45,87.24,90.27Z"/><path class="cls-3" d="M117.12,34.35a.3.3,0,1,0-.43-.34C116.64,34.19,116.74,34.27,117.12,34.35Z"/><path class="cls-3" d="M85.22,58.33a.35.35,0,0,0,0-.51.37.37,0,0,0-.53,0,.44.44,0,0,0,0,.54A.36.36,0,0,0,85.22,58.33Z"/><path class="cls-3" d="M85,55.48c.07-.19-.26-.52-.41-.45s-.2.51,0,.6A.36.36,0,0,0,85,55.48Z"/><path class="cls-3" d="M83.79,59.23c-.06.11-.21.39-.07.51s.35,0,.45-.08a.24.24,0,0,0,.08-.37C84.15,59.15,83.88,59.09,83.79,59.23Z"/><path class="cls-3" d="M85.16,82a.61.61,0,0,0,.27,0q0,.17.27.27a.56.56,0,0,0,.73-.07,10.57,10.57,0,0,1-.57-1.34,1.08,1.08,0,0,0-.44-.23.58.58,0,0,0-.7.31c0,.22,0,.45,0,.68A.74.74,0,0,0,85.16,82Z"/><path class="cls-3" d="M85,83h-.13c0,.22.05.43.08.65a.35.35,0,0,0,.32-.3A.28.28,0,0,0,85,83Z"/><path class="cls-3" d="M94.12,50.32c-.11.16,0,.51.25.62a.33.33,0,0,0,.44-.18c.16-.3.17-.52,0-.63A.62.62,0,0,0,94.12,50.32Z"/><path class="cls-3" d="M95.22,47c.32,0,.35.28.46.49a.38.38,0,0,0,.61.13l.07-.05a.25.25,0,0,0,.12.13.7.7,0,0,0,.3.09c.12-.14.23-.3.35-.44a1.66,1.66,0,0,0-.23-.23.38.38,0,0,0-.38-.05.47.47,0,0,0-.24-.3c-.15-.1-.33-.15-.32-.44.1,0,.25-.09.28-.17l0-.06-1.19.66a.4.4,0,0,0,.07.19S95.19,47,95.22,47Z"/><path class="cls-3" d="M95.25,49.42a.53.53,0,0,1,.36,0,8.33,8.33,0,0,1,.51-.8h0a.87.87,0,0,1-.74,0A.29.29,0,0,0,95,49C94.89,49.17,95.05,49.45,95.25,49.42Z"/><path class="cls-3" d="M93.88,54.64a.46.46,0,0,0,.31,0l.1-.88c-.28-.12-.56-.09-.66.1A.68.68,0,0,0,93.88,54.64Z"/><path class="cls-3" d="M94.64,49.66a.38.38,0,0,0,0-.48.38.38,0,0,0-.53,0,.29.29,0,0,0,0,.46A.36.36,0,0,0,94.64,49.66Z"/><path class="cls-3" d="M85,55.14a.35.35,0,0,0,.51-.09.36.36,0,0,0-.11-.51.43.43,0,0,0-.53.1A.36.36,0,0,0,85,55.14Z"/><path class="cls-3" d="M103.24,43.27l-.07,0h0Z"/><path class="cls-3" d="M114.57,33.71l.17.16a.75.75,0,0,0,.83.06.78.78,0,0,0,.26-.34c-.7,0-1.39.07-2.08.12a.63.63,0,0,0,.33.09A3,3,0,0,0,114.57,33.71Z"/><path class="cls-3" d="M100.19,44.77s0-.05,0-.08a1,1,0,0,0,0-.3.36.36,0,0,0,.19-.07.3.3,0,0,0,.1.14l.08,0,.75-.4a.94.94,0,0,0-.07-.22c-.73.25-1.47.53-2.25.86a2.83,2.83,0,0,1-.06.42.82.82,0,0,0,0,.44A12.76,12.76,0,0,1,100.19,44.77Z"/><path class="cls-3" d="M97.61,45.77l-.06.06c-.06.11-.14.24-.08.34s.41.14.53,0,0-.24,0-.34a.25.25,0,0,0-.22-.1.41.41,0,0,0,.12-.09.38.38,0,0,0,0-.36l-.88.43a.28.28,0,0,0,.1.09A.85.85,0,0,0,97.61,45.77Z"/><path class="cls-3" d="M86,55.27a3.07,3.07,0,0,0,.72.31.76.76,0,0,0,.75-.43c.06-.16.07-.54-.27-.59-.55.2-.55-.22-1-.2a.61.61,0,0,0-.19.06A.47.47,0,0,0,86,55.27Z"/><path class="cls-3" d="M87,53c.06.07.3.17.4.1s0-.21,0-.32a.28.28,0,0,0,0-.32.33.33,0,0,0-.26-.13.5.5,0,0,0-.28.27.23.23,0,0,0,0,.18A.38.38,0,0,0,87,53Z"/><path class="cls-3" d="M87.38,52.08a.53.53,0,0,0,.32.1.74.74,0,0,0,.52-.21l.08.06a.56.56,0,0,0,.77-.12.28.28,0,0,0,.23.1.26.26,0,0,0,.37,0,.35.35,0,0,0,.15-.43.3.3,0,0,0-.26-.21h0a.34.34,0,0,0-.38-.07l-.11-.24.45-.23a1.9,1.9,0,0,0,0-.41.18.18,0,0,0,0-.09c-.79.59-1.56,1.22-2.31,1.87a.3.3,0,0,0,.15-.07S87.36,52.1,87.38,52.08Z"/><path class="cls-3" d="M85.54,57.13a.73.73,0,0,0,.87-.26c.12-.19,0-.53-.28-.66a.5.5,0,0,0-.71.23A.49.49,0,0,0,85.54,57.13Z"/><path class="cls-3" d="M90.89,50.36a1.28,1.28,0,0,0,1-1c0-.26-.21-.41-.33-.51q-.7.48-1.41,1A.79.79,0,0,0,90.89,50.36Z"/><path class="cls-3" d="M91.59,53.83a.3.3,0,0,0,.15.44.4.4,0,0,0,.51-.15.39.39,0,0,0-.18-.5A.37.37,0,0,0,91.59,53.83Z"/><path class="cls-3" d="M92.53,50.73a.67.67,0,0,0,.69-1.16s0,0-.07,0a.63.63,0,0,0-.1-.3.87.87,0,0,0,.68-.44.76.76,0,0,0-.27-1.06s0,0-.06,0l-1,.65a.67.67,0,0,0,.12.65l-.06,0c-.13.12,0,.41.07.54l0,0a1.08,1.08,0,0,0-.18.23A.57.57,0,0,0,92.53,50.73Z"/><path class="cls-3" d="M89.09,53.31a.58.58,0,0,0,.11.75.7.7,0,0,0,.78-.23.52.52,0,1,0-.89-.52Z"/><ellipse class="cls-4" cx="124.56" cy="71.65" rx="23.77" ry="22.91"/><circle class="cls-5" cx="32.54" cy="34.28" r="1.93"/><circle class="cls-5" cx="223.42" cy="67.02" r="1.93"/><path class="cls-6" d="M145,86.21c.35-.52,33.13,20.58,33.66,20.93a1.14,1.14,0,0,1-1.26,1.9C176.84,108.69,144.62,86.74,145,86.21Z"/><path class="cls-6" d="M140.63,50.56c-.49-.39,23.57-31.07,24-31.56a1.14,1.14,0,1,1,1.77,1.43C166,20.92,141.12,51,140.63,50.56Z"/><path class="cls-6" d="M87.44,93.64c.21.59-36.2,14.52-36.79,14.74a1.14,1.14,0,0,1-.79-2.14C50.45,106,87.22,93.05,87.44,93.64Z"/><path class="cls-6" d="M100.59,92.3c.31.55-33.15,20.56-33.69,20.88a1.14,1.14,0,0,1-1.15-2C66.3,110.89,100.27,91.76,100.59,92.3Z"/><path class="cls-7" d="M204.47,18.65c-5.1,0-7.1,2-7.1,7.1,0-5.1-2-7.1-7.09-7.1,5.09,0,7.09-2,7.09-7.1C197.37,16.65,199.37,18.65,204.47,18.65Z"/><path class="cls-7" d="M68.84,18.65c-3.93,0-5.48,1.55-5.48,5.48,0-3.93-1.54-5.48-5.48-5.48,3.94,0,5.48-1.55,5.48-5.48C63.36,17.1,64.91,18.65,68.84,18.65Z"/><path class="cls-7" d="M184.17,79.21c-4.61,0-6.43,1.81-6.43,6.42,0-4.61-1.81-6.42-6.42-6.42,4.61,0,6.42-1.81,6.42-6.43C177.74,77.4,179.56,79.21,184.17,79.21Z"/><path class="cls-7" d="M55.68,124.7c-5.66,0-7.89,2.22-7.89,7.89,0-5.67-2.22-7.89-7.89-7.89,5.67,0,7.89-2.23,7.89-7.89C47.79,122.47,50,124.7,55.68,124.7Z"/><rect class="cls-8" x="90.25" y="33.05" width="3.26" height="3.26" transform="translate(-2.21 62.83) rotate(-37.34)"/><rect class="cls-5" x="121.32" y="112.16" width="3.45" height="3.45" transform="translate(-31.7 49.35) rotate(-20.15)"/><rect class="cls-5" x="182.53" y="5.79" width="3.45" height="3.45" transform="translate(8.69 63.92) rotate(-20.15)"/><polygon class="cls-9" points="157.62 75.65 156.7 74.28 155.06 74.38 154.32 75.86 155.24 77.23 156.88 77.13 157.62 75.65"/><polygon class="cls-8" points="190.14 106.28 194.69 112.2 199.53 108.06 190.14 106.28"/><polygon class="cls-8" points="35.22 67.85 35.44 73.39 44.44 71.26 35.22 67.85"/><polygon class="cls-5" points="70.39 84.46 71.86 88.71 76.11 85.87 70.39 84.46"/><polygon class="cls-5" points="146.29 27.82 141.84 27.18 142.41 32.26 146.29 27.82"/><rect class="cls-2" x="103.66" y="49.81" width="3.16" height="3.16" transform="translate(-11.58 42.97) rotate(-21.9)"/><rect class="cls-2" x="126.85" y="95.84" width="2.46" height="2.46" transform="translate(-32.63 96.96) rotate(-37.12)"/><circle class="cls-9" cx="126.66" cy="56.78" r="1.21"/><circle class="cls-9" cx="109.51" cy="82.54" r="0.73"/><circle class="cls-9" cx="141.21" cy="61.97" r="0.48"/><circle class="cls-9" cx="127.33" cy="88.64" r="0.48"/><circle class="cls-2" cx="121.37" cy="71.16" r="0.48"/><circle class="cls-2" cx="129.94" cy="75.81" r="1.21"/><circle class="cls-2" cx="110.91" cy="63.5" r="1.21"/></g></svg> \ No newline at end of file diff --git a/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg new file mode 100644 index 00000000000000..ac5298be17ccaf --- /dev/null +++ b/x-pack/plugins/global_search_bar/public/assets/illustration_product_no_search_results_light.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 140"><defs><style>.cls-1{fill:#e6ebf2;}.cls-2{fill:#0a89db;}.cls-3{fill:#1c1e23;}.cls-4{fill:#00bfb3;}.cls-5{fill:#fec514;}.cls-6{fill:#ff957d;}.cls-7{fill:#f04e98;}.cls-8{fill:#7de2d1;}.cls-9{fill:#294492;}</style></defs><g id="Layer_1" data-name="Layer 1"><path class="cls-1" d="M80.64,19.05a.66.66,0,0,0-.13.76.61.61,0,0,0,.72.08.46.46,0,0,0,.13-.53C81.17,19.05,80.84,18.91,80.64,19.05Z"/><path class="cls-1" d="M80.8,21.08a.36.36,0,0,0-.07.49.72.72,0,0,0,.33.29l.5-.22a1,1,0,0,0,0-.56C81.53,20.87,81,20.85,80.8,21.08Z"/><path class="cls-1" d="M80.31,18c.15.15.39.05.51,0s.11-.24,0-.37-.32-.2-.41-.07S80.16,17.81,80.31,18Z"/><path class="cls-1" d="M81.86,17.28a.51.51,0,0,0,.67.2.77.77,0,0,0,.18-.93.52.52,0,0,0-.66-.07A.54.54,0,0,0,81.86,17.28Z"/><path class="cls-1" d="M85,18.35a.59.59,0,0,0,.2-.88.63.63,0,0,0-.81-.25c-.27.14-.32.63-.11,1A.48.48,0,0,0,85,18.35Z"/><path class="cls-1" d="M82.43,19.18a.8.8,0,0,0,1,.31.83.83,0,0,0,.25-1c-.17-.32-.62-.38-1-.14A.62.62,0,0,0,82.43,19.18Z"/><path class="cls-1" d="M84.52,20.42s0,0,0,0c-.12-.16-.27-.15-.43,0a.38.38,0,0,0-.18.31Z"/><path class="cls-1" d="M76.75,23.79a.25.25,0,0,0,.06-.41.46.46,0,0,0-.34-.14c-.1,0-.28.33-.19.47A.33.33,0,0,0,76.75,23.79Z"/><path class="cls-1" d="M74.9,21.3c.16-.07.08-.3,0-.37a.21.21,0,0,0-.28-.1.23.23,0,0,0-.07.29C74.61,21.19,74.75,21.37,74.9,21.3Z"/><path class="cls-1" d="M74.53,24.76c.1,0,.17-.18.17-.24a.24.24,0,0,0-.17-.19c-.08,0-.16.11-.18.18A.28.28,0,0,0,74.53,24.76Z"/><path class="cls-1" d="M74.7,23.78a.38.38,0,0,0,.41.27.3.3,0,0,0,.31-.26c0-.28-.14-.4-.38-.52C74.85,23.42,74.62,23.51,74.7,23.78Z"/><path class="cls-1" d="M79.91,18.26a.42.42,0,0,0,.06-.34.3.3,0,0,0-.46-.06.64.64,0,0,0-.07.28C79.59,18.34,79.81,18.38,79.91,18.26Z"/><path class="cls-1" d="M75.8,23.28a.56.56,0,0,0,.13-.41c0-.14-.33-.19-.45-.07s-.09.22-.12.29C75.5,23.32,75.71,23.4,75.8,23.28Z"/><path class="cls-1" d="M73.4,21.57c.23.26.64,0,.63-.2s-.34-.43-.51-.34S73.17,21.32,73.4,21.57Z"/><path class="cls-1" d="M73.37,19.17c.12-.09,0-.23,0-.29a.24.24,0,0,0-.23,0,.27.27,0,0,0,0,.24S73.24,19.27,73.37,19.17Z"/><path class="cls-1" d="M77.34,22.59a.66.66,0,0,0,.74.65l.1,0,.24-.12c.16-.17.18-.4.41-.33a.27.27,0,0,1,.12.07l1-.46c-.09-.06-.17-.09-.17-.14s.09-.45-.19-.51c-.58-.14-.56.42-.75.43S78.3,22,78,22,77.3,22.19,77.34,22.59Z"/><path class="cls-1" d="M77.42,20.93c.1.18.51.22.75.08a.45.45,0,0,0,.2-.62.6.6,0,0,0-.76-.25A.64.64,0,0,0,77.42,20.93Z"/><path class="cls-1" d="M79.38,20c.13-.11.08-.42,0-.52a.29.29,0,0,0-.44,0,.23.23,0,0,0,0,.38C79,19.87,79.26,20.08,79.38,20Z"/><path class="cls-1" d="M77.06,19.1c.08-.15.37-.32.28-.5s-.39-.12-.56-.18l-.08.09a.35.35,0,0,0,.09.41A.44.44,0,0,0,77.06,19.1Z"/><path class="cls-1" d="M77.07,19.39s0,.09,0,.1.09.09.14.07,0-.08,0-.13S77.1,19.38,77.07,19.39Z"/><path class="cls-1" d="M73.53,24.37c.08-.14-.18-.58-.39-.53a.37.37,0,0,0-.26.41C72.94,24.46,73.45,24.52,73.53,24.37Z"/><path class="cls-1" d="M65.58,31.11c.13-.09.09-.3-.06-.42s-.32-.13-.41,0-.23.38,0,.52S65.46,31.19,65.58,31.11Z"/><path class="cls-1" d="M60.51,36a.67.67,0,0,0,.06-.81.65.65,0,0,0-.85,0,.67.67,0,0,0,.15.88A.45.45,0,0,0,60.51,36Z"/><path class="cls-1" d="M82.63,21a.32.32,0,0,0,.14-.46.35.35,0,0,0-.5-.14.39.39,0,0,0-.12.52A.36.36,0,0,0,82.63,21Z"/><path class="cls-1" d="M58.44,38.1a.36.36,0,0,0-.36-.26c-.18,0-.22.16-.13.54A.33.33,0,0,0,58.44,38.1Z"/><path class="cls-1" d="M56.15,42.72a.27.27,0,0,0-.42.06.53.53,0,0,0-.08.6l0,0c0,.07,0,.17,0,.24a.08.08,0,0,0,.08,0c.17-.28.36-.56.53-.84A.68.68,0,0,0,56.15,42.72Z"/><path class="cls-1" d="M56.74,41.7a.37.37,0,0,0,0,.39c.11-.18.22-.37.34-.55A.55.55,0,0,0,56.74,41.7Z"/><path class="cls-1" d="M49.84,57.68a.49.49,0,0,0-.13.42c0,.09.13.18.23.22.06-.24.1-.48.16-.72A.37.37,0,0,0,49.84,57.68Z"/><path class="cls-1" d="M73.46,24.64s0,.18,0,.2.19.14.28.05,0-.23,0-.24A.29.29,0,0,0,73.46,24.64Z"/><path class="cls-1" d="M106.1,30.08a.39.39,0,0,0,.2,0s.08-.16.06-.19a.31.31,0,0,0-.23-.08s0,0-.07,0Z"/><path class="cls-1" d="M50.19,53.75a.26.26,0,0,0-.09-.41.44.44,0,0,0-.37,0c-.09.05-.15.4,0,.5A.35.35,0,0,0,50.19,53.75Z"/><path class="cls-1" d="M52,46.87c.12-.13,0-.27-.11-.36a.26.26,0,0,0-.41.07c0,.12-.14.37.05.46S51.91,47,52,46.87Z"/><path class="cls-1" d="M51.56,51.51c-.17.07-.54,0-.79.09s-.62.45-.44.82a.66.66,0,0,0,.92.35c.49-.21.19-.68.54-.69.15-.41.28-.81.44-1.21a.35.35,0,0,0-.11,0C51.52,50.92,51.73,51.43,51.56,51.51Z"/><path class="cls-1" d="M50.75,47.23a.5.5,0,0,0,0,.28c.21.14.43.11.48,0a.45.45,0,0,0,0-.34A.31.31,0,0,0,50.75,47.23Z"/><path class="cls-1" d="M50.76,48.87a.23.23,0,0,0,.11.36c.1.06.4.17.48,0s-.07-.42-.17-.5S50.87,48.71,50.76,48.87Z"/><path class="cls-1" d="M53.05,48.55A.46.46,0,0,0,53,48c-.29-.22-.65-.25-.79,0a.65.65,0,0,0,.14.76A.6.6,0,0,0,53.05,48.55Z"/><path class="cls-1" d="M71.39,25.93a.37.37,0,0,0-.49.06.41.41,0,0,0,0,.53.19.19,0,0,0,.1.05.77.77,0,0,0-.11.83.2.2,0,0,0,.08.08l1-.71a1.34,1.34,0,0,0-.07-.18.62.62,0,0,0-.5-.28A.48.48,0,0,0,71.39,25.93Z"/><path class="cls-1" d="M65.08,26.93c0-.06.12-.21,0-.26a.26.26,0,0,0-.26,0c0,.05-.11.25,0,.3A.22.22,0,0,0,65.08,26.93Z"/><path class="cls-1" d="M65.31,24.29c.11-.11,0-.23-.09-.29a.23.23,0,0,0-.2,0,.29.29,0,0,0,0,.24A.22.22,0,0,0,65.31,24.29Z"/><path class="cls-1" d="M63.56,28.38c-.29.26-.3.45,0,.73s.43.43.64.64c-.37.42-.36.49.06.71.27-.12.3-.32.19-.58.55-.42.46-.81-.21-1.1a.71.71,0,0,1,0-.14c0-.21-.09-.3-.26-.37A.37.37,0,0,0,63.56,28.38Z"/><path class="cls-1" d="M65.42,25.67a.2.2,0,0,0,.3-.08.22.22,0,0,0-.08-.27.19.19,0,0,0-.27,0A.21.21,0,0,0,65.42,25.67Z"/><path class="cls-1" d="M64.13,33.09a.91.91,0,0,0-.91.3.56.56,0,0,0,0,.64Z"/><path class="cls-1" d="M63.75,27.92c.17-.22.1-.36,0-.45s-.3-.14-.38,0a.41.41,0,0,0,0,.39C63.47,28,63.65,27.91,63.75,27.92Z"/><path class="cls-1" d="M53.45,45.83a.76.76,0,0,0-.15-.93.56.56,0,0,0-.55,1A.51.51,0,0,0,53.45,45.83Z"/><path class="cls-1" d="M61.92,32.22a.48.48,0,0,0,.72-.15.63.63,0,0,0-.17-.94.65.65,0,0,0-.84.16A.72.72,0,0,0,61.92,32.22Z"/><path class="cls-1" d="M71.26,24.25a.56.56,0,0,0,.22-.61.37.37,0,0,0-.08-.18.24.24,0,0,0,0-.22c-.07-.08-.27-.27-.45-.14a.19.19,0,0,0-.08.19c-.19,0-.44,0-.53.15s.11.33.15.45C70.6,24.22,71,24.41,71.26,24.25Z"/><path class="cls-1" d="M69,27.19c.09.06.3-.11.32-.17a.27.27,0,0,0-.12-.26.29.29,0,0,0-.27.1A.3.3,0,0,0,69,27.19Z"/><path class="cls-1" d="M66.92,30.25a.8.8,0,0,0,.27.12l.66-.55a.75.75,0,0,0-.29-.67.76.76,0,0,0-.89.17C66.46,29.6,66.57,30,66.92,30.25Z"/><path class="cls-1" d="M68.92,24.41a.38.38,0,0,0,0-.49.33.33,0,0,0-.48,0,.39.39,0,0,0,0,.5A.32.32,0,0,0,68.92,24.41Z"/><path class="cls-1" d="M68.26,27.16a.63.63,0,0,0,0-.84.56.56,0,0,0-.69.19.52.52,0,0,0,.15.68C67.93,27.36,68.12,27.34,68.26,27.16Z"/><path class="cls-1" d="M69.44,28.55l0,0,.57-.44a.49.49,0,0,0-.32-.31.41.41,0,0,0-.3.73Z"/><path class="cls-1" d="M54.93,41.6A.33.33,0,0,0,55,42c.21.24.34.25.67.08.25.07.46.16.68.2a.24.24,0,0,0,.26-.37c-.16-.21-.27-.48-.58-.55l-.33.18C55.36,41.42,55,41.43,54.93,41.6Z"/><path class="cls-1" d="M54.8,40.5a.33.33,0,0,0,.45-.31c0-.11-.25-.2-.4-.28a.14.14,0,0,0-.22.05A.45.45,0,0,0,54.8,40.5Z"/><path class="cls-1" d="M56.34,36.18a.36.36,0,0,0-.36-.26.32.32,0,0,0-.14.54A.33.33,0,0,0,56.34,36.18Z"/><path class="cls-1" d="M54.2,46.47l.1-.17a1,1,0,0,0-.43.3.59.59,0,0,0-.06.69Z"/><path class="cls-1" d="M56.87,39c.07-.09,0-.23-.06-.25s-.16-.11-.25,0a.14.14,0,0,0,0,.21C56.64,39,56.8,39.07,56.87,39Z"/><path class="cls-1" d="M55.08,43.77c.08-.17-.26-.68-.48-.67s-.31.52-.13.68S55,43.93,55.08,43.77Z"/><path class="cls-1" d="M58.91,31.58a.44.44,0,0,0,0-.54.53.53,0,0,0-.67,0,.59.59,0,0,0,0,.72C58.34,31.91,58.68,31.83,58.91,31.58Z"/><path class="cls-1" d="M61.24,29.65c0-.24,0-.47-.23-.56a.32.32,0,0,0-.42.17.45.45,0,0,0,0,.36C60.76,29.86,61,29.8,61.24,29.65Z"/><path class="cls-1" d="M60.46,33.57a.65.65,0,0,0-.06-.81.59.59,0,0,0-.75.1c-.19.21-.14.44.14.67S60.34,33.71,60.46,33.57Z"/><path class="cls-1" d="M57.47,40.76a.33.33,0,0,0,0-.47.29.29,0,0,0-.43.07.27.27,0,0,0,0,.39A.28.28,0,0,0,57.47,40.76Z"/><path class="cls-1" d="M61.84,35.46l.45-.51A.78.78,0,0,0,62,34.5a.37.37,0,0,0-.51.12.52.52,0,0,0,0,.74A.67.67,0,0,0,61.84,35.46Z"/><path class="cls-1" d="M57.47,35.79c0,.07,0,.15-.07.23a.74.74,0,0,0,.28.77.87.87,0,0,0,.84,0,.44.44,0,0,0,.19-.27.68.68,0,0,0-.48-.83L58,35.6a4.28,4.28,0,0,0,.14-.43c.07-.27,0-.39-.25-.5a.55.55,0,0,0-.63.13.48.48,0,0,0-.07.59A2.43,2.43,0,0,0,57.47,35.79Z"/><path class="cls-1" d="M51.91,83.2a.29.29,0,0,0,.33-.27.33.33,0,0,0-.33-.34.3.3,0,0,0-.28.33A.27.27,0,0,0,51.91,83.2Z"/><path class="cls-1" d="M54.58,89.62a.83.83,0,0,0-.74-.72c-.37,0-.64.34-.65.82a.61.61,0,0,0,.61.63A.8.8,0,0,0,54.58,89.62Z"/><path class="cls-1" d="M54.25,85.16a2.81,2.81,0,0,0,0-.58c0-.16-.1-.22-.28-.21s-.11.09-.11.13a.84.84,0,0,1-.23.47.4.4,0,0,0,.08.34.44.44,0,0,0,.35.1C54.14,85.4,54.23,85.25,54.25,85.16Z"/><path class="cls-1" d="M53.73,87.07a.63.63,0,0,0-.62.58c0,.3.38.59.78.58a.48.48,0,0,0,.5-.54A.59.59,0,0,0,53.73,87.07Z"/><path class="cls-1" d="M52.59,86.13c.22,0,.51-.26.5-.44s-.29-.42-.62-.41-.28.14-.29.32S52.39,86.14,52.59,86.13Z"/><path class="cls-1" d="M52.7,84.44a.43.43,0,0,0,.39-.37c0-.17-.26-.44-.4-.43a.53.53,0,0,0-.41.48A.37.37,0,0,0,52.7,84.44Z"/><path class="cls-1" d="M53.32,83.24c0,.28.21.43.55.41s.42-.21.39-.55-.15-.4-.34-.4A.81.81,0,0,0,53.32,83.24Z"/><path class="cls-1" d="M52.24,93.46a.23.23,0,0,0,.32.21c.11,0,.41-.12.38-.29s-.32-.27-.44-.27S52.22,93.26,52.24,93.46Z"/><path class="cls-1" d="M88.41,130.26a1,1,0,0,0,0-.2l-.7-.29a.36.36,0,0,0,0,.36C87.94,130.37,88.18,130.3,88.41,130.26Z"/><path class="cls-1" d="M53.41,98.18c0,.11.15.19.19.25.27,0,.45-.14.39-.29a.59.59,0,0,0-.29-.31C53.56,97.77,53.37,98,53.41,98.18Z"/><path class="cls-1" d="M55.14,91.46a.33.33,0,0,0,.31-.37.31.31,0,0,0-.32-.35.36.36,0,0,0-.38.36A.39.39,0,0,0,55.14,91.46Z"/><path class="cls-1" d="M53.41,91.37c-.37,0-.65.21-.64.46a.69.69,0,0,0,.6.5.62.62,0,0,0,.43-.58A.46.46,0,0,0,53.41,91.37Z"/><path class="cls-1" d="M55,93c.26,0,.56-.17.58-.37,0-.4-.36-.55-.56-.61s-.47.34-.4.67A.37.37,0,0,0,55,93Z"/><path class="cls-1" d="M51.38,92.41c.26,0,.4-.2.34-.34a.42.42,0,0,0-.26-.23c-.15,0-.33.21-.27.37S51.34,92.37,51.38,92.41Z"/><path class="cls-1" d="M51.44,98.71c-.14-.08-.54.07-.55.26s.07.46.41.39S51.59,98.79,51.44,98.71Z"/><path class="cls-1" d="M50.72,94.45c-.2,0-.3.28-.43.4l0,.11a.32.32,0,0,0,.39.13A.42.42,0,0,0,51,95C50.93,94.81,50.92,94.47,50.72,94.45Z"/><path class="cls-1" d="M51.72,102c-.1,0-.36.09-.34.31s.27.21.36.19a.35.35,0,0,0,.24-.23A.23.23,0,0,0,51.72,102Z"/><path class="cls-1" d="M50.69,91.09a.45.45,0,0,0-.36-.5.46.46,0,0,0-.43.4c0,.18.27.36.61.36C50.55,91.3,50.68,91.2,50.69,91.09Z"/><path class="cls-1" d="M53.79,100.66c-.08-.15-.6-.14-.66.07a.38.38,0,0,0,.22.43C53.57,101.21,53.87,100.8,53.79,100.66Z"/><path class="cls-1" d="M53.67,105.59a.27.27,0,0,0-.05.28.33.33,0,0,0,.32.1c.1,0,.06-.31,0-.36A.26.26,0,0,0,53.67,105.59Z"/><path class="cls-1" d="M52.79,94.47c-.32,0-.63.28-.6.53a.64.64,0,0,0,.58.56c.21,0,.45-.33.45-.61A.44.44,0,0,0,52.79,94.47Z"/><path class="cls-1" d="M55.1,96.16c.25-.49-.28-.65,0-.89s.37.11.72-.25.24-.88-.1-1-.54.18-.61.14-.35-.3-.55-.09c-.4.43.08.69,0,.86s-.41.35-.55.58a.6.6,0,0,0,.18.91A.66.66,0,0,0,55.1,96.16Z"/><path class="cls-1" d="M50.16,83.91c-.07-.08-.32,0-.48,0a.15.15,0,0,0-.14.18.46.46,0,0,0,.48.31A.33.33,0,0,0,50.16,83.91Z"/><path class="cls-1" d="M54.58,97.32a.49.49,0,0,0-.29.23c0,.1.14.41.31.4a.35.35,0,0,0,.31-.37A.26.26,0,0,0,54.58,97.32Z"/><path class="cls-1" d="M51.61,90.92c-.18,0-.34.17-.27.32s.12.38.33.32.24-.3.25-.43S51.76,90.91,51.61,90.92Z"/><path class="cls-1" d="M51.51,98.11c.07,0,.29,0,.31-.19s-.22-.22-.3-.21-.23.05-.23.19S51.43,98.1,51.51,98.11Z"/><path class="cls-1" d="M51.86,89.88c.3,0,.54-.25.52-.47a.76.76,0,0,0-.71-.63.54.54,0,0,0-.4.54A.53.53,0,0,0,51.86,89.88Z"/><path class="cls-1" d="M49,98c-.06,0-.15.14-.13.19a.23.23,0,0,0,.18.16c.06,0,.22,0,.2-.21S49,98,49,98Z"/><path class="cls-1" d="M49.11,88.12a.19.19,0,0,0-.15.15c0,.07.12.14.2.15s.2,0,.2-.16S49.18,88.12,49.11,88.12Z"/><path class="cls-1" d="M48.38,92.64A.35.35,0,0,0,48,93c0,.22.15.35.34.34s.51-.23.49-.4S48.57,92.64,48.38,92.64Z"/><path class="cls-1" d="M48.4,88.57c0-.1-.12-.16-.18-.17s-.26.07-.26.16.19.18.25.18S48.4,88.66,48.4,88.57Z"/><path class="cls-1" d="M51.46,95.09c0-.06-.06-.06-.09-.1s-.09.06-.1.1,0,.09.07.08S51.46,95.14,51.46,95.09Z"/><path class="cls-1" d="M64.28,115.12a.5.5,0,0,0,.09.44.41.41,0,0,0,.39.06.43.43,0,0,0,.13-.65A.38.38,0,0,0,64.28,115.12Z"/><path class="cls-1" d="M62.66,111.52a.26.26,0,0,0-.27-.32.43.43,0,0,0-.33.18c-.05.09.06.42.23.44A.33.33,0,0,0,62.66,111.52Z"/><path class="cls-1" d="M63.25,109.28c.2-.12.28,0,.43,0-.28-.48-.55-1-.81-1.42a1.24,1.24,0,0,0-.27,0,.9.9,0,0,0-.6,1c.1.29.49.21.63,0s.1-.11.15-.11a.16.16,0,0,1,0,.12c-.11.14-.47.26-.65.47s-.32.69,0,.92a.65.65,0,0,0,1-.14C63.44,109.72,62.94,109.47,63.25,109.28Z"/><path class="cls-1" d="M64.67,111c-.2,0-.44.25-.46.5a.47.47,0,0,0,.47.56c.3,0,.54-.11.55-.35a.31.31,0,0,0,0-.08c-.13-.19-.27-.38-.39-.58A.44.44,0,0,0,64.67,111Z"/><path class="cls-1" d="M60,101.92c-.46-1.12-.88-2.25-1.26-3.36-.23,0-.46.23-.46.41a.69.69,0,0,0,.56.59l.11,0a.82.82,0,0,0-.11.3,1.18,1.18,0,0,0,.21.78.63.63,0,0,1,.07.3c0,.39.15.59.54.59a.39.39,0,0,0,0,.14C59.66,101.79,59.83,101.9,60,101.92Z"/><path class="cls-1" d="M68.57,116.84c0,.3.12.59.34.61a.71.71,0,0,0,.57-.36c-.21-.23-.4-.46-.6-.7A.48.48,0,0,0,68.57,116.84Z"/><path class="cls-1" d="M60.82,109.17s0,0,.09,0h0c.21,0,.51-.24.56-.52a.46.46,0,0,0-.35-.56c-.3,0-.66.17-.68.42a.68.68,0,0,0,.19.49.05.05,0,0,0,0,0C60.62,109.09,60.74,109.15,60.82,109.17Z"/><path class="cls-1" d="M62.13,115.56c.09.12.67-.16.7-.32s-.28-.33-.56-.26S62.05,115.44,62.13,115.56Z"/><path class="cls-1" d="M73.13,122.42a.35.35,0,0,0,.44-.28.37.37,0,0,0-.29-.43.45.45,0,0,0-.46.29A.36.36,0,0,0,73.13,122.42Z"/><path class="cls-1" d="M79.4,127.62a.37.37,0,0,0,0,.45c.17.11.56-.14.53-.3S79.52,127.46,79.4,127.62Z"/><path class="cls-1" d="M79.89,126.24a.51.51,0,0,0-.37.11c-.13.18,0,.32.15.45s.39-.09.39-.33C80.06,126.39,80,126.24,79.89,126.24Z"/><path class="cls-1" d="M72.54,120.31a.47.47,0,0,0-.41.4.4.4,0,0,0,.37.39.32.32,0,0,0,.23-.07.1.1,0,0,0,0,0c0,.06.16.13.24.08a.2.2,0,0,0,.06-.24.35.35,0,0,0-.17-.13,0,0,0,0,0,0,0A.37.37,0,0,0,72.54,120.31Z"/><path class="cls-1" d="M59.7,106.31c0-.09-.11-.16-.18-.17s-.26.08-.25.17.18.18.24.17S59.7,106.41,59.7,106.31Z"/><path class="cls-1" d="M69.06,118.39a.35.35,0,0,0,.27.4.34.34,0,0,0,.43-.28.39.39,0,0,0-.32-.47A.36.36,0,0,0,69.06,118.39Z"/><path class="cls-1" d="M73.8,121.8a.24.24,0,0,0-.07.4.93.93,0,0,0,.32.15.38.38,0,0,0,.09-.53C74,121.69,73.92,121.73,73.8,121.8Z"/><path class="cls-1" d="M59.11,107.69c-.2-.06-.35.22-.5.31l0,.12a.34.34,0,0,0,.36.2.45.45,0,0,0,.31-.09C59.25,108.07,59.3,107.74,59.11,107.69Z"/><path class="cls-1" d="M57.67,101.61a.21.21,0,0,0,.21-.13c0-.09-.08-.18-.15-.2s-.26,0-.28.12S57.6,101.61,57.67,101.61Z"/><path class="cls-1" d="M56.88,94.55a.66.66,0,0,0,.61.6.27.27,0,0,0,.19-.11c-.08-.32-.18-.64-.26-1A.61.61,0,0,0,56.88,94.55Z"/><path class="cls-1" d="M57.71,101.24a.44.44,0,0,0,0-.67.38.38,0,0,0-.57.27.49.49,0,0,0,.17.41A.42.42,0,0,0,57.71,101.24Z"/><path class="cls-1" d="M57.47,97.34a.73.73,0,0,0-.67-.6.56.56,0,0,0-.37.57.47.47,0,0,0,.56.47C57.3,97.75,57.5,97.57,57.47,97.34Z"/><path class="cls-1" d="M55,99.17a.47.47,0,0,0,.39.77,2.92,2.92,0,0,0,.78-.09.74.74,0,0,0,.45-.74c0-.17-.11-.29-.27-.3a2.1,2.1,0,0,0-1.21.22A.6.6,0,0,0,55,99.17Z"/><path class="cls-1" d="M61.94,113.84c.08-.08,0-.25-.06-.28a.27.27,0,0,0-.26,0c-.07.06,0,.2,0,.26A.27.27,0,0,0,61.94,113.84Z"/><path class="cls-1" d="M61.66,111.91a.62.62,0,0,0-.23-.37c-.13-.07-.36.12-.35.3s.11.21.14.27C61.49,112.16,61.69,112.06,61.66,111.91Z"/><path class="cls-1" d="M60.52,105.52a.44.44,0,0,0-.21-.27c-.15-.06-.37.15-.34.31a.76.76,0,0,0,.15.24C60.38,105.81,60.55,105.68,60.52,105.52Z"/><path class="cls-1" d="M55.09,84.12c0,.29.24.44.61.4h0c0-.35-.07-.7-.1-1a.21.21,0,0,0-.1,0C55.27,83.46,55.07,83.8,55.09,84.12Z"/><path class="cls-1" d="M60.55,105c.21,0,.3-.26.33-.39s-.11-.24-.27-.26-.35.11-.32.27S60.35,105,60.55,105Z"/><path class="cls-1" d="M61,106.19a1.26,1.26,0,0,0,.28-.2.38.38,0,0,0-.41-.35c-.17,0-.19.15-.18.29S60.82,106.25,61,106.19Z"/><path class="cls-1" d="M56,89.89a.33.33,0,0,0,.29-.38.39.39,0,0,0-.42-.33.29.29,0,0,0-.26.35A.37.37,0,0,0,56,89.89Z"/><path class="cls-1" d="M60.9,104.25a.25.25,0,0,0,.25.27c-.08-.15-.14-.31-.21-.46A.38.38,0,0,0,60.9,104.25Z"/><path class="cls-1" d="M56.53,92.85c.08.12.39.08.6,0-.12-.52-.23-1-.34-1.54-.4,0-.7.43-.54.69s.36.24.34.35S56.4,92.69,56.53,92.85Z"/><path class="cls-1" d="M55,86.25a.61.61,0,0,0,.37.52c.14,0,.41-.19.45-.37a.29.29,0,0,0-.28-.33C55.21,86,55,86.11,55,86.25Z"/><path class="cls-1" d="M58.66,101.17a.18.18,0,0,0-.17.12c0,.07.09.15.16.19s.21,0,.23-.12S58.73,101.19,58.66,101.17Z"/><path class="cls-1" d="M61.05,107.25c.12,0,.43,0,.43-.21s-.27-.34-.39-.36-.29.11-.31.3A.23.23,0,0,0,61.05,107.25Z"/><path class="cls-1" d="M81.66,128.84a.52.52,0,1,0-.55-.87c-.25.17-.38.53-.25.72A.72.72,0,0,0,81.66,128.84Z"/><path class="cls-1" d="M85.2,129.3a.64.64,0,0,0,.29-.53l-.92-.43h0a.58.58,0,0,0-.14.75A.62.62,0,0,0,85.2,129.3Z"/><path class="cls-1" d="M76.73,127.17a.38.38,0,0,0-.12.51.35.35,0,0,0,.5.15.37.37,0,0,0,.14-.5A.45.45,0,0,0,76.73,127.17Z"/><path class="cls-1" d="M80.08,125.89a.61.61,0,0,0,.32.43.69.69,0,0,0,.53.06Z"/><path class="cls-1" d="M77.55,125.74a.27.27,0,0,0-.21-.16.29.29,0,0,0-.27.13.4.4,0,0,0,0,.34l.08,0a.41.41,0,0,0,.06.24.36.36,0,0,0,.52.07.37.37,0,0,0,.11-.54A.4.4,0,0,0,77.55,125.74Z"/><path class="cls-1" d="M54.62,100c.06-.08-.06-.24-.12-.26a.23.23,0,0,0-.25,0c0,.06,0,.2.07.25S54.56,100.07,54.62,100Z"/><path class="cls-1" d="M75.58,125.91c-.16.1-.07.35,0,.46a.24.24,0,0,0,.34.16c.17-.08.28-.32.17-.43S75.73,125.8,75.58,125.91Z"/><path class="cls-1" d="M87.16,129.51l-.49-.2a.22.22,0,0,0-.11.05.35.35,0,0,0,0,.51c.12.13.24.28.4.18S87.24,129.68,87.16,129.51Z"/><path class="cls-1" d="M59.64,108.33s-.1,0-.11.08,0,.1,0,.1.13,0,.14-.07S59.67,108.38,59.64,108.33Z"/><path class="cls-1" d="M59.25,103a.51.51,0,0,0-.49.55.41.41,0,0,0,.33.38.45.45,0,0,0-.17.24c0,.18.2.4.53.46.06,0,.2-.12.23-.22a.48.48,0,0,0-.15-.5.31.31,0,0,0,.21-.32C59.73,103.34,59.43,103,59.25,103Z"/><path class="cls-1" d="M58.46,112.17c-.06.26,0,.45.33.45s.39-.51.26-.62S58.5,112,58.46,112.17Z"/><path class="cls-1" d="M60,119.17a.32.32,0,0,0-.1.27.33.33,0,0,0,.3.16c.1,0,.11-.29.08-.36A.31.31,0,0,0,60,119.17Z"/><path class="cls-1" d="M59.31,111c-.08,0-.24,0-.27.15s.1.21.18.24.3,0,.34-.13S59.38,111.05,59.31,111Z"/><path class="cls-1" d="M60.34,114.29a.4.4,0,0,0,.14.47c.21.09.58-.26.52-.42S60.44,114.1,60.34,114.29Z"/><path class="cls-1" d="M63.87,113a2.1,2.1,0,0,0-1.24,0,.85.85,0,0,0-.16.11.47.47,0,0,0,.25.82,2.9,2.9,0,0,0,.78.06.72.72,0,0,0,.57-.65C64.08,113.16,64,113,63.87,113Z"/><path class="cls-1" d="M62.26,117.77a.67.67,0,0,0-.59-.8.54.54,0,0,0-.65.43c-.06.38.26.88.58.9A.62.62,0,0,0,62.26,117.77Z"/><path class="cls-1" d="M56.73,110.89a.25.25,0,0,0-.17.16c0,.07.09.19.15.2s.23,0,.24-.17S56.8,110.9,56.73,110.89Z"/><path class="cls-1" d="M56.61,106.77a.8.8,0,0,0-.67.72.72.72,0,0,0,.67.71,1,1,0,0,0,.75-.81A.7.7,0,0,0,56.61,106.77Z"/><path class="cls-1" d="M55.15,101.06c-.26.12-.14.49,0,.59s.62-.28.63-.44S55.41,100.93,55.15,101.06Z"/><path class="cls-1" d="M64,117.68c0,.21-.05.47.25.55a.35.35,0,0,0,.44-.21.43.43,0,0,0,0-.35C64.5,117.48,64.33,117.5,64,117.68Z"/><path class="cls-1" d="M54.92,103.12a.54.54,0,0,0-.57.54c0,.39.42.82.73.78a.62.62,0,0,0,.56-.64A.67.67,0,0,0,54.92,103.12Z"/><path class="cls-1" d="M58.49,101.82a.38.38,0,0,0-.35-.39.39.39,0,0,0-.39.36.42.42,0,0,0,.39.42A.38.38,0,0,0,58.49,101.82Z"/><path class="cls-1" d="M58.74,115.24c-.1,0-.37,0-.4.24s.24.26.33.26a.32.32,0,0,0,.27-.18A.23.23,0,0,0,58.74,115.24Z"/><path class="cls-1" d="M57.13,105.48a.34.34,0,0,0-.4.33.31.31,0,0,0,.27.39c.23,0,.54-.13.55-.3S57.32,105.51,57.13,105.48Z"/><path class="cls-1" d="M57.3,103.41c.05.21,0,.47.34.49a.35.35,0,0,0,.4-.28.46.46,0,0,0-.1-.34C57.79,103.12,57.62,103.16,57.3,103.41Z"/><path class="cls-1" d="M71.09,123.3c-.06,0,0,.17,0,.24s.15.15.24.1,0-.22,0-.29A.19.19,0,0,0,71.09,123.3Z"/><path class="cls-1" d="M71.57,121.82c-.14,0-.45.06-.46.25s.22.27.33.31a.25.25,0,0,0,.35-.15C71.84,122.05,71.72,121.8,71.57,121.82Z"/><path class="cls-1" d="M75.59,123.88c-.18-.1-.37-.23-.57,0s-.32.29-.49.43a.3.3,0,0,0-.2.45.39.39,0,0,0,.53.19.94.94,0,0,1,.4-.07.56.56,0,0,0,.59-.24C76,124.46,75.78,124,75.59,123.88Z"/><path class="cls-1" d="M73.11,124.43a.89.89,0,0,0-1.19,0c-.2.23.07.53.29.56s.14,0,.22.26a.38.38,0,0,0,.56.23s.05,0,.07,0,.29-.32.27-.37l-.06-.16A.92.92,0,0,0,73.11,124.43Z"/><path class="cls-1" d="M71,124.39a.24.24,0,0,0-.21.11s0,.25.11.27.18-.15.21-.22S71.07,124.39,71,124.39Z"/><path class="cls-1" d="M74.4,124.05a.73.73,0,0,0,.71-.57c0-.22-.23-.48-.51-.5a.5.5,0,0,0-.57.48C74,123.77,74.16,124,74.4,124.05Z"/><path class="cls-1" d="M71.15,119.08a.6.6,0,0,0,.19-.06c-.28-.27-.54-.56-.81-.84a.59.59,0,0,0-.06.16s0,0,0,.07a.62.62,0,0,0-.39.07.7.7,0,0,0-.27.93,1,1,0,0,0,1.07.25A.62.62,0,0,0,71.15,119.08Z"/><path class="cls-1" d="M67.93,119.33a.38.38,0,0,0-.34-.42c-.18,0-.22.12-.24.25a.25.25,0,0,0,.26.32A1.19,1.19,0,0,0,67.93,119.33Z"/><path class="cls-1" d="M66.34,118.28a.77.77,0,0,0-.38-.67.5.5,0,0,0-.58.45c0,.23.15.44.48.5A.37.37,0,0,0,66.34,118.28Z"/><path class="cls-1" d="M65.66,120.77c-.07,0-.27,0-.28.12s.14.21.21.21.2,0,.21-.13S65.72,120.79,65.66,120.77Z"/><path class="cls-1" d="M67.51,116.4a.36.36,0,0,0-.36-.39h0a.7.7,0,0,0,.07-.37.64.64,0,0,1,.09-.5.6.6,0,0,0,.06-.55L67,114.1a.66.66,0,0,0-.33-.07h-.07a.63.63,0,0,0,.18-.22c-.16-.22-.34-.43-.5-.65-.24,0-.5.14-.53.32a.7.7,0,0,0,.45.69h.11a.56.56,0,0,0-.16.27,1.19,1.19,0,0,0,0,.81.44.44,0,0,1,0,.3.48.48,0,0,0,.43.68.27.27,0,0,0,0,.14c0,.16.18.33.4.36A.44.44,0,0,0,67.51,116.4Z"/><path class="cls-1" d="M65.14,115.89a.38.38,0,0,0-.44.28.43.43,0,0,0,.31.49.37.37,0,0,0,.41-.33A.37.37,0,0,0,65.14,115.89Z"/><path class="cls-1" d="M68.38,123a.89.89,0,0,0-.77.9c0,.31.44.29.61.15s.13-.08.34,0,.56,0,.56-.36A.76.76,0,0,0,68.38,123Z"/><path class="cls-1" d="M69.57,120.75c-.2.07-.41.13-.38.42s0,.43,0,.64a.31.31,0,0,0,.21.46.42.42,0,0,0,.49-.29,1.1,1.1,0,0,1,.2-.35.55.55,0,0,0,.2-.61C70.24,120.84,69.78,120.68,69.57,120.75Z"/><path class="cls-1" d="M67.07,120.52c-.18,0-.22.11-.24.25s.09.34.26.31a1.12,1.12,0,0,0,.31-.15A.36.36,0,0,0,67.07,120.52Z"/><path class="cls-1" d="M124.22,137.55c-.06,0-.23,0-.23.18s.19.13.26.14.15-.12.13-.15A.27.27,0,0,0,124.22,137.55Z"/><path class="cls-1" d="M122.15,137.46c-.13,0-.23.23-.2.44a.21.21,0,0,0,.27.2c.17,0,.31-.11.3-.32A.39.39,0,0,0,122.15,137.46Z"/><path class="cls-1" d="M123.87,135.36a.61.61,0,0,0-.57.7.75.75,0,0,0,.79.64.81.81,0,0,0,.61-.78C124.68,135.56,124.33,135.33,123.87,135.36Z"/><path class="cls-1" d="M122.67,134.54a.17.17,0,0,0-.27,0,.39.39,0,0,0,0,.32.51.51,0,0,0,.1.29c.11.1.25,0,.29-.16A.56.56,0,0,0,122.67,134.54Z"/><path class="cls-1" d="M126.64,132.93c-.38-.05-.75-.12-1.12-.18.14.14.24.35.5.39A.68.68,0,0,0,126.64,132.93Z"/><path class="cls-1" d="M126.87,135.77c-.29,0-.42.28-.35.64a.5.5,0,0,0,.5.37c.23,0,.6-.45.57-.64A.64.64,0,0,0,126.87,135.77Z"/><path class="cls-1" d="M124.3,133.24c-.21.17-.11.59.12.9a.68.68,0,0,0,.87,0,.69.69,0,0,0-.15-.94A.68.68,0,0,0,124.3,133.24Z"/><path class="cls-1" d="M122.12,136.22a.41.41,0,0,0,.18-.32.3.3,0,0,0-.34-.32.33.33,0,0,0-.29.33C121.66,136.06,122,136.3,122.12,136.22Z"/><path class="cls-1" d="M126.86,134.27c0-.24.15-.46-.1-.6a.37.37,0,0,0-.48.09.3.3,0,0,0,0,.4C126.39,134.39,126.61,134.35,126.86,134.27Z"/><path class="cls-1" d="M127.2,133a.67.67,0,0,0,0,.2c0,.32.17.46.4.43s.52-.25.48-.51h0Z"/><path class="cls-1" d="M119.07,137.61a.6.6,0,0,0-.5-.4.39.39,0,0,0-.29.45.37.37,0,0,0,.45.28C119,137.91,119.11,137.77,119.07,137.61Z"/><path class="cls-1" d="M118.77,135.48c.19,0,.37-.41.33-.7a.44.44,0,0,0-.48-.32.51.51,0,0,0-.44.5A.67.67,0,0,0,118.77,135.48Z"/><path class="cls-1" d="M117.34,131.51a.71.71,0,0,0-.47.66.64.64,0,0,0,.6.41.47.47,0,0,0,.39-.42C117.82,131.76,117.59,131.48,117.34,131.51Z"/><path class="cls-1" d="M120.7,135.15a.26.26,0,0,0-.24.34c.05.11.11.24.23.26s.35-.25.31-.43S120.83,135.14,120.7,135.15Z"/><path class="cls-1" d="M120.89,132.07a.38.38,0,0,0-.43-.15.43.43,0,0,0-.32.43c.06.29.31.33.53.39C121,132.43,121,132.26,120.89,132.07Z"/><path class="cls-1" d="M119.69,137.33c-.09,0-.14.17-.13.22s.06.24.15.22a.26.26,0,0,0,.17-.2C119.88,137.51,119.78,137.31,119.69,137.33Z"/><path class="cls-1" d="M119.41,131.92c.27,0,.42-.18.45-.35l-.73-.18a.45.45,0,0,0,0,.2C119.18,131.76,119.23,131.94,119.41,131.92Z"/><path class="cls-1" d="M119.87,133.09a.52.52,0,0,0-.44,0c-.25.15-.09.36,0,.59.28,0,.52,0,.59-.27A.35.35,0,0,0,119.87,133.09Z"/><path class="cls-1" d="M130.54,134.65a.21.21,0,0,0-.2.06c0,.07,0,.18.1.23s.2.05.25,0S130.61,134.69,130.54,134.65Z"/><path class="cls-1" d="M138.53,136.68a.49.49,0,0,0-.69.26c-.13.29-.07.59.16.68a.71.71,0,0,0,.85-.3C139,137.12,138.79,136.79,138.53,136.68Z"/><path class="cls-1" d="M137.83,135.55a.38.38,0,0,0-.13-.51.45.45,0,0,0-.53.13.36.36,0,0,0,.16.5A.35.35,0,0,0,137.83,135.55Z"/><path class="cls-1" d="M136.32,137.83a.27.27,0,0,0-.15-.22.38.38,0,0,0-.48.1.27.27,0,0,0,0,.39c.13.2.3.2.51.15,0,.14,0,.28.09.33s.38-.21.38-.26,0-.38-.15-.48A.16.16,0,0,0,136.32,137.83Z"/><path class="cls-1" d="M137.63,134a.36.36,0,0,0-.06-.45l-.81,0a.65.65,0,0,0,.18.15.39.39,0,0,0,.22.47A.34.34,0,0,0,137.63,134Z"/><path class="cls-1" d="M135.2,134c-.12.05-.42.22-.4.35s.35.22.48.24.27-.2.23-.36S135.37,133.92,135.2,134Z"/><path class="cls-1" d="M139.14,134.6c-.31-.15-.76,0-.81.29a.54.54,0,0,0,.26.59.42.42,0,0,0,.58-.07c.13-.13.47-.33.4-.59S139.26,134.66,139.14,134.6Z"/><path class="cls-1" d="M141.42,135.42a.85.85,0,0,0-.11-.16.48.48,0,0,0-.83.23,2.94,2.94,0,0,0-.06.78.72.72,0,0,0,.63.59c.18,0,.54-.1.48-.44C141.17,136,141.57,135.83,141.42,135.42Z"/><path class="cls-1" d="M142.26,134.09a.52.52,0,1,0-.92-.46.59.59,0,0,0,.15.74A.72.72,0,0,0,142.26,134.09Z"/><path class="cls-1" d="M140,137.94s0-.1,0-.12a.11.11,0,0,0-.15,0,.12.12,0,0,0,.07.14S140,138,140,137.94Z"/><path class="cls-1" d="M139.7,134.2a.35.35,0,0,0,.27.35c.2,0,.41-.41.3-.52S139.72,134,139.7,134.2Z"/><path class="cls-1" d="M128.73,137.17c-.12.05-.41.22-.4.37s.37.19.5.18.25-.13.2-.31S128.9,137.12,128.73,137.17Z"/><path class="cls-1" d="M135.05,133.59h-1.17c0,.08,0,.15-.07.23a.31.31,0,0,0,.05.5.42.42,0,0,0,.56-.11,1,1,0,0,1,.3-.27A.58.58,0,0,0,135.05,133.59Z"/><path class="cls-1" d="M130.09,138.85c-.09,0-.16.2-.15.26s.09.18.18.17.15-.12.15-.19S130.18,138.84,130.09,138.85Z"/><path class="cls-1" d="M128.66,135.61a.41.41,0,0,0-.33.46.34.34,0,0,0,.38.29.37.37,0,0,0,.37-.42A.41.41,0,0,0,128.66,135.61Z"/><path class="cls-1" d="M130.16,133.35l-1.08-.1a.8.8,0,0,0,.43.65C129.8,134,129.94,133.89,130.16,133.35Z"/><path class="cls-1" d="M116.29,133.77a.42.42,0,0,0-.33.47.34.34,0,0,0,.38.28.37.37,0,0,0,.37-.42A.41.41,0,0,0,116.29,133.77Z"/><path class="cls-1" d="M130,136.43a.19.19,0,0,0,.12.07.12.12,0,0,0,.09-.1c0-.05-.06-.11-.12-.1A.13.13,0,0,0,130,136.43Z"/><path class="cls-1" d="M133.13,135.52a.77.77,0,0,0-.48-.86.89.89,0,0,0-1,.6c-.05.3.33.42.54.33s.14,0,.32.11l.06,0a.66.66,0,0,0-.11.45c0,.32.31.47.67.41a.51.51,0,0,0,.46-.63A.54.54,0,0,0,133.13,135.52Z"/><path class="cls-1" d="M134.43,136.87s-.06.24,0,.29.22-.08.27-.14a.15.15,0,0,0-.05-.19A.26.26,0,0,0,134.43,136.87Z"/><path class="cls-1" d="M132.54,133.52l-1.23-.07a.71.71,0,0,0,.68.45A.76.76,0,0,0,132.54,133.52Z"/><path class="cls-1" d="M131.15,134.68a.62.62,0,0,0-.53.56.43.43,0,0,0,.43.38.52.52,0,0,0,.45-.55A.36.36,0,0,0,131.15,134.68Z"/><path class="cls-1" d="M162.93,127.71c.1-.07.25-.25.21-.33a.37.37,0,0,0-.31-.16c-.09,0-.29.22-.27.32S162.88,127.74,162.93,127.71Z"/><path class="cls-1" d="M168.18,123.32a.64.64,0,0,0,.62.52.71.71,0,0,0,.48-.67.55.55,0,0,0-.62-.34C168.3,122.89,168.15,123.05,168.18,123.32Z"/><path class="cls-1" d="M171,120.17c.19,0,.55-.47.53-.63a.39.39,0,0,0-.12-.24l-.76.7A.37.37,0,0,0,171,120.17Z"/><path class="cls-1" d="M127.29,135.35c.1-.07.26-.24.22-.32a.39.39,0,0,0-.3-.18c-.09,0-.3.21-.28.31S127.24,135.38,127.29,135.35Z"/><path class="cls-1" d="M115.08,132.78a.49.49,0,0,0,.57.47c.27,0,.51-.27.47-.47a.74.74,0,0,0-.73-.53A.45.45,0,0,0,115.08,132.78Z"/><path class="cls-1" d="M143.87,134a.31.31,0,0,0,.17.44.42.42,0,0,0,.5-.18.39.39,0,0,0-.2-.49A.36.36,0,0,0,143.87,134Z"/><path class="cls-1" d="M136.29,136.3a.37.37,0,0,0,.18-.17s-.09-.15-.13-.15-.23,0-.24.16S136.26,136.31,136.29,136.3Z"/><path class="cls-1" d="M136,134.59c-.13,0-.44-.09-.51.08s.12.33.21.41a.24.24,0,0,0,.38,0C136.22,134.9,136.2,134.63,136,134.59Z"/><path class="cls-1" d="M151.69,131.15a.25.25,0,0,0,.19.15c.06,0,.15-.12.15-.18a.31.31,0,0,0,0-.08l-.32.1Z"/><path class="cls-1" d="M142.45,139a.38.38,0,0,0-.54.15.4.4,0,0,0,.2.53.38.38,0,0,0,.53-.18A.4.4,0,0,0,142.45,139Z"/><path class="cls-1" d="M176.93,115a.59.59,0,0,0-.67-.26.46.46,0,0,0-.26.48c.11.35.39.57.62.48A.64.64,0,0,0,176.93,115Z"/><path class="cls-1" d="M177,113.69a.37.37,0,0,0,.18-.46.08.08,0,0,0,0,0c-.15.19-.31.38-.47.56A.54.54,0,0,0,177,113.69Z"/><path class="cls-1" d="M166.68,123.34l-.25.19A.33.33,0,0,0,166.68,123.34Z"/><path class="cls-1" d="M177.56,113.1c.25,0,.5-.27.47-.47a.56.56,0,0,0-.17-.34l-.58.71A.44.44,0,0,0,177.56,113.1Z"/><path class="cls-1" d="M115.4,135c-.17,0-.36.35-.29.46a.42.42,0,0,0,.36.13.22.22,0,0,0,.24-.28C115.67,135.18,115.61,135,115.4,135Z"/><path class="cls-1" d="M114.17,130.1c-.09,0-.33.12-.33.24s.25.23.35.25a.27.27,0,0,0,.27-.33A.37.37,0,0,0,114.17,130.1Z"/><path class="cls-1" d="M113.82,132.36a.29.29,0,0,0-.32.34.35.35,0,0,0,.37.36.36.36,0,0,0,.34-.35A.37.37,0,0,0,113.82,132.36Z"/><path class="cls-1" d="M115.67,130.8c0-.26-.26-.4-.6-.35s-.43.19-.41.4.39.58.6.56S115.71,131.14,115.67,130.8Z"/><path class="cls-1" d="M123.75,134.71a.32.32,0,0,0,.19-.23.41.41,0,0,0-.15-.32c-.23-.12-.34.07-.46.23C123.4,134.61,123.51,134.76,123.75,134.71Z"/><path class="cls-1" d="M122.72,133.38a1.21,1.21,0,0,1,.32-.11.54.54,0,0,0,.5-.5c0-.28-.13-.43-.54-.38a5.55,5.55,0,0,0-.68.4.34.34,0,0,0-.07.49A.34.34,0,0,0,122.72,133.38Z"/><path class="cls-1" d="M121.88,132.36a.7.7,0,0,0,.44-.21l-.87-.2a.3.3,0,0,0,0,.13A.34.34,0,0,0,121.88,132.36Z"/><path class="cls-1" d="M151.48,131.64c.1,0,.18.11.3.08s.13-.21,0-.33a.6.6,0,0,0-.39-.17l-.13,0a.18.18,0,0,0-.06.18A.43.43,0,0,0,151.48,131.64Z"/><path class="cls-1" d="M145.54,132.69h0l-.83.16s0,0,.06,0A.62.62,0,0,0,145.54,132.69Z"/><path class="cls-1" d="M171.35,121.34a.41.41,0,0,0-.48-.3.4.4,0,0,0-.27.45.41.41,0,0,0,.45.27A.36.36,0,0,0,171.35,121.34Z"/><path class="cls-1" d="M170.57,122.92a.33.33,0,0,0,.23-.42.43.43,0,0,0-.54-.26,1.18,1.18,0,0,0-.17.39C170.08,122.84,170.35,123,170.57,122.92Z"/><path class="cls-1" d="M172.58,119.48a.55.55,0,0,0-.49.66.67.67,0,1,0,1.33-.2C173.37,119.6,173,119.41,172.58,119.48Z"/><path class="cls-1" d="M173.29,117.48l-.4.39v0c0,.15.15.27.34.24s.31-.14.24-.33A.78.78,0,0,0,173.29,117.48Z"/><path class="cls-1" d="M170.31,120.43l-.06-.05-.4.37.09.15a.14.14,0,0,0,.2.08A.46.46,0,0,0,170.31,120.43Z"/><path class="cls-1" d="M149,132.79c-.07-.07-.27-.12-.33-.07a.6.6,0,0,0-.26.37c0,.28.25.27.48.34C149,133.21,149.15,133,149,132.79Z"/><path class="cls-1" d="M151.86,132.38a.33.33,0,0,0,.11.43c.12.1.5-.05.51-.19a.41.41,0,0,0-.17-.34A.3.3,0,0,0,151.86,132.38Z"/><path class="cls-1" d="M155.06,132a.3.3,0,0,0-.12-.13l.1-.12s0-.18,0-.19a.28.28,0,0,0-.24,0,.21.21,0,0,0,0,.29c-.06,0-.11.1-.1.13s.19.13.27.13S155.07,132,155.06,132Z"/><path class="cls-1" d="M152.2,136.94a.12.12,0,0,0,.06.14s.11,0,.12-.06a.11.11,0,0,0,0-.13A.12.12,0,0,0,152.2,136.94Z"/><path class="cls-1" d="M151.32,136.11a.36.36,0,0,0,.21.48.35.35,0,0,0,.48-.21c.1-.21.07-.4-.08-.47A.61.61,0,0,0,151.32,136.11Z"/><path class="cls-1" d="M151.11,133c-.17-.06-.24,0-.31.16s0,.35.14.38.27.05.35,0S151.28,133.1,151.11,133Z"/><path class="cls-1" d="M153.18,136.76s0,.26,0,.29.22-.08.26-.14,0-.19,0-.19A.27.27,0,0,0,153.18,136.76Z"/><path class="cls-1" d="M154.15,131.17c-.06,0-.08.09-.07.13s.09.07.12.06.09-.05.08-.1A.11.11,0,0,0,154.15,131.17Z"/><path class="cls-1" d="M154.82,130.34a.52.52,0,0,0-.19-.24l-.6.22v.1a.37.37,0,0,0,.46.26C154.73,130.64,154.86,130.5,154.82,130.34Z"/><path class="cls-1" d="M154,131.25a.79.79,0,0,0-.2-.84c-.36.13-.71.27-1.07.39a.59.59,0,0,0,.29.74A.76.76,0,0,0,154,131.25Z"/><path class="cls-1" d="M154,133.16a.39.39,0,0,0-.48.11c-.06.12.07.32.26.42a.2.2,0,0,0,.31-.11C154.16,133.42,154.18,133.26,154,133.16Z"/><path class="cls-1" d="M150.6,138.64a.26.26,0,0,0,.12.27.24.24,0,0,0,.23-.06.24.24,0,0,0,0-.23C150.84,138.59,150.64,138.54,150.6,138.64Z"/><path class="cls-1" d="M152,135.24c0,.09.07.22.11.24s.23.08.26,0a.25.25,0,0,0-.06-.25C152.29,135.19,152.08,135.15,152,135.24Z"/><path class="cls-1" d="M145.81,139.12a.33.33,0,0,0-.46.14.41.41,0,0,0,.14.55.41.41,0,0,0,.5-.18A.45.45,0,0,0,145.81,139.12Z"/><path class="cls-1" d="M146.28,135.66a.72.72,0,0,0-.85.31c-.1.17,0,.45.26.56a.5.5,0,0,0,.71-.21C146.52,136.07,146.46,135.74,146.28,135.66Z"/><path class="cls-1" d="M145.9,133.94a.71.71,0,0,0,.29.76.65.65,0,0,0,.68-.27.47.47,0,0,0-.14-.56C146.38,133.68,146,133.71,145.9,133.94Z"/><path class="cls-1" d="M145.15,137.21a.37.37,0,0,0-.51.12.28.28,0,0,0,.1.45.35.35,0,1,0,.41-.57Z"/><path class="cls-1" d="M149.6,134.31a.43.43,0,0,0-.53.21.5.5,0,0,0,.17.65A.68.68,0,0,0,150,135C150.07,134.8,149.86,134.44,149.6,134.31Z"/><path class="cls-1" d="M144.38,134.93c-.24-.12-.47,0-.63.3a.4.4,0,0,0,.11.56.75.75,0,0,0,.79-.18A.48.48,0,0,0,144.38,134.93Z"/><path class="cls-1" d="M143.26,135.63a.36.36,0,0,0-.3.15c-.05.08-.08.34,0,.41s.32-.08.4-.16A.26.26,0,0,0,143.26,135.63Z"/><path class="cls-1" d="M148,138c.12,0,.23-.11.31-.22a.22.22,0,0,0-.1-.36c-.16-.07-.32-.1-.43.08S147.83,138,148,138Z"/><path class="cls-1" d="M147.7,135.91a.42.42,0,0,0-.51.16.43.43,0,0,0,.2.54.34.34,0,0,0,.45-.16A.37.37,0,0,0,147.7,135.91Z"/><path class="cls-1" d="M165.88,131.08c-.09,0-.15.21-.14.27s.1.18.19.17.15-.14.15-.21S166,131.07,165.88,131.08Z"/><path class="cls-1" d="M164.31,127.91a.42.42,0,0,0-.31.48.33.33,0,0,0,.39.27.37.37,0,0,0,.35-.44A.4.4,0,0,0,164.31,127.91Z"/><path class="cls-1" d="M169,124.3c-.14,0-.17.36-.14.46s.19.15.3.15a.19.19,0,0,0,.18-.27C169.27,124.53,169.11,124.27,169,124.3Z"/><path class="cls-1" d="M164.45,129.47c-.12.05-.4.24-.38.38s.37.17.51.17a.23.23,0,0,0,.18-.33C164.71,129.54,164.61,129.4,164.45,129.47Z"/><path class="cls-1" d="M163.08,132.52c-.11,0-.38.21-.35.31s.29.22.41.24.26-.13.25-.34A.23.23,0,0,0,163.08,132.52Z"/><path class="cls-1" d="M162.92,125.94a.29.29,0,0,0,.25,0c.3,0,.48-.26.44-.5Z"/><path class="cls-1" d="M165.85,128.63a.11.11,0,0,0-.13-.09.12.12,0,0,0-.07.13s.09.07.12.06S165.86,128.68,165.85,128.63Z"/><path class="cls-1" d="M168.15,125.34c0-.38-.35-.7-.65-.67a.91.91,0,0,0-.7.82.72.72,0,0,0,.76.56A.69.69,0,0,0,168.15,125.34Z"/><path class="cls-1" d="M166.76,126.87a.63.63,0,0,0-.51.58.43.43,0,0,0,.45.37.55.55,0,0,0,.43-.57C167.1,127,167,126.86,166.76,126.87Z"/><path class="cls-1" d="M166.32,125.38a.38.38,0,0,0-.13-.48c-.06,0-.17-.08-.22-.05a4.32,4.32,0,0,0-.48.31c-.44-.12-.79,0-.87.23a.76.76,0,0,0,.47.77c.3.1.43,0,.64-.62C166,125.6,166.21,125.69,166.32,125.38Z"/><path class="cls-1" d="M162.21,126.72l.25-.06a1.32,1.32,0,0,0,0-.44l-.63.37h0a.33.33,0,0,0,.09.21s.21,0,.26,0S162.21,126.74,162.21,126.72Z"/><path class="cls-1" d="M157.34,134.59c0,.1,0,.29,0,.34a.34.34,0,0,0,.32-.07c.06-.07.08-.35,0-.4S157.36,134.53,157.34,134.59Z"/><path class="cls-1" d="M157.89,130.05c-.13,0-.22.24-.18.45a.21.21,0,0,0,.27.19c.17,0,.31-.13.29-.33A.38.38,0,0,0,157.89,130.05Z"/><path class="cls-1" d="M159,130.46a.22.22,0,0,0-.36.12c0,.11,0,.43,0,.47s.35-.11.44-.19S159.13,130.57,159,130.46Z"/><path class="cls-1" d="M157.68,132.7a.35.35,0,1,0-.68.11.3.3,0,0,0,.36.3A.36.36,0,0,0,157.68,132.7Z"/><path class="cls-1" d="M155.43,130a.26.26,0,0,0-.13.23c0,.08.08.23.16.2a.23.23,0,0,0,.16-.2C155.62,130.2,155.52,130,155.43,130Z"/><path class="cls-1" d="M159.1,133.9c-.09,0-.25.14-.25.22s.16.21.25.22.33-.15.32-.25S159.16,133.88,159.1,133.9Z"/><path class="cls-1" d="M163.26,128.49a.65.65,0,0,0-.74-.34c-.28.06-.4.3-.32.65a.52.52,0,0,0,.52.36C163,129.11,163.31,128.67,163.26,128.49Z"/><path class="cls-1" d="M155.53,135a.35.35,0,1,0-.31.62.29.29,0,0,0,.45-.12A.36.36,0,0,0,155.53,135Z"/><path class="cls-1" d="M159.52,127.88l-.06,0-.29.14a.63.63,0,0,0-.2.56.75.75,0,0,0,.82.6.78.78,0,0,0,.57-.8C160.33,128,160,127.82,159.52,127.88Z"/><path class="cls-1" d="M160,130c-.06,0-.22,0-.22.19s.19.13.27.13.13-.12.12-.15A.31.31,0,0,0,160,130Z"/><path class="cls-1" d="M190.54,38.34l-.07,0,.35.72a.62.62,0,0,0,.16-.54A.33.33,0,0,0,190.54,38.34Z"/><path class="cls-1" d="M189.52,36.57c.17.31.34.63.5,1a.6.6,0,0,0,.13-.56A.63.63,0,0,0,189.52,36.57Z"/><path class="cls-1" d="M195.63,80a.34.34,0,0,0-.34-.38.36.36,0,0,0-.38.36.45.45,0,0,0,.36.4A.36.36,0,0,0,195.63,80Z"/><path class="cls-1" d="M194.73,83a.75.75,0,0,0-.14.76c.1.19.19.39.48.31s.42-.08.63-.12a.31.31,0,0,0,.41-.28.41.41,0,0,0-.36-.44.91.91,0,0,1-.38-.14A.57.57,0,0,0,194.73,83Z"/><path class="cls-1" d="M189.94,89.92a.49.49,0,0,0,.41-.17.39.39,0,0,0-.67-.41A.38.38,0,0,0,189.94,89.92Z"/><path class="cls-1" d="M196.76,48.1a.78.78,0,0,0-.43.63.5.5,0,0,0,.67.32c.22-.06.32-.33.23-.65A.37.37,0,0,0,196.76,48.1Z"/><path class="cls-1" d="M195.39,43.54a.69.69,0,0,0-.79-.39.74.74,0,0,0-.51.56.52.52,0,0,0-.4-.17.82.82,0,0,0-.76.37c.19.49.37,1,.55,1.48h.07a.36.36,0,0,0,.25-.45.09.09,0,0,0,0-.07.68.68,0,0,0,.48-.55.43.43,0,0,0,0-.05.61.61,0,0,0,.7.12.74.74,0,0,0,.28-.14.32.32,0,0,0,.13.23.5.5,0,0,0,.67-.19.62.62,0,0,0-.06-.68A.49.49,0,0,0,195.39,43.54Z"/><path class="cls-1" d="M197.67,50a.35.35,0,0,0-.38-.3.46.46,0,0,0-.3.19c-.11.19,0,.34.31.58C197.48,50.38,197.73,50.32,197.67,50Z"/><path class="cls-1" d="M195.67,81.32c-.18,0-.41.13-.37.29s.14.42.33.41.23-.27.25-.38A.24.24,0,0,0,195.67,81.32Z"/><path class="cls-1" d="M193.48,42.68a.32.32,0,0,0,.42.23.36.36,0,0,0,.29-.44.4.4,0,0,0-.47-.27A.41.41,0,0,0,193.48,42.68Z"/><path class="cls-1" d="M194.27,40.77c.17-.14.06-.31,0-.46s-.41,0-.46.22a.31.31,0,0,0,.11.27A.49.49,0,0,0,194.27,40.77Z"/><path class="cls-1" d="M198.91,52.32a.63.63,0,0,0-.77-.36.68.68,0,0,0-.45.89.55.55,0,0,0,.68.4A.86.86,0,0,0,198.91,52.32Z"/><path class="cls-1" d="M198.63,55a.7.7,0,0,0-1,0,.5.5,0,0,0,0,.74.64.64,0,0,0,1,.07A.65.65,0,0,0,198.63,55Z"/><path class="cls-1" d="M194.68,77a.73.73,0,0,0,.2-.84.48.48,0,0,0-.33-.26c-.12.45-.26.9-.4,1.35A3.65,3.65,0,0,0,194.68,77Z"/><path class="cls-1" d="M196.29,50.52a.54.54,0,0,0,0-.64.48.48,0,0,0-.55-.21,2.14,2.14,0,0,0-.46.17,2.12,2.12,0,0,0-.2-.12.89.89,0,0,0-.32-.08c.13.5.26,1,.37,1.5l.05-.06a.43.43,0,0,0-.09-.35.69.69,0,0,0,.11-.13l.1-.14a.36.36,0,0,0,.21,0l.2.14A.37.37,0,0,0,196.29,50.52Z"/><path class="cls-1" d="M198.25,58.9c.17-.12.21-.28.06-.4s-.31-.33-.5-.19,0,.41,0,.54S198.1,59,198.25,58.9Z"/><path class="cls-1" d="M199.24,61.26a.75.75,0,0,0,0-.9.66.66,0,0,0-1,0,.55.55,0,0,0-.11.81A.75.75,0,0,0,199.24,61.26Z"/><path class="cls-1" d="M196.87,73.83a.38.38,0,0,0-.38-.36c.13-.09.14-.24,0-.37s-.27-.18-.37,0-.24.4-.15.52.13,0,.23,0a.4.4,0,0,0-.1.26.38.38,0,0,0,.39.4A.41.41,0,0,0,196.87,73.83Z"/><path class="cls-1" d="M196.73,76.84c.16,0,.17-.24.05-.41a.22.22,0,0,0-.37,0c-.08.09-.22.37-.16.44S196.62,76.88,196.73,76.84Z"/><path class="cls-1" d="M195.67,72.38a.38.38,0,0,0,.09-.56.4.4,0,0,0-.26-.12c-.05.24-.09.48-.14.72A.35.35,0,0,0,195.67,72.38Z"/><path class="cls-1" d="M197.25,65.2a.53.53,0,0,0-.7,0,.64.64,0,0,0-.25.32s0,.06,0,.1a.38.38,0,0,0,.08.24.64.64,0,0,0,.79.14A.67.67,0,0,0,197.25,65.2Z"/><path class="cls-1" d="M198.11,63.11a.37.37,0,0,0,.06-.52.39.39,0,0,0-.59.5A.42.42,0,0,0,198.11,63.11Z"/><path class="cls-1" d="M197.3,61.73a1,1,0,0,0,.16-.81.53.53,0,0,0-.78,0c-.2.17-.19.42,0,.7S197.14,61.87,197.3,61.73Z"/><path class="cls-1" d="M191.57,36.79a.31.31,0,0,0,.18-.44.41.41,0,0,0-.48-.22.4.4,0,0,0-.2.49A.38.38,0,0,0,191.57,36.79Z"/><path class="cls-1" d="M200.69,53.33s.09-.26,0-.34-.32,0-.36.09a.29.29,0,0,0,.06.28A.34.34,0,0,0,200.69,53.33Z"/><path class="cls-1" d="M199.29,47.59c0,.06.15.22.23.2s.12-.23.1-.29-.12-.16-.21-.13S199.28,47.52,199.29,47.59Z"/><path class="cls-1" d="M201.1,61.89a.37.37,0,0,0-.11.55.64.64,0,0,0,.82.19.56.56,0,0,0,0-.71A.52.52,0,0,0,201.1,61.89Z"/><path class="cls-1" d="M200.51,54.4c-.27.1-.27.33-.18.62.23,0,.44.15.59-.09a.31.31,0,0,0-.06-.44A.42.42,0,0,0,200.51,54.4Z"/><path class="cls-1" d="M199,52.28a.43.43,0,0,0,.53.09.52.52,0,0,0,.18-.65.59.59,0,0,0-.7-.19C198.85,51.65,198.84,52,199,52.28Z"/><path class="cls-1" d="M197.33,67.64a.7.7,0,0,0-.89-.26.89.89,0,0,0-.21,1.06.72.72,0,0,0,.93.12A.71.71,0,0,0,197.33,67.64Z"/><path class="cls-1" d="M198.76,46.54c.18-.06.27-.23.17-.37a1.13,1.13,0,0,0-.28-.22.37.37,0,0,0-.21.49C198.51,46.6,198.64,46.58,198.76,46.54Z"/><path class="cls-1" d="M195.62,52.59c-.07,0-.11.11-.12.21a2.38,2.38,0,0,1,.05.26.75.75,0,0,0,.09.15c.19.21.51,0,.58-.14S195.78,52.55,195.62,52.59Z"/><path class="cls-1" d="M203.14,60.05a.2.2,0,0,0,0,.31.22.22,0,0,0,.29,0,.19.19,0,0,0,0-.26A.21.21,0,0,0,203.14,60.05Z"/><path class="cls-1" d="M195.64,45.62a.35.35,0,0,0,.23-.42.33.33,0,0,0-.44-.26.39.39,0,0,0-.28.49A.37.37,0,0,0,195.64,45.62Z"/><path class="cls-1" d="M197.85,56.49c.21-.08.13-.32.09-.4a.34.34,0,0,0-.29-.16.23.23,0,0,0-.19.32C197.5,56.35,197.65,56.57,197.85,56.49Z"/><path class="cls-1" d="M196.86,69.66a.62.62,0,0,0-.16.75.42.42,0,0,0,.57.11.55.55,0,0,0,.1-.71A.36.36,0,0,0,196.86,69.66Z"/><path class="cls-1" d="M196.21,57.05a.87.87,0,0,0,0-1,.55.55,0,0,0-.23-.17c.06.42.12.84.16,1.25Z"/><path class="cls-1" d="M195.81,54.6c0,.22.06.43.09.64a.36.36,0,0,0,.34-.33C196.24,54.75,196,54.62,195.81,54.6Z"/><path class="cls-1" d="M196.56,58.23c-.05-.07-.19-.09-.34-.09,0,.31.05.62.07.93l.1,0A.63.63,0,0,0,196.56,58.23Z"/><path class="cls-1" d="M194,45.84a.4.4,0,0,0-.28.37l.16.45a.46.46,0,0,0,.55.09c.29-.1.48-.38.4-.58A.75.75,0,0,0,194,45.84Z"/><path class="cls-1" d="M195.06,48.1a0,0,0,0,0,0,0,.41.41,0,0,0-.5-.23.31.31,0,0,0-.19.2c.11.39.24.78.34,1.18a.47.47,0,0,0,.11-.53.24.24,0,0,0,.27-.17.42.42,0,0,0,.13.07.35.35,0,0,0,.34-.29A.32.32,0,0,0,195.06,48.1Z"/><path class="cls-1" d="M191.45,85a.43.43,0,0,0-.13-.26l-.3.67C191.27,85.33,191.47,85.15,191.45,85Z"/><path class="cls-1" d="M189.68,88l.06,0a.53.53,0,0,1,.3-.07c.39,0,.6-.14.6-.53a.23.23,0,0,0,.14,0,.42.42,0,0,0,.29-.46.44.44,0,0,0-.43-.45.37.37,0,0,0-.32.42h-.07C190.07,87.31,189.87,87.68,189.68,88Z"/><path class="cls-1" d="M193.61,79.53c-.07.08-.15.25-.11.32s.25.11.33.07.21-.29.15-.37S193.66,79.49,193.61,79.53Z"/><path class="cls-1" d="M191.48,40.46c.11.23.21.47.31.7l.05,0a1.29,1.29,0,0,0,.67.14l.08.15a.67.67,0,0,0,.86.14.7.7,0,0,0,0-1,.77.77,0,0,0-.53-.18.3.3,0,0,0,0-.1c-.1-.38-.4-.44-.73-.42,0-.21,0-.46-.29-.5a.34.34,0,0,0-.39.23c-.11.28.12.39.32.51l-.11.38Z"/><path class="cls-1" d="M197.52,53.43a.6.6,0,0,0,.08-.76c-.15-.22-.38-.23-.68,0a.6.6,0,0,0-.2.65A.66.66,0,0,0,197.52,53.43Z"/><path class="cls-1" d="M196.91,71.77s.05-.09,0-.13a.12.12,0,0,0-.16,0,.13.13,0,0,0,0,.16A.17.17,0,0,0,196.91,71.77Z"/><path class="cls-1" d="M198.07,81.6s-.15.06-.14.13a.25.25,0,0,0,.15.19c.06,0,.24-.06.24-.15S198.15,81.61,198.07,81.6Z"/><path class="cls-1" d="M198.15,86.15c-.1,0-.14.17-.14.25s.08.15.15.15.13-.13.15-.2S198.26,86.15,198.15,86.15Z"/><path class="cls-1" d="M198,84.19c0-.22-.05-.55-.45-.49a.76.76,0,0,0-.54.85.89.89,0,0,0,1,.61c.29-.1.21-.49,0-.63S198,84.42,198,84.19Z"/><path class="cls-1" d="M198.16,73.76c-.08.06,0,.26,0,.31a.23.23,0,0,0,.25.05.23.23,0,0,0,0-.25C198.4,73.82,198.23,73.71,198.16,73.76Z"/><path class="cls-1" d="M197.54,66.33c-.11.09,0,.4.1.48s.25,0,.34,0a.18.18,0,0,0,0-.31C197.91,66.4,197.64,66.24,197.54,66.33Z"/><path class="cls-1" d="M198,75.29a.32.32,0,0,0,.17.24.43.43,0,0,0,.34-.06.35.35,0,0,0-.12-.5C198.31,74.91,198,75.05,198,75.29Z"/><path class="cls-1" d="M198.29,79.3c-.13.09-.33.17-.35.36s.22.21.29.19.47-.2.47-.34S198.34,79.27,198.29,79.3Z"/><path class="cls-1" d="M194.67,93.17a.28.28,0,0,0,0,.28.31.31,0,0,0,.28.06.32.32,0,0,0,.11-.32C195,93.09,194.73,93.12,194.67,93.17Z"/><path class="cls-1" d="M194.44,80.81a.36.36,0,0,0-.41-.32.38.38,0,0,0-.38.41.45.45,0,0,0,.47.33A.39.39,0,0,0,194.44,80.81Z"/><path class="cls-1" d="M193.11,86c0,.17.15.19.29.19a.24.24,0,0,0,.26-.31.88.88,0,0,0-.2-.28A.37.37,0,0,0,193.11,86Z"/><path class="cls-1" d="M196.49,89.78a.71.71,0,0,0-.63.75.81.81,0,0,0,.71.68.71.71,0,0,0,.72-.67A1.05,1.05,0,0,0,196.49,89.78Z"/><path class="cls-1" d="M195.33,86.15a.93.93,0,0,0-.2-.29.37.37,0,0,0-.35.4c0,.18.15.19.29.19S195.39,86.31,195.33,86.15Z"/><path class="cls-1" d="M197,78.44a.73.73,0,0,0-.68-.61c-.22,0-.44.31-.41.59a.49.49,0,0,0,.57.48C196.82,78.88,197.05,78.68,197,78.44Z"/><path class="cls-1" d="M195.44,87.43c-.09,0-.16.11-.17.18s.07.26.16.26.18-.19.18-.25S195.53,87.43,195.44,87.43Z"/><path class="cls-1" d="M202,59.15c-.07.06,0,.22,0,.26s.18.16.24.1a.22.22,0,0,0,0-.25C202.25,59.2,202.06,59.08,202,59.15Z"/><path class="cls-1" d="M202,68s-.17.14-.12.26.23.06.25,0a.34.34,0,0,0,.06-.24S202,68,202,68Z"/><path class="cls-1" d="M202.67,69.51a.37.37,0,0,0-.36.33.3.3,0,0,0,.17.36c.26.12.42,0,.6-.23C203,69.74,203,69.49,202.67,69.51Z"/><path class="cls-1" d="M193.26,82.83a.48.48,0,0,0-.54-.23.77.77,0,0,0-.29-.47c-.16.38-.31.76-.47,1.13l.1,0a.75.75,0,0,0,.31-.06.33.33,0,0,0,.06.26.49.49,0,0,0,.7,0A.62.62,0,0,0,193.26,82.83Z"/><path class="cls-1" d="M201.89,57.6c-.09-.08-.31-.13-.38-.07s-.1.25-.13.34c.16.22.32.19.43.1S202,57.72,201.89,57.6Z"/><path class="cls-1" d="M203.61,65.39c-.34,0-.64.4-.54.67a.55.55,0,0,0,.53.37.43.43,0,0,0,.46-.37c0-.18.23-.53,0-.71S203.74,65.37,203.61,65.39Z"/><path class="cls-1" d="M203.59,63.29a.34.34,0,0,0-.1.47.38.38,0,0,0,.49.07.32.32,0,0,0,.1-.46A.38.38,0,0,0,203.59,63.29Z"/><path class="cls-1" d="M200.32,67c-.12-.12-.28-.17-.38,0s-.25.41-.16.51.41,0,.53,0S200.44,67.08,200.32,67Z"/><path class="cls-1" d="M200,63.7a.38.38,0,0,0-.14-.48.37.37,0,0,0-.5,0,.56.56,0,0,0,0,.68A.53.53,0,0,0,200,63.7Z"/><path class="cls-1" d="M198.26,63.87a.45.45,0,0,0-.61,0,1.07,1.07,0,0,0,0,.42c.09.19.39.18.56,0A.34.34,0,0,0,198.26,63.87Z"/><path class="cls-1" d="M198.74,69.46a.62.62,0,0,0,0,.81.58.58,0,0,0,.84-.79A.61.61,0,0,0,198.74,69.46Z"/><path class="cls-1" d="M201,74.07c-.05.05,0,.18,0,.25s.16.13.24.07,0-.23,0-.29A.19.19,0,0,0,201,74.07Z"/><path class="cls-1" d="M200.57,58.15c.19.08.31,0,.41-.16a.34.34,0,0,0,0-.41c-.19-.35-.36-.4-.71-.19s-.52.3-.78.45c-.31-.45-.38-.46-.7-.11.05.29.24.37.52.33.26.63.66.65,1.12.07Z"/><path class="cls-1" d="M201.47,65.28a.4.4,0,0,0-.52-.1.38.38,0,0,0-.07.48.46.46,0,0,0,.53.11A.37.37,0,0,0,201.47,65.28Z"/><path class="cls-1" d="M190.07,35.41a.69.69,0,0,0,.32-.74.63.63,0,0,0-.67-.28.47.47,0,0,0-.29.5C189.55,35.27,189.83,35.5,190.07,35.41Z"/><path class="cls-1" d="M196.77,46.69c.07.16.2.14.32.1a.24.24,0,0,0,.17-.37,1,1,0,0,0-.27-.21A.36.36,0,0,0,196.77,46.69Z"/><path class="cls-1" d="M191.26,95.11a.23.23,0,0,0-.28.25c0,.1.09.36.31.35s.21-.28.2-.37A.33.33,0,0,0,191.26,95.11Z"/><path class="cls-1" d="M190.94,88.59a.38.38,0,0,0-.4.35.39.39,0,0,0,.36.39.42.42,0,0,0,.42-.39A.37.37,0,0,0,190.94,88.59Z"/><path class="cls-1" d="M190.29,91.32c-.16,0-.28.34-.16.6s.49.14.59,0S190.45,91.33,190.29,91.32Z"/><path class="cls-1" d="M192.89,91.46a.68.68,0,0,0-.7.72.54.54,0,0,0,.54.57c.38,0,.82-.41.79-.72A.62.62,0,0,0,192.89,91.46Z"/><path class="cls-1" d="M189.72,93.28c-.15.08-.15.59.06.66a.38.38,0,0,0,.43-.22C190.27,93.5,189.86,93.2,189.72,93.28Z"/><path class="cls-1" d="M193,89.46a.35.35,0,0,0-.28-.4.45.45,0,0,0-.33.1c-.17.15-.12.31.12.64C192.72,89.75,193,89.77,193,89.46Z"/><path class="cls-1" d="M192.69,87.36c-.22,0-.58.3-.59.48a.49.49,0,0,0,.54.5c.24,0,.41-.23.41-.56A.36.36,0,0,0,192.69,87.36Z"/><path class="cls-1" d="M192.35,84a.41.41,0,0,0-.42.39.38.38,0,0,0,.42.32.36.36,0,0,0,.35-.33A.34.34,0,0,0,192.35,84Z"/><path class="cls-1" d="M188.35,90.44c-.15.25-.29.52-.45.77a1.73,1.73,0,0,0,.21.69.46.46,0,0,0,.14.14.47.47,0,0,0,.76-.38,2.91,2.91,0,0,0-.07-.78A.68.68,0,0,0,188.35,90.44Z"/><path class="cls-1" d="M187.21,93a.63.63,0,0,0-.33.29c0,.14.18.34.35.3s.2-.15.25-.19C187.48,93.16,187.35,93,187.21,93Z"/><path class="cls-1" d="M188.79,92.55a.28.28,0,0,0,0,.25c.07.06.2,0,.25-.06s.06-.24,0-.3S188.82,92.5,188.79,92.55Z"/><path class="cls-1" d="M146.46,9.19a.5.5,0,0,0,0-.78.49.49,0,0,0-.76-.12.58.58,0,0,0-.09.83A.62.62,0,0,0,146.46,9.19Z"/><path class="cls-1" d="M162,16.48c.18,0,.21-.13.23-.26s-.11-.34-.28-.3a1.06,1.06,0,0,0-.3.16A.37.37,0,0,0,162,16.48Z"/><path class="cls-1" d="M165.52,23.85a2.15,2.15,0,0,0,1.23-.06.56.56,0,0,0,.16-.13.47.47,0,0,0-.29-.8,2.56,2.56,0,0,0-.78,0,.74.74,0,0,0-.55.67C165.29,23.68,165.35,23.82,165.52,23.85Z"/><path class="cls-1" d="M159.45,16.37c.19-.08.4-.15.36-.44s0-.43,0-.64a.32.32,0,0,0-.23-.45.42.42,0,0,0-.48.31,1,1,0,0,1-.18.36.55.55,0,0,0-.17.62A.77.77,0,0,0,159.45,16.37Z"/><path class="cls-1" d="M157.85,12.8a.26.26,0,0,0,.21-.12c0-.06,0-.25-.13-.26s-.17.15-.2.22S157.77,12.8,157.85,12.8Z"/><path class="cls-1" d="M157.6,18.28c.33.25.68.49,1,.74a.59.59,0,0,0,.07-.2.82.82,0,0,0,0-.32.33.33,0,0,0,.27,0,.48.48,0,0,0,.09-.68.61.61,0,0,0-.64-.22.51.51,0,0,0-.3.51A.83.83,0,0,0,157.6,18.28Z"/><path class="cls-1" d="M162.49,13.29s.16-.06.16-.13-.1-.15-.17-.18-.21,0-.22.14S162.42,13.28,162.49,13.29Z"/><path class="cls-1" d="M164.11,21a.39.39,0,0,0,.43-.3.43.43,0,0,0-.34-.47.36.36,0,0,0-.39.34A.37.37,0,0,0,164.11,21Z"/><path class="cls-1" d="M163.36,16.16c.06,0,.26,0,.27-.13s-.15-.2-.22-.21a.22.22,0,0,0-.21.15C163.19,16.06,163.3,16.14,163.36,16.16Z"/><path class="cls-1" d="M165.2,19.16c0-.21,0-.46-.28-.53a.35.35,0,0,0-.43.23.48.48,0,0,0,.06.35C164.67,19.39,164.84,19.36,165.2,19.16Z"/><path class="cls-1" d="M161.72,20.62a.36.36,0,0,0,.37.37h0a.69.69,0,0,0,0,.36.71.71,0,0,1-.07.48c.56.5,1.1,1,1.63,1.51a.69.69,0,0,0-.47-.57H163a.58.58,0,0,0,.15-.28,1.22,1.22,0,0,0-.09-.8.52.52,0,0,1,0-.3.47.47,0,0,0-.45-.66.37.37,0,0,0,0-.14c0-.16-.2-.32-.42-.34A.44.44,0,0,0,161.72,20.62Z"/><path class="cls-1" d="M163.2,19.33a.49.49,0,0,0,.56-.48c0-.23-.17-.43-.5-.47a.36.36,0,0,0-.46.3A.78.78,0,0,0,163.2,19.33Z"/><path class="cls-1" d="M157.41,15.39c.13,0,.43-.08.44-.27s-.23-.26-.35-.3a.24.24,0,0,0-.33.18C157.12,15.17,157.25,15.42,157.41,15.39Z"/><path class="cls-1" d="M161.52,18.1c.17,0,.21-.12.22-.25s-.1-.34-.27-.31a1.14,1.14,0,0,0-.31.17A.36.36,0,0,0,161.52,18.1Z"/><path class="cls-1" d="M149.56,12.19c.12-.08,0-.41-.07-.53a.21.21,0,0,0-.36,0c-.12.12-.2.26-.08.38S149.44,12.27,149.56,12.19Z"/><path class="cls-1" d="M147.67,12.47l.66.3a.33.33,0,0,0,0-.37.37.37,0,0,0-.54-.13A.34.34,0,0,0,147.67,12.47Z"/><path class="cls-1" d="M156.5,17a.45.45,0,0,0,.39-.43.4.4,0,0,0-.38-.37.37.37,0,0,0-.37.37A.38.38,0,0,0,156.5,17Z"/><path class="cls-1" d="M150.21,10a.2.2,0,0,0,.07-.24.2.2,0,0,0-.24-.05c-.06,0-.18.2-.13.28S150.16,10.08,150.21,10Z"/><path class="cls-1" d="M146.31,5.59a.28.28,0,0,0,.37-.14c.13-.25,0-.43-.19-.61-.23.07-.48.08-.49.37A.37.37,0,0,0,146.31,5.59Z"/><path class="cls-1" d="M147.82,11.26s0-.11,0-.14-.09-.06-.13,0a.1.1,0,1,0,.12.17Z"/><path class="cls-1" d="M149.46,13.27s0,0,0,.06l.59.28a.33.33,0,0,0,.2-.29.41.41,0,0,0-.38-.42A.38.38,0,0,0,149.46,13.27Z"/><path class="cls-1" d="M155.45,15.07s-.06,0-.06.1a.36.36,0,0,0,.31.41.44.44,0,0,0,.44-.31.35.35,0,0,0-.33-.4s-.05,0-.07,0,0-.06,0-.08-.27-.24-.36-.18-.09.31,0,.36A.41.41,0,0,0,155.45,15.07Z"/><path class="cls-1" d="M154.32,14.37a.48.48,0,0,0,.54-.5c0-.31-.15-.57-.39-.57a.71.71,0,0,0-.68.6C153.76,14.12,154,14.37,154.32,14.37Z"/><path class="cls-1" d="M150.68,7.22s.14-.16.09-.25-.23,0-.29,0a.16.16,0,0,0,0,.2C150.47,7.25,150.61,7.24,150.68,7.22Z"/><path class="cls-1" d="M155.79,12.54c.18,0,.24-.19.23-.27s-.14-.48-.28-.5-.28.33-.26.38S155.61,12.5,155.79,12.54Z"/><path class="cls-1" d="M151.46,11.91a.27.27,0,0,0,.26-.14.4.4,0,0,0,0-.34.35.35,0,0,0-.51.06C151.12,11.6,151.22,11.88,151.46,11.91Z"/><path class="cls-1" d="M152.83,12.15c.08-.06.07-.36,0-.48s-.23-.18-.41-.08a.22.22,0,0,0-.06.37C152.48,12.05,152.75,12.21,152.83,12.15Z"/><path class="cls-1" d="M160.53,14.07a.88.88,0,0,0,.73-.94c-.06-.3-.46-.27-.62-.11s-.12.08-.34,0-.56,0-.54.39A.76.76,0,0,0,160.53,14.07Z"/><path class="cls-1" d="M166.79,25.25a.26.26,0,0,0,.29.31.51.51,0,0,0,.32-.19c0-.09-.08-.42-.25-.44A.35.35,0,0,0,166.79,25.25Z"/><path class="cls-1" d="M160.08,18.7a.36.36,0,0,0-.29-.39.34.34,0,0,0-.42.3.4.4,0,0,0,.34.46A.37.37,0,0,0,160.08,18.7Z"/><path class="cls-1" d="M160.64,20.22c0-.3-.15-.59-.37-.59a.66.66,0,0,0-.51.32l.76.61A.62.62,0,0,0,160.64,20.22Z"/><path class="cls-1" d="M165,21.73a.53.53,0,0,0-.12-.43.41.41,0,0,0-.39,0,.44.44,0,0,0-.1.66A.39.39,0,0,0,165,21.73Z"/><path class="cls-1" d="M169.15,28.18a.67.67,0,0,0-.51-.64c-.21,0-.49.27-.53.55,0,0,0,.05,0,.07l.37.46C168.8,28.65,169.14,28.42,169.15,28.18Z"/><path class="cls-1" d="M167.13,21.2c-.09-.12-.66.19-.69.35s.3.32.58.23S167.21,21.31,167.13,21.2Z"/><path class="cls-1" d="M130.1,6.93s-.1,0-.1,0-.08.1-.05.14.08,0,.14,0S130.12,7,130.1,6.93Z"/><path class="cls-1" d="M121.93,6a.49.49,0,0,0-.38-.21.38.38,0,0,0-.31.24.43.43,0,0,0,.4.53A.38.38,0,0,0,121.93,6Z"/><path class="cls-1" d="M167.4,22.91c-.07.07,0,.24.08.27a.23.23,0,0,0,.25,0c.07-.06,0-.2,0-.26S167.47,22.83,167.4,22.91Z"/><path class="cls-1" d="M152.73,13.74a.13.13,0,0,0-.11-.11s-.09.07-.09.1a.12.12,0,0,0,.07.11C152.65,13.84,152.73,13.81,152.73,13.74Z"/><path class="cls-1" d="M112.56,8.76a.44.44,0,0,0,0-.57.4.4,0,0,0-.54,0,.37.37,0,0,0,0,.52.4.4,0,0,0,.39.13h0A.32.32,0,0,0,112.56,8.76Z"/><path class="cls-1" d="M142.94,10c.05,0,0-.24,0-.33a.18.18,0,0,0-.32,0c-.08.09-.25.34-.17.45S142.85,10.09,142.94,10Z"/><path class="cls-1" d="M166.33,15.92a.79.79,0,0,0,.76-.62.71.71,0,0,0-.57-.79,1,1,0,0,0-.86.7A.71.71,0,0,0,166.33,15.92Z"/><path class="cls-1" d="M146.46,11.23a.44.44,0,0,0,.15-.56.54.54,0,0,0-.7-.15.37.37,0,0,0-.19.49A.61.61,0,0,0,146.46,11.23Z"/><path class="cls-1" d="M170.17,25.58c.08,0,.24,0,.25-.16a.23.23,0,0,0-.19-.24c-.07,0-.29,0-.33.15S170.09,25.57,170.17,25.58Z"/><path class="cls-1" d="M169,22.37a.36.36,0,0,0-.17-.45c-.21-.08-.56.28-.5.44S168.88,22.57,169,22.37Z"/><path class="cls-1" d="M170,28.29s.09-.05.11-.08,0-.1-.06-.09-.12,0-.13.07S169.93,28.25,170,28.29Z"/><path class="cls-1" d="M169.08,17.49a.27.27,0,0,0,.09-.27c0-.06-.2-.19-.3-.15s-.1.3-.06.36S169,17.52,169.08,17.49Z"/><path class="cls-1" d="M167.77,24.82a.65.65,0,0,0,.25.35c.13.07.36-.13.34-.31a.77.77,0,0,0-.16-.27C167.93,24.56,167.74,24.67,167.77,24.82Z"/><path class="cls-1" d="M167.52,19.77a.54.54,0,0,0,.63-.47c0-.38-.3-.86-.62-.87a.63.63,0,0,0-.64.56A.69.69,0,0,0,167.52,19.77Z"/><path class="cls-1" d="M172.75,25.6a.25.25,0,0,0,.16-.17c0-.07-.1-.18-.16-.18a.21.21,0,0,0-.24.18C172.51,25.58,172.68,25.6,172.75,25.6Z"/><path class="cls-1" d="M167.43,27.38c.24-.3.29-.71-.06-.92a.65.65,0,0,0-.76,0C166.89,26.75,167.16,27.07,167.43,27.38Z"/><path class="cls-1" d="M170.61,28.28a.44.44,0,0,0-.31.1c.07.16,0,.49.22.54s.34-.24.49-.34l0-.12A.34.34,0,0,0,170.61,28.28Z"/><path class="cls-1" d="M170.37,24.6c.14.1.55,0,.59-.19s0-.46-.36-.44S170.24,24.5,170.37,24.6Z"/><path class="cls-1" d="M170.53,21.35c.1,0,.37,0,.38-.26s-.24-.24-.33-.24a.35.35,0,0,0-.27.2A.23.23,0,0,0,170.53,21.35Z"/><path class="cls-1" d="M120.27,6.07a.37.37,0,1,0,.6-.43.43.43,0,0,0-.57-.09A.37.37,0,0,0,120.27,6.07Z"/><path class="cls-1" d="M144.73,6c.12,0,.08-.22.05-.24a.32.32,0,0,0-.23-.08s-.09.15-.07.18S144.61,6,144.73,6Z"/><path class="cls-1" d="M117.78,8.07c-.2-.21-.51-.28-.66-.13a.39.39,0,0,0-.12.23Z"/><path class="cls-1" d="M124.7,5.27a.24.24,0,0,0,.17-.19c0-.08-.13-.15-.21-.15s-.22.1-.21.2S124.64,5.28,124.7,5.27Z"/><path class="cls-1" d="M123.41,7.06a.29.29,0,0,0,.39.08,2.11,2.11,0,0,0,.82-.93.5.5,0,0,0,0-.2.47.47,0,0,0-.78-.35,3.18,3.18,0,0,0-.56.54A.75.75,0,0,0,123.41,7.06Z"/><path class="cls-1" d="M110.52,8.31a.37.37,0,0,0,.51.07.44.44,0,0,0,.08-.54.36.36,0,0,0-.51,0A.34.34,0,0,0,110.52,8.31Z"/><path class="cls-1" d="M115.84,7.37a.33.33,0,0,0-.08.5.39.39,0,0,0,.56.08.36.36,0,0,0,0-.52A.35.35,0,0,0,115.84,7.37Z"/><path class="cls-1" d="M114.48,8.43s0,.06-.06.08l.85-.12-.09-.08a.37.37,0,0,0,.17-.21c.07-.24-.17-.51-.43-.55a.62.62,0,0,0-.6.31A.5.5,0,0,0,114.48,8.43Z"/><path class="cls-1" d="M116.9,6c.12-.13.06-.23,0-.34a.25.25,0,0,0-.41,0,1.09,1.09,0,0,0-.09.33A.37.37,0,0,0,116.9,6Z"/><path class="cls-1" d="M118.84,7.59a.36.36,0,0,0,.52,0h0a.87.87,0,0,0,.23.29l0,0,1.52-.1a1.21,1.21,0,0,0-.59-.43.57.57,0,0,1-.24-.19.48.48,0,0,0-.79-.14.19.19,0,0,0-.07-.12.41.41,0,0,0-.53.06A.43.43,0,0,0,118.84,7.59Z"/><path class="cls-1" d="M138.82,6.36c.28-.19.41-.6.26-.8a.57.57,0,0,0-.72,0,.5.5,0,0,0-.07.69C138.44,6.44,138.63,6.49,138.82,6.36Z"/><path class="cls-1" d="M138.77,9.19a.38.38,0,0,0-.12.16l.69.18a.36.36,0,0,0-.57-.34Z"/><path class="cls-1" d="M136.58,8.87l.79.16a.75.75,0,0,0,.15-1,.75.75,0,0,0-.9-.12.66.66,0,0,0-.05,1Z"/><path class="cls-1" d="M140.17,8.11a.54.54,0,0,0-.16-.66.37.37,0,0,0-.49.11.36.36,0,0,0,0,.49A.57.57,0,0,0,140.17,8.11Z"/><path class="cls-1" d="M143.42,10.77l1.26.47a.72.72,0,0,0,0-.61.69.69,0,0,0-.9-.23A.81.81,0,0,0,143.42,10.77Z"/><path class="cls-1" d="M143.72,7.94c.11-.09,0-.41,0-.54s-.3-.15-.43,0-.18.26-.05.38S143.62,8,143.72,7.94Z"/><path class="cls-1" d="M142,6.71a.46.46,0,0,0,.15-.52.36.36,0,0,0-.48-.1.41.41,0,0,0-.15.52A.4.4,0,0,0,142,6.71Z"/><path class="cls-1" d="M140.05,9.19a.44.44,0,0,0-.07.5l.38.11.11,0a.36.36,0,0,0,.06-.55A.33.33,0,0,0,140.05,9.19Z"/><path class="cls-1" d="M128.05,6.81a.59.59,0,0,0-.25.68c.1.21.45.24.75.07a.43.43,0,0,0,.13-.52A.52.52,0,0,0,128.05,6.81Z"/><path class="cls-1" d="M128.52,7.79l1,.06a.42.42,0,0,0-.08-.28.65.65,0,0,0-.81-.09A.38.38,0,0,0,128.52,7.79Z"/><path class="cls-1" d="M126.5,6.27c.14,0,.15-.35,0-.45a.65.65,0,0,0-.31-.08c-.21.17-.27.38-.13.46A.57.57,0,0,0,126.5,6.27Z"/><path class="cls-1" d="M130.93,7.1c.17-.11.06-.41.09-.58l-.1-.07a.36.36,0,0,0-.39.14.48.48,0,0,0-.14.29C130.55,7,130.76,7.21,130.93,7.1Z"/><path class="cls-1" d="M131.68,8.06h0Z"/><path class="cls-1" d="M133.89,7.08l0,0,.22.45c-.48.29-.49.35-.16.7.29,0,.38-.21.36-.5.65-.21.7-.61.15-1.11,0,0,0-.09,0-.13s0-.31-.13-.43A.36.36,0,0,0,134,6c-.26.12-.36.24-.34.43a.26.26,0,0,0-.2.07c-.18.15-.26.49-.14.61S133.74,7.2,133.89,7.08Z"/><path class="cls-1" d="M131.37,6.47c.07-.22.19-.43,0-.6a.32.32,0,0,0-.45,0,.44.44,0,0,0-.11.34C130.85,6.52,131.08,6.54,131.37,6.47Z"/><path class="cls-1" d="M134.43,5.21c-.08-.13-.24-.23-.36-.1a.42.42,0,0,0-.11.37c.06.1.25.12.34.16C134.53,5.49,134.51,5.33,134.43,5.21Z"/><path class="cls-1" d="M125.7,7.21a.26.26,0,0,0,.42,0,.5.5,0,0,0,.09-.36c0-.09-.36-.23-.49-.12A.33.33,0,0,0,125.7,7.21Z"/><path class="cls-1" d="M57,62.89a2.35,2.35,0,0,0-2.9,1.23,15.92,15.92,0,0,0-1.48,6.31,2,2,0,0,0,2.14,2A2.12,2.12,0,0,0,56.9,70.6a13.41,13.41,0,0,1,1.25-5.34A1.8,1.8,0,0,0,57,62.89Z"/><path class="cls-1" d="M54,77.6a1.73,1.73,0,1,0,2.08,1.7A1.92,1.92,0,0,0,54,77.6Z"/><path class="cls-1" d="M103.41,124.76a21.07,21.07,0,0,1-5.8-2.62,2.72,2.72,0,0,0-3.11.16c-.8.72-.6,1.94.53,2.64a24.75,24.75,0,0,0,6.87,3.1c1.32.41,2.69-.11,3-1A1.86,1.86,0,0,0,103.41,124.76Z"/><path class="cls-1" d="M113.25,128.11a2.2,2.2,0,0,0-2.62,1.1,1.66,1.66,0,0,0,1.34,2.14,2.19,2.19,0,0,0,2.61-1.09A1.66,1.66,0,0,0,113.25,128.11Z"/><path class="cls-1" d="M180.6,102a2.68,2.68,0,0,0-3.06.47,18.73,18.73,0,0,1-4.84,3.72,1.71,1.71,0,0,0-.77,2.47,2.54,2.54,0,0,0,3.23.4,21.85,21.85,0,0,0,5.72-4.4A1.62,1.62,0,0,0,180.6,102Z"/><path class="cls-1" d="M187.37,95.46a2.39,2.39,0,0,0-2.9.38,1.52,1.52,0,0,0,.46,2.38,2.39,2.39,0,0,0,2.9-.38A1.51,1.51,0,0,0,187.37,95.46Z"/><path class="cls-1" d="M185,50.16c-.75-1.6-3.26-2.48-3.91.38a50,50,0,0,1-2.73,9.86c-2.77,6.48-9.49,4.55-8.77-4.63a2.44,2.44,0,0,0,0-.28c1.55-14.35-10.23-30.15-26.93-36.31s-26-5.82-27.92-5.72c-2.08.11-3.85,1.82-.71,3.17a79.21,79.21,0,0,1,10.69,5.05c6.39,3.89,4.34,8.18-3.6,6.57-16-4.49-37-1.08-47.43,8.93s-14.82,18.54-15.29,20,.91,3.56,3.46,1.54A69.85,69.85,0,0,1,71.07,52c6-3.49,11,1.23,6.41,6.49a30.72,30.72,0,0,0-2.33,2.07l-.2.15a.39.39,0,0,0-.19.23c-10.83,10.91-13.69,28.17-4.93,40.37s16.92,17.37,18.51,18.2c1.76.92,4.42.4,2.86-2.22a55,55,0,0,1-5-9.24C84,102.19,89.8,99.49,95,105c8.49,12.21,28.44,19.6,44.79,16.39s25.48-7.58,26.95-8.56c1.62-1.07,1.86-3.31-1.66-2.92a86.84,86.84,0,0,1-12.28,1c-7.56-.13-7.62-5.46.41-7.68.87-.16,1.73-.34,2.59-.55l.32,0a.82.82,0,0,0,.3-.11c15.47-4,29-16.4,30.2-28.73S185.71,51.6,185,50.16ZM159.52,71.77l0,0,9.62.73Z"/><path class="cls-1" d="M179.09,43.56a2.38,2.38,0,0,0,2.63,1.36,1.78,1.78,0,0,0,1.46-2.39,17.64,17.64,0,0,0-3.33-5.82,2.53,2.53,0,0,0-3.18-.6,1.68,1.68,0,0,0-.4,2.53A15,15,0,0,1,179.09,43.56Z"/><path class="cls-1" d="M173.62,31.57a1.52,1.52,0,0,0,.46-2.38,2.39,2.39,0,0,0-2.9-.38,1.51,1.51,0,0,0-.46,2.38A2.39,2.39,0,0,0,173.62,31.57Z"/><path class="cls-1" d="M98.71,16.39a23.75,23.75,0,0,1,6.58-.67,2.25,2.25,0,0,0,2.39-1.63c.13-1-.9-1.86-2.32-1.88a27.79,27.79,0,0,0-7.78.8c-1.36.31-2.1,1.38-1.68,2.29A2.48,2.48,0,0,0,98.71,16.39Z"/><path class="cls-1" d="M88.35,18.42a1.66,1.66,0,0,0,1.33-2.15,2.21,2.21,0,0,0-2.62-1.09,1.66,1.66,0,0,0-1.33,2.14A2.2,2.2,0,0,0,88.35,18.42Z"/><path class="cls-2" d="M127.18,44l8.18-1.47c8.8-1.58,17.32,3.49,18.21,10.85l.82,6.82a10.82,10.82,0,0,0,2,5l4.23,5.92c4.56,6.37,1.31,14.58-7,17.54L146,91.41a15.11,15.11,0,0,0-5.22,3.11l-5.56,5.12c-6,5.53-16.52,5.53-22.51,0l-5.56-5.12a15,15,0,0,0-5.22-3.11l-7.67-2.75c-8.26-3-11.51-11.17-7-17.54l4.23-5.92a10.94,10.94,0,0,0,2-5l.81-6.82c.89-7.36,9.41-12.43,18.22-10.85L120.73,44A17.93,17.93,0,0,0,127.18,44Z"/><path class="cls-2" d="M141.48,98.29c.09-.06.07-.31.08-.48a.14.14,0,0,0-.13-.18A.44.44,0,0,0,141,98,.33.33,0,0,0,141.48,98.29Z"/><path class="cls-2" d="M141.18,99.49a.73.73,0,0,0,.71-.56c0-.2-.18-.42-.42-.45a.5.5,0,0,0-.62.42C140.82,99.17,141,99.46,141.18,99.49Z"/><path class="cls-2" d="M136.31,98.63l-.31.28A.7.7,0,0,0,136.31,98.63Z"/><path class="cls-2" d="M123.66,103.77l-.47,0A.69.69,0,0,0,123.66,103.77Z"/><path class="cls-2" d="M134.27,100.56a.23.23,0,0,0,0-.13,2.31,2.31,0,0,1-.24.2l.05,0A.31.31,0,0,0,134.27,100.56Z"/><path class="cls-2" d="M134.18,102.39a.33.33,0,0,0,.3.38.39.39,0,0,0,.43-.31c0-.2-.09-.3-.28-.34A.38.38,0,0,0,134.18,102.39Z"/><path class="cls-2" d="M162.65,56.92a.42.42,0,0,0,.44-.31.44.44,0,0,0-.36-.45.34.34,0,0,0-.38.29A.37.37,0,0,0,162.65,56.92Z"/><path class="cls-2" d="M165.37,57.72a.74.74,0,0,0-.88.18c-.19.26-.12.42.38.82-.13.2-.29.4,0,.61a.38.38,0,0,0,.49,0,.27.27,0,0,0,.12-.2,4.45,4.45,0,0,0-.14-.55C165.56,58.24,165.6,57.87,165.37,57.72Z"/><path class="cls-2" d="M121.75,103.76l0,0a.72.72,0,0,0,1-.06c-.39,0-.78-.07-1.17-.12A.38.38,0,0,0,121.75,103.76Z"/><path class="cls-2" d="M162.81,59.49a.36.36,0,0,0,.47-.23.61.61,0,0,0-.39-.66.43.43,0,0,0-.49.3A.54.54,0,0,0,162.81,59.49Z"/><path class="cls-2" d="M163.29,61.54c.16.05.36-.26.3-.54s-.43-.26-.56-.19S163.14,61.49,163.29,61.54Z"/><path class="cls-2" d="M163.13,57.35a.33.33,0,0,0,.16.28.23.23,0,0,0,.33-.17c0-.11,0-.38-.21-.42S163.14,57.26,163.13,57.35Z"/><path class="cls-2" d="M145.87,98.22a.5.5,0,0,0,.87-.35,3.42,3.42,0,0,0-.17-.77.76.76,0,0,0-.79-.36c-.17.05-.48.26-.32.57C145.94,97.64,145.6,97.88,145.87,98.22Z"/><path class="cls-2" d="M162.91,54.33a.52.52,0,0,0-.5.37c0,.24.27.72.46.73s.51-.27.55-.59S163.28,54.37,162.91,54.33Z"/><path class="cls-2" d="M163.1,77.42c.09-.35-.08-.6-.46-.72a9.61,9.61,0,0,1,0,1.2A.72.72,0,0,0,163.1,77.42Z"/><path class="cls-2" d="M88,53.66c-.1-.13-.54-.06-.57.14a.33.33,0,0,0,.25.36C87.87,54.19,88.1,53.78,88,53.66Z"/><path class="cls-2" d="M119.26,103.71a.33.33,0,0,0,.19.26.48.48,0,0,0,.44-.1.5.5,0,0,0,.32.11c.21,0,.29-.16.37-.32l.1-.18c-.3,0-.6-.12-.89-.19,0,0,0,0,0,0a.38.38,0,0,0-.52.21.09.09,0,0,0,0,0,.25.25,0,0,0-.2-.17c-.18,0-.35.06-.34.23a1,1,0,0,0,.13.33A.37.37,0,0,0,119.26,103.71Z"/><path class="cls-2" d="M90.15,89.75a1.26,1.26,0,0,0,.29.27c.16.07.28,0,.32-.2s0-.3-.14-.36A.41.41,0,0,0,90.15,89.75Z"/><path class="cls-2" d="M88.41,90.68a.73.73,0,0,0,.94-.21.41.41,0,0,0,.17.2.32.32,0,0,0,.45-.15.37.37,0,0,0-.17-.46.31.31,0,0,0-.39.07.81.81,0,0,0-.43-.67.66.66,0,0,0-.83.29A.63.63,0,0,0,88.41,90.68Z"/><path class="cls-2" d="M89.79,86.23a.18.18,0,0,0-.13.2.27.27,0,0,0,.23.18c.09,0,.13-.15.12-.24h0l-.14-.11S89.82,86.22,89.79,86.23Z"/><path class="cls-2" d="M85.21,76.58a.5.5,0,0,0,0,.17s0,0,.06.08c0-.14,0-.28,0-.42a.54.54,0,0,0,0,.11Z"/><path class="cls-2" d="M88.31,87.48a.82.82,0,0,0,.71-.41c0-.26-.1-.47-.41-.56s-.46,0-.55.38S88.08,87.43,88.31,87.48Z"/><path class="cls-2" d="M84.83,78.72a1.18,1.18,0,0,0,.41.4l.15,0a12.36,12.36,0,0,1-.14-1.35h0l0,0c0-.15,0-.31,0-.46a.4.4,0,0,0-.15-.14A15.24,15.24,0,0,0,84.83,78.72Z"/><path class="cls-2" d="M92.14,88.24a.22.22,0,0,0,.14.24c0,.13,0,.24.12.37s.36.41.59.21.11-.38.05-.44-.08-.26,0-.21.17,0,.25-.14c-.29-.13-.58-.25-.86-.39a.48.48,0,0,0-.06.21A.2.2,0,0,0,92.14,88.24Z"/><path class="cls-2" d="M89,85.71a.29.29,0,0,0,.15,0c-.21-.2-.41-.41-.61-.61a.05.05,0,0,0,0,0A.65.65,0,0,0,89,85.71Z"/><path class="cls-2" d="M90.66,87.89a.14.14,0,0,0-.09-.21c-.1,0-.16,0-.2.09a.13.13,0,0,0,.08.2C90.56,88,90.62,88,90.66,87.89Z"/><path class="cls-2" d="M96.64,96.57a.52.52,0,0,0,.62-.26.6.6,0,0,0-.29-.67c-.21-.08-.49.14-.59.46A.42.42,0,0,0,96.64,96.57Z"/><path class="cls-2" d="M95.41,94c0-.07-.1-.16-.16-.19s-.21,0-.23.11.14.18.21.2S95.4,94.05,95.41,94Z"/><path class="cls-2" d="M97.54,100.14c0-.21.06-.47-.24-.55a.33.33,0,0,0-.44.2.43.43,0,0,0,0,.35C97,100.32,97.17,100.31,97.54,100.14Z"/><path class="cls-2" d="M93,89.65s.18,0,.19-.07a.3.3,0,0,0-.06-.23.21.21,0,0,0-.29.05C92.79,89.53,93,89.61,93,89.65Z"/><path class="cls-2" d="M94.56,90.05c.15-.11.13-.23-.1-.54a.32.32,0,0,0-.33.45A.33.33,0,0,0,94.56,90.05Z"/><path class="cls-2" d="M92.67,92.23a.48.48,0,0,0,.19-.34c0-.27-.28-.33-.54-.32-.14.17-.27.35-.12.56A.32.32,0,0,0,92.67,92.23Z"/><path class="cls-2" d="M93.37,93.49a.59.59,0,1,0,.27-1.13.46.46,0,0,0-.54.37C93,93,93.16,93.43,93.37,93.49Z"/><path class="cls-2" d="M165.29,65.2a.52.52,0,0,0-.33.58.36.36,0,0,0,.42.26.35.35,0,0,0,.36-.33C165.77,65.41,165.41,65.2,165.29,65.2Z"/><path class="cls-2" d="M159.23,67.72a.35.35,0,0,0,.25.46.41.41,0,0,0,.5-.28.37.37,0,0,0-.33-.41A.36.36,0,0,0,159.23,67.72Z"/><path class="cls-2" d="M161.9,57.84s.13,0,.13-.09-.06-.11-.1-.12-.09.07-.1.1A.12.12,0,0,0,161.9,57.84Z"/><path class="cls-2" d="M160.73,63.12c.19-.11.19-.28,0-.66-.21,0-.46-.08-.57.21a.36.36,0,0,0,.18.46A.47.47,0,0,0,160.73,63.12Z"/><path class="cls-2" d="M161.25,56.46c.16,0,.32,0,.31-.22s-.09-.45-.24-.48-.28.3-.32.43S161.06,56.46,161.25,56.46Z"/><path class="cls-2" d="M160.81,60.76h.11a.48.48,0,0,0,.46.45.6.6,0,0,0,.64-.54.56.56,0,0,0-.35-.54.53.53,0,0,0-.38-.59.85.85,0,0,0-.95.51A.62.62,0,0,0,160.81,60.76Z"/><path class="cls-2" d="M160,64.78a.75.75,0,0,0,.69-.31.47.47,0,0,0-.19-.52s.1,0,.1-.07,0-.23-.08-.27-.21.11-.21.14a.33.33,0,0,0,0,.13l-.06,0c-.23-.07-.45.12-.54.44A.36.36,0,0,0,160,64.78Z"/><path class="cls-2" d="M147,44.37a14.18,14.18,0,0,1,1.45.91c.09-.07.17-.13.22-.12s.3.6.75.64a.68.68,0,0,0,.45-.12,21.94,21.94,0,0,0-1.83-2h0a.36.36,0,0,0-.14,0,.22.22,0,0,0-.18,0A1.2,1.2,0,0,0,147,44.37Z"/><path class="cls-2" d="M150.33,46.32h.08l-.42-.55A.44.44,0,0,0,150.33,46.32Z"/><path class="cls-2" d="M145.84,43.72v0q.32.14.63.3s0,0,0,0a.42.42,0,0,0,0-.63,1.23,1.23,0,0,1,.41.1.43.43,0,0,0,.35-.09,2.82,2.82,0,0,1,.36-.24l-.67-.59-.07.06-.25-.14a.33.33,0,0,0-.21-.05.23.23,0,0,0,.19-.15l-.43-.35a.63.63,0,0,0-.12.23c0,.17.09.27.26.27h.06a.4.4,0,0,0-.2.09.54.54,0,0,0-.12.56.33.33,0,0,0,.19.26A.51.51,0,0,0,145.84,43.72Z"/><path class="cls-2" d="M145,41.65a.31.31,0,0,0,.44-.18s0,0,0,0l-.54-.38a.37.37,0,0,0-.1.16A.32.32,0,0,0,145,41.65Z"/><path class="cls-2" d="M152.08,49.11l0,0c-.17-.33-.35-.67-.55-1l-.22,0A13.06,13.06,0,0,1,152.08,49.11Z"/><path class="cls-2" d="M165.4,53.77a.67.67,0,0,0,.51.24l-.21-1.07h0A.68.68,0,0,0,165.4,53.77Z"/><path class="cls-2" d="M165.26,56.14a.47.47,0,0,0,.36.6.83.83,0,0,0,.57-.47c0-.21-.1-.35-.39-.4S165.3,55.91,165.26,56.14Z"/><path class="cls-2" d="M164,59.23c-.1.19.17.53.33.54a.76.76,0,0,0-.49.51.7.7,0,0,0,1.33.39.9.9,0,0,0-.56-.92.74.74,0,0,0-.21,0c.15-.09.25-.52.08-.63A.39.39,0,0,0,164,59.23Z"/><path class="cls-2" d="M165.48,52.39h.1c0-.17-.07-.34-.11-.51a.27.27,0,0,0-.16.25A.31.31,0,0,0,165.48,52.39Z"/><path class="cls-2" d="M165.22,50.83a.18.18,0,0,0,.05.23C165.25,51,165.24,50.91,165.22,50.83Z"/><path class="cls-2" d="M164,55.07c-.11,0-.3.24-.29.3s.15.32.25.3a.36.36,0,0,0,.25-.24C164.23,55.34,164.1,55.08,164,55.07Z"/><path class="cls-2" d="M161.7,63.59a.4.4,0,0,0,.76.2.44.44,0,0,0,0-.23l.08,0a.26.26,0,0,0,.26-.1.45.45,0,0,0,.61.13.39.39,0,0,0-.11-.63.57.57,0,0,0-.44.06.44.44,0,0,0-.06.12c0-.14-.1-.28-.19-.29s-.3.27-.36.39a.16.16,0,0,0,0,.07h0A.44.44,0,0,0,161.7,63.59Z"/><path class="cls-2" d="M87.33,59.76a.32.32,0,0,0-.3,0c-.15.12,0,.3.06.34s.46.2.57.11,0,0,0-.09l.07,0a.33.33,0,0,0-.07-.5.3.3,0,0,0-.29,0A.35.35,0,0,0,87.33,59.76Z"/><path class="cls-2" d="M166.5,62.36c.06-.36-.05-.56-.32-.61a.66.66,0,0,0-.58.23,1.21,1.21,0,0,0,0-.46.5.5,0,0,0-.1-.17.47.47,0,0,0-.84.18,3.15,3.15,0,0,0-.09.42.46.46,0,0,0-.14.2.19.19,0,0,0,.14.25.77.77,0,0,0,.57.53c.17,0,.32,0,.37-.18l.09-.33a.67.67,0,0,0,.43.41C166.19,62.87,166.46,62.63,166.5,62.36Z"/><path class="cls-2" d="M166.46,58c0-.21,0-.42-.06-.63-.09,0-.16.12-.17.31A.32.32,0,0,0,166.46,58Z"/><path class="cls-2" d="M165.47,55.32a.38.38,0,0,0,.05-.49.3.3,0,0,0-.37-.15c-.27.09-.29.31-.29.58C165.09,55.36,165.27,55.53,165.47,55.32Z"/><path class="cls-2" d="M162.14,51.75s.07.17.1.17a.32.32,0,0,0,.22-.12.22.22,0,0,0-.11-.27C162.2,51.49,162.16,51.68,162.14,51.75Z"/><path class="cls-2" d="M163.12,46s.13,0,.13-.09-.06-.11-.1-.12a.17.17,0,0,0-.1.1S163.07,46,163.12,46Z"/><path class="cls-2" d="M162.7,50.1a.38.38,0,0,0,.41-.25c0-.14-.15-.29-.36-.32s-.26,0-.27.2S162.5,50.06,162.7,50.1Z"/><path class="cls-2" d="M162.16,46.54a.32.32,0,0,0,.22-.12c0-.05,0-.25-.11-.26s-.19.14-.21.21S162.13,46.54,162.16,46.54Z"/><path class="cls-2" d="M163.64,47.63c.06,0,.28,0,.28-.11a.25.25,0,0,0-.18-.19c-.08,0-.24,0-.24.08A.24.24,0,0,0,163.64,47.63Z"/><path class="cls-2" d="M163.82,46.84a.39.39,0,0,0,.2-.05c-.07-.23-.15-.45-.22-.67a.38.38,0,0,0-.2.3C163.57,46.66,163.66,46.83,163.82,46.84Z"/><path class="cls-2" d="M163.88,52.72c.36.08.68-.19.77-.64a.61.61,0,0,0-.51-.75.78.78,0,0,0-.84.58A.82.82,0,0,0,163.88,52.72Z"/><path class="cls-2" d="M164.57,50.42a.3.3,0,0,0,.4-.24.32.32,0,0,0-.23-.37c-.15-.05-.47.2-.43.34A.41.41,0,0,0,164.57,50.42Z"/><path class="cls-2" d="M95.18,95.23c.08-.26-.06-.45-.4-.55s-.58.07-.63.25a.65.65,0,0,0,.38.71A.6.6,0,0,0,95.18,95.23Z"/><path class="cls-2" d="M96.56,92.49a.5.5,0,0,0-.18-.57,4.23,4.23,0,0,0-.42-.25,2,2,0,0,0,0-.24.75.75,0,0,0-.58-.59.86.86,0,0,0-.75.37.45.45,0,0,0-.07.32.69.69,0,0,0,.78.56l.25,0a3.14,3.14,0,0,0,0,.45c.05.27.16.36.43.35A.55.55,0,0,0,96.56,92.49Z"/><path class="cls-2" d="M95.76,96.81c0,.09.09.19.15.2s.26,0,.28-.1-.14-.22-.21-.23S95.78,96.72,95.76,96.81Z"/><path class="cls-2" d="M126.88,104.32a.75.75,0,0,0,.66-.29.35.35,0,0,0,.41.15c.17-.08.17-.21,0-.55a.27.27,0,0,0-.25,0,.63.63,0,0,0,0-.27c-.43.09-.87.17-1.31.22A.84.84,0,0,0,126.88,104.32Z"/><path class="cls-2" d="M133.18,101.54a.32.32,0,0,0,.42-.22.41.41,0,0,0-.09-.36l-.57.38A.33.33,0,0,0,133.18,101.54Z"/><path class="cls-2" d="M136.24,100.6a.37.37,0,0,0-.35-.39.4.4,0,0,0-.19,0,.85.85,0,0,0-.59-.52c-.26.24-.53.46-.81.68a.8.8,0,0,0,.52.68.78.78,0,0,0,.7-.26.25.25,0,0,0,.2.16A.62.62,0,0,0,136.24,100.6Z"/><path class="cls-2" d="M129.66,102.86l-.37.11A.5.5,0,0,0,129.66,102.86Z"/><path class="cls-2" d="M135,104c0-.13-.16-.29-.37-.32a.21.21,0,0,0-.27.2.81.81,0,0,0,0,.16.31.31,0,0,0-.28.23.31.31,0,0,0,.23.41.36.36,0,0,0,.47-.23.28.28,0,0,0,0-.22A.4.4,0,0,0,135,104Z"/><path class="cls-2" d="M129.65,103.56c-.07.21,0,.34.19.35s.33,0,.37-.14-.1-.31-.2-.4S129.69,103.4,129.65,103.56Z"/><path class="cls-2" d="M143.65,43a16.35,16.35,0,0,1,1.58.55c.72-.17.71-.74.51-1.22a.77.77,0,0,0-.33-.38.8.8,0,0,0-.68,0c-.25.08-.5,0-.7.13A1.18,1.18,0,0,0,143.65,43Z"/><path class="cls-2" d="M85.76,61.52c.07-.06,0-.23-.05-.29a.15.15,0,0,0-.2,0,.23.23,0,0,0,0,.24S85.69,61.59,85.76,61.52Z"/><path class="cls-2" d="M86.53,82.51a.34.34,0,0,0,.19.44.65.65,0,0,0,.22.08c-.14-.22-.29-.44-.41-.66A.65.65,0,0,1,86.53,82.51Z"/><path class="cls-2" d="M86.55,57.49a.49.49,0,0,0,0,.74c.23.22.54.25.69.06a.68.68,0,0,0,.1-.69.14.14,0,0,0,.1-.05s0-.1,0-.13a.12.12,0,0,0-.15,0h0a.56.56,0,0,1,0-.08A.53.53,0,0,0,86.55,57.49Z"/><path class="cls-2" d="M87.69,87.12a.42.42,0,0,0,.1-.79.57.57,0,0,0-.6.33A.54.54,0,0,0,87.69,87.12Z"/><path class="cls-2" d="M87,85.76c.15,0,.38-.24.4-.43a.26.26,0,0,0,0-.12.37.37,0,0,0,.38-.22.42.42,0,0,0-.26-.51.41.41,0,0,0-.47.24.41.41,0,0,0,.08.33c-.3,0-.5.1-.51.25a.31.31,0,0,0,0,.08.54.54,0,0,0-.26-.2c-.09.06-.27.12-.35.25s0,.43.25.5a.34.34,0,0,0,.42-.24.34.34,0,0,0,0-.22A.63.63,0,0,0,87,85.76Z"/><path class="cls-2" d="M91.73,92c.12-.28-.19-.54-.18-.87,0-.78-.57-.89-1.09-.81a.84.84,0,0,0-.44.25.8.8,0,0,0-.18.65c0,.27-.15.48,0,.72a1.12,1.12,0,0,0,1.21.58A.92.92,0,0,0,91.73,92Z"/><path class="cls-2" d="M90.53,93.13a.17.17,0,0,0,.11.16.24.24,0,0,0,.21-.11c0-.05,0-.25-.1-.27S90.56,93.05,90.53,93.13Z"/><path class="cls-2" d="M85.81,84.28c-.26,0-.3.24-.28.46a.42.42,0,0,0,.65,0C86.14,84.47,86.08,84.24,85.81,84.28Z"/><path class="cls-2" d="M94.48,96.67a1.12,1.12,0,0,0-.31.15.36.36,0,0,0,.32.42c.18,0,.22-.11.24-.24A.25.25,0,0,0,94.48,96.67Z"/><path class="cls-2" d="M166.33,58.4a.57.57,0,0,0,.21.51c0-.24,0-.47-.06-.71A.29.29,0,0,0,166.33,58.4Z"/><path class="cls-2" d="M163.78,71.73a.32.32,0,0,0,.22-.12s0-.22-.11-.26-.18.14-.21.21S163.75,71.73,163.78,71.73Z"/><path class="cls-2" d="M163.36,70.83a.28.28,0,0,0-.27.17,1.11,1.11,0,0,0-.79-.28.77.77,0,0,0-.48.16.78.78,0,0,0-.29.62c0,.26-.24.44-.15.69a1.53,1.53,0,0,0,.14.29l0,.08v0a3.13,3.13,0,0,1,.15.32.86.86,0,0,0,.36,0,1,1,0,0,0,.47.09.86.86,0,0,0,.77-.38c.18-.26-.08-.57,0-.89a1.22,1.22,0,0,0,0-.17.33.33,0,0,0,.36,0c.15-.1.23-.26.12-.41S163.49,70.83,163.36,70.83Z"/><path class="cls-2" d="M89.37,58.87a.41.41,0,0,0,.17.54.37.37,0,0,0,.53-.16.39.39,0,0,0-.15-.5A.38.38,0,0,0,89.37,58.87Z"/><path class="cls-2" d="M89.07,55.11a.5.5,0,0,0-.1-.17.47.47,0,0,0-.84.19,3.41,3.41,0,0,0-.11.78.74.74,0,0,0,.61.61c.17,0,.54-.06.5-.41C88.8,55.63,89.2,55.53,89.07,55.11Z"/><path class="cls-2" d="M164.57,65.65c.23,0,.43-.14.51-.47a.55.55,0,0,0-.28-.61.7.7,0,0,0-.72.39.41.41,0,0,0,0,.11.66.66,0,0,0-.26-.18,1.19,1.19,0,0,0-.81,0,.51.51,0,0,1-.3,0,.48.48,0,0,0-.71.37.19.19,0,0,0-.13-.06c-.17,0-.35.15-.39.37a.43.43,0,0,0,.29.54.35.35,0,0,0,.42-.32h0a.66.66,0,0,0,.36.09A.72.72,0,0,1,163,66a.77.77,0,0,0,.75,0,.66.66,0,0,0,.41-.61v-.07A.57.57,0,0,0,164.57,65.65Z"/><path class="cls-2" d="M148.33,99.6c-.15.18-.2.36,0,.52s.25.07.29,0a.47.47,0,0,0,.16-.35C148.72,99.57,148.51,99.59,148.33,99.6Z"/><path class="cls-2" d="M135.81,101.65c0-.09-.12-.19-.17-.19s-.24,0-.25.09a.25.25,0,0,0,.14.21C135.59,101.78,135.81,101.74,135.81,101.65Z"/><path class="cls-2" d="M164.81,68.32a.51.51,0,0,0,.52-.4h0a.68.68,0,0,0,.62-.37c0-.2-.23-.54-.48-.61a.32.32,0,0,0-.41.24,1.49,1.49,0,0,0,0,.21h0a.48.48,0,0,0-.12,0,.41.41,0,0,0,0-.08.38.38,0,0,0-.29-.44.4.4,0,0,0-.47.31.43.43,0,0,0,.19.43.37.37,0,0,0-.08.18C164.29,68,164.54,68.29,164.81,68.32Z"/><path class="cls-2" d="M166.64,60.62c0-.26,0-.5,0-.76l0,0a1.43,1.43,0,0,0,0,.29h0C166.48,60.4,166.53,60.57,166.64,60.62Z"/><path class="cls-2" d="M139,100.66c.22,0,.48-.18.55-.5a.27.27,0,0,0-.23-.36.52.52,0,0,0-.62.25A.6.6,0,0,0,139,100.66Z"/><path class="cls-2" d="M147.52,98.6c-.19.05-.26.52-.12.59s.53-.13.49-.33A.36.36,0,0,0,147.52,98.6Z"/><path class="cls-2" d="M140.18,99.37a.43.43,0,0,0-.36-.45.35.35,0,0,0-.38.29.38.38,0,0,0,.31.47A.4.4,0,0,0,140.18,99.37Z"/><path class="cls-2" d="M135.19,103.86a.41.41,0,0,0,.64,0c0-.23-.09-.46-.37-.42S135.16,103.64,135.19,103.86Z"/><path class="cls-2" d="M136.34,100.77a.48.48,0,0,0,.39.62.59.59,0,0,0,.77-.48.63.63,0,0,0-.39-.75C136.82,100.07,136.43,100.38,136.34,100.77Z"/><path class="cls-2" d="M137.85,101.27c0,.19.29.48.58.52a.43.43,0,0,0,.44-.37.51.51,0,0,0-.36-.56A.66.66,0,0,0,137.85,101.27Z"/><path class="cls-2" d="M90.13,87.83a.52.52,0,0,0-.33-.63.35.35,0,0,0-.52.18.74.74,0,0,0,0,.65.41.41,0,0,0-.22,0,.61.61,0,0,0-.36.67A.52.52,0,0,0,89,89a.71.71,0,0,0,.94-.34,2.6,2.6,0,0,0-.19-.42.27.27,0,0,0-.08-.07A.67.67,0,0,0,90.13,87.83Z"/><path class="cls-2" d="M92.19,57.19a.29.29,0,0,0,.07.46.36.36,0,0,0,.52-.08.36.36,0,0,0-.08-.48A.37.37,0,0,0,92.19,57.19Z"/><path class="cls-2" d="M135.56,41.64c-.28,0-.49.15-.48.36a1,1,0,0,0,.31.5c.21,0,.41,0,.62-.08a.62.62,0,0,0,.11-.37C136.12,41.79,135.92,41.64,135.56,41.64Z"/><path class="cls-2" d="M135.06,41.06a.35.35,0,0,0-.46.24.33.33,0,0,0,.19.44c.23.07.56,0,.61-.19S135.24,41.13,135.06,41.06Z"/><path class="cls-2" d="M133.85,41.36a.43.43,0,0,0-.35.41.38.38,0,0,0,.37.37.42.42,0,0,0,.37-.43A.41.41,0,0,0,133.85,41.36Z"/><path class="cls-2" d="M134.63,39.28a.4.4,0,0,0-.45.22.45.45,0,0,0,.27.58.47.47,0,0,0,.48-.48A.3.3,0,0,0,134.63,39.28Z"/><path class="cls-2" d="M140.21,41.75c-.35-.13-.69,0-.77.19v0a.36.36,0,0,0-.36.26c.44,0,.88,0,1.31.08l0-.06A.47.47,0,0,0,140.21,41.75Z"/><path class="cls-2" d="M90.59,56c.09.07.33-.07.41-.14a.26.26,0,0,0-.1-.4.36.36,0,0,0-.31.13C90.54,55.62,90.49,55.88,90.59,56Z"/><path class="cls-2" d="M88.05,56.67a.13.13,0,0,0,.16,0,.17.17,0,0,0,0-.14s-.1,0-.13,0A.1.1,0,0,0,88.05,56.67Z"/><path class="cls-2" d="M90,54.47A.38.38,0,0,0,90,55a.4.4,0,0,0,.56,0,.37.37,0,0,0,0-.56A.39.39,0,0,0,90,54.47Z"/><path class="cls-2" d="M90.26,56.86a.31.31,0,0,0-.05.29c.05.09.18.21.27.2.25,0,.32-.3.28-.43A.34.34,0,0,0,90.26,56.86Z"/><path class="cls-2" d="M94.1,51.42a.34.34,0,0,0,0,.34c0,.07.3.18.38.12s0-.33,0-.37S94.17,51.38,94.1,51.42Z"/><path class="cls-2" d="M93,55.87a.46.46,0,0,0,.24.57.5.5,0,0,0,.71-.16l.06-.47a.36.36,0,0,0-.14-.21A.75.75,0,0,0,93,55.87Z"/><path class="cls-2" d="M92.05,54.78c-.23-.14-.47,0-.64.26a.4.4,0,0,0,.08.57.79.79,0,0,0,.8-.14A.48.48,0,0,0,92.05,54.78Z"/><path class="cls-2" d="M92.33,52a.56.56,0,0,0,.22.73.57.57,0,0,0,.64-.95A.83.83,0,0,0,92.33,52Z"/><path class="cls-2" d="M91.64,51.29a.44.44,0,0,0-.48,0,.38.38,0,0,0-.47.13.57.57,0,0,0,.19.69.33.33,0,0,0,.46-.12s0-.05,0-.07a.32.32,0,0,0,.28-.1A.41.41,0,0,0,91.64,51.29Z"/><path class="cls-2" d="M130.52,36.24a.51.51,0,0,0-.65-.25.76.76,0,0,0-.25.91.53.53,0,0,0,.63.13.68.68,0,1,0,1,.74.9.9,0,0,0-.69-.83.53.53,0,0,0-.24.06A.53.53,0,0,0,130.52,36.24Z"/><path class="cls-2" d="M130.17,39.2c-.09,0,0,.22,0,.29a.19.19,0,0,0,.2.07c.06,0,.06-.17,0-.25S130.27,39.15,130.17,39.2Z"/><path class="cls-2" d="M117.05,35.82c.06,0,.22-.14.17-.24s-.21-.07-.25,0-.16.1-.11.21A.13.13,0,0,0,117.05,35.82Z"/><path class="cls-2" d="M114.5,35.47a.33.33,0,0,0,0-.57.34.34,0,0,0-.38.22A.32.32,0,0,0,114.5,35.47Z"/><path class="cls-2" d="M94.56,52a.39.39,0,0,0,0,.24c0-.1,0-.21.07-.31Z"/><path class="cls-2" d="M123.92,37.86c.24-.14.29-.37.12-.67s-.37-.28-.67-.11a.4.4,0,0,0-.21.48A.81.81,0,0,0,123.92,37.86Z"/><path class="cls-2" d="M131.06,40.32c.08,0,.08-.24,0-.3a.2.2,0,0,0-.23-.09c-.08,0-.09.17-.07.24S131,40.36,131.06,40.32Z"/><path class="cls-2" d="M159.84,70a.57.57,0,0,0,.15-.12.52.52,0,0,0,.26.36.72.72,0,0,0,1-.08.83.83,0,0,0,0-.74.34.34,0,0,0,.37-.25.4.4,0,0,0-.3-.44.37.37,0,0,0-.45.26s0,0,0,.07a.68.68,0,0,0-.69.22l0,0a.62.62,0,0,0-.54-.46.89.89,0,0,0-.32,0,.29.29,0,0,0,0-.26.47.47,0,0,0-.61-.2Z"/><path class="cls-2" d="M159.25,66c0-.17-.1-.22-.23-.25s-.35.06-.33.23a1.19,1.19,0,0,0,.12.32A.37.37,0,0,0,159.25,66Z"/><path class="cls-2" d="M161.09,66.1a.54.54,0,0,0-.64.08.43.43,0,0,0-.1.58c.08.13.16.4.33.5a.28.28,0,0,0-.08.15.75.75,0,0,0,.54.72.43.43,0,0,0,.52-.38.47.47,0,0,0-.42-.63h-.17a.41.41,0,0,1,.07-.15A.6.6,0,0,0,161.09,66.1Z"/><path class="cls-2" d="M132.12,36.76a.4.4,0,1,0,.26.75c.16-.09.21-.4.06-.7A.54.54,0,0,0,132.12,36.76Z"/><path class="cls-2" d="M132.86,39.23c0-.37-.14-.55-.41-.56a.65.65,0,0,0-.61.53.69.69,0,0,0,.58.58C132.63,39.78,132.86,39.5,132.86,39.23Z"/><path class="cls-2" d="M112.7,99.64l-.15-.14-.07,0c.14.22.31.48.52.76a1,1,0,0,1,.37-.12C113.14,100,112.91,99.84,112.7,99.64Z"/><path class="cls-2" d="M113.53,101l.33.37A.61.61,0,0,0,113.53,101Z"/><path class="cls-2" d="M116.25,102.05h0c-.44-.22-.87-.45-1.29-.71-.19.12-.48.28-.53.6h0c.28.27.59.54.92.82A1.34,1.34,0,0,0,116.25,102.05Z"/><path class="cls-2" d="M118.71,103.16a.37.37,0,0,0,0-.18l-.62-.19-.37.07a.33.33,0,0,0-.18-.11.36.36,0,0,0-.18,0c-.14-.11-.21-.06-.46.15,0,.16.23.35,0,.47a2.91,2.91,0,0,0-.37-.33.49.49,0,0,0-.53,0l-.07.06c.27.21.56.41.87.61l0,0v.06a16.78,16.78,0,0,0,1.75,1l.15-.1c.27-.26.25-.5-.05-.88A2.55,2.55,0,0,0,118.71,103.16Z"/><path class="cls-2" d="M133.27,103.3c-.29-.06-.59.08-.63.3a.8.8,0,0,0,.58.79c.22,0,.46-.18.51-.48S133.65,103.37,133.27,103.3Z"/><path class="cls-2" d="M121.65,105a.51.51,0,0,0-.38-.24.66.66,0,0,0-.28,0c0-.06,0-.12-.08-.15-.2-.13-.41-.05-.54.23,0,0,0,.07,0,.1s-.19.18-.31,0a1.53,1.53,0,0,0-.11-.16.64.64,0,0,0-.72-.13.55.55,0,0,0-.29.21c.54.24,1.11.46,1.72.67a1.09,1.09,0,0,0,.08-.15.8.8,0,0,0,.31.27c.34.11.7.21,1.07.3a1.1,1.1,0,0,0,.1-.45C122.21,105.34,121.91,105.08,121.65,105Z"/><path class="cls-2" d="M133.43,105.92a.38.38,0,0,0-.09-.44.51.51,0,0,0-.58,0,.36.36,0,0,0-.24.21.5.5,0,0,0-.37-.32.31.31,0,0,0-.37.26.57.57,0,0,0,.12.43.4.4,0,0,0,.61-.12,1.2,1.2,0,0,1,0,.42.32.32,0,0,0,0,.16l1-.13-.18-.16Z"/><path class="cls-2" d="M136,105.47a.38.38,0,0,0-.21,0,.49.49,0,0,0-.25-.16.75.75,0,0,0-.75.25.85.85,0,0,0-.51.15.16.16,0,0,0-.08,0c-.14,0-.18.14-.21.21s0,.1.07.13,0,0,0,0a.62.62,0,0,0,0,.19c.78-.13,1.58-.28,2.41-.45A.57.57,0,0,0,136,105.47Z"/><path class="cls-2" d="M132,103.27c.17.07.44,0,.59-.35l0,0a.38.38,0,0,0,.41-.26.37.37,0,0,0-.31-.43.24.24,0,0,0-.17,0,.4.4,0,0,0-.43-.2c-.3.09-.32.29-.43.24s-.12-.1-.21-.15a3.88,3.88,0,0,1-.36.15,1.23,1.23,0,0,0,0,.62c0,.09.27.28.12.43a3.39,3.39,0,0,1-.34.23.37.37,0,0,0-.27.16.49.49,0,0,0,.08.33.65.65,0,0,0,.15.65c.28.21.41-.07.61-.12a.48.48,0,0,0,.39-.5c0-.27-.19-.52-.15-.65S131.76,103.19,132,103.27Z"/><path class="cls-2" d="M125.83,105.43a.45.45,0,0,0,.17-.2c0-.19-.23-.53-.48-.6a.31.31,0,0,0-.41.23.63.63,0,0,0,.17.69.3.3,0,0,0,0,.29.32.32,0,0,0,.45.18.46.46,0,0,0,.24-.31A.38.38,0,0,0,125.83,105.43Z"/><path class="cls-2" d="M130.06,105.34a.35.35,0,0,0,.41.16.32.32,0,0,0,0-.56C130.17,104.84,130,105.18,130.06,105.34Z"/><path class="cls-2" d="M124.82,104.6a.71.71,0,0,0-.69-.82h-.41a.77.77,0,0,0-.33.15.8.8,0,0,0-.28.61c0,.27-.23.45-.14.7a1.24,1.24,0,0,0,.34.51l-.07,0c-.21.09-.38.2-.62,0s-.34,0-.43.21c0,0,0,0,0,0,.62.15,1.27.28,2,.38a1,1,0,0,0-.06-.44.9.9,0,0,0,.73-.39.5.5,0,0,0,0-.4A.36.36,0,0,0,125,105,.39.39,0,0,0,124.82,104.6Z"/><path class="cls-2" d="M129.85,105.92a.36.36,0,0,0-.46,0l-.24.14-.09-.06a1,1,0,0,0,0-.24.65.65,0,0,0,0-.2.71.71,0,0,0,.7-.52.71.71,0,0,0-1.37-.32.79.79,0,0,0,0,.34.85.85,0,0,0-.56.23.55.55,0,0,0-.43-.48c-.45-.1-.85.09-.91.43a.58.58,0,0,0,0,.29.36.36,0,0,0-.32,0,.38.38,0,0,0-.23.44c0,.23.22.27.42.31a.56.56,0,0,0-.31.28s0,0,0,.08l1,0a.74.74,0,0,0-.36-.4.35.35,0,0,0-.11,0,.87.87,0,0,0,.18-.38.63.63,0,0,0,.26.15.79.79,0,0,0,.71-.3.67.67,0,0,0,.66.56l.24,0a.86.86,0,0,1,0,.16.43.43,0,0,0-.05.21v0c.44,0,.9,0,1.37,0v0a1.36,1.36,0,0,1,0-.24C130,106.19,130,106,129.85,105.92Z"/><path class="cls-2" d="M96.7,102.18a.43.43,0,0,0-.14.65.38.38,0,0,0,.61-.14.54.54,0,0,0-.08-.44A.42.42,0,0,0,96.7,102.18Z"/><path class="cls-2" d="M93.16,100.26l-.5-.19.77.91s0,0,0,0C93.61,100.57,93.54,100.4,93.16,100.26Z"/><path class="cls-2" d="M94.18,98.59c0-.18-.08-.35-.25-.32a1.06,1.06,0,0,0-.32.14.36.36,0,0,0,.33.42C94.11,98.84,94.15,98.72,94.18,98.59Z"/><path class="cls-2" d="M93.23,94.74a.89.89,0,0,0,.79-.89c0-.3-.44-.3-.61-.15s-.13.07-.34,0a.45.45,0,0,0-.5.11.35.35,0,0,0-.14-.06.59.59,0,0,0-.5.11.35.35,0,0,0-.33-.19c-.25,0-.32.22-.41.44.16.17.31.39.57.25l.06,0a.91.91,0,0,0,.53.42.36.36,0,0,0,.37-.21A.89.89,0,0,0,93.23,94.74Z"/><path class="cls-2" d="M92.39,96.54c0-.21,0-.42,0-.64a.32.32,0,0,0-.2-.46.41.41,0,0,0-.5.28s0,.07,0,.1a.64.64,0,0,0-.44-.31.65.65,0,0,0-.89.53.73.73,0,0,0,.58.84.62.62,0,0,0,.44-.14A.82.82,0,0,0,92,97C92.19,96.89,92.41,96.84,92.39,96.54Z"/><path class="cls-2" d="M90.48,95.61c0-.19-.21-.28-.32-.33h0a.77.77,0,0,0,.34-.45.81.81,0,0,0-.51-.85.68.68,0,0,0-.83.42,1.1,1.1,0,0,0,.57,1,1,1,0,0,0,.17,0,.43.43,0,0,0,0,.07c-.06.18,0,.43.21.42S90.46,95.79,90.48,95.61Z"/><path class="cls-2" d="M142.74,40.49a.69.69,0,0,0-.21.81c.11.24.55.26.9.12a.67.67,0,0,0,.26-.83A.69.69,0,0,0,142.74,40.49Z"/><path class="cls-2" d="M91.17,98.12l.36.49a.5.5,0,0,0-.07-.27A.66.66,0,0,0,91.17,98.12Z"/><path class="cls-2" d="M93.83,101.31s-.07,0-.1,0l.27.31C94,101.52,93.91,101.33,93.83,101.31Z"/><path class="cls-2" d="M92.77,97.19a.64.64,0,1,0,1.24.29c.07-.31-.23-.65-.64-.73A.5.5,0,0,0,92.77,97.19Z"/><path class="cls-2" d="M89.12,92.37a.66.66,0,0,0-.75-.43.69.69,0,0,0-.3.9.67.67,0,0,0,.74.38C89.08,93.17,89.19,92.75,89.12,92.37Z"/><path class="cls-2" d="M99.09,106a.32.32,0,0,0-.35.2l.43.36a.26.26,0,0,0,.14-.11C99.37,106.39,99.26,106.05,99.09,106Z"/><path class="cls-2" d="M96.77,101.63a.43.43,0,0,0-.3-.49.36.36,0,0,0-.41.32.37.37,0,0,0,.26.45A.39.39,0,0,0,96.77,101.63Z"/><path class="cls-2" d="M95.66,99.22h-.13a.3.3,0,0,0,.27-.3.45.45,0,0,0-.15-.33c-.25-.15-.43,0-.6.24.1.19.16.4.38.41a.31.31,0,0,0-.25.27.74.74,0,0,0,.36.67.5.5,0,0,0,.59-.44C96.18,99.5,96,99.29,95.66,99.22Z"/><path class="cls-2" d="M95.24,102.21a.48.48,0,0,0-.4-.69.21.21,0,0,0,0-.13c0-.17-.18-.34-.4-.37a.43.43,0,0,0-.52.33.37.37,0,0,0,.34.4h0a1,1,0,0,0,0,.18c.35.38.71.76,1.07,1.13a1.46,1.46,0,0,0-.09-.54A.66.66,0,0,1,95.24,102.21Z"/><path class="cls-2" d="M98.93,104.74a.46.46,0,0,0-.23-.82,2.91,2.91,0,0,0-.78-.07.72.72,0,0,0-.58.63.29.29,0,0,0,.2.35,2.06,2.06,0,0,0,1.23,0A.55.55,0,0,0,98.93,104.74Z"/><path class="cls-2" d="M136.84,104.19a.35.35,0,0,0-.21-.25c-.15,0-.46.2-.42.35a.6.6,0,0,0,.06.11,0,0,0,0,0,0,0,.63.63,0,0,0,.44.47c.14,0,.38-.24.39-.43S137,104.22,136.84,104.19Z"/><path class="cls-2" d="M162,80.89l.15-.23-.06,0C162.09,80.71,162.07,80.8,162,80.89Z"/><path class="cls-2" d="M162.64,78.5h0c0,.26-.09.52-.14.79.32,0,.52,0,.57-.18A.63.63,0,0,0,162.64,78.5Z"/><path class="cls-2" d="M162.39,79.7h0a.52.52,0,0,0,0-.27A2.8,2.8,0,0,1,162.39,79.7Z"/><path class="cls-2" d="M163,78.09a.35.35,0,0,0,.22.44.33.33,0,0,0,.14,0c.09-.18.18-.37.26-.56a.27.27,0,0,0-.13-.1A.39.39,0,0,0,163,78.09Z"/><path class="cls-2" d="M156.47,95.31s-.21,0-.23.11.15.18.22.2a.19.19,0,0,0,.17-.12C156.64,95.43,156.54,95.34,156.47,95.31Z"/><path class="cls-2" d="M164,73.79a.31.31,0,0,0-.26-.26.44.44,0,0,0-.46.28.34.34,0,0,0,.12.31c-.15.24-.23.55-.33.57s-.43-.25-.78-.25a10.12,10.12,0,0,1,.37,1.66.45.45,0,0,0,.23,0A.31.31,0,0,0,163,76s-.06.06-.07.09a.61.61,0,0,0,.35.64h0a.74.74,0,0,0,.45.84c.18-.42.36-.86.53-1.31a1,1,0,0,0-.46,0,.45.45,0,0,0-.22-.49.61.61,0,0,0-.53.14c.11-.16.15-.37.21-.39s.54.34,1,.21a.61.61,0,0,0,.31-.24c.1-.28.19-.56.28-.85a0,0,0,0,0,0,0,1.07,1.07,0,0,0-.38-.68A1,1,0,0,0,164,73.79Z"/><path class="cls-2" d="M153.41,97a.42.42,0,0,0-.5.28,1.1,1.1,0,0,1-.2.35.54.54,0,0,0-.22.6c0,.1.19.19.36.25l.76-.52v-.5A.31.31,0,0,0,153.41,97Z"/><path class="cls-2" d="M154.64,95.22c-.17.14-.13.07-.35,0s-.56,0-.57.36a.76.76,0,0,0,.73.69.88.88,0,0,0,.79-.89C155.2,95.07,154.8,95.07,154.64,95.22Z"/><path class="cls-2" d="M165.06,70.61a.2.2,0,0,0-.21-.24.23.23,0,0,0-.2.2.19.19,0,0,0,.15.23A.2.2,0,0,0,165.06,70.61Z"/><path class="cls-2" d="M166.31,65.77a.72.72,0,0,0,.17.59c0-.13,0-.26,0-.39l0-.5A.5.5,0,0,0,166.31,65.77Z"/><path class="cls-2" d="M165.93,68.32a.33.33,0,0,0,.28.27c0-.25.08-.5.11-.75a.34.34,0,0,0-.16,0A.38.38,0,0,0,165.93,68.32Z"/><path class="cls-2" d="M165.82,70.88c0-.11,0-.22.07-.33a.29.29,0,0,0-.09.15A.36.36,0,0,0,165.82,70.88Z"/><path class="cls-2" d="M166.29,63.63a.41.41,0,0,0,0,.19l-.05,0c-.19-.07-.41.13-.43.36a.34.34,0,0,0,.32.36.44.44,0,0,0,.4-.32l.06,0h0c0-.33,0-.66,0-1A.46.46,0,0,0,166.29,63.63Z"/><path class="cls-2" d="M165,72.8a.51.51,0,0,0-.69.35c-.07.27,0,.57.25.62a.71.71,0,0,0,.65-.26c0-.13.08-.27.11-.4A.52.52,0,0,0,165,72.8Z"/><path class="cls-2" d="M165.05,74.05c0-.13.07-.25.1-.38a.84.84,0,0,0-.16.24A1.14,1.14,0,0,0,165.05,74.05Z"/><path class="cls-2" d="M165.37,69.22c-.25-.07-.56.06-.6.26a.28.28,0,0,0,0,.09.74.74,0,0,0-.52-.38.65.65,0,0,0-.24,0,1,1,0,0,0,.06-.92.71.71,0,0,0-.85-.19c-.21.1-.38.21-.62,0a.2.2,0,0,0-.22,0l-.05,0a.39.39,0,0,0-.42.27.32.32,0,0,0,.24.41.46.46,0,0,0,.32-.13.31.31,0,0,0,.18,0,3.18,3.18,0,0,0,.38.59.81.81,0,0,0,.68.15.74.74,0,0,0-.19.44c-.07.52.06.71.48.81a.7.7,0,0,0,.84-.63s0-.06,0-.1a.72.72,0,0,0,.44.4c.2.06.45-.14.52-.4S165.72,69.32,165.37,69.22Z"/><path class="cls-2" d="M164.7,71.4a.35.35,0,0,0-.22-.43.34.34,0,0,0-.45.17c-.08.22,0,.56.16.61s.27,0,.4-.16l0,0a.56.56,0,0,0,.42.7,2,2,0,0,0,.51,0c.09-.35.17-.69.25-1.05a.67.67,0,0,0-.33-.24A.89.89,0,0,0,164.7,71.4Z"/><path class="cls-2" d="M151.38,96.8A.25.25,0,0,0,151,97c-.05.17.06.42.22.41s.44-.06.46-.24S151.49,96.85,151.38,96.8Z"/><path class="cls-2" d="M143.26,100.28a.38.38,0,0,0,.34.4.37.37,0,0,0,.38-.36.32.32,0,0,0-.3-.37A.42.42,0,0,0,143.26,100.28Z"/><path class="cls-2" d="M141.49,102.71c-.18,0-.37,0-.4.17s.08.52.28.55a.4.4,0,0,0,.48-.35A.34.34,0,0,0,141.49,102.71Z"/><path class="cls-2" d="M141.55,100.37a.62.62,0,0,0-.48.29.52.52,0,0,0-.35-.38.36.36,0,0,0-.41.32.42.42,0,0,0,.25.47.64.64,0,0,0,.44-.15.47.47,0,0,0,.29.41c.4.07.73-.07.77-.32A.7.7,0,0,0,141.55,100.37Z"/><path class="cls-2" d="M141.68,102.23a.82.82,0,0,0-.38-.72c-.27-.06-.46.09-.53.43s.1.45.43.51A.37.37,0,0,0,141.68,102.23Z"/><path class="cls-2" d="M142.05,100a.26.26,0,0,0-.33.19.3.3,0,0,0,.16.4.34.34,0,0,0,.42-.23A.3.3,0,0,0,142.05,100Z"/><path class="cls-2" d="M138,102.32a.63.63,0,0,0-.6.23.44.44,0,0,0,.09.36h-.09c-.12,0-.26,0-.31.15s.14.4.33.42.21-.11.23-.25a.26.26,0,0,0,0-.15h0a.28.28,0,0,0,.38-.18C138.12,102.61,138.11,102.39,138,102.32Z"/><path class="cls-2" d="M140.54,103.49a.68.68,0,0,0-.54-.5.35.35,0,0,0-.15,0,.62.62,0,0,0,.12-.42c-.07-.28-.32-.19-.56-.19-.1.27-.15.5.09.65a.31.31,0,0,0,.17,0,.65.65,0,0,0-.21.42.51.51,0,0,0,.34.52.45.45,0,0,0,.22,0c.18.28.32.36.52.29a.35.35,0,0,0,.25-.37A.41.41,0,0,0,140.54,103.49Z"/><path class="cls-2" d="M143.21,99.6c.26,0,.45-.14.5-.48a.41.41,0,0,0-.27-.51c-.22,0-.67.22-.7.42S142.87,99.54,143.21,99.6Z"/><path class="cls-2" d="M138.88,104c-.37-.07-.65,0-.69.28a.66.66,0,0,0,.48.61.65.65,0,0,0,.52-.45A.41.41,0,0,0,138.88,104Z"/><path class="cls-2" d="M140,101.85s0-.13-.1-.14-.27-.19-.39-.34a.39.39,0,0,0-.35,0,.45.45,0,0,0-.18.31c0,.08.11.2.2.24a2.49,2.49,0,0,0,.55.16C139.93,102.11,140,102,140,101.85Z"/><path class="cls-2" d="M144,102.87a.12.12,0,0,0-.16.11c0,.08,0,.23.12.27s.19-.14.18-.18S144.14,102.89,144,102.87Z"/><path class="cls-2" d="M148.2,100.47a.7.7,0,0,0-.41.38l-.1,0c-.38-.08-.57.15-.71.46-.21-.07-.4-.24-.58,0a.35.35,0,0,0,0,.45c.2.24.4.08.6,0l.13.13c.63-.33,1.25-.68,1.86-1A.68.68,0,0,0,148.2,100.47Z"/><path class="cls-2" d="M137.46,104.91a.41.41,0,0,0-.31,0c-.12.07-.07.24.07.32a.6.6,0,0,0,.45.05.18.18,0,0,0,.09-.26A.36.36,0,0,0,137.46,104.91Z"/><path class="cls-2" d="M150,97.16a.36.36,0,0,0-.3-.43.35.35,0,0,0-.44.27.37.37,0,0,0,.28.44A.44.44,0,0,0,150,97.16Z"/><path class="cls-2" d="M145.25,101.67a.33.33,0,0,0-.39.28.54.54,0,0,0,0,.31c-.19,0-.38.13-.39.28s.22.29.43.38l.36-.18a.75.75,0,0,0,0-.2.39.39,0,0,0,.41-.29A.57.57,0,0,0,145.25,101.67Z"/><path class="cls-2" d="M146.12,99.25a.7.7,0,0,0-.64.5.52.52,0,1,0,1,.15A.58.58,0,0,0,146.12,99.25Z"/><path class="cls-2" d="M143.46,101.66a.61.61,0,0,0-.67.43.62.62,0,0,0,.41.69.85.85,0,0,0,.73-.52A.57.57,0,0,0,143.46,101.66Z"/><path class="cls-2" d="M150.3,98.06a.4.4,0,0,0-.06.79.43.43,0,0,0,.41-.39A.4.4,0,0,0,150.3,98.06Z"/><path class="cls-2" d="M144.15,98c-.11,0-.28.18-.33.27s.09.37.24.34a.35.35,0,0,0,.24-.23C144.32,98.26,144.27,98,144.15,98Z"/><path class="cls-2" d="M92.2,98.92a.35.35,0,0,0-.36.11c.17.23.36.46.54.68a.77.77,0,0,0,0-.21.48.48,0,0,0,.07-.18A.34.34,0,0,0,92.2,98.92Z"/><path class="cls-2" d="M126.76,34.21a.44.44,0,0,0,.21-.08l-.47-.08A.2.2,0,0,0,126.76,34.21Z"/><path class="cls-2" d="M126.13,37.72a.28.28,0,0,0,.41.12c.24-.12.39-.42.3-.6a.59.59,0,0,0-.63-.25C126,37.09,126,37.44,126.13,37.72Z"/><path class="cls-2" d="M125.9,35.05a.63.63,0,0,0,.3-.56.53.53,0,0,0-.54-.23.27.27,0,0,0-.17.38C125.59,34.93,125.75,35.09,125.9,35.05Z"/><path class="cls-2" d="M88.25,91.5c.06-.27-.19-.29-.27-.4a.59.59,0,0,0-.85-.14h0c.18.42.38.86.6,1.29a.59.59,0,0,0,0-.08.43.43,0,0,0,0-.4C88,91.75,88.2,91.7,88.25,91.5Z"/><path class="cls-2" d="M128.41,34.37l-.5-.09a.36.36,0,0,0,.3.17A.32.32,0,0,0,128.41,34.37Z"/><path class="cls-2" d="M127.63,38.89a.59.59,0,0,0,.54-.63.61.61,0,0,0-.62-.52.58.58,0,0,0,.08,1.15Z"/><path class="cls-2" d="M128.76,37c.25,0,.42-.1.44-.3a.61.61,0,0,0-.49-.59.44.44,0,0,0-.44.38A.53.53,0,0,0,128.76,37Z"/><path class="cls-2" d="M128,36.11c.27-.12.36-.6.18-1a.49.49,0,0,0-.71-.21.59.59,0,0,0-.26.87A.63.63,0,0,0,128,36.11Z"/><path class="cls-2" d="M122.79,33.75a.15.15,0,0,0,0-.09l-.36,0a.37.37,0,0,0,.08.14C122.59,33.82,122.74,33.83,122.79,33.75Z"/><path class="cls-2" d="M121,35.71c.2.06.39.21.55.13a.36.36,0,0,0,.21-.25.57.57,0,0,0,.07-.34.78.78,0,0,0-.4-.55.58.58,0,0,0-.61.09.47.47,0,0,0-.15.47A.6.6,0,0,0,121,35.71Z"/><path class="cls-2" d="M129.79,35.12a.62.62,0,0,0,.33-.38l-1.38-.3a.79.79,0,0,0,0,.46C128.91,35.24,129.35,35.33,129.79,35.12Z"/><path class="cls-2" d="M122.75,37.11c0,.1.17.14.25.15s.15-.07.15-.14S123,37,123,37,122.76,37,122.75,37.11Z"/><path class="cls-2" d="M125.17,38.41a.38.38,0,0,0,.1-.52.41.41,0,0,0-.5-.18.52.52,0,0,0-.21.55A.54.54,0,0,0,125.17,38.41Z"/><path class="cls-2" d="M123.93,35.88c.25-.15.28-.4.09-.72a.52.52,0,0,0-.6-.16.87.87,0,0,0-.3.81A.64.64,0,0,0,123.93,35.88Z"/><path class="cls-2" d="M124.81,36.82a.77.77,0,0,1,.52,0c.08,0,.23-.12.27-.22a.43.43,0,0,0-.07-.36c0-.06-.21-.07-.3,0a2.71,2.71,0,0,0-.52.24c-.15.07-.16.19-.06.34S124.77,36.84,124.81,36.82Z"/><path class="cls-2" d="M124.14,34.43c.3-.18.43-.43.34-.62l-.85-.08a.69.69,0,0,0,0,.58A.42.42,0,0,0,124.14,34.43Z"/><path class="cls-2" d="M130.69,35.88c-.1.22-.23.45.05.61a.38.38,0,0,0,.5-.05s.11-.15.09-.21-.15-.34-.23-.52a.89.89,0,0,0,.08-.71l-.64-.16a.77.77,0,0,0-.36.3C130,35.42,130.13,35.57,130.69,35.88Z"/><path class="cls-2" d="M138.51,41.28c.21,0,.35-.19.4-.31s-.06-.26-.21-.31-.37,0-.37.2S138.31,41.26,138.51,41.28Z"/><path class="cls-2" d="M137.6,37.36c0-.09-.11-.19-.18-.22a.19.19,0,0,0-.19.08c0,.07.06.17.12.22S137.56,37.46,137.6,37.36Z"/><path class="cls-2" d="M137.92,37.27l-.28-.12a.33.33,0,0,0,.13.11A.4.4,0,0,0,137.92,37.27Z"/><path class="cls-2" d="M139.26,38.51a.75.75,0,0,1,.57,0,.42.42,0,0,0,.51-.17c-.61-.3-1.25-.59-1.92-.88a.79.79,0,0,0,0,.13c.17.25.4.58.64.89A.26.26,0,0,0,139.26,38.51Z"/><path class="cls-2" d="M137.83,41.7a.71.71,0,0,0,.1.26c.25.07.45,0,.45-.19a.41.41,0,0,0-.16-.31C138.09,41.37,137.83,41.53,137.83,41.7Z"/><path class="cls-2" d="M139.92,39.63s.05-.05.05-.08a.76.76,0,0,0-.42-.85.48.48,0,0,0-.43.15.32.32,0,0,0-.17-.06.55.55,0,0,0-.33.45.4.4,0,0,0,.42.38h0a.49.49,0,0,0,.26.18.47.47,0,0,0,.16,0,.51.51,0,0,0-.09.25.7.7,0,0,0,.46.52.68.68,0,0,0,.59-.55C140.44,39.81,140.23,39.67,139.92,39.63Z"/><path class="cls-2" d="M143.09,40a1,1,0,0,1,.24,0s0,0,.06,0c-.32-.2-.66-.4-1-.59a.32.32,0,0,1,.12.15A.48.48,0,0,0,143.09,40Z"/><path class="cls-2" d="M141.62,41.88a.47.47,0,0,0-.17,0,1.12,1.12,0,0,0-.73.51c.45.06.89.12,1.33.2a.29.29,0,0,1,0-.09.42.42,0,0,0,.18-.21.33.33,0,0,0-.18-.45A.38.38,0,0,0,141.62,41.88Z"/><path class="cls-2" d="M140.94,41a.8.8,0,0,0,1-.39.77.77,0,0,0-.13-.67.56.56,0,0,0,.21-.17c.06-.09.1-.19.19-.36-.21-.13-.42-.25-.65-.37a.57.57,0,0,0-.13,0c-.39.08-.5.34-.32.63a.91.91,0,0,0-.5.52A.62.62,0,0,0,140.94,41Z"/><path class="cls-2" d="M132.44,41.09a.34.34,0,0,0,.37.31.45.45,0,0,0,.36-.49,1.25,1.25,0,0,0-.36-.24C132.6,40.63,132.43,40.86,132.44,41.09Z"/><path class="cls-2" d="M132.6,35.91l.15,0a.23.23,0,0,0,.16-.06s0,0,0,0a.61.61,0,0,0,.15-.24l-1-.28s0,.05,0,.08A.63.63,0,0,0,132.6,35.91Z"/><path class="cls-2" d="M132,36.11c.08-.09.28-.28.15-.44s-.39-.08-.51,0-.12.24-.05.37S131.87,36.23,132,36.11Z"/><path class="cls-2" d="M132.54,36.77c0,.19,0,.41.25.45.09,0,.24-.09.3-.18a.28.28,0,0,0,0-.29C132.91,36.57,132.73,36.62,132.54,36.77Z"/><path class="cls-2" d="M130.92,39.07c-.07,0-.17.17-.19.27s.07.27.23.23.41-.14.4-.28S131,39.06,130.92,39.07Z"/><path class="cls-2" d="M118.78,34.08c.09,0,.22.14.12.18s-.16.14-.08.39a.38.38,0,0,0,.47.32.56.56,0,0,0,.43-.67c-.06-.31-.24-.32-.26-.53a.27.27,0,0,0-.23-.24h-.59a.22.22,0,0,0-.12.19C118.49,34,118.68,34.08,118.78,34.08Z"/><path class="cls-2" d="M136.72,36.78l-.4-.14a.41.41,0,0,0,.35.15Z"/><path class="cls-2" d="M136.32,38.82l-.14,0c-.34-.06-.5.07-.52.41s0,.56.42.69a1.36,1.36,0,0,1-.37.17.36.36,0,0,0-.34.35.5.5,0,0,0,.22.55c.67.39.89.35,1.36-.25l.05,0h0a.86.86,0,0,0-.12.27.24.24,0,0,0,.22.32.29.29,0,0,0,.35-.26.75.75,0,0,0-.15-.34.33.33,0,0,0,.12,0,.47.47,0,0,1,.1,0c.06,0,.21-.08.26-.18a.44.44,0,0,0-.15-.6.47.47,0,0,0-.45.09h-.07c-.25.07-.36-.07-.53-.19C136.9,39.31,136.79,39,136.32,38.82Z"/><path class="cls-2" d="M134.08,37.16a.9.9,0,0,0,.13,1,.76.76,0,0,0,.5.17.4.4,0,0,0,.32,0l.07-.06a1,1,0,0,0,.47-.38.37.37,0,0,1,.37-.19.44.44,0,0,0,.35-.33.1.1,0,0,0,.07,0,.23.23,0,0,0,.24-.08.22.22,0,0,0-.11-.23.4.4,0,0,0-.22,0c-.06-.15-.19-.18-.35-.16a.86.86,0,0,1-.42,0,9.2,9.2,0,0,1-.86-.39,2.42,2.42,0,0,0,0-.49l-.88-.28a.49.49,0,0,0-.27.71A4.61,4.61,0,0,0,134.08,37.16Z"/><path class="cls-2" d="M124.94,35.24c.06,0,.25-.06.26-.15s-.18-.19-.24-.19a.22.22,0,0,0-.2.16C124.76,35.15,124.87,35.23,124.94,35.24Z"/><path class="cls-2" d="M82.18,64.48a.2.2,0,0,0,0,.21s.19,0,.25,0,.11-.18,0-.25S82.24,64.43,82.18,64.48Z"/><path class="cls-2" d="M80.81,58.91a.53.53,0,0,0,.12,0,.64.64,0,0,0,.57-.35.47.47,0,0,0-.07-.43Z"/><path class="cls-2" d="M82.93,60.66a1,1,0,0,1-.16-.38.55.55,0,0,0-.37-.52c-.18-.06-.59.21-.65.42s-.15.41.1.56a6.28,6.28,0,0,1,.52.38.31.31,0,0,0,.49.1A.41.41,0,0,0,82.93,60.66Z"/><path class="cls-2" d="M82.83,62.47a.9.9,0,0,0,.25,1.17c.27.14.5-.19.48-.41s0-.15.21-.27a.38.38,0,0,0,0-.67A.76.76,0,0,0,82.83,62.47Z"/><path class="cls-2" d="M80.06,62.05c-.11.13,0,.24.06.33a.24.24,0,0,0,.4,0,1.21,1.21,0,0,0,.07-.34A.37.37,0,0,0,80.06,62.05Z"/><path class="cls-2" d="M77.7,65.77a.81.81,0,0,0,0,1,.73.73,0,0,0,1,.06,1,1,0,0,0,0-1.1A.71.71,0,0,0,77.7,65.77Z"/><path class="cls-2" d="M79.41,63.33s-.13.23-.07.29.26,0,.3,0,.09-.18,0-.25S79.47,63.29,79.41,63.33Z"/><path class="cls-2" d="M83.78,58.05a.21.21,0,0,0,0-.06.54.54,0,0,0,.31-.24c0-.16,0-.38-.11-.49s-.3.08-.32.15,0,.06,0,.09a.35.35,0,0,0-.5,0,.46.46,0,0,0,.07.57A.4.4,0,0,0,83.78,58.05Z"/><path class="cls-2" d="M79.55,61a1.21,1.21,0,0,0,.07-.34.41.41,0,0,0-.11-.07l-.33.45C79.31,61.13,79.48,61.13,79.55,61Z"/><path class="cls-2" d="M75.19,67s0,0,0,0h0Z"/><path class="cls-2" d="M87.24,88.91c.06-.54-.08-.63-.58-.8a.53.53,0,0,0-.57.22l.34,1,.12.06,0,0a.33.33,0,0,0,.24,0A.52.52,0,0,0,87.24,88.91Z"/><path class="cls-2" d="M86.07,83.68a.47.47,0,0,0-.26-.62.31.31,0,0,0-.41.16.39.39,0,0,0,0,.5A.43.43,0,0,0,86.07,83.68Z"/><path class="cls-2" d="M86.42,87a.76.76,0,0,0-.5-.81.8.8,0,0,0-.46,0c.11.43.23.86.37,1.3A.74.74,0,0,0,86.42,87Z"/><path class="cls-2" d="M87.24,90.27a.43.43,0,0,0-.48-.16c.11.28.23.56.36.85a1.45,1.45,0,0,0,.09-.22C87.25,90.59,87.35,90.45,87.24,90.27Z"/><path class="cls-2" d="M117.12,34.35a.3.3,0,1,0-.43-.34C116.64,34.19,116.74,34.27,117.12,34.35Z"/><path class="cls-2" d="M85.22,58.33a.35.35,0,0,0,0-.51.37.37,0,0,0-.53,0,.44.44,0,0,0,0,.54A.36.36,0,0,0,85.22,58.33Z"/><path class="cls-2" d="M85,55.48c.07-.19-.26-.52-.41-.45s-.2.51,0,.6A.36.36,0,0,0,85,55.48Z"/><path class="cls-2" d="M83.79,59.23c-.06.11-.21.39-.07.51s.35,0,.45-.08a.24.24,0,0,0,.08-.37C84.15,59.15,83.88,59.09,83.79,59.23Z"/><path class="cls-2" d="M85.16,82a.61.61,0,0,0,.27,0q0,.17.27.27a.56.56,0,0,0,.73-.07,10.57,10.57,0,0,1-.57-1.34,1.08,1.08,0,0,0-.44-.23.58.58,0,0,0-.7.31c0,.22,0,.45,0,.68A.74.74,0,0,0,85.16,82Z"/><path class="cls-2" d="M85,83h-.13c0,.22.05.43.08.65a.35.35,0,0,0,.32-.3A.28.28,0,0,0,85,83Z"/><path class="cls-2" d="M94.12,50.32c-.11.16,0,.51.25.62a.33.33,0,0,0,.44-.18c.16-.3.17-.52,0-.63A.62.62,0,0,0,94.12,50.32Z"/><path class="cls-2" d="M95.22,47c.32,0,.35.28.46.49a.38.38,0,0,0,.61.13l.07-.05a.25.25,0,0,0,.12.13.7.7,0,0,0,.3.09c.12-.14.23-.3.35-.44a1.66,1.66,0,0,0-.23-.23.38.38,0,0,0-.38-.05.47.47,0,0,0-.24-.3c-.15-.1-.33-.15-.32-.44.1,0,.25-.09.28-.17l0-.06-1.19.66a.4.4,0,0,0,.07.19S95.19,47,95.22,47Z"/><path class="cls-2" d="M95.25,49.42a.53.53,0,0,1,.36,0,8.33,8.33,0,0,1,.51-.8h0a.87.87,0,0,1-.74,0A.29.29,0,0,0,95,49C94.89,49.17,95.05,49.45,95.25,49.42Z"/><path class="cls-2" d="M93.88,54.64a.46.46,0,0,0,.31,0l.1-.88c-.28-.12-.56-.09-.66.1A.68.68,0,0,0,93.88,54.64Z"/><path class="cls-2" d="M94.64,49.66a.38.38,0,0,0,0-.48.38.38,0,0,0-.53,0,.29.29,0,0,0,0,.46A.36.36,0,0,0,94.64,49.66Z"/><path class="cls-2" d="M85,55.14a.35.35,0,0,0,.51-.09.36.36,0,0,0-.11-.51.43.43,0,0,0-.53.1A.36.36,0,0,0,85,55.14Z"/><path class="cls-2" d="M103.24,43.27l-.07,0h0Z"/><path class="cls-2" d="M114.57,33.71l.17.16a.75.75,0,0,0,.83.06.78.78,0,0,0,.26-.34c-.7,0-1.39.07-2.08.12a.63.63,0,0,0,.33.09A3,3,0,0,0,114.57,33.71Z"/><path class="cls-2" d="M100.19,44.77s0-.05,0-.08a1,1,0,0,0,0-.3.36.36,0,0,0,.19-.07.3.3,0,0,0,.1.14l.08,0,.75-.4a.94.94,0,0,0-.07-.22c-.73.25-1.47.53-2.25.86a2.83,2.83,0,0,1-.06.42.82.82,0,0,0,0,.44A12.76,12.76,0,0,1,100.19,44.77Z"/><path class="cls-2" d="M97.61,45.77l-.06.06c-.06.11-.14.24-.08.34s.41.14.53,0,0-.24,0-.34a.25.25,0,0,0-.22-.1.41.41,0,0,0,.12-.09.38.38,0,0,0,0-.36l-.88.43a.28.28,0,0,0,.1.09A.85.85,0,0,0,97.61,45.77Z"/><path class="cls-2" d="M86,55.27a3.07,3.07,0,0,0,.72.31.76.76,0,0,0,.75-.43c.06-.16.07-.54-.27-.59-.55.2-.55-.22-1-.2a.61.61,0,0,0-.19.06A.47.47,0,0,0,86,55.27Z"/><path class="cls-2" d="M87,53c.06.07.3.17.4.1s0-.21,0-.32a.28.28,0,0,0,0-.32.33.33,0,0,0-.26-.13.5.5,0,0,0-.28.27.23.23,0,0,0,0,.18A.38.38,0,0,0,87,53Z"/><path class="cls-2" d="M87.38,52.08a.53.53,0,0,0,.32.1.74.74,0,0,0,.52-.21l.08.06a.56.56,0,0,0,.77-.12.28.28,0,0,0,.23.1.26.26,0,0,0,.37,0,.35.35,0,0,0,.15-.43.3.3,0,0,0-.26-.21h0a.34.34,0,0,0-.38-.07l-.11-.24.45-.23a1.9,1.9,0,0,0,0-.41.18.18,0,0,0,0-.09c-.79.59-1.56,1.22-2.31,1.87a.3.3,0,0,0,.15-.07S87.36,52.1,87.38,52.08Z"/><path class="cls-2" d="M85.54,57.13a.73.73,0,0,0,.87-.26c.12-.19,0-.53-.28-.66a.5.5,0,0,0-.71.23A.49.49,0,0,0,85.54,57.13Z"/><path class="cls-2" d="M90.89,50.36a1.28,1.28,0,0,0,1-1c0-.26-.21-.41-.33-.51q-.7.48-1.41,1A.79.79,0,0,0,90.89,50.36Z"/><path class="cls-2" d="M91.59,53.83a.3.3,0,0,0,.15.44.4.4,0,0,0,.51-.15.39.39,0,0,0-.18-.5A.37.37,0,0,0,91.59,53.83Z"/><path class="cls-2" d="M92.53,50.73a.67.67,0,0,0,.69-1.16s0,0-.07,0a.63.63,0,0,0-.1-.3.87.87,0,0,0,.68-.44.76.76,0,0,0-.27-1.06s0,0-.06,0l-1,.65a.67.67,0,0,0,.12.65l-.06,0c-.13.12,0,.41.07.54l0,0a1.08,1.08,0,0,0-.18.23A.57.57,0,0,0,92.53,50.73Z"/><path class="cls-2" d="M89.09,53.31a.58.58,0,0,0,.11.75.7.7,0,0,0,.78-.23.52.52,0,1,0-.89-.52Z"/><ellipse class="cls-3" cx="124.56" cy="71.65" rx="23.77" ry="22.91"/><circle class="cls-4" cx="32.54" cy="34.28" r="1.93"/><circle class="cls-4" cx="223.42" cy="67.02" r="1.93"/><path class="cls-5" d="M145,86.21c.35-.52,33.13,20.58,33.66,20.93a1.14,1.14,0,0,1-1.26,1.9C176.84,108.69,144.62,86.74,145,86.21Z"/><path class="cls-5" d="M140.63,50.56c-.49-.39,23.57-31.07,24-31.56a1.14,1.14,0,1,1,1.77,1.43C166,20.92,141.12,51,140.63,50.56Z"/><path class="cls-5" d="M87.44,93.64c.21.59-36.2,14.52-36.79,14.74a1.14,1.14,0,0,1-.79-2.14C50.45,106,87.22,93.05,87.44,93.64Z"/><path class="cls-5" d="M100.59,92.3c.31.55-33.15,20.56-33.69,20.88a1.14,1.14,0,0,1-1.15-2C66.3,110.89,100.27,91.76,100.59,92.3Z"/><path class="cls-6" d="M204.47,18.65c-5.1,0-7.1,2-7.1,7.1,0-5.1-2-7.1-7.09-7.1,5.09,0,7.09-2,7.09-7.1C197.37,16.65,199.37,18.65,204.47,18.65Z"/><path class="cls-6" d="M68.84,18.65c-3.93,0-5.48,1.55-5.48,5.48,0-3.93-1.54-5.48-5.48-5.48,3.94,0,5.48-1.55,5.48-5.48C63.36,17.1,64.91,18.65,68.84,18.65Z"/><path class="cls-6" d="M184.17,79.21c-4.61,0-6.43,1.81-6.43,6.42,0-4.61-1.81-6.42-6.42-6.42,4.61,0,6.42-1.81,6.42-6.43C177.74,77.4,179.56,79.21,184.17,79.21Z"/><path class="cls-6" d="M55.68,124.7c-5.66,0-7.89,2.22-7.89,7.89,0-5.67-2.22-7.89-7.89-7.89,5.67,0,7.89-2.23,7.89-7.89C47.79,122.47,50,124.7,55.68,124.7Z"/><rect class="cls-7" x="90.25" y="33.05" width="3.26" height="3.26" transform="translate(-2.21 62.83) rotate(-37.34)"/><rect class="cls-4" x="121.32" y="112.16" width="3.45" height="3.45" transform="translate(-31.7 49.35) rotate(-20.15)"/><rect class="cls-4" x="182.53" y="5.79" width="3.45" height="3.45" transform="translate(8.69 63.92) rotate(-20.15)"/><polygon class="cls-8" points="157.62 75.65 156.7 74.28 155.06 74.38 154.32 75.86 155.24 77.23 156.88 77.13 157.62 75.65"/><polygon class="cls-7" points="190.14 106.28 194.69 112.2 199.53 108.06 190.14 106.28"/><polygon class="cls-7" points="35.22 67.85 35.44 73.39 44.44 71.26 35.22 67.85"/><polygon class="cls-9" points="70.39 84.46 71.86 88.71 76.11 85.87 70.39 84.46"/><polygon class="cls-9" points="146.29 27.82 141.84 27.18 142.41 32.26 146.29 27.82"/><rect class="cls-9" x="103.66" y="49.81" width="3.16" height="3.16" transform="translate(-11.58 42.97) rotate(-21.9)"/><rect class="cls-9" x="126.85" y="95.84" width="2.46" height="2.46" transform="translate(-32.63 96.96) rotate(-37.12)"/><circle class="cls-8" cx="126.66" cy="56.78" r="1.21"/><circle class="cls-8" cx="109.51" cy="82.54" r="0.73"/><circle class="cls-8" cx="141.21" cy="61.97" r="0.48"/><circle class="cls-8" cx="127.33" cy="88.64" r="0.48"/><circle class="cls-9" cx="121.37" cy="71.16" r="0.48"/><circle class="cls-9" cx="129.94" cy="75.81" r="1.21"/><circle class="cls-9" cx="110.91" cy="63.5" r="1.21"/></g></svg> \ No newline at end of file diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx index 11fbc7931e6201..6fad3335c5efce 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { wait } from '@testing-library/react'; import { of } from 'rxjs'; import { mountWithIntl } from 'test_utils/enzyme_helpers'; +import { httpServiceMock, uiSettingsServiceMock } from '../../../../../src/core/public/mocks'; import { GlobalSearchBatchedResults, GlobalSearchPluginStart, @@ -47,6 +48,10 @@ const getSearchProps: any = (component: any) => component.find('EuiFieldSearch') describe('SearchBar', () => { let searchService: GlobalSearchPluginStart; let findSpy: jest.SpyInstance; + const http = httpServiceMock.createSetupContract({ basePath: '/test' }); + const basePathUrl = http.basePath.prepend('/plugins/globalSearchBar/assets/'); + const uiSettings = uiSettingsServiceMock.createStartContract(); + const darkMode = uiSettings.get('theme:darkMode'); beforeEach(() => { searchService = globalSearchPluginMock.createStartContract(); @@ -66,7 +71,12 @@ describe('SearchBar', () => { .mockReturnValueOnce(of(createBatch('Discover', { id: 'My Dashboard', type: 'test' }))); const component = mountWithIntl( - <SearchBar globalSearch={searchService.find} navigateToUrl={navigate} /> + <SearchBar + globalSearch={searchService.find} + navigateToUrl={navigate} + basePathUrl={basePathUrl} + darkMode={darkMode} + /> ); expect(findSpy).toHaveBeenCalledTimes(0); @@ -85,7 +95,14 @@ describe('SearchBar', () => { }); it('supports keyboard shortcuts', () => { - mountWithIntl(<SearchBar globalSearch={searchService.find} navigateToUrl={jest.fn()} />); + mountWithIntl( + <SearchBar + globalSearch={searchService.find} + navigateToUrl={jest.fn()} + basePathUrl={basePathUrl} + darkMode={darkMode} + /> + ); const searchEvent = new KeyboardEvent('keydown', { key: '/', diff --git a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx index 0dde28db0436d4..4ca0f8cf81b7bd 100644 --- a/x-pack/plugins/global_search_bar/public/components/search_bar.tsx +++ b/x-pack/plugins/global_search_bar/public/components/search_bar.tsx @@ -12,6 +12,7 @@ import { EuiSelectableTemplateSitewideOption, EuiText, EuiIcon, + EuiImage, EuiHeaderSectionItemButton, EuiSelectableMessage, } from '@elastic/eui'; @@ -27,6 +28,8 @@ import { GlobalSearchPluginStart, GlobalSearchResult } from '../../../global_sea interface Props { globalSearch: GlobalSearchPluginStart['find']; navigateToUrl: ApplicationStart['navigateToUrl']; + basePathUrl: string; + darkMode: boolean; } const clearField = (field: HTMLInputElement) => { @@ -42,7 +45,7 @@ const clearField = (field: HTMLInputElement) => { const cleanMeta = (str: string) => (str.charAt(0).toUpperCase() + str.slice(1)).replace(/-/g, ' '); const blurEvent = new FocusEvent('blur'); -export function SearchBar({ globalSearch, navigateToUrl }: Props) { +export function SearchBar({ globalSearch, navigateToUrl, basePathUrl, darkMode }: Props) { const isMounted = useMountedState(); const [searchValue, setSearchValue] = useState<string>(''); const [searchRef, setSearchRef] = useState<HTMLInputElement | null>(null); @@ -134,6 +137,34 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) { } }; + const emptyMessage = ( + <EuiSelectableMessage style={{ minHeight: 300 }}> + <EuiImage + alt={i18n.translate('xpack.globalSearchBar.searchBar.noResultsImageAlt', { + defaultMessage: 'Illustration of black hole', + })} + size="fullWidth" + url={`${basePathUrl}illustration_product_no_search_results_${ + darkMode ? 'dark' : 'light' + }.svg`} + /> + <EuiText size="m"> + <p> + <FormattedMessage + id="xpack.globalSearchBar.searchBar.noResultsHeading" + defaultMessage="No results found" + /> + </p> + </EuiText> + <p> + <FormattedMessage + id="xpack.globalSearchBar.searchBar.noResults" + defaultMessage="Try searching for applications, dashboards, visualizations, and more." + /> + </p> + </EuiSelectableMessage> + ); + useEvent('keydown', onKeyDown); return ( @@ -164,22 +195,8 @@ export function SearchBar({ globalSearch, navigateToUrl }: Props) { popoverProps={{ repositionOnScroll: true, }} - emptyMessage={ - <EuiSelectableMessage style={{ minHeight: 300 }}> - <p> - <FormattedMessage - id="xpack.globalSearchBar.searchBar.noResultsHeading" - defaultMessage="No results found" - /> - </p> - <p> - <FormattedMessage - id="xpack.globalSearchBar.searchBar.noResults" - defaultMessage="Try searching for applications and saved objects by name." - /> - </p> - </EuiSelectableMessage> - } + emptyMessage={emptyMessage} + noMatchesMessage={emptyMessage} popoverFooter={ <EuiText color="subdued" size="xs"> <EuiFlexGroup diff --git a/x-pack/plugins/global_search_bar/public/plugin.tsx b/x-pack/plugins/global_search_bar/public/plugin.tsx index 0c8cc2e64726af..9bc6b824b87166 100644 --- a/x-pack/plugins/global_search_bar/public/plugin.tsx +++ b/x-pack/plugins/global_search_bar/public/plugin.tsx @@ -24,7 +24,14 @@ export class GlobalSearchBarPlugin implements Plugin<{}, {}> { public start(core: CoreStart, { globalSearch }: GlobalSearchBarPluginStartDeps) { core.chrome.navControls.registerCenter({ order: 1000, - mount: (target) => this.mount(target, globalSearch, core.application.navigateToUrl), + mount: (target) => + this.mount( + target, + globalSearch, + core.application.navigateToUrl, + core.http.basePath.prepend('/plugins/globalSearchBar/assets/'), + core.uiSettings.get('theme:darkMode') + ), }); return {}; } @@ -32,11 +39,18 @@ export class GlobalSearchBarPlugin implements Plugin<{}, {}> { private mount( targetDomElement: HTMLElement, globalSearch: GlobalSearchPluginStart, - navigateToUrl: ApplicationStart['navigateToUrl'] + navigateToUrl: ApplicationStart['navigateToUrl'], + basePathUrl: string, + darkMode: boolean ) { ReactDOM.render( <I18nProvider> - <SearchBar globalSearch={globalSearch.find} navigateToUrl={navigateToUrl} /> + <SearchBar + globalSearch={globalSearch.find} + navigateToUrl={navigateToUrl} + basePathUrl={basePathUrl} + darkMode={darkMode} + /> </I18nProvider>, targetDomElement ); From 95bf8750cda2937afbc022163a1d28c0d1326e3e Mon Sep 17 00:00:00 2001 From: Bohdan Tsymbala <bohdan.tsymbala@elastic.co> Date: Fri, 2 Oct 2020 16:00:09 +0200 Subject: [PATCH 081/128] Refactored store code to group properties related to location so that would be easy to introduce a new view type parameter. (#79083) --- .../public/management/common/routing.test.ts | 22 +++---- .../public/management/common/routing.ts | 35 ++++++----- .../state/trusted_apps_list_page_state.ts | 12 ++-- .../trusted_apps/store/middleware.test.ts | 58 ++++++++++++------ .../pages/trusted_apps/store/middleware.ts | 12 ++-- .../pages/trusted_apps/store/reducer.test.ts | 14 ++--- .../pages/trusted_apps/store/reducer.ts | 33 ++++------ .../trusted_apps/store/selectors.test.ts | 58 +++++++----------- .../pages/trusted_apps/store/selectors.ts | 60 ++++++------------- .../pages/trusted_apps/test_utils/index.ts | 43 ++----------- .../trusted_apps/view/trusted_apps_list.tsx | 8 +-- .../trusted_apps/view/trusted_apps_page.tsx | 17 +++--- 12 files changed, 159 insertions(+), 213 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/common/routing.test.ts b/x-pack/plugins/security_solution/public/management/common/routing.test.ts index 7a36654dcffc30..7082ab0ce5c4f4 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.test.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.test.ts @@ -4,57 +4,57 @@ * you may not use this file except in compliance with the Elastic License. */ -import { extractListPaginationParams, getTrustedAppsListPath } from './routing'; +import { extractTrustedAppsListPageLocation, getTrustedAppsListPath } from './routing'; import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from './constants'; describe('routing', () => { describe('extractListPaginationParams()', () => { it('extracts default page index when not provided', () => { - expect(extractListPaginationParams({}).page_index).toBe(MANAGEMENT_DEFAULT_PAGE); + expect(extractTrustedAppsListPageLocation({}).page_index).toBe(MANAGEMENT_DEFAULT_PAGE); }); it('extracts default page index when too small value provided', () => { - expect(extractListPaginationParams({ page_index: '-1' }).page_index).toBe( + expect(extractTrustedAppsListPageLocation({ page_index: '-1' }).page_index).toBe( MANAGEMENT_DEFAULT_PAGE ); }); it('extracts default page index when not a number provided', () => { - expect(extractListPaginationParams({ page_index: 'a' }).page_index).toBe( + expect(extractTrustedAppsListPageLocation({ page_index: 'a' }).page_index).toBe( MANAGEMENT_DEFAULT_PAGE ); }); it('extracts only last page index when multiple values provided', () => { - expect(extractListPaginationParams({ page_index: ['1', '2'] }).page_index).toBe(2); + expect(extractTrustedAppsListPageLocation({ page_index: ['1', '2'] }).page_index).toBe(2); }); it('extracts proper page index when single valid value provided', () => { - expect(extractListPaginationParams({ page_index: '2' }).page_index).toBe(2); + expect(extractTrustedAppsListPageLocation({ page_index: '2' }).page_index).toBe(2); }); it('extracts default page size when not provided', () => { - expect(extractListPaginationParams({}).page_size).toBe(MANAGEMENT_DEFAULT_PAGE_SIZE); + expect(extractTrustedAppsListPageLocation({}).page_size).toBe(MANAGEMENT_DEFAULT_PAGE_SIZE); }); it('extracts default page size when invalid option provided', () => { - expect(extractListPaginationParams({ page_size: '25' }).page_size).toBe( + expect(extractTrustedAppsListPageLocation({ page_size: '25' }).page_size).toBe( MANAGEMENT_DEFAULT_PAGE_SIZE ); }); it('extracts default page size when not a number provided', () => { - expect(extractListPaginationParams({ page_size: 'a' }).page_size).toBe( + expect(extractTrustedAppsListPageLocation({ page_size: 'a' }).page_size).toBe( MANAGEMENT_DEFAULT_PAGE_SIZE ); }); it('extracts only last page size when multiple values provided', () => { - expect(extractListPaginationParams({ page_size: ['10', '20'] }).page_size).toBe(20); + expect(extractTrustedAppsListPageLocation({ page_size: ['10', '20'] }).page_size).toBe(20); }); it('extracts proper page size when single valid value provided', () => { - expect(extractListPaginationParams({ page_size: '20' }).page_size).toBe(20); + expect(extractTrustedAppsListPageLocation({ page_size: '20' }).page_size).toBe(20); }); }); diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index cb4ed9b098fce2..9acf4a1613c0b2 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -21,7 +21,7 @@ import { import { AdministrationSubTab } from '../types'; import { appendSearch } from '../../common/components/link_to/helpers'; import { EndpointIndexUIQueryParams } from '../pages/endpoint_hosts/types'; -import { TrustedAppsUrlParams } from '../pages/trusted_apps/types'; +import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state'; // Taken from: https://github.com/microsoft/TypeScript/issues/12936#issuecomment-559034150 type ExactKeys<T1, T2> = Exclude<keyof T1, keyof T2> extends never ? T1 : never; @@ -94,18 +94,18 @@ const isDefaultOrMissing = <T>(value: T | undefined, defaultValue: T) => { return value === undefined || value === defaultValue; }; -const normalizeListPaginationParams = ( - params?: Partial<TrustedAppsUrlParams> -): Partial<TrustedAppsUrlParams> => { - if (params) { +const normalizeTrustedAppsPageLocation = ( + location?: Partial<TrustedAppsListPageLocation> +): Partial<TrustedAppsListPageLocation> => { + if (location) { return { - ...(!isDefaultOrMissing(params.page_index, MANAGEMENT_DEFAULT_PAGE) - ? { page_index: params.page_index } + ...(!isDefaultOrMissing(location.page_index, MANAGEMENT_DEFAULT_PAGE) + ? { page_index: location.page_index } : {}), - ...(!isDefaultOrMissing(params.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE) - ? { page_size: params.page_size } + ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE) + ? { page_size: location.page_size } : {}), - ...(!isDefaultOrMissing(params.show, undefined) ? { show: params.show } : {}), + ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), }; } else { return {}; @@ -135,17 +135,22 @@ const extractPageSize = (query: querystring.ParsedUrlQuery): number => { return MANAGEMENT_PAGE_SIZE_OPTIONS.includes(pageSize) ? pageSize : MANAGEMENT_DEFAULT_PAGE_SIZE; }; -export const extractListPaginationParams = ( - query: querystring.ParsedUrlQuery -): TrustedAppsUrlParams => ({ +export const extractListPaginationParams = (query: querystring.ParsedUrlQuery) => ({ page_index: extractPageIndex(query), page_size: extractPageSize(query), }); -export const getTrustedAppsListPath = (params?: Partial<TrustedAppsUrlParams>): string => { +export const extractTrustedAppsListPageLocation = ( + query: querystring.ParsedUrlQuery +): TrustedAppsListPageLocation => ({ + ...extractListPaginationParams(query), + show: extractFirstParamValue(query, 'show') === 'create' ? 'create' : undefined, +}); + +export const getTrustedAppsListPath = (params?: Partial<TrustedAppsListPageLocation>): string => { const path = generatePath(MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, { tabName: AdministrationSubTab.trustedApps, }); - return `${path}${appendSearch(querystring.stringify(normalizeListPaginationParams(params)))}`; + return `${path}${appendSearch(querystring.stringify(normalizeTrustedAppsPageLocation(params)))}`; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts index 4c38ac0c4239a0..a98ec03a006f53 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts @@ -7,7 +7,6 @@ import { ServerApiError } from '../../../../common/types'; import { NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types/trusted_apps'; import { AsyncResourceState } from '.'; -import { TrustedAppsUrlParams } from '../types'; export interface PaginationInfo { index: number; @@ -39,12 +38,16 @@ export interface TrustedAppCreateFailure { data: ServerApiError; } +export interface TrustedAppsListPageLocation { + page_index: number; + page_size: number; + show?: 'create'; +} + export interface TrustedAppsListPageState { listView: { - currentListResourceState: AsyncResourceState<TrustedAppsListData>; - currentPaginationInfo: PaginationInfo; + listResourceState: AsyncResourceState<TrustedAppsListData>; freshDataTimestamp: number; - show: TrustedAppsUrlParams['show'] | undefined; }; deletionDialog: { entry?: TrustedApp; @@ -56,5 +59,6 @@ export interface TrustedAppsListPageState { | TrustedAppCreatePending | TrustedAppCreateSuccess | TrustedAppCreateFailure; + location: TrustedAppsListPageLocation; active: boolean; } diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts index 19c2d3a62781f4..2143b5135c5753 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts @@ -9,13 +9,12 @@ import { applyMiddleware, createStore } from 'redux'; import { createSpyMiddleware } from '../../../../common/store/test_utils'; import { - createFailedListViewWithPagination, createListLoadedResourceState, createLoadedListViewWithPagination, - createLoadingListViewWithPagination, createSampleTrustedApp, createSampleTrustedApps, createServerApiError, + createUninitialisedResourceState, createUserChangedUrlAction, } from '../test_utils'; @@ -76,6 +75,7 @@ describe('middleware', () => { describe('refreshing list resource state', () => { it('refreshes the list when location changes and data gets outdated', async () => { const pagination = { index: 2, size: 50 }; + const location = { page_index: 2, page_size: 50, show: undefined }; const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); @@ -87,21 +87,30 @@ describe('middleware', () => { expect(store.getState()).toStrictEqual({ ...initialState, - listView: createLoadingListViewWithPagination(initialNow, pagination), + listView: { + listResourceState: { + type: 'LoadingResourceState', + previousState: createUninitialisedResourceState(), + }, + freshDataTimestamp: initialNow, + }, active: true, + location, }); await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); expect(store.getState()).toStrictEqual({ ...initialState, - listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500), + listView: createLoadedListViewWithPagination(initialNow, pagination, 500), active: true, + location, }); }); it('does not refresh the list when location changes and data does not get outdated', async () => { const pagination = { index: 2, size: 50 }; + const location = { page_index: 2, page_size: 50, show: undefined }; const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); @@ -118,14 +127,16 @@ describe('middleware', () => { expect(service.getTrustedAppsList).toBeCalledTimes(1); expect(store.getState()).toStrictEqual({ ...initialState, - listView: createLoadedListViewWithPagination(initialNow, pagination, pagination, 500), + listView: createLoadedListViewWithPagination(initialNow, pagination, 500), active: true, + location, }); }); it('refreshes the list when data gets outdated with and outdate action', async () => { const newNow = 222222; const pagination = { index: 0, size: 10 }; + const location = { page_index: 0, page_size: 10, show: undefined }; const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); @@ -143,20 +154,24 @@ describe('middleware', () => { expect(store.getState()).toStrictEqual({ ...initialState, - listView: createLoadingListViewWithPagination( - newNow, - pagination, - createListLoadedResourceState(pagination, 500, initialNow) - ), + listView: { + listResourceState: { + type: 'LoadingResourceState', + previousState: createListLoadedResourceState(pagination, 500, initialNow), + }, + freshDataTimestamp: newNow, + }, active: true, + location, }); await spyMiddleware.waitForAction('trustedAppsListResourceStateChanged'); expect(store.getState()).toStrictEqual({ ...initialState, - listView: createLoadedListViewWithPagination(newNow, pagination, pagination, 500), + listView: createLoadedListViewWithPagination(newNow, pagination, 500), active: true, + location, }); }); @@ -172,12 +187,16 @@ describe('middleware', () => { expect(store.getState()).toStrictEqual({ ...initialState, - listView: createFailedListViewWithPagination( - initialNow, - { index: 2, size: 50 }, - createServerApiError('Internal Server Error') - ), + listView: { + listResourceState: { + type: 'FailedResourceState', + error: createServerApiError('Internal Server Error'), + lastLoadedState: undefined, + }, + freshDataTimestamp: initialNow, + }, active: true, + location: { page_index: 2, page_size: 50, show: undefined }, }); const infiniteLoopTest = async () => { @@ -193,10 +212,11 @@ describe('middleware', () => { const entry = createSampleTrustedApp(3); const notFoundError = createServerApiError('Not Found'); const pagination = { index: 0, size: 10 }; + const location = { page_index: 0, page_size: 10, show: undefined }; const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination, 500); - const listView = createLoadedListViewWithPagination(initialNow, pagination, pagination, 500); - const listViewNew = createLoadedListViewWithPagination(newNow, pagination, pagination, 500); - const testStartState = { ...initialState, listView, active: true }; + const listView = createLoadedListViewWithPagination(initialNow, pagination, 500); + const listViewNew = createLoadedListViewWithPagination(newNow, pagination, 500); + const testStartState = { ...initialState, listView, active: true, location }; it('does not submit when entry is undefined', async () => { const service = createTrustedAppsServiceMock(); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts index dd96c8d8070481..9fa456dc5ffe23 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.ts @@ -29,12 +29,12 @@ import { } from './action'; import { - getCurrentListResourceState, + getListResourceState, getDeletionDialogEntry, getDeletionSubmissionResourceState, getLastLoadedListResourceState, - getListCurrentPageIndex, - getListCurrentPageSize, + getCurrentLocationPageIndex, + getCurrentLocationPageSize, getTrustedAppCreateData, isCreatePending, needsRefreshOfListData, @@ -56,15 +56,15 @@ const refreshListIfNeeded = async ( createTrustedAppsListResourceStateChangedAction({ type: 'LoadingResourceState', // need to think on how to avoid the casting - previousState: getCurrentListResourceState(store.getState()) as Immutable< + previousState: getListResourceState(store.getState()) as Immutable< StaleResourceState<TrustedAppsListData> >, }) ); try { - const pageIndex = getListCurrentPageIndex(store.getState()); - const pageSize = getListCurrentPageSize(store.getState()); + const pageIndex = getCurrentLocationPageIndex(store.getState()); + const pageSize = getCurrentLocationPageSize(store.getState()); const response = await trustedAppsService.getTrustedAppsList({ page: pageIndex + 1, per_page: pageSize, diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts index 228f0932edd28a..94fcdb39bb1692 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts @@ -32,17 +32,14 @@ describe('reducer', () => { expect(result).toStrictEqual({ ...initialState, - listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } }, + location: { page_index: 5, page_size: 50, show: undefined }, active: true, }); }); it('extracts default pagination parameters when none provided', () => { const result = trustedAppsPageReducer( - { - ...initialState, - listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } }, - }, + { ...initialState, location: { page_index: 5, page_size: 50 } }, createUserChangedUrlAction('/trusted_apps', '?page_index=b&page_size=60') ); @@ -51,10 +48,7 @@ describe('reducer', () => { it('extracts default pagination parameters when invalid provided', () => { const result = trustedAppsPageReducer( - { - ...initialState, - listView: { ...initialState.listView, currentPaginationInfo: { index: 5, size: 50 } }, - }, + { ...initialState, location: { page_index: 5, page_size: 50 } }, createUserChangedUrlAction('/trusted_apps') ); @@ -85,7 +79,7 @@ describe('reducer', () => { expect(result).toStrictEqual({ ...initialState, - listView: { ...initialState.listView, currentListResourceState: listResourceState }, + listView: { ...initialState.listView, listResourceState }, }); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts index ec210254bf76fa..f4056f02a41404 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts @@ -11,7 +11,8 @@ import { ImmutableReducer } from '../../../../common/store'; import { AppLocation, Immutable } from '../../../../../common/endpoint/types'; import { UserChangedUrl } from '../../../../common/store/routing/action'; import { AppAction } from '../../../../common/store/actions'; -import { extractFirstParamValue, extractListPaginationParams } from '../../../common/routing'; +import { extractTrustedAppsListPageLocation } from '../../../common/routing'; + import { MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, MANAGEMENT_DEFAULT_PAGE, @@ -29,6 +30,7 @@ import { ServerReturnedCreateTrustedAppSuccess, UserClickedSaveNewTrustedAppButton, } from './action'; + import { TrustedAppsListPageState } from '../state'; type StateReducer = ImmutableReducer<TrustedAppsListPageState, AppAction>; @@ -64,7 +66,7 @@ const trustedAppsListResourceStateChanged: CaseReducer<TrustedAppsListResourceSt ...state, listView: { ...state.listView, - currentListResourceState: action.payload.newState, + listResourceState: action.payload.newState, }, }; }; @@ -110,22 +112,13 @@ const trustedAppDeletionDialogClosed: CaseReducer<TrustedAppDeletionDialogClosed const userChangedUrl: CaseReducer<UserChangedUrl> = (state, action) => { if (isTrustedAppsPageLocation(action.payload)) { const parsedUrlsParams = parse(action.payload.search.slice(1)); - const paginationParams = extractListPaginationParams(parsedUrlsParams); - const show = - extractFirstParamValue(parsedUrlsParams, 'show') === 'create' ? 'create' : undefined; + const location = extractTrustedAppsListPageLocation(parsedUrlsParams); return { ...state, - listView: { - ...state.listView, - currentPaginationInfo: { - index: paginationParams.page_index, - size: paginationParams.page_size, - }, - show, - }, - createView: show ? state.createView : undefined, + createView: location.show ? state.createView : undefined, active: true, + location, }; } else { return initialTrustedAppsPageState(); @@ -150,16 +143,16 @@ const initialDeletionDialogState = (): TrustedAppsListPageState['deletionDialog' export const initialTrustedAppsPageState = (): TrustedAppsListPageState => ({ listView: { - currentListResourceState: { type: 'UninitialisedResourceState' }, - currentPaginationInfo: { - index: MANAGEMENT_DEFAULT_PAGE, - size: MANAGEMENT_DEFAULT_PAGE_SIZE, - }, + listResourceState: { type: 'UninitialisedResourceState' }, freshDataTimestamp: Date.now(), - show: undefined, }, deletionDialog: initialDeletionDialogState(), createView: undefined, + location: { + page_index: MANAGEMENT_DEFAULT_PAGE, + page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, + show: undefined, + }, active: false, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts index 0be4d0b05acc44..01fe3e5bf202ea 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts @@ -7,10 +7,10 @@ import { AsyncResourceState, TrustedAppsListPageState } from '../state'; import { initialTrustedAppsPageState } from './reducer'; import { - getCurrentListResourceState, + getListResourceState, getLastLoadedListResourceState, - getListCurrentPageIndex, - getListCurrentPageSize, + getCurrentLocationPageIndex, + getCurrentLocationPageSize, getListErrorMessage, getListItems, getListTotalItemsCount, @@ -25,7 +25,6 @@ import { } from './selectors'; import { - createDefaultListView, createDefaultPaginationInfo, createListComplexLoadingResourceState, createListFailedResourceState, @@ -85,16 +84,17 @@ describe('selectors', () => { it('returns false when current loaded data is up to date', () => { const listView = createLoadedListViewWithPagination(initialNow); + const location = { page_index: 0, page_size: 10 }; - expect(needsRefreshOfListData({ ...initialState, listView, active: true })).toBe(false); + expect(needsRefreshOfListData({ ...initialState, listView, active: true, location })).toBe( + false + ); }); }); - describe('getCurrentListResourceState()', () => { + describe('getListResourceState()', () => { it('returns current list resource state', () => { - const state = { ...initialState, listView: createDefaultListView(initialNow) }; - - expect(getCurrentListResourceState(state)).toStrictEqual(createUninitialisedResourceState()); + expect(getListResourceState(initialState)).toStrictEqual(createUninitialisedResourceState()); }); }); @@ -103,14 +103,12 @@ describe('selectors', () => { const state = { ...initialState, listView: { - currentListResourceState: createListComplexLoadingResourceState( + listResourceState: createListComplexLoadingResourceState( createDefaultPaginationInfo(), 200, initialNow ), - currentPaginationInfo: createDefaultPaginationInfo(), freshDataTimestamp: initialNow, - show: undefined, }, }; @@ -122,23 +120,19 @@ describe('selectors', () => { describe('getListItems()', () => { it('returns empty list when no valid data loaded', () => { - const state = { ...initialState, listView: createDefaultListView(initialNow) }; - - expect(getListItems(state)).toStrictEqual([]); + expect(getListItems(initialState)).toStrictEqual([]); }); it('returns last loaded list items', () => { const state = { ...initialState, listView: { - currentListResourceState: createListComplexLoadingResourceState( + listResourceState: createListComplexLoadingResourceState( createDefaultPaginationInfo(), 200, initialNow ), - currentPaginationInfo: createDefaultPaginationInfo(), freshDataTimestamp: initialNow, - show: undefined, }, }; @@ -150,23 +144,19 @@ describe('selectors', () => { describe('getListTotalItemsCount()', () => { it('returns 0 when no valid data loaded', () => { - const state = { ...initialState, listView: createDefaultListView(initialNow) }; - - expect(getListTotalItemsCount(state)).toBe(0); + expect(getListTotalItemsCount(initialState)).toBe(0); }); it('returns last loaded total items count', () => { const state = { ...initialState, listView: { - currentListResourceState: createListComplexLoadingResourceState( + listResourceState: createListComplexLoadingResourceState( createDefaultPaginationInfo(), 200, initialNow ), - currentPaginationInfo: createDefaultPaginationInfo(), freshDataTimestamp: initialNow, - show: undefined, }, }; @@ -176,17 +166,17 @@ describe('selectors', () => { describe('getListCurrentPageIndex()', () => { it('returns page index', () => { - const state = { ...initialState, listView: createDefaultListView(initialNow) }; + const state = { ...initialState, location: { page_index: 3, page_size: 10 } }; - expect(getListCurrentPageIndex(state)).toBe(0); + expect(getCurrentLocationPageIndex(state)).toBe(3); }); }); describe('getListCurrentPageSize()', () => { it('returns page size', () => { - const state = { ...initialState, listView: createDefaultListView(initialNow) }; + const state = { ...initialState, location: { page_index: 0, page_size: 20 } }; - expect(getListCurrentPageSize(state)).toBe(20); + expect(getCurrentLocationPageSize(state)).toBe(20); }); }); @@ -195,14 +185,12 @@ describe('selectors', () => { const state = { ...initialState, listView: { - currentListResourceState: createListComplexLoadingResourceState( + listResourceState: createListComplexLoadingResourceState( createDefaultPaginationInfo(), 200, initialNow ), - currentPaginationInfo: createDefaultPaginationInfo(), freshDataTimestamp: initialNow, - show: undefined, }, }; @@ -213,10 +201,8 @@ describe('selectors', () => { const state = { ...initialState, listView: { - currentListResourceState: createListFailedResourceState('Internal Server Error'), - currentPaginationInfo: createDefaultPaginationInfo(), + listResourceState: createListFailedResourceState('Internal Server Error'), freshDataTimestamp: initialNow, - show: undefined, }, }; @@ -233,14 +219,12 @@ describe('selectors', () => { const state = { ...initialState, listView: { - currentListResourceState: createListComplexLoadingResourceState( + listResourceState: createListComplexLoadingResourceState( createDefaultPaginationInfo(), 200, initialNow ), - currentPaginationInfo: createDefaultPaginationInfo(), freshDataTimestamp: initialNow, - show: undefined, }, }; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts index 6239b425efe2fc..62ffa364e4a6c8 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createSelector } from 'reselect'; import { ServerApiError } from '../../../../common/types'; import { Immutable, NewTrustedApp, TrustedApp } from '../../../../../common/endpoint/types'; @@ -17,97 +16,76 @@ import { isLoadingResourceState, isOutdatedResourceState, LoadedResourceState, - PaginationInfo, TrustedAppCreateFailure, TrustedAppsListData, + TrustedAppsListPageLocation, TrustedAppsListPageState, } from '../state'; -import { TrustedAppsUrlParams } from '../types'; import { isTrustedAppCreateFailureState, isTrustedAppCreatePendingState, isTrustedAppCreateSuccessState, } from '../state/type_guards'; -const pageInfosEqual = (pageInfo1: PaginationInfo, pageInfo2: PaginationInfo): boolean => - pageInfo1.index === pageInfo2.index && pageInfo1.size === pageInfo2.size; - export const needsRefreshOfListData = (state: Immutable<TrustedAppsListPageState>): boolean => { - const currentPageInfo = state.listView.currentPaginationInfo; - const currentPage = state.listView.currentListResourceState; const freshDataTimestamp = state.listView.freshDataTimestamp; + const currentPage = state.listView.listResourceState; + const location = state.location; return ( state.active && isOutdatedResourceState(currentPage, (data) => { return ( - pageInfosEqual(currentPageInfo, data.paginationInfo) && data.timestamp >= freshDataTimestamp + data.paginationInfo.index === location.page_index && + data.paginationInfo.size === location.page_size && + data.timestamp >= freshDataTimestamp ); }) ); }; -export const getCurrentListResourceState = ( +export const getListResourceState = ( state: Immutable<TrustedAppsListPageState> ): Immutable<AsyncResourceState<TrustedAppsListData>> | undefined => { - return state.listView.currentListResourceState; + return state.listView.listResourceState; }; export const getLastLoadedListResourceState = ( state: Immutable<TrustedAppsListPageState> ): Immutable<LoadedResourceState<TrustedAppsListData>> | undefined => { - return getLastLoadedResourceState(state.listView.currentListResourceState); + return getLastLoadedResourceState(state.listView.listResourceState); }; export const getListItems = ( state: Immutable<TrustedAppsListPageState> ): Immutable<TrustedApp[]> => { - return getLastLoadedResourceState(state.listView.currentListResourceState)?.data.items || []; + return getLastLoadedResourceState(state.listView.listResourceState)?.data.items || []; }; -export const getListCurrentPageIndex = (state: Immutable<TrustedAppsListPageState>): number => { - return state.listView.currentPaginationInfo.index; +export const getCurrentLocationPageIndex = (state: Immutable<TrustedAppsListPageState>): number => { + return state.location.page_index; }; -export const getListCurrentPageSize = (state: Immutable<TrustedAppsListPageState>): number => { - return state.listView.currentPaginationInfo.size; +export const getCurrentLocationPageSize = (state: Immutable<TrustedAppsListPageState>): number => { + return state.location.page_size; }; export const getListTotalItemsCount = (state: Immutable<TrustedAppsListPageState>): number => { - return ( - getLastLoadedResourceState(state.listView.currentListResourceState)?.data.totalItemsCount || 0 - ); -}; - -export const getListCurrentShowValue: ( - state: Immutable<TrustedAppsListPageState> -) => TrustedAppsListPageState['listView']['show'] = (state) => { - return state.listView.show; + return getLastLoadedResourceState(state.listView.listResourceState)?.data.totalItemsCount || 0; }; -export const getListUrlSearchParams: ( +export const getCurrentLocation = ( state: Immutable<TrustedAppsListPageState> -) => TrustedAppsUrlParams = createSelector( - getListCurrentPageIndex, - getListCurrentPageSize, - getListCurrentShowValue, - (pageIndex, pageSize, showValue) => { - return { - page_index: pageIndex, - page_size: pageSize, - show: showValue, - }; - } -); +): TrustedAppsListPageLocation => state.location; export const getListErrorMessage = ( state: Immutable<TrustedAppsListPageState> ): string | undefined => { - return getCurrentResourceError(state.listView.currentListResourceState)?.message; + return getCurrentResourceError(state.listView.listResourceState)?.message; }; export const isListLoading = (state: Immutable<TrustedAppsListPageState>): boolean => { - return isLoadingResourceState(state.listView.currentListResourceState); + return isLoadingResourceState(state.listView.listResourceState); }; export const isDeletionDialogOpen = (state: Immutable<TrustedAppsListPageState>): boolean => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts index 020a87f526e52e..c23b6ceae7b078 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/test_utils/index.ts @@ -5,11 +5,12 @@ */ import { combineReducers, createStore } from 'redux'; -import { ServerApiError } from '../../../../common/types'; import { TrustedApp } from '../../../../../common/endpoint/types'; import { RoutingAction } from '../../../../common/store/routing'; import { + MANAGEMENT_DEFAULT_PAGE, + MANAGEMENT_DEFAULT_PAGE_SIZE, MANAGEMENT_STORE_GLOBAL_NAMESPACE, MANAGEMENT_STORE_TRUSTED_APPS_NAMESPACE, } from '../../../common/constants'; @@ -105,54 +106,22 @@ export const createListComplexLoadingResourceState = ( ) ); -export const createDefaultPaginationInfo = () => ({ index: 0, size: 20 }); - -export const createDefaultListView = ( - freshDataTimestamp: number -): TrustedAppsListPageState['listView'] => ({ - currentListResourceState: createUninitialisedResourceState(), - currentPaginationInfo: createDefaultPaginationInfo(), - freshDataTimestamp, - show: undefined, -}); - -export const createLoadingListViewWithPagination = ( - freshDataTimestamp: number, - currentPaginationInfo: PaginationInfo, - previousState: StaleResourceState<TrustedAppsListData> = createUninitialisedResourceState() -): TrustedAppsListPageState['listView'] => ({ - currentListResourceState: { type: 'LoadingResourceState', previousState }, - currentPaginationInfo, - freshDataTimestamp, - show: undefined, +export const createDefaultPaginationInfo = () => ({ + index: MANAGEMENT_DEFAULT_PAGE, + size: MANAGEMENT_DEFAULT_PAGE_SIZE, }); export const createLoadedListViewWithPagination = ( freshDataTimestamp: number, paginationInfo: PaginationInfo = createDefaultPaginationInfo(), - currentPaginationInfo: PaginationInfo = createDefaultPaginationInfo(), totalItemsCount: number = 200 ): TrustedAppsListPageState['listView'] => ({ - currentListResourceState: createListLoadedResourceState( + listResourceState: createListLoadedResourceState( paginationInfo, totalItemsCount, freshDataTimestamp ), - currentPaginationInfo, - freshDataTimestamp, - show: undefined, -}); - -export const createFailedListViewWithPagination = ( - freshDataTimestamp: number, - currentPaginationInfo: PaginationInfo, - error: ServerApiError, - lastLoadedState?: LoadedResourceState<TrustedAppsListData> -): TrustedAppsListPageState['listView'] => ({ - currentListResourceState: { type: 'FailedResourceState', error, lastLoadedState }, - currentPaginationInfo, freshDataTimestamp, - show: undefined, }); export const createUserChangedUrlAction = (path: string, search: string = ''): RoutingAction => { diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx index d0c1fb477ea46e..ae1f314842aab1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_list.tsx @@ -23,8 +23,8 @@ import { MANAGEMENT_PAGE_SIZE_OPTIONS } from '../../../common/constants'; import { getTrustedAppsListPath } from '../../../common/routing'; import { - getListCurrentPageIndex, - getListCurrentPageSize, + getCurrentLocationPageIndex, + getCurrentLocationPageSize, getListErrorMessage, getListItems, getListTotalItemsCount, @@ -149,8 +149,8 @@ const getColumnDefinitions = (context: TrustedAppsListContext): ColumnsList => { export const TrustedAppsList = memo(() => { const [detailsMap, setDetailsMap] = useState<DetailsMap>({}); - const pageIndex = useTrustedAppsSelector(getListCurrentPageIndex); - const pageSize = useTrustedAppsSelector(getListCurrentPageSize); + const pageIndex = useTrustedAppsSelector(getCurrentLocationPageIndex); + const pageSize = useTrustedAppsSelector(getCurrentLocationPageSize); const totalItemCount = useTrustedAppsSelector(getListTotalItemsCount); const listItems = useTrustedAppsSelector(getListItems); const dispatch = useDispatch(); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index 878818d9b77fe6..d63cda5b513dc1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -15,27 +15,26 @@ import { TrustedAppsNotifications } from './trusted_apps_notifications'; import { CreateTrustedAppFlyout } from './components/create_trusted_app_flyout'; import { getTrustedAppsListPath } from '../../../common/routing'; import { useTrustedAppsSelector } from './hooks'; -import { getListCurrentShowValue, getListUrlSearchParams } from '../store/selectors'; +import { getCurrentLocation } from '../store/selectors'; import { TrustedAppsListPageRouteState } from '../../../../../common/endpoint/types'; import { useNavigateToAppEventHandler } from '../../../../common/hooks/endpoint/use_navigate_to_app_event_handler'; export const TrustedAppsPage = memo(() => { const history = useHistory(); const { state: routeState } = useLocation<TrustedAppsListPageRouteState | undefined>(); - const urlParams = useTrustedAppsSelector(getListUrlSearchParams); - const showAddFlout = useTrustedAppsSelector(getListCurrentShowValue) === 'create'; + const location = useTrustedAppsSelector(getCurrentLocation); const handleAddButtonClick = useCallback(() => { history.push( getTrustedAppsListPath({ - ...urlParams, + ...location, show: 'create', }) ); - }, [history, urlParams]); + }, [history, location]); const handleAddFlyoutClose = useCallback(() => { - const { show, ...paginationParamsOnly } = urlParams; + const { show, ...paginationParamsOnly } = location; history.push(getTrustedAppsListPath(paginationParamsOnly)); - }, [history, urlParams]); + }, [history, location]); const backButton = useMemo(() => { if (routeState && routeState.onBackButtonNavigateTo) { @@ -50,7 +49,7 @@ export const TrustedAppsPage = memo(() => { <EuiButton fill iconType="plusInCircle" - isDisabled={showAddFlout} + isDisabled={location.show === 'create'} onClick={handleAddButtonClick} data-test-subj="trustedAppsListAddButton" > @@ -82,7 +81,7 @@ export const TrustedAppsPage = memo(() => { > <TrustedAppsNotifications /> <TrustedAppDeletionDialog /> - {showAddFlout && ( + {location.show === 'create' && ( <CreateTrustedAppFlyout onClose={handleAddFlyoutClose} size="s" From 46af5fcc2d996e003a9d078361e498b442e67c72 Mon Sep 17 00:00:00 2001 From: Marshall Main <55718608+marshallmain@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:10:38 -0400 Subject: [PATCH 082/128] [Security Solution][Detections] Enrich shell signals with fields common to all building blocks (#79130) * Enrich shell signals with fields common to all building blocks * PR comments + additional unit test --- .../signals/build_bulk_body.test.ts | 448 +++++++++++++++++- .../signals/build_bulk_body.ts | 53 +++ 2 files changed, 498 insertions(+), 3 deletions(-) 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 2f7dd22c0c78e7..75a7de8cd2c443 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 @@ -11,8 +11,15 @@ import { sampleIdGuid, sampleDocWithAncestors, sampleRuleSO, + sampleDocNoSortIdNoVersion, } from './__mocks__/es_results'; -import { buildBulkBody, buildSignalFromSequence, buildSignalFromEvent } from './build_bulk_body'; +import { + buildBulkBody, + buildSignalFromSequence, + buildSignalFromEvent, + objectPairIntersection, + objectArrayIntersection, +} from './build_bulk_body'; import { SignalHit } from './types'; import { getListArrayMock } from '../../../../common/detection_engine/schemas/types/lists.mock'; @@ -438,13 +445,20 @@ describe('buildBulkBody', () => { describe('buildSignalFromSequence', () => { test('builds a basic signal from a sequence of building blocks', () => { - const blocks = [sampleDocWithAncestors().hits.hits[0], sampleDocWithAncestors().hits.hits[0]]; + const block1 = sampleDocWithAncestors().hits.hits[0]; + block1._source.new_key = 'new_key_value'; + block1._source.new_key2 = 'new_key2_value'; + const block2 = sampleDocWithAncestors().hits.hits[0]; + block2._source.new_key = 'new_key_value'; + const blocks = [block1, block2]; const ruleSO = sampleRuleSO(); const signal = buildSignalFromSequence(blocks, ruleSO); // Timestamp will potentially always be different so remove it for the test // @ts-expect-error delete signal['@timestamp']; - const expected: Omit<SignalHit, '@timestamp'> = { + const expected: Omit<SignalHit, '@timestamp'> & { someKey: string; new_key: string } = { + someKey: 'someValue', + new_key: 'new_key_value', event: { kind: 'signal', }, @@ -539,6 +553,96 @@ describe('buildSignalFromSequence', () => { }; expect(signal).toEqual(expected); }); + + test('builds a basic signal if there is no overlap between source events', () => { + const block1 = sampleDocNoSortIdNoVersion(); + const block2 = sampleDocNoSortIdNoVersion(); + block2._source['@timestamp'] = '2021-05-20T22:28:46+0000'; + block2._source.someKey = 'someOtherValue'; + const ruleSO = sampleRuleSO(); + const signal = buildSignalFromSequence([block1, block2], ruleSO); + // Timestamp will potentially always be different so remove it for the test + // @ts-expect-error + delete signal['@timestamp']; + const expected: Omit<SignalHit, '@timestamp'> = { + event: { + kind: 'signal', + }, + signal: { + parents: [ + { + id: sampleIdGuid, + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + { + id: sampleIdGuid, + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + ancestors: [ + { + id: sampleIdGuid, + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + { + id: sampleIdGuid, + type: 'event', + index: 'myFakeSignalIndex', + depth: 0, + }, + ], + status: 'open', + rule: { + actions: [], + author: ['Elastic'], + building_block_type: 'default', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + rule_id: 'rule-1', + false_positives: [], + max_signals: 10000, + risk_score: 50, + risk_score_mapping: [], + output_index: '.siem-signals', + description: 'Detecting root and admin users', + from: 'now-6m', + immutable: false, + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + language: 'kuery', + license: 'Elastic License', + name: 'rule-name', + query: 'user.name: root or user.name: admin', + references: ['http://google.com'], + severity: 'high', + severity_mapping: [], + tags: ['some fake tag 1', 'some fake tag 2'], + threat: [], + type: 'query', + to: 'now', + note: '', + enabled: true, + created_by: 'sample user', + updated_by: 'sample user', + version: 1, + updated_at: ruleSO.updated_at ?? '', + created_at: ruleSO.attributes.createdAt, + throttle: 'no_actions', + exceptions_list: getListArrayMock(), + }, + depth: 1, + group: { + id: '269c1f5754bff92fb8040283b687258e99b03e8b2ab1262cc20c82442e5de5ea', + }, + }, + }; + expect(signal).toEqual(expected); + }); }); describe('buildSignalFromEvent', () => { @@ -632,3 +736,341 @@ describe('buildSignalFromEvent', () => { expect(signal).toEqual(expected); }); }); + +describe('recursive intersection between objects', () => { + test('should treat numbers and strings as unequal', () => { + const a = { + field1: 1, + field2: 1, + }; + const b = { + field1: 1, + field2: '1', + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field1: 1, + }; + expect(intersection).toEqual(expected); + }); + + test('should strip unequal numbers and strings', () => { + const a = { + field1: 1, + field2: 1, + field3: 'abcd', + field4: 'abcd', + }; + const b = { + field1: 1, + field2: 100, + field3: 'abcd', + field4: 'wxyz', + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field1: 1, + field3: 'abcd', + }; + expect(intersection).toEqual(expected); + }); + + test('should handle null values', () => { + const a = { + field1: 1, + field2: '1', + field3: null, + }; + const b = { + field1: null, + field2: null, + field3: null, + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field3: null, + }; + expect(intersection).toEqual(expected); + }); + + test('should handle explicit undefined values and return undefined if left with only undefined fields', () => { + const a = { + field1: 1, + field2: '1', + field3: undefined, + }; + const b = { + field1: undefined, + field2: undefined, + field3: undefined, + }; + const intersection = objectPairIntersection(a, b); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + + test('should strip arrays out regardless of whether they are equal', () => { + const a = { + array_field1: [1, 2], + array_field2: [1, 2], + }; + const b = { + array_field1: [1, 2], + array_field2: [3, 4], + }; + const intersection = objectPairIntersection(a, b); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + + test('should strip fields that are not in both objects', () => { + const a = { + field1: 1, + }; + const b = { + field2: 1, + }; + const intersection = objectPairIntersection(a, b); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + + test('should work on objects within objects', () => { + const a = { + container_field: { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + nested_container_field: { + field1: 1, + field2: 1, + }, + nested_container_field2: { + field1: undefined, + }, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + container_field: { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + nested_container_field: { + field1: 1, + field2: 2, + }, + nested_container_field2: { + field1: undefined, + }, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectPairIntersection(a, b); + const expected = { + container_field: { + field1: 1, + field6: null, + nested_container_field: { + field1: 1, + }, + }, + }; + expect(intersection).toEqual(expected); + }); + + test('should work on objects with a variety of fields', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectPairIntersection(a, b); + const expected = { + field1: 1, + field6: null, + container_field: { + sub_field1: 1, + }, + }; + expect(intersection).toEqual(expected); + }); +}); + +describe('objectArrayIntersection', () => { + test('should return undefined if the array is empty', () => { + const intersection = objectArrayIntersection([]); + const expected = undefined; + expect(intersection).toEqual(expected); + }); + test('should return the initial object if there is only 1', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const intersection = objectArrayIntersection([a]); + const expected = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + expect(intersection).toEqual(expected); + }); + test('should work with exactly 2 objects', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectArrayIntersection([a, b]); + const expected = { + field1: 1, + field6: null, + container_field: { + sub_field1: 1, + }, + }; + expect(intersection).toEqual(expected); + }); + + test('should work with 3 or more objects', () => { + const a = { + field1: 1, + field2: 1, + field3: 10, + field5: 1, + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 1, + sub_field3: 10, + }, + container_field_without_intersection: { + sub_field1: 1, + }, + }; + const b = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + field6: null, + array_field: [1, 2], + container_field: { + sub_field1: 1, + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const c = { + field1: 1, + field2: 2, + field4: 10, + field5: '1', + array_field: [1, 2], + container_field: { + sub_field2: 2, + sub_field4: 10, + }, + container_field_without_intersection: { + sub_field2: 1, + }, + }; + const intersection = objectArrayIntersection([a, b, c]); + const expected = { + field1: 1, + }; + expect(intersection).toEqual(expected); + }); +}); 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 f8632a85c77e9e..8e9571fe8a445d 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 @@ -129,7 +129,9 @@ export const buildSignalFromSequence = ( ): SignalHit => { const rule = buildRuleWithoutOverrides(ruleSO); const signal: Signal = buildSignal(events, rule); + const mergedEvents = objectArrayIntersection(events.map((event) => event._source)); return { + ...mergedEvents, '@timestamp': new Date().toISOString(), event: { kind: 'signal', @@ -167,3 +169,54 @@ export const buildSignalFromEvent = ( }; return signalHit; }; + +export const objectArrayIntersection = (objects: object[]) => { + if (objects.length === 0) { + return undefined; + } else if (objects.length === 1) { + return objects[0]; + } else { + return objects + .slice(1) + .reduce( + (acc: object | undefined, obj): object | undefined => objectPairIntersection(acc, obj), + objects[0] + ); + } +}; + +export const objectPairIntersection = (a: object | undefined, b: object | undefined) => { + if (a === undefined || b === undefined) { + return undefined; + } + const intersection: Record<string, unknown> = {}; + Object.entries(a).forEach(([key, aVal]) => { + if (key in b) { + const bVal = (b as Record<string, unknown>)[key]; + if ( + typeof aVal === 'object' && + !(aVal instanceof Array) && + aVal !== null && + typeof bVal === 'object' && + !(bVal instanceof Array) && + bVal !== null + ) { + intersection[key] = objectPairIntersection(aVal, bVal); + } else if (aVal === bVal) { + intersection[key] = aVal; + } + } + }); + // Count up the number of entries that are NOT undefined in the intersection + // If there are no keys OR all entries are undefined, return undefined + if ( + Object.values(intersection).reduce( + (acc: number, value) => (value !== undefined ? acc + 1 : acc), + 0 + ) === 0 + ) { + return undefined; + } else { + return intersection; + } +}; From f398b492002dd33c0fc4f764be7a1e6d0d81c6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= <alejandro.haro@elastic.co> Date: Fri, 2 Oct 2020 16:03:42 +0100 Subject: [PATCH 083/128] [Usage Collection] [schema] `actions` (#78832) --- x-pack/.telemetryrc.json | 1 - .../server/usage/actions_usage_collector.ts | 23 +++++- .../schema/xpack_plugins.json | 78 +++++++++++++++++++ 3 files changed, 100 insertions(+), 2 deletions(-) diff --git a/x-pack/.telemetryrc.json b/x-pack/.telemetryrc.json index c7430666c538f6..db50727c599a9d 100644 --- a/x-pack/.telemetryrc.json +++ b/x-pack/.telemetryrc.json @@ -2,7 +2,6 @@ "output": "plugins/telemetry_collection_xpack/schema/xpack_plugins.json", "root": "plugins/", "exclude": [ - "plugins/actions/server/usage/actions_usage_collector.ts", "plugins/alerts/server/usage/alerts_usage_collector.ts", "plugins/apm/server/lib/apm_telemetry/index.ts" ] 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 aa546e08ea1ba0..fac57b6282c445 100644 --- a/x-pack/plugins/actions/server/usage/actions_usage_collector.ts +++ b/x-pack/plugins/actions/server/usage/actions_usage_collector.ts @@ -4,11 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { MakeSchemaFrom, UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { get } from 'lodash'; import { TaskManagerStartContract } from '../../../task_manager/server'; import { ActionsUsage } from './types'; +const byTypeSchema: MakeSchemaFrom<ActionsUsage>['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' }, + '__server-log': { type: 'long' }, + __slack: { type: 'long' }, + __webhook: { type: 'long' }, + __servicenow: { type: 'long' }, + __jira: { type: 'long' }, + __resilient: { type: 'long' }, +}; + export function createActionsUsageCollector( usageCollection: UsageCollectionSetup, taskManager: TaskManagerStartContract @@ -16,6 +31,12 @@ export function createActionsUsageCollector( return usageCollection.makeUsageCollector<ActionsUsage>({ type: 'actions', isReady: () => true, + schema: { + count_total: { type: 'long' }, + count_active_total: { type: 'long' }, + count_by_type: byTypeSchema, + count_active_by_type: byTypeSchema, + }, fetch: async () => { try { const doc = await getLatestTaskState(await taskManager); 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 b08585066f100a..bdafbfd8ec967c 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -1,5 +1,83 @@ { "properties": { + "actions": { + "properties": { + "count_total": { + "type": "long" + }, + "count_active_total": { + "type": "long" + }, + "count_by_type": { + "properties": { + "DYNAMIC_KEY": { + "type": "long" + }, + "__email": { + "type": "long" + }, + "__index": { + "type": "long" + }, + "__pagerduty": { + "type": "long" + }, + "__server-log": { + "type": "long" + }, + "__slack": { + "type": "long" + }, + "__webhook": { + "type": "long" + }, + "__servicenow": { + "type": "long" + }, + "__jira": { + "type": "long" + }, + "__resilient": { + "type": "long" + } + } + }, + "count_active_by_type": { + "properties": { + "DYNAMIC_KEY": { + "type": "long" + }, + "__email": { + "type": "long" + }, + "__index": { + "type": "long" + }, + "__pagerduty": { + "type": "long" + }, + "__server-log": { + "type": "long" + }, + "__slack": { + "type": "long" + }, + "__webhook": { + "type": "long" + }, + "__servicenow": { + "type": "long" + }, + "__jira": { + "type": "long" + }, + "__resilient": { + "type": "long" + } + } + } + } + }, "canvas": { "properties": { "workpads": { From fccfad24cb7856f0a871381524e3e404dedfa839 Mon Sep 17 00:00:00 2001 From: Marta Bondyra <marta.bondyra@gmail.com> Date: Fri, 2 Oct 2020 17:18:20 +0200 Subject: [PATCH 084/128] [Lens] remove test warnings about improper HTML structure (#79251) * [Lens] remove test warnings about improper HTML structure --- .../workspace_panel/workspace_panel.tsx | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx index 2a5798ac6a70c0..3993b4ffc02b0c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_panel.tsx @@ -208,27 +208,22 @@ export function InnerWorkspacePanel({ > <h2> <strong> - {expression === null ? ( - <FormattedMessage - id="xpack.lens.editorFrame.emptyWorkspace" - defaultMessage="Drop some fields here to start" - /> - ) : ( - <FormattedMessage - id="xpack.lens.editorFrame.emptyWorkspaceSimple" - defaultMessage="Drop field here" - /> - )} + {expression === null + ? i18n.translate('xpack.lens.editorFrame.emptyWorkspace', { + defaultMessage: 'Drop some fields here to start', + }) + : i18n.translate('xpack.lens.editorFrame.emptyWorkspaceSimple', { + defaultMessage: 'Drop field here', + })} </strong> </h2> <DropIllustration aria-hidden={true} className="lnsWorkspacePanel__dropIllustration" /> {expression === null && ( <> <p> - <FormattedMessage - id="xpack.lens.editorFrame.emptyWorkspaceHeading" - defaultMessage="Lens is a new tool for creating visualizations" - /> + {i18n.translate('xpack.lens.editorFrame.emptyWorkspaceHeading', { + defaultMessage: 'Lens is a new tool for creating visualization', + })} </p> <p> <small> @@ -237,10 +232,9 @@ export function InnerWorkspacePanel({ target="_blank" external > - <FormattedMessage - id="xpack.lens.editorFrame.goToForums" - defaultMessage="Make requests and give feedback" - /> + {i18n.translate('xpack.lens.editorFrame.goToForums', { + defaultMessage: 'Make requests and give feedback', + })} </EuiLink> </small> </p> From 6364c14ffd99fc86cf257beaea81e56e04a41c68 Mon Sep 17 00:00:00 2001 From: Devon Thomson <devon.thomson@hotmail.com> Date: Fri, 2 Oct 2020 12:31:08 -0400 Subject: [PATCH 085/128] Panel Description Tooltip Design Change (#79213) * wrapped Embeddable Panel title in EuiTooltip and centered description icon --- .../public/lib/panel/_embeddable_panel.scss | 5 ++ .../lib/panel/panel_header/panel_header.tsx | 55 ++++++++++--------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss index 36a7fee14cce1e..cdc0f9f0e04513 100644 --- a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss +++ b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss @@ -54,9 +54,14 @@ .embPanel__titleInner { overflow: hidden; display: flex; + align-items: center; padding-right: $euiSizeS; } + .embPanel__titleTooltipAnchor { + max-width: 100%; + } + .embPanel__titleText { @include euiTextTruncate; } diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx index c538b98949a43e..ea6a6a78c2b677 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx @@ -99,16 +99,6 @@ function renderNotifications( }); } -function renderTooltip(description: string) { - return ( - description !== '' && ( - <EuiToolTip content={description} delay="regular" position="right"> - <EuiIcon type="iInCircle" /> - </EuiToolTip> - ) - ); -} - type EmbeddableWithDescription = IEmbeddable & { getDescription: () => string }; function getViewDescription(embeddable: IEmbeddable | EmbeddableWithDescription) { @@ -134,9 +124,10 @@ export function PanelHeader({ embeddable, headerId, }: PanelHeaderProps) { - const viewDescription = getViewDescription(embeddable); - const showTitle = !hidePanelTitle && (!isViewMode || title || viewDescription !== ''); - const showPanelBar = !isViewMode || badges.length > 0 || notifications.length > 0 || showTitle; + const description = getViewDescription(embeddable); + const showTitle = !hidePanelTitle && (!isViewMode || title); + const showPanelBar = + !isViewMode || badges.length > 0 || notifications.length > 0 || showTitle || description; const classes = classNames('embPanel__header', { // eslint-disable-next-line @typescript-eslint/naming-convention 'embPanel__header--floater': !showPanelBar, @@ -174,26 +165,36 @@ export function PanelHeader({ ); } + const renderTitle = () => { + const titleComponent = showTitle ? ( + <span className={title ? 'embPanel__titleText' : 'embPanel__placeholderTitleText'}> + {title || placeholderTitle} + </span> + ) : undefined; + return description ? ( + <EuiToolTip + content={description} + delay="regular" + position="top" + anchorClassName="embPanel__titleTooltipAnchor" + > + <span className="embPanel__titleInner"> + {titleComponent} <EuiIcon type="iInCircle" color="subdued" /> + </span> + </EuiToolTip> + ) : ( + titleComponent + ); + }; + return ( <figcaption className={classes} data-test-subj={`embeddablePanelHeading-${(title || '').replace(/\s/g, '')}`} > <h2 data-test-subj="dashboardPanelTitle" className="embPanel__title embPanel__dragger"> - {showTitle ? ( - <span className="embPanel__titleInner"> - <span - className={title ? 'embPanel__titleText' : 'embPanel__placeholderTitleText'} - aria-hidden="true" - > - {title || placeholderTitle} - </span> - <EuiScreenReaderOnly>{getAriaLabel()}</EuiScreenReaderOnly> - {renderTooltip(viewDescription)} - </span> - ) : ( - <EuiScreenReaderOnly>{getAriaLabel()}</EuiScreenReaderOnly> - )} + <EuiScreenReaderOnly>{getAriaLabel()}</EuiScreenReaderOnly> + {renderTitle()} {renderBadges(badges, embeddable)} </h2> {renderNotifications(notifications, embeddable)} From d67962453224404647e4859ba6c4f996e64f2e48 Mon Sep 17 00:00:00 2001 From: Marco Liberati <dej611@users.noreply.github.com> Date: Fri, 2 Oct 2020 18:41:40 +0200 Subject: [PATCH 086/128] [Lens] Fix open custom ranges saved issue (#78915) Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../definitions/ranges/advanced_editor.tsx | 4 +- .../definitions/ranges/ranges.test.tsx | 42 ++++++++++++++++++- .../operations/definitions/ranges/ranges.tsx | 19 ++++++--- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index a6756df403ba7c..16b861ae034fa0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -132,11 +132,11 @@ export const RangePopover = ({ </EuiFlexItem> <EuiFlexItem> <EuiFieldNumber - value={isFinite(to) ? Number(to) : ''} + value={isValidNumber(to) ? Number(to) : ''} onChange={({ target }) => { const newRange = { ...tempRange, - to: target.value !== '' ? Number(target.value) : -Infinity, + to: target.value !== '' ? Number(target.value) : Infinity, }; setTempRange(newRange); saveRangeAndReset(newRange); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index 2409406afcdbc2..fb6cf6df8573f8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -485,7 +485,7 @@ describe('ranges', () => { /> ); - // This series of act clojures are made to make it work properly the update flush + // This series of act closures are made to make it work properly the update flush act(() => { instance.find(RangePopover).find(EuiLink).prop('onClick')!({} as ReactMouseEvent); }); @@ -550,6 +550,46 @@ describe('ranges', () => { expect(instance.find(RangePopover)).toHaveLength(1); }); }); + + it('should handle correctly open ranges when saved', () => { + const setStateSpy = jest.fn(); + + // Add an extra open range: + (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.ranges.push({ + from: null, + to: null, + label: '', + }); + + const instance = mount( + <InlineOptions + {...defaultOptions} + state={state} + setState={setStateSpy} + columnId="col1" + currentColumn={state.layers.first.columns.col1 as RangeIndexPatternColumn} + layerId="first" + /> + ); + + act(() => { + instance.find(RangePopover).last().find(EuiLink).prop('onClick')!({} as ReactMouseEvent); + }); + + act(() => { + // need another wrapping for this in order to work + instance.update(); + + // Check UI values for open ranges + expect( + instance.find(RangePopover).last().find(EuiFieldNumber).first().prop('value') + ).toBe(''); + + expect(instance.find(RangePopover).last().find(EuiFieldNumber).last().prop('value')).toBe( + '' + ); + }); + }); }); }); }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index a59780ef59939e..a8304456262eb5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -16,7 +16,13 @@ import { updateColumnParam, changeColumn } from '../../../state_helpers'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; type RangeType = Omit<Range, 'type'>; -export type RangeTypeLens = RangeType & { label: string }; +// Try to cover all possible serialized states for ranges +export type RangeTypeLens = (RangeType | { from: Range['from'] | null; to: Range['to'] | null }) & { + label: string; +}; + +// This is a subset of RangeTypeLens which has both from and to defined +type FullRangeTypeLens = Extract<RangeTypeLens, NonNullable<RangeType>>; export type MODES_TYPES = typeof MODES[keyof typeof MODES]; @@ -35,10 +41,13 @@ export type UpdateParamsFnType = <K extends keyof RangeColumnParams>( value: RangeColumnParams[K] ) => void; -export const isValidNumber = (value: number | '') => - value !== '' && !isNaN(value) && isFinite(value); -export const isRangeWithin = (range: RangeTypeLens): boolean => range.from <= range.to; -const isFullRange = ({ from, to }: RangeType) => isValidNumber(from) && isValidNumber(to); +// on initialization values can be null (from the Infinity serialization), so handle it correctly +// or they will be casted to 0 by the editor ( see #78867 ) +export const isValidNumber = (value: number | '' | null): value is number => + value != null && value !== '' && !isNaN(value) && isFinite(value); +export const isRangeWithin = (range: RangeType): boolean => range.from <= range.to; +const isFullRange = (range: RangeTypeLens): range is FullRangeTypeLens => + isValidNumber(range.from) && isValidNumber(range.to); export const isValidRange = (range: RangeTypeLens): boolean => { if (isFullRange(range)) { return isRangeWithin(range); From 7afb8b4d7b2813e6235bcb164bbebc4c30f1d43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= <alejandro.haro@elastic.co> Date: Fri, 2 Oct 2020 17:45:47 +0100 Subject: [PATCH 087/128] [Usage Collection] [schema] `alerts` (#78933) --- x-pack/.telemetryrc.json | 1 - .../server/usage/alerts_usage_collector.ts | 57 +++++- .../schema/xpack_plugins.json | 192 ++++++++++++++++++ 3 files changed, 248 insertions(+), 2 deletions(-) diff --git a/x-pack/.telemetryrc.json b/x-pack/.telemetryrc.json index db50727c599a9d..706decfc93e9cb 100644 --- a/x-pack/.telemetryrc.json +++ b/x-pack/.telemetryrc.json @@ -2,7 +2,6 @@ "output": "plugins/telemetry_collection_xpack/schema/xpack_plugins.json", "root": "plugins/", "exclude": [ - "plugins/alerts/server/usage/alerts_usage_collector.ts", "plugins/apm/server/lib/apm_telemetry/index.ts" ] } diff --git a/x-pack/plugins/alerts/server/usage/alerts_usage_collector.ts b/x-pack/plugins/alerts/server/usage/alerts_usage_collector.ts index 64d3ad54a23186..de82dd31877afb 100644 --- a/x-pack/plugins/alerts/server/usage/alerts_usage_collector.ts +++ b/x-pack/plugins/alerts/server/usage/alerts_usage_collector.ts @@ -4,11 +4,44 @@ * you may not use this file except in compliance with the Elastic License. */ -import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; +import { MakeSchemaFrom, UsageCollectionSetup } from 'src/plugins/usage_collection/server'; import { get } from 'lodash'; import { TaskManagerStartContract } from '../../../task_manager/server'; import { AlertsUsage } from './types'; +const byTypeSchema: MakeSchemaFrom<AlertsUsage>['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 alerts (searching the use of the alerts API `registerType`: + // Built-in + '__index-threshold': { type: 'long' }, + // APM + apm__error_rate: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + apm__transaction_error_rate: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + apm__transaction_duration: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + apm__transaction_duration_anomaly: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + // Infra + metrics__alert__threshold: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + metrics__alert__inventory__threshold: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + logs__alert__document__count: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + // Monitoring + monitoring_alert_cluster_health: { type: 'long' }, + monitoring_alert_cpu_usage: { type: 'long' }, + monitoring_alert_disk_usage: { type: 'long' }, + monitoring_alert_elasticsearch_version_mismatch: { type: 'long' }, + monitoring_alert_kibana_version_mismatch: { type: 'long' }, + monitoring_alert_license_expiration: { type: 'long' }, + monitoring_alert_logstash_version_mismatch: { type: 'long' }, + monitoring_alert_nodes_changed: { type: 'long' }, + // Security Solution + siem__signals: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + siem__notifications: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + // Uptime + xpack__uptime__alerts__monitorStatus: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + xpack__uptime__alerts__tls: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention + xpack__uptime__alerts__durationAnomaly: { type: 'long' }, // eslint-disable-line @typescript-eslint/naming-convention +}; + export function createAlertsUsageCollector( usageCollection: UsageCollectionSetup, taskManager: TaskManagerStartContract @@ -50,6 +83,28 @@ export function createAlertsUsageCollector( }; } }, + schema: { + count_total: { type: 'long' }, + count_active_total: { type: 'long' }, + count_disabled_total: { type: 'long' }, + throttle_time: { + min: { type: 'long' }, + avg: { type: 'float' }, + max: { type: 'long' }, + }, + schedule_time: { + min: { type: 'long' }, + avg: { type: 'float' }, + max: { type: 'long' }, + }, + connectors_per_alert: { + min: { type: 'long' }, + avg: { type: 'float' }, + max: { type: 'long' }, + }, + count_active_by_type: byTypeSchema, + count_by_type: byTypeSchema, + }, }); } 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 bdafbfd8ec967c..98230f143d3d6b 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -78,6 +78,198 @@ } } }, + "alerts": { + "properties": { + "count_total": { + "type": "long" + }, + "count_active_total": { + "type": "long" + }, + "count_disabled_total": { + "type": "long" + }, + "throttle_time": { + "properties": { + "min": { + "type": "long" + }, + "avg": { + "type": "float" + }, + "max": { + "type": "long" + } + } + }, + "schedule_time": { + "properties": { + "min": { + "type": "long" + }, + "avg": { + "type": "float" + }, + "max": { + "type": "long" + } + } + }, + "connectors_per_alert": { + "properties": { + "min": { + "type": "long" + }, + "avg": { + "type": "float" + }, + "max": { + "type": "long" + } + } + }, + "count_active_by_type": { + "properties": { + "DYNAMIC_KEY": { + "type": "long" + }, + "__index-threshold": { + "type": "long" + }, + "apm__error_rate": { + "type": "long" + }, + "apm__transaction_error_rate": { + "type": "long" + }, + "apm__transaction_duration": { + "type": "long" + }, + "apm__transaction_duration_anomaly": { + "type": "long" + }, + "metrics__alert__threshold": { + "type": "long" + }, + "metrics__alert__inventory__threshold": { + "type": "long" + }, + "logs__alert__document__count": { + "type": "long" + }, + "monitoring_alert_cluster_health": { + "type": "long" + }, + "monitoring_alert_cpu_usage": { + "type": "long" + }, + "monitoring_alert_disk_usage": { + "type": "long" + }, + "monitoring_alert_elasticsearch_version_mismatch": { + "type": "long" + }, + "monitoring_alert_kibana_version_mismatch": { + "type": "long" + }, + "monitoring_alert_license_expiration": { + "type": "long" + }, + "monitoring_alert_logstash_version_mismatch": { + "type": "long" + }, + "monitoring_alert_nodes_changed": { + "type": "long" + }, + "siem__signals": { + "type": "long" + }, + "siem__notifications": { + "type": "long" + }, + "xpack__uptime__alerts__monitorStatus": { + "type": "long" + }, + "xpack__uptime__alerts__tls": { + "type": "long" + }, + "xpack__uptime__alerts__durationAnomaly": { + "type": "long" + } + } + }, + "count_by_type": { + "properties": { + "DYNAMIC_KEY": { + "type": "long" + }, + "__index-threshold": { + "type": "long" + }, + "apm__error_rate": { + "type": "long" + }, + "apm__transaction_error_rate": { + "type": "long" + }, + "apm__transaction_duration": { + "type": "long" + }, + "apm__transaction_duration_anomaly": { + "type": "long" + }, + "metrics__alert__threshold": { + "type": "long" + }, + "metrics__alert__inventory__threshold": { + "type": "long" + }, + "logs__alert__document__count": { + "type": "long" + }, + "monitoring_alert_cluster_health": { + "type": "long" + }, + "monitoring_alert_cpu_usage": { + "type": "long" + }, + "monitoring_alert_disk_usage": { + "type": "long" + }, + "monitoring_alert_elasticsearch_version_mismatch": { + "type": "long" + }, + "monitoring_alert_kibana_version_mismatch": { + "type": "long" + }, + "monitoring_alert_license_expiration": { + "type": "long" + }, + "monitoring_alert_logstash_version_mismatch": { + "type": "long" + }, + "monitoring_alert_nodes_changed": { + "type": "long" + }, + "siem__signals": { + "type": "long" + }, + "siem__notifications": { + "type": "long" + }, + "xpack__uptime__alerts__monitorStatus": { + "type": "long" + }, + "xpack__uptime__alerts__tls": { + "type": "long" + }, + "xpack__uptime__alerts__durationAnomaly": { + "type": "long" + } + } + } + } + }, "canvas": { "properties": { "workpads": { From d9915fdee08e179a11c494b852faa72ead77f33b Mon Sep 17 00:00:00 2001 From: Ryan Keairns <contactryank@gmail.com> Date: Fri, 2 Oct 2020 11:47:20 -0500 Subject: [PATCH 088/128] Re-style and re-order top menu buttons (#79206) * Re-style and re-order top menu buttons * Update snapshot due to removed fill prop * Fix link order for Maps --- .../application/top_nav/get_top_nav_config.ts | 12 +- .../top_nav_menu_item.test.tsx.snap | 1 - .../public/top_nav_menu/_index.scss | 4 + .../public/top_nav_menu/top_nav_menu_item.tsx | 2 +- .../application/utils/get_top_nav_config.tsx | 185 +++++++++--------- .../lens/public/app_plugin/lens_top_nav.tsx | 36 ++-- .../routes/maps_app/top_nav_config.tsx | 125 ++++++------ 7 files changed, 187 insertions(+), 178 deletions(-) diff --git a/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts b/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts index dbdadeb4e4e7c7..77c4a2235d4716 100644 --- a/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts +++ b/src/plugins/dashboard/public/application/top_nav/get_top_nav_config.ts @@ -48,12 +48,12 @@ export function getTopNavConfig( ]; case ViewMode.EDIT: return [ - getCreateNewConfig(actions[TopNavIds.VISUALIZE]), - getSaveConfig(actions[TopNavIds.SAVE]), - getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]), - getAddConfig(actions[TopNavIds.ADD_EXISTING]), getOptionsConfig(actions[TopNavIds.OPTIONS]), getShareConfig(actions[TopNavIds.SHARE]), + getAddConfig(actions[TopNavIds.ADD_EXISTING]), + getViewConfig(actions[TopNavIds.EXIT_EDIT_MODE]), + getSaveConfig(actions[TopNavIds.SAVE]), + getCreateNewConfig(actions[TopNavIds.VISUALIZE]), ]; default: return []; @@ -79,7 +79,9 @@ function getFullScreenConfig(action: NavAction) { */ function getEditConfig(action: NavAction) { return { + emphasize: true, id: 'edit', + iconType: 'pencil', label: i18n.translate('dashboard.topNave.editButtonAriaLabel', { defaultMessage: 'edit', }), @@ -168,7 +170,7 @@ function getAddConfig(action: NavAction) { function getCreateNewConfig(action: NavAction) { return { emphasize: true, - iconType: 'plusInCircle', + iconType: 'plusInCircleFilled', id: 'addNew', label: i18n.translate('dashboard.topNave.addNewButtonAriaLabel', { defaultMessage: 'Create new', diff --git a/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap b/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap index 570699aa0c0e2e..155377e5ea3352 100644 --- a/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap +++ b/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap @@ -2,7 +2,6 @@ exports[`TopNavMenu Should render emphasized item which should be clickable 1`] = ` <EuiButton - fill={true} iconSide="right" iconType="beaker" isDisabled={false} diff --git a/src/plugins/navigation/public/top_nav_menu/_index.scss b/src/plugins/navigation/public/top_nav_menu/_index.scss index 44a76b7700ee00..976ddd789ad221 100644 --- a/src/plugins/navigation/public/top_nav_menu/_index.scss +++ b/src/plugins/navigation/public/top_nav_menu/_index.scss @@ -1,3 +1,7 @@ +.kbnTopNavMenu { + margin-right: $euiSizeXS; +} + .kbnTopNavMenu > * > * { // TEMP fix to adjust spacing between EuiHeaderList__list items margin: 0 $euiSizeXS; diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx index 96a205b7372733..e503ebb839f48d 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx @@ -48,7 +48,7 @@ export function TopNavMenuItem(props: TopNavMenuData) { }; const btn = props.emphasize ? ( - <EuiButton size="s" fill {...commonButtonProps}> + <EuiButton size="s" {...commonButtonProps}> {upperFirst(props.label || props.id!)} </EuiButton> ) : ( diff --git a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx index 12720f3f22e7cf..cb68a647cb81d5 100644 --- a/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx +++ b/src/plugins/visualize/public/application/utils/get_top_nav_config.tsx @@ -175,54 +175,61 @@ export const getTopNavConfig = ( }; const topNavMenu: TopNavMenuData[] = [ - ...(originatingApp && ((savedVis && savedVis.id) || embeddableId) - ? [ - { - id: 'saveAndReturn', - label: i18n.translate('visualize.topNavMenu.saveAndReturnVisualizationButtonLabel', { - defaultMessage: 'Save and return', - }), - emphasize: true, - iconType: 'check', - description: i18n.translate( - 'visualize.topNavMenu.saveAndReturnVisualizationButtonAriaLabel', - { - defaultMessage: 'Finish editing visualization and return to the last app', - } - ), - testId: 'visualizesaveAndReturnButton', - disableButton: hasUnappliedChanges, - tooltip() { - if (hasUnappliedChanges) { - return i18n.translate( - 'visualize.topNavMenu.saveAndReturnVisualizationDisabledButtonTooltip', - { - defaultMessage: 'Apply or Discard your changes before finishing', - } - ); - } - }, - run: async () => { - const saveOptions = { - confirmOverwrite: false, - returnToOrigin: true, - }; - if ( - originatingApp === 'dashboards' && - dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && - !savedVis - ) { - return createVisReference(); - } - return doSave(saveOptions); + { + id: 'inspector', + label: i18n.translate('visualize.topNavMenu.openInspectorButtonLabel', { + defaultMessage: 'inspect', + }), + description: i18n.translate('visualize.topNavMenu.openInspectorButtonAriaLabel', { + defaultMessage: 'Open Inspector for visualization', + }), + testId: 'openInspectorButton', + disableButton() { + return !embeddableHandler.hasInspector || !embeddableHandler.hasInspector(); + }, + run: openInspector, + tooltip() { + if (!embeddableHandler.hasInspector || !embeddableHandler.hasInspector()) { + return i18n.translate('visualize.topNavMenu.openInspectorDisabledButtonTooltip', { + defaultMessage: `This visualization doesn't support any inspectors.`, + }); + } + }, + }, + { + id: 'share', + label: i18n.translate('visualize.topNavMenu.shareVisualizationButtonLabel', { + defaultMessage: 'share', + }), + description: i18n.translate('visualize.topNavMenu.shareVisualizationButtonAriaLabel', { + defaultMessage: 'Share Visualization', + }), + testId: 'shareTopNavButton', + run: (anchorElement) => { + if (share && !embeddableId) { + // TODO: support sharing in by-value mode + share.toggleShareContextMenu({ + anchorElement, + allowEmbed: true, + allowShortUrl: visualizeCapabilities.createShortUrl, + shareableUrl: unhashUrl(window.location.href), + objectId: savedVis?.id, + objectType: 'visualization', + sharingData: { + title: savedVis?.title, }, - }, - ] - : []), + isDirty: hasUnappliedChanges || hasUnsavedChanges, + }); + } + }, + // disable the Share button if no action specified + disableButton: !share || !!embeddableId, + }, ...(visualizeCapabilities.save && !embeddableId ? [ { id: 'save', + iconType: savedVis?.id && originatingApp ? undefined : 'save', label: savedVis?.id && originatingApp ? i18n.translate('visualize.topNavMenu.saveVisualizationAsButtonLabel', { @@ -303,56 +310,50 @@ export const getTopNavConfig = ( }, ] : []), - { - id: 'share', - label: i18n.translate('visualize.topNavMenu.shareVisualizationButtonLabel', { - defaultMessage: 'share', - }), - description: i18n.translate('visualize.topNavMenu.shareVisualizationButtonAriaLabel', { - defaultMessage: 'Share Visualization', - }), - testId: 'shareTopNavButton', - run: (anchorElement) => { - if (share && !embeddableId) { - // TODO: support sharing in by-value mode - share.toggleShareContextMenu({ - anchorElement, - allowEmbed: true, - allowShortUrl: visualizeCapabilities.createShortUrl, - shareableUrl: unhashUrl(window.location.href), - objectId: savedVis?.id, - objectType: 'visualization', - sharingData: { - title: savedVis?.title, + ...(originatingApp && ((savedVis && savedVis.id) || embeddableId) + ? [ + { + id: 'saveAndReturn', + label: i18n.translate('visualize.topNavMenu.saveAndReturnVisualizationButtonLabel', { + defaultMessage: 'Save and return', + }), + emphasize: true, + iconType: 'checkInCircleFilled', + description: i18n.translate( + 'visualize.topNavMenu.saveAndReturnVisualizationButtonAriaLabel', + { + defaultMessage: 'Finish editing visualization and return to the last app', + } + ), + testId: 'visualizesaveAndReturnButton', + disableButton: hasUnappliedChanges, + tooltip() { + if (hasUnappliedChanges) { + return i18n.translate( + 'visualize.topNavMenu.saveAndReturnVisualizationDisabledButtonTooltip', + { + defaultMessage: 'Apply or Discard your changes before finishing', + } + ); + } }, - isDirty: hasUnappliedChanges || hasUnsavedChanges, - }); - } - }, - // disable the Share button if no action specified - disableButton: !share || !!embeddableId, - }, - { - id: 'inspector', - label: i18n.translate('visualize.topNavMenu.openInspectorButtonLabel', { - defaultMessage: 'inspect', - }), - description: i18n.translate('visualize.topNavMenu.openInspectorButtonAriaLabel', { - defaultMessage: 'Open Inspector for visualization', - }), - testId: 'openInspectorButton', - disableButton() { - return !embeddableHandler.hasInspector || !embeddableHandler.hasInspector(); - }, - run: openInspector, - tooltip() { - if (!embeddableHandler.hasInspector || !embeddableHandler.hasInspector()) { - return i18n.translate('visualize.topNavMenu.openInspectorDisabledButtonTooltip', { - defaultMessage: `This visualization doesn't support any inspectors.`, - }); - } - }, - }, + run: async () => { + const saveOptions = { + confirmOverwrite: false, + returnToOrigin: true, + }; + if ( + originatingApp === 'dashboards' && + dashboard.dashboardFeatureFlagConfig.allowByValueEmbeddables && + !savedVis + ) { + return createVisReference(); + } + return doSave(saveOptions); + }, + }, + ] + : []), ]; return topNavMenu; diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx index f6234d063d8cdf..9162af52052ee9 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -30,24 +30,22 @@ export function getLensTopNavConfig(options: { defaultMessage: 'Save', }); - if (showSaveAndReturn) { + if (showCancel) { topNavMenu.push({ - label: i18n.translate('xpack.lens.app.saveAndReturn', { - defaultMessage: 'Save and return', + label: i18n.translate('xpack.lens.app.cancel', { + defaultMessage: 'cancel', }), - emphasize: true, - iconType: 'check', - run: actions.saveAndReturn, - testId: 'lnsApp_saveAndReturnButton', - disableButton: !savingPermitted, - description: i18n.translate('xpack.lens.app.saveAndReturnButtonAriaLabel', { - defaultMessage: 'Save the current lens visualization and return to the last app', + run: actions.cancel, + testId: 'lnsApp_cancelButton', + description: i18n.translate('xpack.lens.app.cancelButtonAriaLabel', { + defaultMessage: 'Return to the last app without saving changes', }), }); } topNavMenu.push({ label: saveButtonLabel, + iconType: !showSaveAndReturn ? 'save' : undefined, emphasize: !showSaveAndReturn, run: actions.showSaveModal, testId: 'lnsApp_saveButton', @@ -57,17 +55,21 @@ export function getLensTopNavConfig(options: { disableButton: !savingPermitted, }); - if (showCancel) { + if (showSaveAndReturn) { topNavMenu.push({ - label: i18n.translate('xpack.lens.app.cancel', { - defaultMessage: 'cancel', + label: i18n.translate('xpack.lens.app.saveAndReturn', { + defaultMessage: 'Save and return', }), - run: actions.cancel, - testId: 'lnsApp_cancelButton', - description: i18n.translate('xpack.lens.app.cancelButtonAriaLabel', { - defaultMessage: 'Return to the last app without saving changes', + emphasize: true, + iconType: 'checkInCircleFilled', + run: actions.saveAndReturn, + testId: 'lnsApp_saveAndReturnButton', + disableButton: !savingPermitted, + description: i18n.translate('xpack.lens.app.saveAndReturnButtonAriaLabel', { + defaultMessage: 'Save the current lens visualization and return to the last app', }), }); } + return topNavMenu; } diff --git a/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx b/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx index 8a0eb8db4d7aa0..917abebfb6b25e 100644 --- a/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx +++ b/x-pack/plugins/maps/public/routing/routes/maps_app/top_nav_config.tsx @@ -123,31 +123,56 @@ export function getTopNavConfig({ return { id: savedObjectId }; } - if (hasSaveAndReturnConfig) { - topNavConfigs.push({ - id: 'saveAndReturn', - label: i18n.translate('xpack.maps.topNav.saveAndReturnButtonLabel', { - defaultMessage: 'Save and return', + topNavConfigs.push( + { + id: 'mapSettings', + label: i18n.translate('xpack.maps.topNav.openSettingsButtonLabel', { + defaultMessage: `Map settings`, }), - emphasize: true, - iconType: 'check', - run: () => { - onSave({ - newTitle: savedMap.title ? savedMap.title : '', - newDescription: savedMap.description ? savedMap.description : '', - newCopyOnSave: false, - isTitleDuplicateConfirmed: false, - returnToOrigin: true, - onTitleDuplicate: () => {}, - }); + description: i18n.translate('xpack.maps.topNav.openSettingsDescription', { + defaultMessage: `Open map settings`, + }), + testId: 'openSettingsButton', + disableButton() { + return isOpenSettingsDisabled; }, - testId: 'mapSaveAndReturnButton', - }); - } + run() { + openMapSettings(); + }, + }, + { + id: 'inspect', + label: i18n.translate('xpack.maps.topNav.openInspectorButtonLabel', { + defaultMessage: `inspect`, + }), + description: i18n.translate('xpack.maps.topNav.openInspectorDescription', { + defaultMessage: `Open Inspector`, + }), + testId: 'openInspectorButton', + run() { + getInspector().open(inspectorAdapters, {}); + }, + }, + { + id: 'full-screen', + label: i18n.translate('xpack.maps.topNav.fullScreenButtonLabel', { + defaultMessage: `full screen`, + }), + description: i18n.translate('xpack.maps.topNav.fullScreenDescription', { + defaultMessage: `full screen`, + }), + testId: 'mapsFullScreenMode', + run() { + getCoreChrome().setIsVisible(false); + enableFullScreen(); + }, + } + ); if (hasWritePermissions) { topNavConfigs.push({ id: 'save', + iconType: hasSaveAndReturnConfig ? undefined : 'save', label: hasSaveAndReturnConfig ? i18n.translate('xpack.maps.topNav.saveAsButtonLabel', { defaultMessage: 'Save as', @@ -192,51 +217,27 @@ export function getTopNavConfig({ }); } - topNavConfigs.push( - { - id: 'mapSettings', - label: i18n.translate('xpack.maps.topNav.openSettingsButtonLabel', { - defaultMessage: `Map settings`, - }), - description: i18n.translate('xpack.maps.topNav.openSettingsDescription', { - defaultMessage: `Open map settings`, - }), - testId: 'openSettingsButton', - disableButton() { - return isOpenSettingsDisabled; - }, - run() { - openMapSettings(); - }, - }, - { - id: 'inspect', - label: i18n.translate('xpack.maps.topNav.openInspectorButtonLabel', { - defaultMessage: `inspect`, - }), - description: i18n.translate('xpack.maps.topNav.openInspectorDescription', { - defaultMessage: `Open Inspector`, - }), - testId: 'openInspectorButton', - run() { - getInspector().open(inspectorAdapters, {}); - }, - }, - { - id: 'full-screen', - label: i18n.translate('xpack.maps.topNav.fullScreenButtonLabel', { - defaultMessage: `full screen`, - }), - description: i18n.translate('xpack.maps.topNav.fullScreenDescription', { - defaultMessage: `full screen`, + if (hasSaveAndReturnConfig) { + topNavConfigs.push({ + id: 'saveAndReturn', + label: i18n.translate('xpack.maps.topNav.saveAndReturnButtonLabel', { + defaultMessage: 'Save and return', }), - testId: 'mapsFullScreenMode', - run() { - getCoreChrome().setIsVisible(false); - enableFullScreen(); + emphasize: true, + iconType: 'checkInCircleFilled', + run: () => { + onSave({ + newTitle: savedMap.title ? savedMap.title : '', + newDescription: savedMap.description ? savedMap.description : '', + newCopyOnSave: false, + isTitleDuplicateConfirmed: false, + returnToOrigin: true, + onTitleDuplicate: () => {}, + }); }, - } - ); + testId: 'mapSaveAndReturnButton', + }); + } return topNavConfigs; } From bb4ad196ea25858234176248030814144a087f0b Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Fri, 2 Oct 2020 09:50:49 -0700 Subject: [PATCH 089/128] normalize paths before printing them into the generated plugin list (#79232) Co-authored-by: spalger <spalger@users.noreply.github.com> --- docs/developer/plugin-list.asciidoc | 12 ++++++------ .../src/plugin_list/discover_plugins.ts | 2 +- .../src/plugin_list/generate_plugin_list.ts | 10 ++++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index bf11f87b96ce9f..67b7aa8e6a0118 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -16,7 +16,7 @@ NOTE: [discrete] === src/plugins -[%header,cols=2*] +[%header,cols=2*] |=== |Name |Description @@ -259,7 +259,7 @@ which will load the visualization's editor. [discrete] === x-pack/plugins -[%header,cols=2*] +[%header,cols=2*] |=== |Name |Description @@ -515,6 +515,10 @@ As a developer you can reuse and extend built-in alerts and actions UI functiona in their infrastructure. +|{kib-repo}blob/{branch}/x-pack/plugins/drilldowns/url_drilldown/README.md[urlDrilldown] +|NOTE: This plugin contains implementation of URL drilldown. For drilldowns infrastructure code refer to ui_actions_enhanced plugin. + + |{kib-repo}blob/{branch}/x-pack/plugins/watcher/README.md[watcher] |This plugins adopts some conventions in addition to or in place of conventions in Kibana (at the time of the plugin's creation): @@ -523,10 +527,6 @@ in their infrastructure. |Contains HTTP endpoints and UiSettings that are slated for removal. -|{kib-repo}blob/{branch}/x-pack/plugins/drilldowns/url_drilldown/README.md[urlDrilldown] -|NOTE: This plugin contains implementation of URL drilldown. For drilldowns infrastructure code refer to ui_actions_enhanced plugin. - - |=== include::{kibana-root}/src/plugins/dashboard/README.asciidoc[leveloffset=+1] diff --git a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts index 5d92ddb600aa9e..e8f6735205b197 100644 --- a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts +++ b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts @@ -29,7 +29,7 @@ import { extractAsciidocInfo } from './extract_asciidoc_info'; export interface Plugin { id: string; - relativeDir?: string; + relativeDir: string; relativeReadmePath?: string; readmeSnippet?: string; readmeAsciidocAnchor?: string; diff --git a/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts b/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts index e1a1323553113a..680c220adb18cf 100644 --- a/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts +++ b/packages/kbn-dev-utils/src/plugin_list/generate_plugin_list.ts @@ -24,9 +24,11 @@ import { REPO_ROOT } from '@kbn/utils'; import { Plugins } from './discover_plugins'; +const sortPlugins = (plugins: Plugins) => plugins.sort((a, b) => a.id.localeCompare(b.id)); + function* printPlugins(plugins: Plugins, includes: string[]) { - for (const plugin of plugins) { - const path = plugin.relativeReadmePath || plugin.relativeDir; + for (const plugin of sortPlugins(plugins)) { + const path = normalizePath(plugin.relativeReadmePath || plugin.relativeDir); yield ''; if (plugin.readmeAsciidocAnchor) { @@ -67,7 +69,7 @@ NOTE: [discrete] === src/plugins -[%header,cols=2*] +[%header,cols=2*] |=== |Name |Description @@ -79,7 +81,7 @@ ${Array.from(printPlugins(ossPlugins, includes)).join('\n')} [discrete] === x-pack/plugins -[%header,cols=2*] +[%header,cols=2*] |=== |Name |Description From 2899e83df8b849f5fb2a898944c6aada69dff12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= <alejandro.fernandez@elastic.co> Date: Fri, 2 Oct 2020 18:57:50 +0200 Subject: [PATCH 090/128] [Logs UI] Remove legacy singletons (#77743) Removes the `npStart` legacy singleton used during the migration to the new platform. The singleton was used in API calls to access the `http.fetch` service. To remove the singleton we have injected `fetch` as a dependency in all functions. --- .../common/components/get_alert_preview.ts | 4 +- .../logs/log_analysis/api/ml_cleanup.ts | 56 ++++++------- .../api/ml_get_jobs_summary_api.ts | 26 +++--- .../logs/log_analysis/api/ml_get_module.ts | 16 ++-- .../log_analysis/api/ml_setup_module_api.ts | 52 +++++++----- .../log_analysis/api/validate_datasets.ts | 19 +++-- .../logs/log_analysis/api/validate_indices.ts | 26 +++--- .../log_analysis_capabilities.tsx | 15 ++-- .../log_analysis/log_analysis_cleanup.tsx | 21 ++--- .../logs/log_analysis/log_analysis_module.tsx | 28 +++++-- .../log_analysis_module_definition.tsx | 4 +- .../log_analysis/log_analysis_module_types.ts | 24 ++++-- .../log_analysis/log_analysis_setup_state.ts | 8 +- .../log_entry_categories/module_descriptor.ts | 80 ++++++++++++------- .../log_entry_rate/module_descriptor.ts | 72 ++++++++++------- .../logs/log_entries/api/fetch_log_entries.ts | 13 ++- .../log_entries/api/fetch_log_entries_item.ts | 19 ++--- .../containers/logs/log_entries/index.ts | 6 +- .../public/containers/logs/log_flyout.tsx | 4 +- .../api/fetch_log_entries_highlights.ts | 19 ++--- .../api/fetch_log_summary_highlights.ts | 18 ++--- .../log_highlights/log_entry_highlights.tsx | 23 +++--- .../log_highlights/log_summary_highlights.ts | 21 +++-- .../api/fetch_log_source_configuration.ts | 7 +- .../log_source/api/fetch_log_source_status.ts | 4 +- .../api/patch_log_source_configuration.ts | 4 +- .../containers/logs/log_source/log_source.ts | 10 +-- .../containers/logs/log_stream/index.ts | 19 +++-- .../logs/log_summary/api/fetch_log_summary.ts | 19 ++--- .../logs/log_summary/log_summary.test.tsx | 24 ++++-- .../logs/log_summary/log_summary.tsx | 19 +++-- .../public/containers/ml/api/ml_cleanup.ts | 57 ++++++------- .../ml/api/ml_get_jobs_summary_api.ts | 26 +++--- .../public/containers/ml/api/ml_get_module.ts | 16 ++-- .../containers/ml/api/ml_setup_module_api.ts | 52 +++++++----- .../containers/ml/infra_ml_capabilities.tsx | 5 +- .../public/containers/ml/infra_ml_cleanup.tsx | 20 +++-- .../public/containers/ml/infra_ml_module.tsx | 31 ++++--- .../ml/infra_ml_module_definition.tsx | 4 +- .../containers/ml/infra_ml_module_types.ts | 37 ++++++--- .../metrics_hosts/module_descriptor.ts | 53 ++++++------ .../modules/metrics_k8s/module_descriptor.ts | 53 ++++++------ .../plugins/infra/public/legacy_singletons.ts | 14 ---- .../pages/link_to/link_to_logs.test.tsx | 1 - .../get_log_entry_category_datasets.ts | 27 ++++--- .../get_log_entry_category_examples.ts | 31 +++---- .../get_top_log_entry_categories.ts | 30 +++---- .../use_log_entry_categories_results.ts | 20 +++-- .../use_log_entry_category_examples.tsx | 16 ++-- .../service_calls/get_log_entry_anomalies.ts | 23 +++--- .../get_log_entry_anomalies_datasets.ts | 16 ++-- .../service_calls/get_log_entry_examples.ts | 33 ++++---- .../service_calls/get_log_entry_rate.ts | 31 ++++--- .../use_log_entry_anomalies_results.ts | 25 ++++-- .../log_entry_rate/use_log_entry_examples.ts | 17 ++-- .../use_log_entry_rate_results.ts | 15 ++-- .../hooks/use_metrics_hosts_anomalies.ts | 43 ++++++---- .../hooks/use_metrics_k8s_anomalies.ts | 45 +++++++---- x-pack/plugins/infra/public/plugin.ts | 5 +- 59 files changed, 798 insertions(+), 628 deletions(-) delete mode 100644 x-pack/plugins/infra/public/legacy_singletons.ts diff --git a/x-pack/plugins/infra/public/alerting/common/components/get_alert_preview.ts b/x-pack/plugins/infra/public/alerting/common/components/get_alert_preview.ts index 207d8a722a8c69..ea50ea6f11f3ab 100644 --- a/x-pack/plugins/infra/public/alerting/common/components/get_alert_preview.ts +++ b/x-pack/plugins/infra/public/alerting/common/components/get_alert_preview.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'src/core/public'; +import type { HttpHandler } from 'src/core/public'; import { INFRA_ALERT_PREVIEW_PATH, METRIC_THRESHOLD_ALERT_TYPE_ID, @@ -22,7 +22,7 @@ export async function getAlertPreview({ params, alertType, }: { - fetch: HttpSetup['fetch']; + fetch: HttpHandler; params: AlertPreviewRequestParams; alertType: PreviewableAlertTypes; }): Promise<AlertPreviewSuccessResponsePayload> { diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts index 6fa2ac175ace6a..4fdd6bdd282baa 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_cleanup.ts @@ -5,21 +5,25 @@ */ import * as rt from 'io-ts'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getDatafeedId, getJobId } from '../../../../../common/log_analysis'; -import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +interface DeleteJobsRequestArgs<JobType extends string> { + spaceId: string; + sourceId: string; + jobTypes: JobType[]; +} export const callDeleteJobs = async <JobType extends string>( - spaceId: string, - sourceId: string, - jobTypes: JobType[] + requestArgs: DeleteJobsRequestArgs<JobType>, + fetch: HttpHandler ) => { + const { spaceId, sourceId, jobTypes } = requestArgs; + // NOTE: Deleting the jobs via this API will delete the datafeeds at the same time - const deleteJobsResponse = await npStart.http.fetch('/api/ml/jobs/delete_jobs', { + const deleteJobsResponse = await fetch('/api/ml/jobs/delete_jobs', { method: 'POST', body: JSON.stringify( deleteJobsRequestPayloadRT.encode({ @@ -28,28 +32,29 @@ export const callDeleteJobs = async <JobType extends string>( ), }); - return pipe( - deleteJobsResponsePayloadRT.decode(deleteJobsResponse), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(deleteJobsResponsePayloadRT)(deleteJobsResponse); }; -export const callGetJobDeletionTasks = async () => { - const jobDeletionTasksResponse = await npStart.http.fetch('/api/ml/jobs/deleting_jobs_tasks'); +export const callGetJobDeletionTasks = async (fetch: HttpHandler) => { + const jobDeletionTasksResponse = await fetch('/api/ml/jobs/deleting_jobs_tasks'); - return pipe( - getJobDeletionTasksResponsePayloadRT.decode(jobDeletionTasksResponse), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getJobDeletionTasksResponsePayloadRT)(jobDeletionTasksResponse); }; +interface StopDatafeedsRequestArgs<JobType extends string> { + spaceId: string; + sourceId: string; + jobTypes: JobType[]; +} + export const callStopDatafeeds = async <JobType extends string>( - spaceId: string, - sourceId: string, - jobTypes: JobType[] + requestArgs: StopDatafeedsRequestArgs<JobType>, + fetch: HttpHandler ) => { + const { spaceId, sourceId, jobTypes } = requestArgs; + // Stop datafeed due to https://github.com/elastic/kibana/issues/44652 - const stopDatafeedResponse = await npStart.http.fetch('/api/ml/jobs/stop_datafeeds', { + const stopDatafeedResponse = await fetch('/api/ml/jobs/stop_datafeeds', { method: 'POST', body: JSON.stringify( stopDatafeedsRequestPayloadRT.encode({ @@ -58,10 +63,7 @@ export const callStopDatafeeds = async <JobType extends string>( ), }); - return pipe( - stopDatafeedsResponsePayloadRT.decode(stopDatafeedResponse), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(stopDatafeedsResponsePayloadRT)(stopDatafeedResponse); }; export const deleteJobsRequestPayloadRT = rt.type({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts index 7441c0ab7d34cf..7cb477dbe5b376 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_jobs_summary_api.ts @@ -4,21 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getJobId, jobCustomSettingsRT } from '../../../../../common/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +interface RequestArgs<JobType extends string> { + spaceId: string; + sourceId: string; + jobTypes: JobType[]; +} export const callJobsSummaryAPI = async <JobType extends string>( - spaceId: string, - sourceId: string, - jobTypes: JobType[] + requestArgs: RequestArgs<JobType>, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch('/api/ml/jobs/jobs_summary', { + const { spaceId, sourceId, jobTypes } = requestArgs; + const response = await fetch('/api/ml/jobs/jobs_summary', { method: 'POST', body: JSON.stringify( fetchJobStatusRequestPayloadRT.encode({ @@ -26,10 +29,7 @@ export const callJobsSummaryAPI = async <JobType extends string>( }) ), }); - return pipe( - fetchJobStatusResponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(fetchJobStatusResponsePayloadRT)(response); }; export const fetchJobStatusRequestPayloadRT = rt.type({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts index b6b40d6dc651f7..2bf18d4e52c79a 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_get_module.ts @@ -4,24 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { jobCustomSettingsRT } from '../../../../../common/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; -export const callGetMlModuleAPI = async (moduleId: string) => { - const response = await npStart.http.fetch(`/api/ml/modules/get_module/${moduleId}`, { +export const callGetMlModuleAPI = async (moduleId: string, fetch: HttpHandler) => { + const response = await fetch(`/api/ml/modules/get_module/${moduleId}`, { method: 'GET', }); - return pipe( - getMlModuleResponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getMlModuleResponsePayloadRT)(response); }; const jobDefinitionRT = rt.type({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts index 7c8d63374924c3..1f203ef9618b81 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/ml_setup_module_api.ts @@ -4,27 +4,38 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getJobIdPrefix, jobCustomSettingsRT } from '../../../../../common/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; - -export const callSetupMlModuleAPI = async ( - moduleId: string, - start: number | undefined, - end: number | undefined, - spaceId: string, - sourceId: string, - indexPattern: string, - jobOverrides: SetupMlModuleJobOverrides[] = [], - datafeedOverrides: SetupMlModuleDatafeedOverrides[] = [], - query?: object -) => { - const response = await npStart.http.fetch(`/api/ml/modules/setup/${moduleId}`, { +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +interface RequestArgs { + moduleId: string; + start?: number; + end?: number; + spaceId: string; + sourceId: string; + indexPattern: string; + jobOverrides?: SetupMlModuleJobOverrides[]; + datafeedOverrides?: SetupMlModuleDatafeedOverrides[]; + query?: object; +} + +export const callSetupMlModuleAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { + const { + moduleId, + start, + end, + spaceId, + sourceId, + indexPattern, + jobOverrides = [], + datafeedOverrides = [], + query, + } = requestArgs; + + const response = await fetch(`/api/ml/modules/setup/${moduleId}`, { method: 'POST', body: JSON.stringify( setupMlModuleRequestPayloadRT.encode({ @@ -40,10 +51,7 @@ export const callSetupMlModuleAPI = async ( ), }); - return pipe( - setupMlModuleResponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(setupMlModuleResponsePayloadRT)(response); }; const setupMlModuleTimeParamsRT = rt.partial({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_datasets.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_datasets.ts index 6c9d5e439d359e..ec08d3ac107e55 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_datasets.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_datasets.ts @@ -4,21 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ +import type { HttpHandler } from 'src/core/public'; import { LOG_ANALYSIS_VALIDATE_DATASETS_PATH, validateLogEntryDatasetsRequestPayloadRT, validateLogEntryDatasetsResponsePayloadRT, } from '../../../../../common/http_api'; import { decodeOrThrow } from '../../../../../common/runtime_types'; -import { npStart } from '../../../../legacy_singletons'; -export const callValidateDatasetsAPI = async ( - indices: string[], - timestampField: string, - startTime: number, - endTime: number -) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_VALIDATE_DATASETS_PATH, { +interface RequestArgs { + indices: string[]; + timestampField: string; + startTime: number; + endTime: number; +} + +export const callValidateDatasetsAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { + const { indices, timestampField, startTime, endTime } = requestArgs; + const response = await fetch(LOG_ANALYSIS_VALIDATE_DATASETS_PATH, { method: 'POST', body: JSON.stringify( validateLogEntryDatasetsRequestPayloadRT.encode({ diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts index bbef7d201045ff..465d09a744b198 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/api/validate_indices.ts @@ -4,10 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; + import { LOG_ANALYSIS_VALIDATE_INDICES_PATH, ValidationIndicesFieldSpecification, @@ -15,19 +13,19 @@ import { validationIndicesResponsePayloadRT, } from '../../../../../common/http_api'; -import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +interface RequestArgs { + indices: string[]; + fields: ValidationIndicesFieldSpecification[]; +} -export const callValidateIndicesAPI = async ( - indices: string[], - fields: ValidationIndicesFieldSpecification[] -) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_VALIDATE_INDICES_PATH, { +export const callValidateIndicesAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { + const { indices, fields } = requestArgs; + const response = await fetch(LOG_ANALYSIS_VALIDATE_INDICES_PATH, { method: 'POST', body: JSON.stringify(validationIndicesRequestPayloadRT.encode({ data: { indices, fields } })), }); - return pipe( - validationIndicesResponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(validationIndicesResponsePayloadRT)(response); }; diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx index 9116900ec21968..74b316f78259fc 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_capabilities.tsx @@ -6,18 +6,16 @@ import createContainer from 'constate'; import { useMemo, useState, useEffect } from 'react'; -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; -import { npStart } from '../../../legacy_singletons'; import { getMlCapabilitiesResponsePayloadRT, GetMlCapabilitiesResponsePayload, } from './api/ml_api_types'; -import { throwErrors, createPlainError } from '../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../common/runtime_types'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export const useLogAnalysisCapabilities = () => { + const { services } = useKibanaContextForPlugin(); const [mlCapabilities, setMlCapabilities] = useState<GetMlCapabilitiesResponsePayload>( initialMlCapabilities ); @@ -26,12 +24,9 @@ export const useLogAnalysisCapabilities = () => { { cancelPreviousOn: 'resolution', createPromise: async () => { - const rawResponse = await npStart.http.fetch('/api/ml/ml_capabilities'); + const rawResponse = await services.http.fetch('/api/ml/ml_capabilities'); - return pipe( - getMlCapabilitiesResponsePayloadRT.decode(rawResponse), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getMlCapabilitiesResponsePayloadRT)(rawResponse); }, onResolve: (response) => { setMlCapabilities(response); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx index 522616f83d0cb9..ec5e879131aa1f 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_cleanup.tsx @@ -3,17 +3,18 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import type { HttpHandler } from 'src/core/public'; import { getJobId } from '../../../../common/log_analysis'; import { callDeleteJobs, callGetJobDeletionTasks, callStopDatafeeds } from './api/ml_cleanup'; export const cleanUpJobsAndDatafeeds = async <JobType extends string>( spaceId: string, sourceId: string, - jobTypes: JobType[] + jobTypes: JobType[], + fetch: HttpHandler ) => { try { - await callStopDatafeeds(spaceId, sourceId, jobTypes); + await callStopDatafeeds({ spaceId, sourceId, jobTypes }, fetch); } catch (err) { // Proceed only if datafeed has been deleted or didn't exist in the first place if (err?.res?.status !== 404) { @@ -21,27 +22,29 @@ export const cleanUpJobsAndDatafeeds = async <JobType extends string>( } } - return await deleteJobs(spaceId, sourceId, jobTypes); + return await deleteJobs(spaceId, sourceId, jobTypes, fetch); }; const deleteJobs = async <JobType extends string>( spaceId: string, sourceId: string, - jobTypes: JobType[] + jobTypes: JobType[], + fetch: HttpHandler ) => { - const deleteJobsResponse = await callDeleteJobs(spaceId, sourceId, jobTypes); - await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes); + const deleteJobsResponse = await callDeleteJobs({ spaceId, sourceId, jobTypes }, fetch); + await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes, fetch); return deleteJobsResponse; }; const waitUntilJobsAreDeleted = async <JobType extends string>( spaceId: string, sourceId: string, - jobTypes: JobType[] + jobTypes: JobType[], + fetch: HttpHandler ) => { const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)); while (true) { - const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(); + const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(fetch); const needToWait = jobIdsBeingDeleted.some((jobId) => moduleJobIds.includes(jobId)); if (needToWait) { diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx index 79768302a7310c..27ef0039ae49f8 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module.tsx @@ -6,6 +6,7 @@ import { useCallback, useMemo } from 'react'; import { DatasetFilter } from '../../../../common/log_analysis'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { useModuleStatus } from './log_analysis_module_status'; import { ModuleDescriptor, ModuleSourceConfiguration } from './log_analysis_module_types'; @@ -17,6 +18,7 @@ export const useLogAnalysisModule = <JobType extends string>({ sourceConfiguration: ModuleSourceConfiguration; moduleDescriptor: ModuleDescriptor<JobType>; }) => { + const { services } = useKibanaContextForPlugin(); const { spaceId, sourceId, timestampField } = sourceConfiguration; const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes); @@ -25,7 +27,7 @@ export const useLogAnalysisModule = <JobType extends string>({ cancelPreviousOn: 'resolution', createPromise: async () => { dispatchModuleStatus({ type: 'fetchingJobStatuses' }); - return await moduleDescriptor.getJobSummary(spaceId, sourceId); + return await moduleDescriptor.getJobSummary(spaceId, sourceId, services.http.fetch); }, onResolve: (jobResponse) => { dispatchModuleStatus({ @@ -52,13 +54,23 @@ export const useLogAnalysisModule = <JobType extends string>({ datasetFilter: DatasetFilter ) => { dispatchModuleStatus({ type: 'startedSetup' }); - const setupResult = await moduleDescriptor.setUpModule(start, end, datasetFilter, { - indices: selectedIndices, - sourceId, + const setupResult = await moduleDescriptor.setUpModule( + start, + end, + datasetFilter, + { + indices: selectedIndices, + sourceId, + spaceId, + timestampField, + }, + services.http.fetch + ); + const jobSummaries = await moduleDescriptor.getJobSummary( spaceId, - timestampField, - }); - const jobSummaries = await moduleDescriptor.getJobSummary(spaceId, sourceId); + sourceId, + services.http.fetch + ); return { setupResult, jobSummaries }; }, onResolve: ({ setupResult: { datafeeds, jobs }, jobSummaries }) => { @@ -82,7 +94,7 @@ export const useLogAnalysisModule = <JobType extends string>({ { cancelPreviousOn: 'resolution', createPromise: async () => { - return await moduleDescriptor.cleanUpModule(spaceId, sourceId); + return await moduleDescriptor.cleanUpModule(spaceId, sourceId, services.http.fetch); }, }, [spaceId, sourceId] diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_definition.tsx b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_definition.tsx index 1f643d0e5eb349..7a5c1d354dc34f 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_definition.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_definition.tsx @@ -6,6 +6,7 @@ import { useCallback, useMemo, useState } from 'react'; import { getJobId } from '../../../../common/log_analysis'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { JobSummary } from './api/ml_get_jobs_summary_api'; import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; @@ -18,6 +19,7 @@ export const useLogAnalysisModuleDefinition = <JobType extends string>({ sourceConfiguration: ModuleSourceConfiguration; moduleDescriptor: ModuleDescriptor<JobType>; }) => { + const { services } = useKibanaContextForPlugin(); const [moduleDefinition, setModuleDefinition] = useState< GetMlModuleResponsePayload | undefined >(); @@ -40,7 +42,7 @@ export const useLogAnalysisModuleDefinition = <JobType extends string>({ { cancelPreviousOn: 'resolution', createPromise: async () => { - return await moduleDescriptor.getModuleDefinition(); + return await moduleDescriptor.getModuleDefinition(services.http.fetch); }, onResolve: (response) => { setModuleDefinition(response); diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts index ba355ad195b11d..c42704860b032c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_module_types.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +import type { HttpHandler } from 'src/core/public'; import { ValidateLogEntryDatasetsResponsePayload, ValidationIndicesResponsePayload, @@ -23,24 +24,35 @@ export interface ModuleDescriptor<JobType extends string> { jobTypes: JobType[]; bucketSpan: number; getJobIds: (spaceId: string, sourceId: string) => Record<JobType, string>; - getJobSummary: (spaceId: string, sourceId: string) => Promise<FetchJobStatusResponsePayload>; - getModuleDefinition: () => Promise<GetMlModuleResponsePayload>; + getJobSummary: ( + spaceId: string, + sourceId: string, + fetch: HttpHandler + ) => Promise<FetchJobStatusResponsePayload>; + getModuleDefinition: (fetch: HttpHandler) => Promise<GetMlModuleResponsePayload>; setUpModule: ( start: number | undefined, end: number | undefined, datasetFilter: DatasetFilter, - sourceConfiguration: ModuleSourceConfiguration + sourceConfiguration: ModuleSourceConfiguration, + fetch: HttpHandler ) => Promise<SetupMlModuleResponsePayload>; - cleanUpModule: (spaceId: string, sourceId: string) => Promise<DeleteJobsResponsePayload>; + cleanUpModule: ( + spaceId: string, + sourceId: string, + fetch: HttpHandler + ) => Promise<DeleteJobsResponsePayload>; validateSetupIndices: ( indices: string[], - timestampField: string + timestampField: string, + fetch: HttpHandler ) => Promise<ValidationIndicesResponsePayload>; validateSetupDatasets: ( indices: string[], timestampField: string, startTime: number, - endTime: number + endTime: number, + fetch: HttpHandler ) => Promise<ValidateLogEntryDatasetsResponsePayload>; } diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.ts index e6fe8f4e92cc4d..750a7104a3a98f 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/log_analysis_setup_state.ts @@ -18,6 +18,7 @@ import { ValidationIndicesError, ValidationUIError, } from '../../../components/logging/log_analysis_setup/initial_configuration_step'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { ModuleDescriptor, ModuleSourceConfiguration } from './log_analysis_module_types'; @@ -43,6 +44,7 @@ export const useAnalysisSetupState = <JobType extends string>({ setUpModule, sourceConfiguration, }: AnalysisSetupStateArguments<JobType>) => { + const { services } = useKibanaContextForPlugin(); const [startTime, setStartTime] = useState<number | undefined>(Date.now() - fourWeeksInMs); const [endTime, setEndTime] = useState<number | undefined>(undefined); @@ -158,7 +160,8 @@ export const useAnalysisSetupState = <JobType extends string>({ createPromise: async () => { return await validateSetupIndices( sourceConfiguration.indices, - sourceConfiguration.timestampField + sourceConfiguration.timestampField, + services.http.fetch ); }, onResolve: ({ data: { errors } }) => { @@ -183,7 +186,8 @@ export const useAnalysisSetupState = <JobType extends string>({ validIndexNames, sourceConfiguration.timestampField, startTime ?? 0, - endTime ?? Date.now() + endTime ?? Date.now(), + services.http.fetch ); }, onResolve: ({ data: { datasets } }) => { diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts index 9682b3e74db3bf..46b28e091cc5cd 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_categories/module_descriptor.ts @@ -5,6 +5,7 @@ */ import { i18n } from '@kbn/i18n'; +import type { HttpHandler } from 'src/core/public'; import { bucketSpan, categoriesMessageField, @@ -42,22 +43,26 @@ const getJobIds = (spaceId: string, sourceId: string) => {} as Record<LogEntryCategoriesJobType, string> ); -const getJobSummary = async (spaceId: string, sourceId: string) => { - const response = await callJobsSummaryAPI(spaceId, sourceId, logEntryCategoriesJobTypes); +const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + const response = await callJobsSummaryAPI( + { spaceId, sourceId, jobTypes: logEntryCategoriesJobTypes }, + fetch + ); const jobIds = Object.values(getJobIds(spaceId, sourceId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; -const getModuleDefinition = async () => { - return await callGetMlModuleAPI(moduleId); +const getModuleDefinition = async (fetch: HttpHandler) => { + return await callGetMlModuleAPI(moduleId, fetch); }; const setUpModule = async ( start: number | undefined, end: number | undefined, datasetFilter: DatasetFilter, - { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration + { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, + fetch: HttpHandler ) => { const indexNamePattern = indices.join(','); const jobOverrides = [ @@ -101,46 +106,59 @@ const setUpModule = async ( }; return callSetupMlModuleAPI( - moduleId, - start, - end, - spaceId, - sourceId, - indexNamePattern, - jobOverrides, - [], - query + { + moduleId, + start, + end, + spaceId, + sourceId, + indexPattern: indexNamePattern, + jobOverrides, + query, + }, + fetch ); }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryCategoriesJobTypes); +const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryCategoriesJobTypes, fetch); }; -const validateSetupIndices = async (indices: string[], timestampField: string) => { - return await callValidateIndicesAPI(indices, [ - { - name: timestampField, - validTypes: ['date'], - }, - { - name: partitionField, - validTypes: ['keyword'], - }, +const validateSetupIndices = async ( + indices: string[], + timestampField: string, + fetch: HttpHandler +) => { + return await callValidateIndicesAPI( { - name: categoriesMessageField, - validTypes: ['text'], + indices, + fields: [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + { + name: categoriesMessageField, + validTypes: ['text'], + }, + ], }, - ]); + fetch + ); }; const validateSetupDatasets = async ( indices: string[], timestampField: string, startTime: number, - endTime: number + endTime: number, + fetch: HttpHandler ) => { - return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); + return await callValidateDatasetsAPI({ indices, timestampField, startTime, endTime }, fetch); }; export const logEntryCategoriesModule: ModuleDescriptor<LogEntryCategoriesJobType> = { diff --git a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts index 001174a2b7558b..b97ec55105f5df 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_analysis/modules/log_entry_rate/module_descriptor.ts @@ -5,6 +5,7 @@ */ import { i18n } from '@kbn/i18n'; +import type { HttpHandler } from 'src/core/public'; import { bucketSpan, DatasetFilter, @@ -41,22 +42,26 @@ const getJobIds = (spaceId: string, sourceId: string) => {} as Record<LogEntryRateJobType, string> ); -const getJobSummary = async (spaceId: string, sourceId: string) => { - const response = await callJobsSummaryAPI(spaceId, sourceId, logEntryRateJobTypes); +const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + const response = await callJobsSummaryAPI( + { spaceId, sourceId, jobTypes: logEntryRateJobTypes }, + fetch + ); const jobIds = Object.values(getJobIds(spaceId, sourceId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; -const getModuleDefinition = async () => { - return await callGetMlModuleAPI(moduleId); +const getModuleDefinition = async (fetch: HttpHandler) => { + return await callGetMlModuleAPI(moduleId, fetch); }; const setUpModule = async ( start: number | undefined, end: number | undefined, datasetFilter: DatasetFilter, - { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration + { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, + fetch: HttpHandler ) => { const indexNamePattern = indices.join(','); const jobOverrides = [ @@ -93,42 +98,55 @@ const setUpModule = async ( : undefined; return callSetupMlModuleAPI( - moduleId, - start, - end, - spaceId, - sourceId, - indexNamePattern, - jobOverrides, - [], - query + { + moduleId, + start, + end, + spaceId, + sourceId, + indexPattern: indexNamePattern, + jobOverrides, + query, + }, + fetch ); }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryRateJobTypes); +const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, logEntryRateJobTypes, fetch); }; -const validateSetupIndices = async (indices: string[], timestampField: string) => { - return await callValidateIndicesAPI(indices, [ - { - name: timestampField, - validTypes: ['date'], - }, +const validateSetupIndices = async ( + indices: string[], + timestampField: string, + fetch: HttpHandler +) => { + return await callValidateIndicesAPI( { - name: partitionField, - validTypes: ['keyword'], + indices, + fields: [ + { + name: timestampField, + validTypes: ['date'], + }, + { + name: partitionField, + validTypes: ['keyword'], + }, + ], }, - ]); + fetch + ); }; const validateSetupDatasets = async ( indices: string[], timestampField: string, startTime: number, - endTime: number + endTime: number, + fetch: HttpHandler ) => { - return await callValidateDatasetsAPI(indices, timestampField, startTime, endTime); + return await callValidateDatasetsAPI({ indices, timestampField, startTime, endTime }, fetch); }; export const logEntryRateModule: ModuleDescriptor<LogEntryRateJobType> = { diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts index 2a19a828924279..3bbd86cb0ef75c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries.ts @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; -import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; import { LOG_ENTRIES_PATH, @@ -18,11 +15,11 @@ import { logEntriesResponseRT, } from '../../../../../common/http_api'; -export const fetchLogEntries = async (requestArgs: LogEntriesRequest) => { - const response = await npStart.http.fetch(LOG_ENTRIES_PATH, { +export const fetchLogEntries = async (requestArgs: LogEntriesRequest, fetch: HttpHandler) => { + const response = await fetch(LOG_ENTRIES_PATH, { method: 'POST', body: JSON.stringify(logEntriesRequestRT.encode(requestArgs)), }); - return pipe(logEntriesResponseRT.decode(response), fold(throwErrors(createPlainError), identity)); + return decodeOrThrow(logEntriesResponseRT)(response); }; diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts index 5fde01e458e369..d459fba6cf9577 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/api/fetch_log_entries_item.ts @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; -import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; import { LOG_ENTRIES_ITEM_PATH, @@ -18,14 +15,14 @@ import { logEntriesItemResponseRT, } from '../../../../../common/http_api'; -export const fetchLogEntriesItem = async (requestArgs: LogEntriesItemRequest) => { - const response = await npStart.http.fetch(LOG_ENTRIES_ITEM_PATH, { +export const fetchLogEntriesItem = async ( + requestArgs: LogEntriesItemRequest, + fetch: HttpHandler +) => { + const response = await fetch(LOG_ENTRIES_ITEM_PATH, { method: 'POST', body: JSON.stringify(logEntriesItemRequestRT.encode(requestArgs)), }); - return pipe( - logEntriesItemResponseRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(logEntriesItemResponseRT)(response); }; diff --git a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts index d5b2a0aaa61c01..4c8c610794b2eb 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_entries/index.ts @@ -14,6 +14,7 @@ import { LogEntriesBaseRequest, } from '../../../../common/http_api'; import { fetchLogEntries } from './api/fetch_log_entries'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; const DESIRED_BUFFER_PAGES = 2; const LIVE_STREAM_INTERVAL = 5000; @@ -144,6 +145,7 @@ const useFetchEntriesEffect = ( dispatch: Dispatch, props: LogEntriesProps ) => { + const { services } = useKibanaContextForPlugin(); const [prevParams, cachePrevParams] = useState<LogEntriesProps | undefined>(); const [startedStreaming, setStartedStreaming] = useState(false); @@ -172,7 +174,7 @@ const useFetchEntriesEffect = ( before: 'last', }; - const { data: payload } = await fetchLogEntries(fetchArgs); + const { data: payload } = await fetchLogEntries(fetchArgs, services.http.fetch); dispatch({ type: Action.ReceiveNewEntries, payload }); // Move position to the bottom if it's the first load. @@ -228,7 +230,7 @@ const useFetchEntriesEffect = ( after: state.bottomCursor, }; - const { data: payload } = await fetchLogEntries(fetchArgs); + const { data: payload } = await fetchLogEntries(fetchArgs, services.http.fetch); dispatch({ type: getEntriesBefore ? Action.ReceiveEntriesBefore : Action.ReceiveEntriesAfter, diff --git a/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx b/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx index 0489892e58f2a3..9ed2f5ad175c75 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_flyout.tsx @@ -9,6 +9,7 @@ import { isString } from 'lodash'; import React, { useContext, useEffect, useMemo, useState } from 'react'; import { LogEntriesItem } from '../../../common/http_api'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { UrlStateContainer } from '../../utils/url_state'; import { useTrackedPromise } from '../../utils/use_tracked_promise'; import { fetchLogEntriesItem } from './log_entries/api/fetch_log_entries_item'; @@ -26,6 +27,7 @@ export interface FlyoutOptionsUrlState { } export const useLogFlyout = () => { + const { services } = useKibanaContextForPlugin(); const { sourceId } = useLogSourceContext(); const [flyoutVisible, setFlyoutVisibility] = useState<boolean>(false); const [flyoutId, setFlyoutId] = useState<string | null>(null); @@ -39,7 +41,7 @@ export const useLogFlyout = () => { if (!flyoutId) { return; } - return await fetchLogEntriesItem({ sourceId, id: flyoutId }); + return await fetchLogEntriesItem({ sourceId, id: flyoutId }, services.http.fetch); }, onResolve: (response) => { if (response) { diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts index 030a9d180c7b5c..25865a30467f57 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_entries_highlights.ts @@ -4,12 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; -import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; import { LOG_ENTRIES_HIGHLIGHTS_PATH, @@ -18,14 +15,14 @@ import { logEntriesHighlightsResponseRT, } from '../../../../../common/http_api'; -export const fetchLogEntriesHighlights = async (requestArgs: LogEntriesHighlightsRequest) => { - const response = await npStart.http.fetch(LOG_ENTRIES_HIGHLIGHTS_PATH, { +export const fetchLogEntriesHighlights = async ( + requestArgs: LogEntriesHighlightsRequest, + fetch: HttpHandler +) => { + const response = await fetch(LOG_ENTRIES_HIGHLIGHTS_PATH, { method: 'POST', body: JSON.stringify(logEntriesHighlightsRequestRT.encode(requestArgs)), }); - return pipe( - logEntriesHighlightsResponseRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(logEntriesHighlightsResponseRT)(response); }; diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts index bda8f535549c7d..1cf95bc08a521b 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/api/fetch_log_summary_highlights.ts @@ -3,11 +3,9 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; -import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; + +import type { HttpHandler } from 'src/core/public'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; import { LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, @@ -17,15 +15,13 @@ import { } from '../../../../../common/http_api'; export const fetchLogSummaryHighlights = async ( - requestArgs: LogEntriesSummaryHighlightsRequest + requestArgs: LogEntriesSummaryHighlightsRequest, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch(LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, { + const response = await fetch(LOG_ENTRIES_SUMMARY_HIGHLIGHTS_PATH, { method: 'POST', body: JSON.stringify(logEntriesSummaryHighlightsRequestRT.encode(requestArgs)), }); - return pipe( - logEntriesSummaryHighlightsResponseRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(logEntriesSummaryHighlightsResponseRT)(response); }; diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx index dbeb8c71c11ebc..b4edebe8f8207c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_entry_highlights.tsx @@ -10,6 +10,7 @@ import { TimeKey } from '../../../../common/time'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogEntriesHighlights } from './api/fetch_log_entries_highlights'; import { LogEntry, LogEntriesHighlightsResponse } from '../../../../common/http_api'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export const useLogEntryHighlights = ( sourceId: string, @@ -21,6 +22,7 @@ export const useLogEntryHighlights = ( filterQuery: string | null, highlightTerms: string[] ) => { + const { services } = useKibanaContextForPlugin(); const [logEntryHighlights, setLogEntryHighlights] = useState< LogEntriesHighlightsResponse['data'] >([]); @@ -32,15 +34,18 @@ export const useLogEntryHighlights = ( throw new Error('Skipping request: Insufficient parameters'); } - return await fetchLogEntriesHighlights({ - sourceId, - startTimestamp, - endTimestamp, - center: centerPoint, - size, - query: filterQuery || undefined, - highlightTerms, - }); + return await fetchLogEntriesHighlights( + { + sourceId, + startTimestamp, + endTimestamp, + center: centerPoint, + size, + query: filterQuery || undefined, + highlightTerms, + }, + services.http.fetch + ); }, onResolve: (response) => { setLogEntryHighlights(response.data); diff --git a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts index 6d982ee004ccc5..14366891dbf593 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_highlights/log_summary_highlights.ts @@ -11,6 +11,7 @@ import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { fetchLogSummaryHighlights } from './api/fetch_log_summary_highlights'; import { LogEntriesSummaryHighlightsResponse } from '../../../../common/http_api'; import { useBucketSize } from '../log_summary/bucket_size'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export const useLogSummaryHighlights = ( sourceId: string, @@ -20,6 +21,7 @@ export const useLogSummaryHighlights = ( filterQuery: string | null, highlightTerms: string[] ) => { + const { services } = useKibanaContextForPlugin(); const [logSummaryHighlights, setLogSummaryHighlights] = useState< LogEntriesSummaryHighlightsResponse['data'] >([]); @@ -34,14 +36,17 @@ export const useLogSummaryHighlights = ( throw new Error('Skipping request: Insufficient parameters'); } - return await fetchLogSummaryHighlights({ - sourceId, - startTimestamp, - endTimestamp, - bucketSize, - query: filterQuery, - highlightTerms, - }); + return await fetchLogSummaryHighlights( + { + sourceId, + startTimestamp, + endTimestamp, + bucketSize, + query: filterQuery, + highlightTerms, + }, + services.http.fetch + ); }, onResolve: (response) => { setLogSummaryHighlights(response.data); diff --git a/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_configuration.ts b/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_configuration.ts index e847302a6d367d..c9ced069473a34 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_configuration.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_configuration.ts @@ -4,17 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'src/core/public'; +import type { HttpHandler } from 'src/core/public'; import { getLogSourceConfigurationPath, getLogSourceConfigurationSuccessResponsePayloadRT, } from '../../../../../common/http_api/log_sources'; import { decodeOrThrow } from '../../../../../common/runtime_types'; -export const callFetchLogSourceConfigurationAPI = async ( - sourceId: string, - fetch: HttpSetup['fetch'] -) => { +export const callFetchLogSourceConfigurationAPI = async (sourceId: string, fetch: HttpHandler) => { const response = await fetch(getLogSourceConfigurationPath(sourceId), { method: 'GET', }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_status.ts b/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_status.ts index 20e67a0a59c9fb..5bc409115e595f 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_status.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_source/api/fetch_log_source_status.ts @@ -4,14 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'src/core/public'; +import type { HttpHandler } from 'src/core/public'; import { getLogSourceStatusPath, getLogSourceStatusSuccessResponsePayloadRT, } from '../../../../../common/http_api/log_sources'; import { decodeOrThrow } from '../../../../../common/runtime_types'; -export const callFetchLogSourceStatusAPI = async (sourceId: string, fetch: HttpSetup['fetch']) => { +export const callFetchLogSourceStatusAPI = async (sourceId: string, fetch: HttpHandler) => { const response = await fetch(getLogSourceStatusPath(sourceId), { method: 'GET', }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_source/api/patch_log_source_configuration.ts b/x-pack/plugins/infra/public/containers/logs/log_source/api/patch_log_source_configuration.ts index 4361e4bef827f7..33212c5d3b0f22 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_source/api/patch_log_source_configuration.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_source/api/patch_log_source_configuration.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'src/core/public'; +import type { HttpHandler } from 'src/core/public'; import { getLogSourceConfigurationPath, patchLogSourceConfigurationSuccessResponsePayloadRT, @@ -16,7 +16,7 @@ import { decodeOrThrow } from '../../../../../common/runtime_types'; export const callPatchLogSourceConfigurationAPI = async ( sourceId: string, patchedProperties: LogSourceConfigurationPropertiesPatch, - fetch: HttpSetup['fetch'] + fetch: HttpHandler ) => { const response = await fetch(getLogSourceConfigurationPath(sourceId), { method: 'PATCH', diff --git a/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts b/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts index 51b32a4c4eacf1..e2dd4c523c03f7 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_source/log_source.ts @@ -7,7 +7,7 @@ import createContainer from 'constate'; import { useCallback, useMemo, useState } from 'react'; import { useMountedState } from 'react-use'; -import { HttpSetup } from 'src/core/public'; +import type { HttpHandler } from 'src/core/public'; import { LogSourceConfiguration, LogSourceConfigurationProperties, @@ -26,13 +26,7 @@ export { LogSourceStatus, }; -export const useLogSource = ({ - sourceId, - fetch, -}: { - sourceId: string; - fetch: HttpSetup['fetch']; -}) => { +export const useLogSource = ({ sourceId, fetch }: { sourceId: string; fetch: HttpHandler }) => { const getIsMounted = useMountedState(); const [sourceConfiguration, setSourceConfiguration] = useState< LogSourceConfiguration | undefined diff --git a/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts b/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts index b414408512db29..4a6da6063e9603 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_stream/index.ts @@ -9,6 +9,7 @@ import { esKuery } from '../../../../../../../src/plugins/data/public'; import { fetchLogEntries } from '../log_entries/api/fetch_log_entries'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { LogEntry, LogEntriesCursor } from '../../../../common/http_api'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; interface LogStreamProps { sourceId: string; @@ -31,6 +32,7 @@ export function useLogStream({ query, center, }: LogStreamProps): LogStreamState { + const { services } = useKibanaContextForPlugin(); const [entries, setEntries] = useState<LogStreamState['entries']>([]); const parsedQuery = useMemo(() => { @@ -47,13 +49,16 @@ export function useLogStream({ setEntries([]); const fetchPosition = center ? { center } : { before: 'last' }; - return fetchLogEntries({ - sourceId, - startTimestamp, - endTimestamp, - query: parsedQuery, - ...fetchPosition, - }); + return fetchLogEntries( + { + sourceId, + startTimestamp, + endTimestamp, + query: parsedQuery, + ...fetchPosition, + }, + services.http.fetch + ); }, onResolve: ({ data }) => { setEntries(data.entries); diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts b/x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts index f74f0dc0e31178..2be6538e21ebe5 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/api/fetch_log_summary.ts @@ -4,11 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; -import { throwErrors, createPlainError } from '../../../../../common/runtime_types'; +import type { HttpHandler } from 'src/core/public'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; import { LOG_ENTRIES_SUMMARY_PATH, @@ -17,14 +14,14 @@ import { logEntriesSummaryResponseRT, } from '../../../../../common/http_api'; -export const fetchLogSummary = async (requestArgs: LogEntriesSummaryRequest) => { - const response = await npStart.http.fetch(LOG_ENTRIES_SUMMARY_PATH, { +export const fetchLogSummary = async ( + requestArgs: LogEntriesSummaryRequest, + fetch: HttpHandler +) => { + const response = await fetch(LOG_ENTRIES_SUMMARY_PATH, { method: 'POST', body: JSON.stringify(logEntriesSummaryRequestRT.encode(requestArgs)), }); - return pipe( - logEntriesSummaryResponseRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(logEntriesSummaryResponseRT)(response); }; diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx index 73d0e5efdf06b4..652ea8c71dc44c 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.test.tsx @@ -5,6 +5,8 @@ */ import { renderHook } from '@testing-library/react-hooks'; +// We are using this inside a `jest.mock` call. Jest requires dynamic dependencies to be prefixed with `mock` +import { coreMock as mockCoreMock } from 'src/core/public/mocks'; import { useLogSummary } from './log_summary'; @@ -16,6 +18,10 @@ import { datemathToEpochMillis } from '../../../utils/datemath'; jest.mock('./api/fetch_log_summary', () => ({ fetchLogSummary: jest.fn() })); const fetchLogSummaryMock = fetchLogSummary as jest.MockedFunction<typeof fetchLogSummary>; +jest.mock('../../../hooks/use_kibana', () => ({ + useKibanaContextForPlugin: () => ({ services: mockCoreMock.createStart() }), +})); + describe('useLogSummary hook', () => { beforeEach(() => { fetchLogSummaryMock.mockClear(); @@ -53,7 +59,8 @@ describe('useLogSummary hook', () => { expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ sourceId: 'INITIAL_SOURCE_ID', - }) + }), + expect.anything() ); expect(result.current.buckets).toEqual(firstMockResponse.data.buckets); @@ -64,7 +71,8 @@ describe('useLogSummary hook', () => { expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ sourceId: 'CHANGED_SOURCE_ID', - }) + }), + expect.anything() ); expect(result.current.buckets).toEqual(secondMockResponse.data.buckets); }); @@ -96,7 +104,8 @@ describe('useLogSummary hook', () => { expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ query: 'INITIAL_FILTER_QUERY', - }) + }), + expect.anything() ); expect(result.current.buckets).toEqual(firstMockResponse.data.buckets); @@ -107,7 +116,8 @@ describe('useLogSummary hook', () => { expect(fetchLogSummaryMock).toHaveBeenLastCalledWith( expect.objectContaining({ query: 'CHANGED_FILTER_QUERY', - }) + }), + expect.anything() ); expect(result.current.buckets).toEqual(secondMockResponse.data.buckets); }); @@ -132,7 +142,8 @@ describe('useLogSummary hook', () => { expect.objectContaining({ startTimestamp: firstRange.startTimestamp, endTimestamp: firstRange.endTimestamp, - }) + }), + expect.anything() ); const secondRange = createMockDateRange('now-20s', 'now'); @@ -145,7 +156,8 @@ describe('useLogSummary hook', () => { expect.objectContaining({ startTimestamp: secondRange.startTimestamp, endTimestamp: secondRange.endTimestamp, - }) + }), + expect.anything() ); }); }); diff --git a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx index b83be776568631..be0d87f5d267d1 100644 --- a/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx +++ b/x-pack/plugins/infra/public/containers/logs/log_summary/log_summary.tsx @@ -10,6 +10,7 @@ import { useCancellableEffect } from '../../../utils/cancellable_effect'; import { fetchLogSummary } from './api/fetch_log_summary'; import { LogEntriesSummaryResponse } from '../../../../common/http_api'; import { useBucketSize } from './bucket_size'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export type LogSummaryBuckets = LogEntriesSummaryResponse['data']['buckets']; @@ -19,6 +20,7 @@ export const useLogSummary = ( endTimestamp: number | null, filterQuery: string | null ) => { + const { services } = useKibanaContextForPlugin(); const [logSummaryBuckets, setLogSummaryBuckets] = useState<LogSummaryBuckets>([]); const bucketSize = useBucketSize(startTimestamp, endTimestamp); @@ -28,13 +30,16 @@ export const useLogSummary = ( return; } - fetchLogSummary({ - sourceId, - startTimestamp, - endTimestamp, - bucketSize, - query: filterQuery, - }).then((response) => { + fetchLogSummary( + { + sourceId, + startTimestamp, + endTimestamp, + bucketSize, + query: filterQuery, + }, + services.http.fetch + ).then((response) => { if (!getIsCancelled()) { setLogSummaryBuckets(response.data.buckets); } diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts index 23fa338e74f147..fa7d8f14c6a9a9 100644 --- a/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_cleanup.ts @@ -5,21 +5,24 @@ */ import * as rt from 'io-ts'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../legacy_singletons'; - +import type { HttpHandler } from 'src/core/public'; import { getDatafeedId, getJobId } from '../../../../common/infra_ml'; -import { throwErrors, createPlainError } from '../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../common/runtime_types'; + +interface DeleteJobsRequestArgs<JobType extends string> { + spaceId: string; + sourceId: string; + jobTypes: JobType[]; +} export const callDeleteJobs = async <JobType extends string>( - spaceId: string, - sourceId: string, - jobTypes: JobType[] + requestArgs: DeleteJobsRequestArgs<JobType>, + fetch: HttpHandler ) => { + const { spaceId, sourceId, jobTypes } = requestArgs; + // NOTE: Deleting the jobs via this API will delete the datafeeds at the same time - const deleteJobsResponse = await npStart.http.fetch('/api/ml/jobs/delete_jobs', { + const deleteJobsResponse = await fetch('/api/ml/jobs/delete_jobs', { method: 'POST', body: JSON.stringify( deleteJobsRequestPayloadRT.encode({ @@ -28,28 +31,29 @@ export const callDeleteJobs = async <JobType extends string>( ), }); - return pipe( - deleteJobsResponsePayloadRT.decode(deleteJobsResponse), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(deleteJobsResponsePayloadRT)(deleteJobsResponse); }; -export const callGetJobDeletionTasks = async () => { - const jobDeletionTasksResponse = await npStart.http.fetch('/api/ml/jobs/deleting_jobs_tasks'); +export const callGetJobDeletionTasks = async (fetch: HttpHandler) => { + const jobDeletionTasksResponse = await fetch('/api/ml/jobs/deleting_jobs_tasks'); - return pipe( - getJobDeletionTasksResponsePayloadRT.decode(jobDeletionTasksResponse), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getJobDeletionTasksResponsePayloadRT)(jobDeletionTasksResponse); }; +interface StopDatafeedsRequestArgs<JobType extends string> { + spaceId: string; + sourceId: string; + jobTypes: JobType[]; +} + export const callStopDatafeeds = async <JobType extends string>( - spaceId: string, - sourceId: string, - jobTypes: JobType[] + requestArgs: StopDatafeedsRequestArgs<JobType>, + fetch: HttpHandler ) => { + const { spaceId, sourceId, jobTypes } = requestArgs; + // Stop datafeed due to https://github.com/elastic/kibana/issues/44652 - const stopDatafeedResponse = await npStart.http.fetch('/api/ml/jobs/stop_datafeeds', { + const stopDatafeedResponse = await fetch('/api/ml/jobs/stop_datafeeds', { method: 'POST', body: JSON.stringify( stopDatafeedsRequestPayloadRT.encode({ @@ -58,10 +62,7 @@ export const callStopDatafeeds = async <JobType extends string>( ), }); - return pipe( - stopDatafeedsResponsePayloadRT.decode(stopDatafeedResponse), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(stopDatafeedsResponsePayloadRT)(stopDatafeedResponse); }; export const deleteJobsRequestPayloadRT = rt.type({ diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts index 3fddb63f69791b..84b5df3d172c75 100644 --- a/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_get_jobs_summary_api.ts @@ -4,21 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { npStart } from '../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getJobId, jobCustomSettingsRT } from '../../../../common/infra_ml'; -import { createPlainError, throwErrors } from '../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../common/runtime_types'; + +interface RequestArgs<JobType extends string> { + spaceId: string; + sourceId: string; + jobTypes: JobType[]; +} export const callJobsSummaryAPI = async <JobType extends string>( - spaceId: string, - sourceId: string, - jobTypes: JobType[] + requestArgs: RequestArgs<JobType>, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch('/api/ml/jobs/jobs_summary', { + const { spaceId, sourceId, jobTypes } = requestArgs; + const response = await fetch('/api/ml/jobs/jobs_summary', { method: 'POST', body: JSON.stringify( fetchJobStatusRequestPayloadRT.encode({ @@ -26,10 +29,7 @@ export const callJobsSummaryAPI = async <JobType extends string>( }) ), }); - return pipe( - fetchJobStatusResponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(fetchJobStatusResponsePayloadRT)(response); }; export const fetchJobStatusRequestPayloadRT = rt.type({ diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts index d492522c120a11..75ce335fbe49c4 100644 --- a/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_get_module.ts @@ -4,24 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { npStart } from '../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { jobCustomSettingsRT } from '../../../../common/log_analysis'; -import { createPlainError, throwErrors } from '../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../common/runtime_types'; -export const callGetMlModuleAPI = async (moduleId: string) => { - const response = await npStart.http.fetch(`/api/ml/modules/get_module/${moduleId}`, { +export const callGetMlModuleAPI = async (moduleId: string, fetch: HttpHandler) => { + const response = await fetch(`/api/ml/modules/get_module/${moduleId}`, { method: 'GET', }); - return pipe( - getMlModuleResponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getMlModuleResponsePayloadRT)(response); }; const jobDefinitionRT = rt.type({ diff --git a/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts b/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts index 06b0e075387b01..36dced1bd26800 100644 --- a/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts +++ b/x-pack/plugins/infra/public/containers/ml/api/ml_setup_module_api.ts @@ -4,27 +4,38 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { identity } from 'fp-ts/lib/function'; -import { pipe } from 'fp-ts/lib/pipeable'; import * as rt from 'io-ts'; -import { npStart } from '../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getJobIdPrefix, jobCustomSettingsRT } from '../../../../common/infra_ml'; -import { createPlainError, throwErrors } from '../../../../common/runtime_types'; - -export const callSetupMlModuleAPI = async ( - moduleId: string, - start: number | undefined, - end: number | undefined, - spaceId: string, - sourceId: string, - indexPattern: string, - jobOverrides: SetupMlModuleJobOverrides[] = [], - datafeedOverrides: SetupMlModuleDatafeedOverrides[] = [], - query?: object -) => { - const response = await npStart.http.fetch(`/api/ml/modules/setup/${moduleId}`, { +import { decodeOrThrow } from '../../../../common/runtime_types'; + +interface RequestArgs { + moduleId: string; + start?: number; + end?: number; + spaceId: string; + sourceId: string; + indexPattern: string; + jobOverrides?: SetupMlModuleJobOverrides[]; + datafeedOverrides?: SetupMlModuleDatafeedOverrides[]; + query?: object; +} + +export const callSetupMlModuleAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { + const { + moduleId, + start, + end, + spaceId, + sourceId, + indexPattern, + jobOverrides = [], + datafeedOverrides = [], + query, + } = requestArgs; + + const response = await fetch(`/api/ml/modules/setup/${moduleId}`, { method: 'POST', body: JSON.stringify( setupMlModuleRequestPayloadRT.encode({ @@ -40,10 +51,7 @@ export const callSetupMlModuleAPI = async ( ), }); - return pipe( - setupMlModuleResponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(setupMlModuleResponsePayloadRT)(response); }; const setupMlModuleTimeParamsRT = rt.partial({ diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx index f4c90a459af6ac..bc488a51e2aff7 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_capabilities.tsx @@ -10,14 +10,15 @@ import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import { identity } from 'fp-ts/lib/function'; import { useTrackedPromise } from '../../utils/use_tracked_promise'; -import { npStart } from '../../legacy_singletons'; import { getMlCapabilitiesResponsePayloadRT, GetMlCapabilitiesResponsePayload, } from './api/ml_api_types'; import { throwErrors, createPlainError } from '../../../common/runtime_types'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; export const useInfraMLCapabilities = () => { + const { services } = useKibanaContextForPlugin(); const [mlCapabilities, setMlCapabilities] = useState<GetMlCapabilitiesResponsePayload>( initialMlCapabilities ); @@ -26,7 +27,7 @@ export const useInfraMLCapabilities = () => { { cancelPreviousOn: 'resolution', createPromise: async () => { - const rawResponse = await npStart.http.fetch('/api/ml/ml_capabilities'); + const rawResponse = await services.http.fetch('/api/ml/ml_capabilities'); return pipe( getMlCapabilitiesResponsePayloadRT.decode(rawResponse), diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx index 736982c8043b14..871e61ecfe5079 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_cleanup.tsx @@ -4,16 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ +import { HttpHandler } from 'src/core/public'; import { getJobId } from '../../../common/infra_ml'; import { callDeleteJobs, callGetJobDeletionTasks, callStopDatafeeds } from './api/ml_cleanup'; export const cleanUpJobsAndDatafeeds = async <JobType extends string>( spaceId: string, sourceId: string, - jobTypes: JobType[] + jobTypes: JobType[], + fetch: HttpHandler ) => { try { - await callStopDatafeeds(spaceId, sourceId, jobTypes); + await callStopDatafeeds({ spaceId, sourceId, jobTypes }, fetch); } catch (err) { // Proceed only if datafeed has been deleted or didn't exist in the first place if (err?.res?.status !== 404) { @@ -21,27 +23,29 @@ export const cleanUpJobsAndDatafeeds = async <JobType extends string>( } } - return await deleteJobs(spaceId, sourceId, jobTypes); + return await deleteJobs(spaceId, sourceId, jobTypes, fetch); }; const deleteJobs = async <JobType extends string>( spaceId: string, sourceId: string, - jobTypes: JobType[] + jobTypes: JobType[], + fetch: HttpHandler ) => { - const deleteJobsResponse = await callDeleteJobs(spaceId, sourceId, jobTypes); - await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes); + const deleteJobsResponse = await callDeleteJobs({ spaceId, sourceId, jobTypes }, fetch); + await waitUntilJobsAreDeleted(spaceId, sourceId, jobTypes, fetch); return deleteJobsResponse; }; const waitUntilJobsAreDeleted = async <JobType extends string>( spaceId: string, sourceId: string, - jobTypes: JobType[] + jobTypes: JobType[], + fetch: HttpHandler ) => { const moduleJobIds = jobTypes.map((jobType) => getJobId(spaceId, sourceId, jobType)); while (true) { - const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(); + const { jobIds: jobIdsBeingDeleted } = await callGetJobDeletionTasks(fetch); const needToWait = jobIdsBeingDeleted.some((jobId) => moduleJobIds.includes(jobId)); if (needToWait) { diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx index 349541d108f5ed..5408084a5246e9 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module.tsx @@ -6,6 +6,7 @@ import { useCallback, useMemo } from 'react'; import { DatasetFilter } from '../../../common/infra_ml'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { useTrackedPromise } from '../../utils/use_tracked_promise'; import { useModuleStatus } from './infra_ml_module_status'; import { ModuleDescriptor, ModuleSourceConfiguration } from './infra_ml_module_types'; @@ -17,6 +18,7 @@ export const useInfraMLModule = <JobType extends string>({ sourceConfiguration: ModuleSourceConfiguration; moduleDescriptor: ModuleDescriptor<JobType>; }) => { + const { services } = useKibanaContextForPlugin(); const { spaceId, sourceId, timestampField } = sourceConfiguration; const [moduleStatus, dispatchModuleStatus] = useModuleStatus(moduleDescriptor.jobTypes); @@ -25,7 +27,7 @@ export const useInfraMLModule = <JobType extends string>({ cancelPreviousOn: 'resolution', createPromise: async () => { dispatchModuleStatus({ type: 'fetchingJobStatuses' }); - return await moduleDescriptor.getJobSummary(spaceId, sourceId); + return await moduleDescriptor.getJobSummary(spaceId, sourceId, services.http.fetch); }, onResolve: (jobResponse) => { dispatchModuleStatus({ @@ -54,18 +56,25 @@ export const useInfraMLModule = <JobType extends string>({ ) => { dispatchModuleStatus({ type: 'startedSetup' }); const setupResult = await moduleDescriptor.setUpModule( - start, - end, - datasetFilter, { - indices: selectedIndices, - sourceId, - spaceId, - timestampField, + start, + end, + datasetFilter, + moduleSourceConfiguration: { + indices: selectedIndices, + sourceId, + spaceId, + timestampField, + }, + partitionField, }, - partitionField + services.http.fetch + ); + const jobSummaries = await moduleDescriptor.getJobSummary( + spaceId, + sourceId, + services.http.fetch ); - const jobSummaries = await moduleDescriptor.getJobSummary(spaceId, sourceId); return { setupResult, jobSummaries }; }, onResolve: ({ setupResult: { datafeeds, jobs }, jobSummaries }) => { @@ -89,7 +98,7 @@ export const useInfraMLModule = <JobType extends string>({ { cancelPreviousOn: 'resolution', createPromise: async () => { - return await moduleDescriptor.cleanUpModule(spaceId, sourceId); + return await moduleDescriptor.cleanUpModule(spaceId, sourceId, services.http.fetch); }, }, [spaceId, sourceId] diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx index 3c7ffcfd4a4e25..a747a2853d1f71 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_definition.tsx @@ -6,6 +6,7 @@ import { useCallback, useMemo, useState } from 'react'; import { getJobId } from '../../../common/log_analysis'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { useTrackedPromise } from '../../utils/use_tracked_promise'; import { JobSummary } from './api/ml_get_jobs_summary_api'; import { GetMlModuleResponsePayload, JobDefinition } from './api/ml_get_module'; @@ -18,6 +19,7 @@ export const useInfraMLModuleDefinition = <JobType extends string>({ sourceConfiguration: ModuleSourceConfiguration; moduleDescriptor: ModuleDescriptor<JobType>; }) => { + const { services } = useKibanaContextForPlugin(); const [moduleDefinition, setModuleDefinition] = useState< GetMlModuleResponsePayload | undefined >(); @@ -40,7 +42,7 @@ export const useInfraMLModuleDefinition = <JobType extends string>({ { cancelPreviousOn: 'resolution', createPromise: async () => { - return await moduleDescriptor.getModuleDefinition(); + return await moduleDescriptor.getModuleDefinition(services.http.fetch); }, onResolve: (response) => { setModuleDefinition(response); diff --git a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts index e36f38add641aa..976a64e8034bca 100644 --- a/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts +++ b/x-pack/plugins/infra/public/containers/ml/infra_ml_module_types.ts @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { HttpHandler } from 'src/core/public'; import { ValidateLogEntryDatasetsResponsePayload, ValidationIndicesResponsePayload, @@ -16,6 +16,14 @@ import { SetupMlModuleResponsePayload } from './api/ml_setup_module_api'; export { JobModelSizeStats, JobSummary } from './api/ml_get_jobs_summary_api'; +export interface SetUpModuleArgs { + start?: number | undefined; + end?: number | undefined; + datasetFilter?: DatasetFilter; + moduleSourceConfiguration: ModuleSourceConfiguration; + partitionField?: string; +} + export interface ModuleDescriptor<JobType extends string> { moduleId: string; moduleName: string; @@ -23,25 +31,32 @@ export interface ModuleDescriptor<JobType extends string> { jobTypes: JobType[]; bucketSpan: number; getJobIds: (spaceId: string, sourceId: string) => Record<JobType, string>; - getJobSummary: (spaceId: string, sourceId: string) => Promise<FetchJobStatusResponsePayload>; - getModuleDefinition: () => Promise<GetMlModuleResponsePayload>; + getJobSummary: ( + spaceId: string, + sourceId: string, + fetch: HttpHandler + ) => Promise<FetchJobStatusResponsePayload>; + getModuleDefinition: (fetch: HttpHandler) => Promise<GetMlModuleResponsePayload>; setUpModule: ( - start: number | undefined, - end: number | undefined, - datasetFilter: DatasetFilter, - sourceConfiguration: ModuleSourceConfiguration, - partitionField?: string + setUpModuleArgs: SetUpModuleArgs, + fetch: HttpHandler ) => Promise<SetupMlModuleResponsePayload>; - cleanUpModule: (spaceId: string, sourceId: string) => Promise<DeleteJobsResponsePayload>; + cleanUpModule: ( + spaceId: string, + sourceId: string, + fetch: HttpHandler + ) => Promise<DeleteJobsResponsePayload>; validateSetupIndices?: ( indices: string[], - timestampField: string + timestampField: string, + fetch: HttpHandler ) => Promise<ValidationIndicesResponsePayload>; validateSetupDatasets?: ( indices: string[], timestampField: string, startTime: number, - endTime: number + endTime: number, + fetch: HttpHandler ) => Promise<ValidateLogEntryDatasetsResponsePayload>; } diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts index 7ea87c3d21322d..47230cbed977f3 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_hosts/module_descriptor.ts @@ -5,7 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { ModuleDescriptor, ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { HttpHandler } from 'src/core/public'; +import { ModuleDescriptor, SetUpModuleArgs } from '../../infra_ml_module_types'; import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; @@ -14,7 +15,6 @@ import { metricsHostsJobTypes, getJobId, MetricsHostsJobType, - DatasetFilter, bucketSpan, } from '../../../../../common/infra_ml'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -48,24 +48,28 @@ const getJobIds = (spaceId: string, sourceId: string) => {} as Record<MetricsHostsJobType, string> ); -const getJobSummary = async (spaceId: string, sourceId: string) => { - const response = await callJobsSummaryAPI(spaceId, sourceId, metricsHostsJobTypes); +const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + const response = await callJobsSummaryAPI( + { spaceId, sourceId, jobTypes: metricsHostsJobTypes }, + fetch + ); const jobIds = Object.values(getJobIds(spaceId, sourceId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; -const getModuleDefinition = async () => { - return await callGetMlModuleAPI(moduleId); +const getModuleDefinition = async (fetch: HttpHandler) => { + return await callGetMlModuleAPI(moduleId, fetch); }; -const setUpModule = async ( - start: number | undefined, - end: number | undefined, - datasetFilter: DatasetFilter, - { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - partitionField?: string -) => { +const setUpModule = async (setUpModuleArgs: SetUpModuleArgs, fetch: HttpHandler) => { + const { + start, + end, + moduleSourceConfiguration: { spaceId, sourceId, indices, timestampField }, + partitionField, + } = setUpModuleArgs; + const indexNamePattern = indices.join(','); const jobIds: JobType[] = ['hosts_memory_usage', 'hosts_network_in', 'hosts_network_out']; @@ -128,14 +132,17 @@ const setUpModule = async ( }); return callSetupMlModuleAPI( - moduleId, - start, - end, - spaceId, - sourceId, - indexNamePattern, - jobOverrides, - datafeedOverrides + { + moduleId, + start, + end, + spaceId, + sourceId, + indexPattern: indexNamePattern, + jobOverrides, + datafeedOverrides, + }, + fetch ); }; @@ -159,8 +166,8 @@ const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => { } }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes); +const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsHostsJobTypes, fetch); }; export const metricHostsModule: ModuleDescriptor<MetricsHostsJobType> = { diff --git a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts index eaf7489c84eb4d..488803dc113b0b 100644 --- a/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts +++ b/x-pack/plugins/infra/public/containers/ml/modules/metrics_k8s/module_descriptor.ts @@ -5,7 +5,8 @@ */ import { i18n } from '@kbn/i18n'; -import { ModuleDescriptor, ModuleSourceConfiguration } from '../../infra_ml_module_types'; +import { HttpHandler } from 'src/core/public'; +import { ModuleDescriptor, SetUpModuleArgs } from '../../infra_ml_module_types'; import { cleanUpJobsAndDatafeeds } from '../../infra_ml_cleanup'; import { callJobsSummaryAPI } from '../../api/ml_get_jobs_summary_api'; import { callGetMlModuleAPI } from '../../api/ml_get_module'; @@ -14,7 +15,6 @@ import { metricsK8SJobTypes, getJobId, MetricK8sJobType, - DatasetFilter, bucketSpan, } from '../../../../../common/infra_ml'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths @@ -49,24 +49,28 @@ const getJobIds = (spaceId: string, sourceId: string) => {} as Record<MetricK8sJobType, string> ); -const getJobSummary = async (spaceId: string, sourceId: string) => { - const response = await callJobsSummaryAPI(spaceId, sourceId, metricsK8SJobTypes); +const getJobSummary = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + const response = await callJobsSummaryAPI( + { spaceId, sourceId, jobTypes: metricsK8SJobTypes }, + fetch + ); const jobIds = Object.values(getJobIds(spaceId, sourceId)); return response.filter((jobSummary) => jobIds.includes(jobSummary.id)); }; -const getModuleDefinition = async () => { - return await callGetMlModuleAPI(moduleId); +const getModuleDefinition = async (fetch: HttpHandler) => { + return await callGetMlModuleAPI(moduleId, fetch); }; -const setUpModule = async ( - start: number | undefined, - end: number | undefined, - datasetFilter: DatasetFilter, - { spaceId, sourceId, indices, timestampField }: ModuleSourceConfiguration, - partitionField?: string -) => { +const setUpModule = async (setUpModuleArgs: SetUpModuleArgs, fetch: HttpHandler) => { + const { + start, + end, + moduleSourceConfiguration: { spaceId, sourceId, indices, timestampField }, + partitionField, + } = setUpModuleArgs; + const indexNamePattern = indices.join(','); const jobIds: JobType[] = ['k8s_memory_usage', 'k8s_network_in', 'k8s_network_out']; const jobOverrides = jobIds.map((id) => { @@ -133,14 +137,17 @@ const setUpModule = async ( }); return callSetupMlModuleAPI( - moduleId, - start, - end, - spaceId, - sourceId, - indexNamePattern, - jobOverrides, - datafeedOverrides + { + moduleId, + start, + end, + spaceId, + sourceId, + indexPattern: indexNamePattern, + jobOverrides, + datafeedOverrides, + }, + fetch ); }; @@ -164,8 +171,8 @@ const getDefaultJobConfigs = (jobId: JobType): { datafeed: any; job: any } => { } }; -const cleanUpModule = async (spaceId: string, sourceId: string) => { - return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes); +const cleanUpModule = async (spaceId: string, sourceId: string, fetch: HttpHandler) => { + return await cleanUpJobsAndDatafeeds(spaceId, sourceId, metricsK8SJobTypes, fetch); }; export const metricHostsModule: ModuleDescriptor<MetricK8sJobType> = { diff --git a/x-pack/plugins/infra/public/legacy_singletons.ts b/x-pack/plugins/infra/public/legacy_singletons.ts deleted file mode 100644 index f57047f21c2816..00000000000000 --- a/x-pack/plugins/infra/public/legacy_singletons.ts +++ /dev/null @@ -1,14 +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; - * you may not use this file except in compliance with the Elastic License. - */ -import { CoreStart } from 'kibana/public'; - -let npStart: CoreStart; - -export function registerStartSingleton(start: CoreStart) { - npStart = start; -} - -export { npStart }; diff --git a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx index 945b299674aaa6..4f83e37d7e0292 100644 --- a/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx +++ b/x-pack/plugins/infra/public/pages/link_to/link_to_logs.test.tsx @@ -14,7 +14,6 @@ import { createMemoryHistory } from 'history'; import React from 'react'; import { Route, Router, Switch } from 'react-router-dom'; import { httpServiceMock } from 'src/core/public/mocks'; -// import { HttpSetup } from 'src/core/public'; import { KibanaContextProvider } from 'src/plugins/kibana_react/public'; import { useLogSource } from '../../containers/logs/log_source'; import { diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts index a8cd7854efb6bc..5f34d45635b608 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_datasets.ts @@ -4,24 +4,28 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getLogEntryCategoryDatasetsRequestPayloadRT, getLogEntryCategoryDatasetsSuccessReponsePayloadRT, LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH, } from '../../../../../common/http_api/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; +} export const callGetLogEntryCategoryDatasetsAPI = async ( - sourceId: string, - startTime: number, - endTime: number + requestArgs: RequestArgs, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH, { + const { sourceId, startTime, endTime } = requestArgs; + + const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_DATASETS_PATH, { method: 'POST', body: JSON.stringify( getLogEntryCategoryDatasetsRequestPayloadRT.encode({ @@ -36,8 +40,5 @@ export const callGetLogEntryCategoryDatasetsAPI = async ( ), }); - return pipe( - getLogEntryCategoryDatasetsSuccessReponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getLogEntryCategoryDatasetsSuccessReponsePayloadRT)(response); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts index a10d077a2dd4ff..c4b756ebf5d58d 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_log_entry_category_examples.ts @@ -4,26 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getLogEntryCategoryExamplesRequestPayloadRT, getLogEntryCategoryExamplesSuccessReponsePayloadRT, LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_EXAMPLES_PATH, } from '../../../../../common/http_api/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; + categoryId: number; + exampleCount: number; +} export const callGetLogEntryCategoryExamplesAPI = async ( - sourceId: string, - startTime: number, - endTime: number, - categoryId: number, - exampleCount: number + requestArgs: RequestArgs, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_EXAMPLES_PATH, { + const { sourceId, startTime, endTime, categoryId, exampleCount } = requestArgs; + + const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORY_EXAMPLES_PATH, { method: 'POST', body: JSON.stringify( getLogEntryCategoryExamplesRequestPayloadRT.encode({ @@ -40,8 +44,5 @@ export const callGetLogEntryCategoryExamplesAPI = async ( ), }); - return pipe( - getLogEntryCategoryExamplesSuccessReponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getLogEntryCategoryExamplesSuccessReponsePayloadRT)(response); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts index 2ebcff4fd3ca53..fd53803796339f 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/service_calls/get_top_log_entry_categories.ts @@ -4,28 +4,31 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getLogEntryCategoriesRequestPayloadRT, getLogEntryCategoriesSuccessReponsePayloadRT, LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORIES_PATH, } from '../../../../../common/http_api/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; + +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; + categoryCount: number; + datasets?: string[]; +} export const callGetTopLogEntryCategoriesAPI = async ( - sourceId: string, - startTime: number, - endTime: number, - categoryCount: number, - datasets?: string[] + requestArgs: RequestArgs, + fetch: HttpHandler ) => { + const { sourceId, startTime, endTime, categoryCount, datasets } = requestArgs; const intervalDuration = endTime - startTime; - const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORIES_PATH, { + const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_CATEGORIES_PATH, { method: 'POST', body: JSON.stringify( getLogEntryCategoriesRequestPayloadRT.encode({ @@ -60,8 +63,5 @@ export const callGetTopLogEntryCategoriesAPI = async ( ), }); - return pipe( - getLogEntryCategoriesSuccessReponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getLogEntryCategoriesSuccessReponsePayloadRT)(response); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts index 123b188046b853..0a12c433db60a2 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_categories_results.ts @@ -13,6 +13,7 @@ import { import { useTrackedPromise, CanceledPromiseError } from '../../../utils/use_tracked_promise'; import { callGetTopLogEntryCategoriesAPI } from './service_calls/get_top_log_entry_categories'; import { callGetLogEntryCategoryDatasetsAPI } from './service_calls/get_log_entry_category_datasets'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; type TopLogEntryCategories = GetLogEntryCategoriesSuccessResponsePayload['data']['categories']; type LogEntryCategoryDatasets = GetLogEntryCategoryDatasetsSuccessResponsePayload['data']['datasets']; @@ -34,6 +35,7 @@ export const useLogEntryCategoriesResults = ({ sourceId: string; startTime: number; }) => { + const { services } = useKibanaContextForPlugin(); const [topLogEntryCategories, setTopLogEntryCategories] = useState<TopLogEntryCategories>([]); const [logEntryCategoryDatasets, setLogEntryCategoryDatasets] = useState< LogEntryCategoryDatasets @@ -44,11 +46,14 @@ export const useLogEntryCategoriesResults = ({ cancelPreviousOn: 'creation', createPromise: async () => { return await callGetTopLogEntryCategoriesAPI( - sourceId, - startTime, - endTime, - categoriesCount, - filteredDatasets + { + sourceId, + startTime, + endTime, + categoryCount: categoriesCount, + datasets: filteredDatasets, + }, + services.http.fetch ); }, onResolve: ({ data: { categories } }) => { @@ -71,7 +76,10 @@ export const useLogEntryCategoriesResults = ({ { cancelPreviousOn: 'creation', createPromise: async () => { - return await callGetLogEntryCategoryDatasetsAPI(sourceId, startTime, endTime); + return await callGetLogEntryCategoryDatasetsAPI( + { sourceId, startTime, endTime }, + services.http.fetch + ); }, onResolve: ({ data: { datasets } }) => { setLogEntryCategoryDatasets(datasets); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx index cdf3b642a80129..84b9f045288cc3 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/use_log_entry_category_examples.tsx @@ -7,6 +7,7 @@ import { useMemo, useState } from 'react'; import { LogEntryCategoryExample } from '../../../../common/http_api'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callGetLogEntryCategoryExamplesAPI } from './service_calls/get_log_entry_category_examples'; @@ -23,6 +24,8 @@ export const useLogEntryCategoryExamples = ({ sourceId: string; startTime: number; }) => { + const { services } = useKibanaContextForPlugin(); + const [logEntryCategoryExamples, setLogEntryCategoryExamples] = useState< LogEntryCategoryExample[] >([]); @@ -32,11 +35,14 @@ export const useLogEntryCategoryExamples = ({ cancelPreviousOn: 'creation', createPromise: async () => { return await callGetLogEntryCategoryExamplesAPI( - sourceId, - startTime, - endTime, - categoryId, - exampleCount + { + sourceId, + startTime, + endTime, + categoryId, + exampleCount, + }, + services.http.fetch ); }, onResolve: ({ data: { examples } }) => { diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts index 21696df566ed9d..7f90604bfefdd3 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getLogEntryAnomaliesRequestPayloadRT, getLogEntryAnomaliesSuccessReponsePayloadRT, @@ -13,15 +13,18 @@ import { import { decodeOrThrow } from '../../../../../common/runtime_types'; import { Sort, Pagination } from '../../../../../common/http_api/log_analysis'; -export const callGetLogEntryAnomaliesAPI = async ( - sourceId: string, - startTime: number, - endTime: number, - sort: Sort, - pagination: Pagination, - datasets?: string[] -) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_PATH, { +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; + sort: Sort; + pagination: Pagination; + datasets?: string[]; +} + +export const callGetLogEntryAnomaliesAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { + const { sourceId, startTime, endTime, sort, pagination, datasets } = requestArgs; + const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_PATH, { method: 'POST', body: JSON.stringify( getLogEntryAnomaliesRequestPayloadRT.encode({ diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts index 24be5a646d1039..c62bec691590c0 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_anomalies_datasets.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { getLogEntryAnomaliesDatasetsRequestPayloadRT, @@ -12,12 +12,18 @@ import { LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_DATASETS_PATH, } from '../../../../../common/http_api/log_analysis'; +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; +} + export const callGetLogEntryAnomaliesDatasetsAPI = async ( - sourceId: string, - startTime: number, - endTime: number + requestArgs: RequestArgs, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_DATASETS_PATH, { + const { sourceId, startTime, endTime } = requestArgs; + const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_ANOMALIES_DATASETS_PATH, { method: 'POST', body: JSON.stringify( getLogEntryAnomaliesDatasetsRequestPayloadRT.encode({ diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts index a125b53f9e6356..ab724a2f435b29 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_examples.ts @@ -4,27 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getLogEntryExamplesRequestPayloadRT, getLogEntryExamplesSuccessReponsePayloadRT, LOG_ANALYSIS_GET_LOG_ENTRY_RATE_EXAMPLES_PATH, } from '../../../../../common/http_api/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; -export const callGetLogEntryExamplesAPI = async ( - sourceId: string, - startTime: number, - endTime: number, - dataset: string, - exampleCount: number, - categoryId?: string -) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_EXAMPLES_PATH, { +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; + dataset: string; + exampleCount: number; + categoryId?: string; +} + +export const callGetLogEntryExamplesAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { + const { sourceId, startTime, endTime, dataset, exampleCount, categoryId } = requestArgs; + const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_EXAMPLES_PATH, { method: 'POST', body: JSON.stringify( getLogEntryExamplesRequestPayloadRT.encode({ @@ -42,8 +42,5 @@ export const callGetLogEntryExamplesAPI = async ( ), }); - return pipe( - getLogEntryExamplesSuccessReponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getLogEntryExamplesSuccessReponsePayloadRT)(response); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts index 77111d279309df..c9189bd8039551 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/service_calls/get_log_entry_rate.ts @@ -4,25 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -import { fold } from 'fp-ts/lib/Either'; -import { pipe } from 'fp-ts/lib/pipeable'; -import { identity } from 'fp-ts/lib/function'; -import { npStart } from '../../../../legacy_singletons'; +import type { HttpHandler } from 'src/core/public'; import { getLogEntryRateRequestPayloadRT, getLogEntryRateSuccessReponsePayloadRT, LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH, } from '../../../../../common/http_api/log_analysis'; -import { createPlainError, throwErrors } from '../../../../../common/runtime_types'; +import { decodeOrThrow } from '../../../../../common/runtime_types'; -export const callGetLogEntryRateAPI = async ( - sourceId: string, - startTime: number, - endTime: number, - bucketDuration: number, - datasets?: string[] -) => { - const response = await npStart.http.fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH, { +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; + bucketDuration: number; + datasets?: string[]; +} + +export const callGetLogEntryRateAPI = async (requestArgs: RequestArgs, fetch: HttpHandler) => { + const { sourceId, startTime, endTime, bucketDuration, datasets } = requestArgs; + const response = await fetch(LOG_ANALYSIS_GET_LOG_ENTRY_RATE_PATH, { method: 'POST', body: JSON.stringify( getLogEntryRateRequestPayloadRT.encode({ @@ -38,8 +38,5 @@ export const callGetLogEntryRateAPI = async ( }) ), }); - return pipe( - getLogEntryRateSuccessReponsePayloadRT.decode(response), - fold(throwErrors(createPlainError), identity) - ); + return decodeOrThrow(getLogEntryRateSuccessReponsePayloadRT)(response); }; diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts index 52632e54390a9b..37c99272f0872f 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_anomalies_results.ts @@ -16,6 +16,7 @@ import { GetLogEntryAnomaliesDatasetsSuccessResponsePayload, LogEntryAnomaly, } from '../../../../common/http_api/log_analysis'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; export type SortOptions = Sort; export type PaginationOptions = Pick<Pagination, 'pageSize'>; @@ -161,6 +162,8 @@ export const useLogEntryAnomaliesResults = ({ }; }; + const { services } = useKibanaContextForPlugin(); + const [reducerState, dispatch] = useReducer(stateReducer, STATE_DEFAULTS, initStateReducer); const [logEntryAnomalies, setLogEntryAnomalies] = useState<LogEntryAnomalies>([]); @@ -177,15 +180,18 @@ export const useLogEntryAnomaliesResults = ({ filteredDatasets: queryFilteredDatasets, } = reducerState; return await callGetLogEntryAnomaliesAPI( - sourceId, - queryStartTime, - queryEndTime, - sortOptions, { - ...paginationOptions, - cursor: paginationCursor, + sourceId, + startTime: queryStartTime, + endTime: queryEndTime, + sort: sortOptions, + pagination: { + ...paginationOptions, + cursor: paginationCursor, + }, + datasets: queryFilteredDatasets, }, - queryFilteredDatasets + services.http.fetch ); }, onResolve: ({ data: { anomalies, paginationCursors: requestCursors, hasMoreEntries } }) => { @@ -286,7 +292,10 @@ export const useLogEntryAnomaliesResults = ({ { cancelPreviousOn: 'creation', createPromise: async () => { - return await callGetLogEntryAnomaliesDatasetsAPI(sourceId, startTime, endTime); + return await callGetLogEntryAnomaliesDatasetsAPI( + { sourceId, startTime, endTime }, + services.http.fetch + ); }, onResolve: ({ data: { datasets } }) => { setLogEntryAnomaliesDatasets(datasets); diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts index fae5bd200a4154..e809ab9cd5a6f2 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_examples.ts @@ -7,6 +7,7 @@ import { useMemo, useState } from 'react'; import { LogEntryExample } from '../../../../common/http_api'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callGetLogEntryExamplesAPI } from './service_calls/get_log_entry_examples'; @@ -25,6 +26,7 @@ export const useLogEntryExamples = ({ startTime: number; categoryId?: string; }) => { + const { services } = useKibanaContextForPlugin(); const [logEntryExamples, setLogEntryExamples] = useState<LogEntryExample[]>([]); const [getLogEntryExamplesRequest, getLogEntryExamples] = useTrackedPromise( @@ -32,12 +34,15 @@ export const useLogEntryExamples = ({ cancelPreviousOn: 'creation', createPromise: async () => { return await callGetLogEntryExamplesAPI( - sourceId, - startTime, - endTime, - dataset, - exampleCount, - categoryId + { + sourceId, + startTime, + endTime, + dataset, + exampleCount, + categoryId, + }, + services.http.fetch ); }, onResolve: ({ data: { examples } }) => { diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts index a52dab58cb018d..aef94afa505f10 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_rate/use_log_entry_rate_results.ts @@ -12,6 +12,7 @@ import { LogEntryRatePartition, LogEntryRateAnomaly, } from '../../../../common/http_api/log_analysis'; +import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; import { useTrackedPromise } from '../../../utils/use_tracked_promise'; import { callGetLogEntryRateAPI } from './service_calls/get_log_entry_rate'; @@ -49,6 +50,7 @@ export const useLogEntryRateResults = ({ bucketDuration: number; filteredDatasets?: string[]; }) => { + const { services } = useKibanaContextForPlugin(); const [logEntryRate, setLogEntryRate] = useState<LogEntryRateResults | null>(null); const [getLogEntryRateRequest, getLogEntryRate] = useTrackedPromise( @@ -56,11 +58,14 @@ export const useLogEntryRateResults = ({ cancelPreviousOn: 'resolution', createPromise: async () => { return await callGetLogEntryRateAPI( - sourceId, - startTime, - endTime, - bucketDuration, - filteredDatasets + { + sourceId, + startTime, + endTime, + bucketDuration, + datasets: filteredDatasets, + }, + services.http.fetch ); }, onResolve: ({ data }) => { diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts index f33e3ea16b3896..02170f41a32ca8 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_hosts_anomalies.ts @@ -5,6 +5,7 @@ */ import { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; +import { HttpHandler } from 'src/core/public'; import { INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, Metric, @@ -16,8 +17,8 @@ import { getMetricsHostsAnomaliesSuccessReponsePayloadRT, } from '../../../../../common/http_api/infra_ml'; import { useTrackedPromise } from '../../../../utils/use_tracked_promise'; -import { npStart } from '../../../../legacy_singletons'; import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; export type SortOptions = Sort; export type PaginationOptions = Pick<Pagination, 'pageSize'>; @@ -149,6 +150,7 @@ export const useMetricsHostsAnomaliesResults = ({ onGetMetricsHostsAnomaliesDatasetsError?: (error: Error) => void; filteredDatasets?: string[]; }) => { + const { services } = useKibanaContextForPlugin(); const initStateReducer = (stateDefaults: ReducerStateDefaults): ReducerState => { return { ...stateDefaults, @@ -177,15 +179,18 @@ export const useMetricsHostsAnomaliesResults = ({ paginationCursor, } = reducerState; return await callGetMetricHostsAnomaliesAPI( - sourceId, - queryStartTime, - queryEndTime, - metric, - sortOptions, { - ...paginationOptions, - cursor: paginationCursor, - } + sourceId, + startTime: queryStartTime, + endTime: queryEndTime, + metric, + sort: sortOptions, + pagination: { + ...paginationOptions, + cursor: paginationCursor, + }, + }, + services.http.fetch ); }, onResolve: ({ data: { anomalies, paginationCursors: requestCursors, hasMoreEntries } }) => { @@ -288,15 +293,21 @@ export const useMetricsHostsAnomaliesResults = ({ }; }; +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; + metric: Metric; + sort: Sort; + pagination: Pagination; +} + export const callGetMetricHostsAnomaliesAPI = async ( - sourceId: string, - startTime: number, - endTime: number, - metric: Metric, - sort: Sort, - pagination: Pagination + requestArgs: RequestArgs, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch(INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, { + const { sourceId, startTime, endTime, metric, sort, pagination } = requestArgs; + const response = await fetch(INFA_ML_GET_METRICS_HOSTS_ANOMALIES_PATH, { method: 'POST', body: JSON.stringify( getMetricsHostsAnomaliesRequestPayloadRT.encode({ diff --git a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts index 89e70c4c5c4c79..951951b9b61068 100644 --- a/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts +++ b/x-pack/plugins/infra/public/pages/metrics/inventory_view/hooks/use_metrics_k8s_anomalies.ts @@ -5,6 +5,7 @@ */ import { useMemo, useState, useCallback, useEffect, useReducer } from 'react'; +import { HttpHandler } from 'src/core/public'; import { Sort, Pagination, @@ -16,8 +17,8 @@ import { Metric, } from '../../../../../common/http_api/infra_ml'; import { useTrackedPromise } from '../../../../utils/use_tracked_promise'; -import { npStart } from '../../../../legacy_singletons'; import { decodeOrThrow } from '../../../../../common/runtime_types'; +import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; export type SortOptions = Sort; export type PaginationOptions = Pick<Pagination, 'pageSize'>; @@ -149,6 +150,7 @@ export const useMetricsK8sAnomaliesResults = ({ onGetMetricsHostsAnomaliesDatasetsError?: (error: Error) => void; filteredDatasets?: string[]; }) => { + const { services } = useKibanaContextForPlugin(); const initStateReducer = (stateDefaults: ReducerStateDefaults): ReducerState => { return { ...stateDefaults, @@ -178,16 +180,19 @@ export const useMetricsK8sAnomaliesResults = ({ filteredDatasets: queryFilteredDatasets, } = reducerState; return await callGetMetricsK8sAnomaliesAPI( - sourceId, - queryStartTime, - queryEndTime, - metric, - sortOptions, { - ...paginationOptions, - cursor: paginationCursor, + sourceId, + startTime: queryStartTime, + endTime: queryEndTime, + metric, + sort: sortOptions, + pagination: { + ...paginationOptions, + cursor: paginationCursor, + }, + datasets: queryFilteredDatasets, }, - queryFilteredDatasets + services.http.fetch ); }, onResolve: ({ data: { anomalies, paginationCursors: requestCursors, hasMoreEntries } }) => { @@ -290,16 +295,22 @@ export const useMetricsK8sAnomaliesResults = ({ }; }; +interface RequestArgs { + sourceId: string; + startTime: number; + endTime: number; + metric: Metric; + sort: Sort; + pagination: Pagination; + datasets?: string[]; +} + export const callGetMetricsK8sAnomaliesAPI = async ( - sourceId: string, - startTime: number, - endTime: number, - metric: Metric, - sort: Sort, - pagination: Pagination, - datasets?: string[] + requestArgs: RequestArgs, + fetch: HttpHandler ) => { - const response = await npStart.http.fetch(INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, { + const { sourceId, startTime, endTime, metric, sort, pagination, datasets } = requestArgs; + const response = await fetch(INFA_ML_GET_METRICS_K8S_ANOMALIES_PATH, { method: 'POST', body: JSON.stringify( getMetricsK8sAnomaliesRequestPayloadRT.encode({ diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 3c6b1a14cfd475..0e49ca93010fdf 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -9,7 +9,6 @@ import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public'; import { createMetricThresholdAlertType } from './alerting/metric_threshold'; import { createInventoryMetricAlertType } from './alerting/inventory'; import { getAlertType as getLogsAlertType } from './alerting/log_threshold'; -import { registerStartSingleton } from './legacy_singletons'; import { registerFeatures } from './register_feature'; import { InfraClientSetupDeps, @@ -98,9 +97,7 @@ export class Plugin implements InfraClientPluginClass { }); } - start(core: InfraClientCoreStart, _plugins: InfraClientStartDeps) { - registerStartSingleton(core); - } + start(_core: InfraClientCoreStart, _plugins: InfraClientStartDeps) {} stop() {} } From e9fd3902c5a503083fa629e787d7deff1fc7a3bd Mon Sep 17 00:00:00 2001 From: Nick Partridge <nick.ryan.partridge@gmail.com> Date: Fri, 2 Oct 2020 12:48:40 -0500 Subject: [PATCH 091/128] upgrade @elastic/charts to v23.0.0 (#79226) Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- package.json | 2 +- packages/kbn-ui-shared-deps/package.json | 2 +- .../lens/public/xy_visualization/expression.test.tsx | 2 +- yarn.lock | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index ff98d7f85dceff..5089e6e1a140d8 100644 --- a/package.json +++ b/package.json @@ -230,7 +230,7 @@ "@babel/parser": "^7.11.2", "@babel/types": "^7.11.0", "@elastic/apm-rum": "^5.6.1", - "@elastic/charts": "21.1.2", + "@elastic/charts": "23.0.0", "@elastic/ems-client": "7.10.0", "@elastic/eslint-config-kibana": "0.15.0", "@elastic/eslint-plugin-eui": "0.0.2", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index 278e8efd2d29e2..e5f1a06e5bffa9 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -9,7 +9,7 @@ "kbn:watch": "node scripts/build --dev --watch" }, "dependencies": { - "@elastic/charts": "21.1.2", + "@elastic/charts": "23.0.0", "@elastic/eui": "29.0.0", "@elastic/numeral": "^2.5.0", "@kbn/i18n": "1.0.0", diff --git a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx index 5fc89d831a961c..405491ddc372a8 100644 --- a/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx +++ b/x-pack/plugins/lens/public/xy_visualization/expression.test.tsx @@ -751,7 +751,7 @@ describe('xy_expression', () => { }); test('onElementClick returns correct context data', () => { - const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null }; + const geometry: GeometryValue = { x: 5, y: 1, accessor: 'y1', mark: null, datum: {} }; const series = { key: 'spec{d}yAccessor{d}splitAccessors{b-2}', specId: 'd', diff --git a/yarn.lock b/yarn.lock index 2d72b6d6c3bb6e..971a94bfe56c3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1218,10 +1218,10 @@ dependencies: "@elastic/apm-rum-core" "^5.7.0" -"@elastic/charts@21.1.2": - version "21.1.2" - resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-21.1.2.tgz#da7e9c1025bf730a738b6ac6d7024d97dd2b5aa2" - integrity sha512-Uri+Xolgii7/mRSarfXTfA6X2JC76ILIxTPO8RlYdI44gzprJfUO7Aw5s8vVQke3x6Cu39a+9B0s6TY4GAaApQ== +"@elastic/charts@23.0.0": + version "23.0.0" + resolved "https://registry.yarnpkg.com/@elastic/charts/-/charts-23.0.0.tgz#6152f5ef0e31b2d7d7a5c95a2f7baba0f4296c18" + integrity sha512-FUZ72mzkIVYubtZMPA1bT7rrua1X2ZnsdzO+qRwR81QLfUYM6ht3WLaffVAUT1y6eXmnXFs+d88U7jlsontN6w== dependencies: "@popperjs/core" "^2.4.0" chroma-js "^2.1.0" From 85528d0ecd8422f9f855ee519ad94f55100f5149 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck <thomas@elastic.co> Date: Fri, 2 Oct 2020 14:04:03 -0400 Subject: [PATCH 092/128] [Maps] Register gold+ feature use (#79011) --- x-pack/plugins/maps/common/constants.ts | 1 + .../maps/public/actions/layer_actions.test.ts | 51 ++++++++++++++++ .../maps/public/actions/layer_actions.ts | 8 ++- .../blended_vector_layer.ts | 8 +++ .../maps/public/classes/layers/layer.tsx | 6 ++ .../layers/vector_layer/vector_layer.tsx | 4 ++ .../es_geo_grid_source/es_geo_grid_source.js | 9 +++ .../es_geo_grid_source.test.ts | 30 +++++++-- .../maps/public/classes/sources/source.ts | 6 ++ .../maps/public/index_pattern_util.test.ts | 5 +- .../plugins/maps/public/index_pattern_util.ts | 3 +- x-pack/plugins/maps/public/kibana_services.ts | 9 +-- .../plugins/maps/public/licensed_features.ts | 61 +++++++++++++++++++ x-pack/plugins/maps/public/meta.test.js | 6 +- x-pack/plugins/maps/public/meta.ts | 2 +- x-pack/plugins/maps/public/plugin.ts | 17 ++---- .../maps/public/selectors/map_selectors.ts | 6 +- 17 files changed, 197 insertions(+), 35 deletions(-) create mode 100644 x-pack/plugins/maps/public/actions/layer_actions.test.ts create mode 100644 x-pack/plugins/maps/public/licensed_features.ts diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index be891b6e596088..469a4023434a85 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -5,6 +5,7 @@ */ import { i18n } from '@kbn/i18n'; import { FeatureCollection } from 'geojson'; + export const EMS_APP_NAME = 'kibana'; export const EMS_CATALOGUE_PATH = 'ems/catalogue'; diff --git a/x-pack/plugins/maps/public/actions/layer_actions.test.ts b/x-pack/plugins/maps/public/actions/layer_actions.test.ts new file mode 100644 index 00000000000000..09a22dca271d79 --- /dev/null +++ b/x-pack/plugins/maps/public/actions/layer_actions.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { addLayer } from './layer_actions'; +import { LayerDescriptor } from '../../common/descriptor_types'; +import { LICENSED_FEATURES } from '../licensed_features'; + +const getStoreMock = jest.fn(); +const dispatchMock = jest.fn(); + +describe('layer_actions', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('addLayer', () => { + const notifyLicensedFeatureUsageMock = jest.fn(); + + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('../licensed_features').notifyLicensedFeatureUsage = (feature: LICENSED_FEATURES) => { + notifyLicensedFeatureUsageMock(feature); + }; + + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('../selectors/map_selectors').getMapReady = () => { + return true; + }; + + // eslint-disable-next-line @typescript-eslint/no-var-requires + require('../selectors/map_selectors').createLayerInstance = () => { + return { + getLicensedFeatures() { + return [LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE]; + }, + }; + }; + }); + + it('should register feature-use', async () => { + const action = addLayer(({} as unknown) as LayerDescriptor); + await action(dispatchMock, getStoreMock); + expect(notifyLicensedFeatureUsageMock).toHaveBeenCalledWith( + LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE + ); + }); + }); +}); diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index 675bb14722889f..19c9adfadd45a0 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -14,6 +14,7 @@ import { getSelectedLayerId, getMapReady, getMapColors, + createLayerInstance, } from '../selectors/map_selectors'; import { FLYOUT_STATE } from '../reducers/ui'; import { cancelRequest } from '../reducers/non_serializable_instances'; @@ -42,6 +43,7 @@ import { ILayer } from '../classes/layers/layer'; import { IVectorLayer } from '../classes/layers/vector_layer/vector_layer'; import { LAYER_STYLE_TYPE, LAYER_TYPE } from '../../common/constants'; import { IVectorStyle } from '../classes/styles/vector/vector_style'; +import { notifyLicensedFeatureUsage } from '../licensed_features'; export function trackCurrentLayerState(layerId: string) { return { @@ -108,7 +110,7 @@ export function cloneLayer(layerId: string) { } export function addLayer(layerDescriptor: LayerDescriptor) { - return (dispatch: Dispatch, getState: () => MapStoreState) => { + return async (dispatch: Dispatch, getState: () => MapStoreState) => { const isMapReady = getMapReady(getState()); if (!isMapReady) { dispatch({ @@ -123,6 +125,10 @@ export function addLayer(layerDescriptor: LayerDescriptor) { layer: layerDescriptor, }); dispatch<any>(syncDataForLayerId(layerDescriptor.id)); + + const layer = createLayerInstance(layerDescriptor); + const features = await layer.getLicensedFeatures(); + features.forEach(notifyLicensedFeatureUsage); }; } diff --git a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts index 9b6a67ac28ad09..65a76f0c54ffb3 100644 --- a/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts +++ b/x-pack/plugins/maps/public/classes/layers/blended_vector_layer/blended_vector_layer.ts @@ -38,6 +38,7 @@ import { VectorLayerDescriptor, } from '../../../../common/descriptor_types'; import { IVectorSource } from '../../sources/vector_source'; +import { LICENSED_FEATURES } from '../../../licensed_features'; const ACTIVE_COUNT_DATA_ID = 'ACTIVE_COUNT_DATA_ID'; @@ -327,4 +328,11 @@ export class BlendedVectorLayer extends VectorLayer implements IVectorLayer { super._syncData(syncContext, activeSource, activeStyle); } + + async getLicensedFeatures(): Promise<LICENSED_FEATURES[]> { + return [ + ...(await this._clusterSource.getLicensedFeatures()), + ...(await this._documentSource.getLicensedFeatures()), + ]; + } } diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index d6bd5180375ce5..d7fd5d34a9dd0f 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -34,6 +34,7 @@ import { Attribution, ImmutableSourceProperty, ISource, SourceEditorArgs } from import { DataRequestContext } from '../../actions'; import { IStyle } from '../styles/style'; import { getJoinAggKey } from '../../../common/get_agg_key'; +import { LICENSED_FEATURES } from '../../licensed_features'; export interface ILayer { getBounds(dataRequestContext: DataRequestContext): Promise<MapExtent | null>; @@ -91,6 +92,7 @@ export interface ILayer { showJoinEditor(): boolean; getJoinsDisabledReason(): string | null; isFittable(): Promise<boolean>; + getLicensedFeatures(): Promise<LICENSED_FEATURES[]>; } export type Footnote = { icon: ReactElement<any>; @@ -538,4 +540,8 @@ export class AbstractLayer implements ILayer { supportsLabelsOnTop(): boolean { return false; } + + async getLicensedFeatures(): Promise<LICENSED_FEATURES[]> { + return []; + } } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index a2532d4e7b10ef..c44ebcf969f7cc 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -1090,4 +1090,8 @@ export class VectorLayer extends AbstractLayer { }); return targetFeature ? targetFeature : null; } + + async getLicensedFeatures() { + return await this._source.getLicensedFeatures(); + } } diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js index 89258f04612fd1..181af6b17b7ddb 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.js @@ -24,12 +24,14 @@ import { MVT_GETGRIDTILE_API_PATH, GEOTILE_GRID_AGG_NAME, GEOCENTROID_AGG_NAME, + ES_GEO_FIELD_TYPE, } from '../../../../common/constants'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { AbstractESAggSource, DEFAULT_METRIC } from '../es_agg_source'; import { DataRequestAbortError } from '../../util/data_request'; import { registerSource } from '../source_registry'; +import { LICENSED_FEATURES } from '../../../licensed_features'; import rison from 'rison-node'; import { getHttp } from '../../../kibana_services'; @@ -399,6 +401,13 @@ export class ESGeoGridSource extends AbstractESAggSource { return [VECTOR_SHAPE_TYPE.POINT]; } + + async getLicensedFeatures() { + const geoField = await this._getGeoField(); + return geoField.type === ES_GEO_FIELD_TYPE.GEO_SHAPE + ? [LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE] + : []; + } } registerSource({ diff --git a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts index 06df68283c434e..3b1cf3293c0d3d 100644 --- a/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts +++ b/x-pack/plugins/maps/public/classes/sources/es_geo_grid_source/es_geo_grid_source.test.ts @@ -4,10 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ import { MapExtent, VectorSourceRequestMeta } from '../../../../common/descriptor_types'; - -jest.mock('../../../kibana_services'); - -import { getIndexPatternService, getSearchService, getHttp } from '../../../kibana_services'; +import { getHttp, getIndexPatternService, getSearchService } from '../../../kibana_services'; import { ESGeoGridSource } from './es_geo_grid_source'; import { ES_GEO_FIELD_TYPE, @@ -16,6 +13,9 @@ import { SOURCE_TYPES, } from '../../../../common/constants'; import { SearchSource } from 'src/plugins/data/public'; +import { LICENSED_FEATURES } from '../../../licensed_features'; + +jest.mock('../../../kibana_services'); export class MockSearchSource { setField = jest.fn(); @@ -27,6 +27,8 @@ export class MockSearchSource { describe('ESGeoGridSource', () => { const geoFieldName = 'bar'; + + let esGeoFieldType = ES_GEO_FIELD_TYPE.GEO_POINT; const mockIndexPatternService = { get() { return { @@ -34,7 +36,7 @@ describe('ESGeoGridSource', () => { getByName() { return { name: geoFieldName, - type: ES_GEO_FIELD_TYPE.GEO_POINT, + type: esGeoFieldType, }; }, }, @@ -127,6 +129,11 @@ describe('ESGeoGridSource', () => { }); }); + afterEach(() => { + esGeoFieldType = ES_GEO_FIELD_TYPE.GEO_POINT; + jest.resetAllMocks(); + }); + const extent: MapExtent = { minLon: -160, minLat: -80, @@ -271,4 +278,17 @@ describe('ESGeoGridSource', () => { ); }); }); + + describe('Gold+ usage', () => { + it('Should have none for points', async () => { + expect(await geogridSource.getLicensedFeatures()).toEqual([]); + }); + + it('Should have shape-aggs for geo_shape', async () => { + esGeoFieldType = ES_GEO_FIELD_TYPE.GEO_SHAPE; + expect(await geogridSource.getLicensedFeatures()).toEqual([ + LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE, + ]); + }); + }); }); diff --git a/x-pack/plugins/maps/public/classes/sources/source.ts b/x-pack/plugins/maps/public/classes/sources/source.ts index 946381817b8fc4..c4fb5178c0b565 100644 --- a/x-pack/plugins/maps/public/classes/sources/source.ts +++ b/x-pack/plugins/maps/public/classes/sources/source.ts @@ -15,6 +15,7 @@ import { IField } from '../fields/field'; import { FieldFormatter, MAX_ZOOM, MIN_ZOOM } from '../../../common/constants'; import { AbstractSourceDescriptor } from '../../../common/descriptor_types'; import { OnSourceChangeArgs } from '../../connected_components/layer_panel/view'; +import { LICENSED_FEATURES } from '../../licensed_features'; export type SourceEditorArgs = { onChange: (...args: OnSourceChangeArgs[]) => void; @@ -66,6 +67,7 @@ export interface ISource { getValueSuggestions(field: IField, query: string): Promise<string[]>; getMinZoom(): number; getMaxZoom(): number; + getLicensedFeatures(): Promise<LICENSED_FEATURES[]>; } export class AbstractSource implements ISource { @@ -188,4 +190,8 @@ export class AbstractSource implements ISource { getMaxZoom() { return MAX_ZOOM; } + + async getLicensedFeatures(): Promise<LICENSED_FEATURES[]> { + return []; + } } diff --git a/x-pack/plugins/maps/public/index_pattern_util.test.ts b/x-pack/plugins/maps/public/index_pattern_util.test.ts index ffcc6da52677a4..010c847f96eba2 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.test.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.test.ts @@ -5,6 +5,7 @@ */ jest.mock('./kibana_services', () => ({})); +jest.mock('./licensed_features', () => ({})); import { getSourceFields, @@ -69,7 +70,7 @@ describe('Gold+ licensing', () => { describe('basic license', () => { beforeEach(() => { // eslint-disable-next-line @typescript-eslint/no-var-requires - require('./kibana_services').getIsGoldPlus = () => false; + require('./licensed_features').getIsGoldPlus = () => false; }); describe('getAggregatableGeoFieldTypes', () => { @@ -92,7 +93,7 @@ describe('Gold+ licensing', () => { describe('gold license', () => { beforeEach(() => { // eslint-disable-next-line @typescript-eslint/no-var-requires - require('./kibana_services').getIsGoldPlus = () => true; + require('./licensed_features').getIsGoldPlus = () => true; }); describe('getAggregatableGeoFieldTypes', () => { test('Should add geo_shape field', () => { diff --git a/x-pack/plugins/maps/public/index_pattern_util.ts b/x-pack/plugins/maps/public/index_pattern_util.ts index bd2a14619ac419..7af1571a0bc5b9 100644 --- a/x-pack/plugins/maps/public/index_pattern_util.ts +++ b/x-pack/plugins/maps/public/index_pattern_util.ts @@ -6,9 +6,10 @@ import { IFieldType, IndexPattern } from 'src/plugins/data/public'; import { i18n } from '@kbn/i18n'; -import { getIndexPatternService, getIsGoldPlus } from './kibana_services'; +import { getIndexPatternService } from './kibana_services'; import { indexPatterns } from '../../../../src/plugins/data/public'; import { ES_GEO_FIELD_TYPE, ES_GEO_FIELD_TYPES } from '../common/constants'; +import { getIsGoldPlus } from './licensed_features'; export function getGeoTileAggNotSupportedReason(field: IFieldType): string | null { if (!field.aggregatable) { diff --git a/x-pack/plugins/maps/public/kibana_services.ts b/x-pack/plugins/maps/public/kibana_services.ts index c1dfb61e9f3b62..b520e0cb2df017 100644 --- a/x-pack/plugins/maps/public/kibana_services.ts +++ b/x-pack/plugins/maps/public/kibana_services.ts @@ -5,17 +5,10 @@ */ import _ from 'lodash'; +import { CoreStart } from 'kibana/public'; import { MapsLegacyConfig } from '../../../../src/plugins/maps_legacy/config'; import { MapsConfigType } from '../config'; import { MapsPluginStartDependencies } from './plugin'; -import { CoreStart } from '../../../../src/core/public'; - -let licenseId: string | undefined; -export const setLicenseId = (latestLicenseId: string | undefined) => (licenseId = latestLicenseId); -export const getLicenseId = () => licenseId; -let isGoldPlus: boolean = false; -export const setIsGoldPlus = (igp: boolean) => (isGoldPlus = igp); -export const getIsGoldPlus = () => isGoldPlus; let kibanaVersion: string; export const setKibanaVersion = (version: string) => (kibanaVersion = version); diff --git a/x-pack/plugins/maps/public/licensed_features.ts b/x-pack/plugins/maps/public/licensed_features.ts new file mode 100644 index 00000000000000..67fa526da0cbdb --- /dev/null +++ b/x-pack/plugins/maps/public/licensed_features.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ILicense, LicenseType } from '../../licensing/common/types'; +import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; +import { APP_ID } from '../common/constants'; + +export enum LICENSED_FEATURES { + GEO_SHAPE_AGGS_GEO_TILE = 'GEO_SHAPE_AGGS_GEO_TILE', +} + +export interface LicensedFeatureDetail { + name: string; + license: LicenseType; +} + +export const LICENCED_FEATURES_DETAILS: Record<LICENSED_FEATURES, LicensedFeatureDetail> = { + [LICENSED_FEATURES.GEO_SHAPE_AGGS_GEO_TILE]: { + name: 'geo_tile aggregation on geo_shape field-type', + license: 'gold', + }, +}; + +let licenseId: string | undefined; +let isGoldPlus: boolean = false; + +export const getLicenseId = () => licenseId; +export const getIsGoldPlus = () => isGoldPlus; + +export function registerLicensedFeatures(licensingPlugin: LicensingPluginSetup) { + for (const licensedFeature of Object.values(LICENSED_FEATURES)) { + licensingPlugin.featureUsage.register( + LICENCED_FEATURES_DETAILS[licensedFeature].name, + LICENCED_FEATURES_DETAILS[licensedFeature].license + ); + } +} + +let licensingPluginStart: LicensingPluginStart; +export function setLicensingPluginStart(licensingPlugin: LicensingPluginStart) { + licensingPluginStart = licensingPlugin; + licensingPluginStart.license$.subscribe((license: ILicense) => { + const gold = license.check(APP_ID, 'gold'); + isGoldPlus = gold.state === 'valid'; + licenseId = license.uid; + }); +} + +export function notifyLicensedFeatureUsage(licensedFeature: LICENSED_FEATURES) { + if (!licensingPluginStart) { + // eslint-disable-next-line no-console + console.error('May not call notifyLicensedFeatureUsage before plugin start'); + return; + } + licensingPluginStart.featureUsage.notifyUsage( + LICENCED_FEATURES_DETAILS[LICENSED_FEATURES[licensedFeature]].name + ); +} diff --git a/x-pack/plugins/maps/public/meta.test.js b/x-pack/plugins/maps/public/meta.test.js index 3486bf003aee08..c414c8a2d400e5 100644 --- a/x-pack/plugins/maps/public/meta.test.js +++ b/x-pack/plugins/maps/public/meta.test.js @@ -12,14 +12,14 @@ jest.mock('@elastic/ems-client'); describe('default use without proxy', () => { beforeEach(() => { require('./kibana_services').getProxyElasticMapsServiceInMaps = () => false; - require('./kibana_services').getLicenseId = () => { - return 'foobarlicenseid'; - }; require('./kibana_services').getIsEmsEnabled = () => true; require('./kibana_services').getEmsTileLayerId = () => '123'; require('./kibana_services').getEmsFileApiUrl = () => 'https://file-api'; require('./kibana_services').getEmsTileApiUrl = () => 'https://tile-api'; require('./kibana_services').getEmsLandingPageUrl = () => 'http://test.com'; + require('./licensed_features').getLicenseId = () => { + return 'foobarlicenseid'; + }; }); test('should construct EMSClient with absolute file and tile API urls', async () => { diff --git a/x-pack/plugins/maps/public/meta.ts b/x-pack/plugins/maps/public/meta.ts index 5142793bede34a..4eca6c3e671b7a 100644 --- a/x-pack/plugins/maps/public/meta.ts +++ b/x-pack/plugins/maps/public/meta.ts @@ -18,7 +18,6 @@ import { } from '../common/constants'; import { getHttp, - getLicenseId, getIsEmsEnabled, getRegionmapLayers, getTilemap, @@ -29,6 +28,7 @@ import { getProxyElasticMapsServiceInMaps, getKibanaVersion, } from './kibana_services'; +import { getLicenseId } from './licensed_features'; export function getKibanaRegionList(): unknown[] { return getRegionmapLayers(); diff --git a/x-pack/plugins/maps/public/plugin.ts b/x-pack/plugins/maps/public/plugin.ts index 696964f0258d4b..5b79863d0dd97b 100644 --- a/x-pack/plugins/maps/public/plugin.ts +++ b/x-pack/plugins/maps/public/plugin.ts @@ -18,10 +18,8 @@ import { // @ts-ignore import { MapView } from './inspector/views/map_view'; import { - setIsGoldPlus, setKibanaCommonConfig, setKibanaVersion, - setLicenseId, setMapAppConfig, setStartServices, } from './kibana_services'; @@ -42,7 +40,6 @@ import { MapEmbeddableFactory } from './embeddable/map_embeddable_factory'; import { EmbeddableSetup } from '../../../../src/plugins/embeddable/public'; import { MapsXPackConfig, MapsConfigType } from '../config'; import { getAppTitle } from '../common/i18n_getters'; -import { ILicense } from '../../licensing/common/types'; import { lazyLoadMapModules } from './lazy_load_bundle'; import { MapsStartApi } from './api'; import { createSecurityLayerDescriptors, registerLayerWizard, registerSource } from './api'; @@ -50,8 +47,9 @@ import { SharePluginSetup, SharePluginStart } from '../../../../src/plugins/shar import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; import { MapsLegacyConfig } from '../../../../src/plugins/maps_legacy/config'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; -import { LicensingPluginStart } from '../../licensing/public'; +import { LicensingPluginSetup, LicensingPluginStart } from '../../licensing/public'; import { StartContract as FileUploadStartContract } from '../../file_upload/public'; +import { registerLicensedFeatures, setLicensingPluginStart } from './licensed_features'; export interface MapsPluginSetupDependencies { inspector: InspectorSetupContract; @@ -60,6 +58,7 @@ export interface MapsPluginSetupDependencies { embeddable: EmbeddableSetup; mapsLegacy: { config: MapsLegacyConfig }; share: SharePluginSetup; + licensing: LicensingPluginSetup; } export interface MapsPluginStartDependencies { @@ -97,6 +96,8 @@ export class MapsPlugin } public setup(core: CoreSetup, plugins: MapsPluginSetupDependencies) { + registerLicensedFeatures(plugins.licensing); + const config = this._initializerContext.config.get<MapsConfigType>(); setKibanaCommonConfig(plugins.mapsLegacy.config); setMapAppConfig(config); @@ -138,13 +139,7 @@ export class MapsPlugin } public start(core: CoreStart, plugins: MapsPluginStartDependencies): MapsStartApi { - if (plugins.licensing) { - plugins.licensing.license$.subscribe((license: ILicense) => { - const gold = license.check(APP_ID, 'gold'); - setIsGoldPlus(gold.state === 'valid'); - setLicenseId(license.uid); - }); - } + setLicensingPluginStart(plugins.licensing); plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction); setStartServices(core, plugins); diff --git a/x-pack/plugins/maps/public/selectors/map_selectors.ts b/x-pack/plugins/maps/public/selectors/map_selectors.ts index db4371e9cd590f..4b5122050eb71b 100644 --- a/x-pack/plugins/maps/public/selectors/map_selectors.ts +++ b/x-pack/plugins/maps/public/selectors/map_selectors.ts @@ -52,9 +52,9 @@ import { ITMSSource } from '../classes/sources/tms_source'; import { IVectorSource } from '../classes/sources/vector_source'; import { ILayer } from '../classes/layers/layer'; -function createLayerInstance( +export function createLayerInstance( layerDescriptor: LayerDescriptor, - inspectorAdapters: Adapters + inspectorAdapters?: Adapters ): ILayer { const source: ISource = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters); @@ -94,7 +94,7 @@ function createLayerInstance( } } -function createSourceInstance(sourceDescriptor: any, inspectorAdapters: Adapters): ISource { +function createSourceInstance(sourceDescriptor: any, inspectorAdapters?: Adapters): ISource { const source = getSourceByType(sourceDescriptor.type); if (!source) { throw new Error(`Unrecognized sourceType ${sourceDescriptor.type}`); From 86cb97adf6b1692277695fab7089f4a559346cc2 Mon Sep 17 00:00:00 2001 From: Thomas Neirynck <thomas@elastic.co> Date: Fri, 2 Oct 2020 14:04:31 -0400 Subject: [PATCH 093/128] [Maps] Simplify IDynamicStyle-api (#79217) --- .../properties/dynamic_style_property.tsx | 54 ++++++++++++++----- .../classes/styles/vector/vector_style.tsx | 40 +++++--------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx index 2bc819daeea905..98b58def905eb7 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/properties/dynamic_style_property.tsx @@ -6,7 +6,8 @@ import _ from 'lodash'; import React from 'react'; -import { Feature } from 'geojson'; +import { Feature, FeatureCollection } from 'geojson'; +import { FeatureIdentifier, Map as MbMap } from 'mapbox-gl'; import { AbstractStyleProperty, IStyleProperty } from './style_property'; import { DEFAULT_SIGMA } from '../vector_style_defaults'; import { @@ -44,20 +45,14 @@ export interface IDynamicStyleProperty<T> extends IStyleProperty<T> { isOrdinal(): boolean; supportsFieldMeta(): boolean; getFieldMetaRequest(): Promise<unknown>; - supportsMbFeatureState(): boolean; - getMbLookupFunction(): MB_LOOKUP_FUNCTION; pluckOrdinalStyleMetaFromFeatures(features: Feature[]): RangeFieldMeta | null; pluckCategoricalStyleMetaFromFeatures(features: Feature[]): CategoryFieldMeta | null; getValueSuggestions(query: string): Promise<string[]>; - - // Returns the name that should be used for accessing the data from the mb-style rule - // Depending on - // - whether the field is used for labeling, icon-orientation, or other properties (color, size, ...), `feature-state` and or `get` is used - // - whether the field was run through a field-formatter, a new dynamic field is created with the formatted-value - // The combination of both will inform what field-name (e.g. the "raw" field name from the properties, the "computed field-name" for an on-the-fly created property (e.g. for feature-state or field-formatting). - // todo: There is an existing limitation to .mvt backed sources, where the field-formatters are not applied. Here, the raw-data needs to be accessed. - getMbPropertyName(): string; - getMbPropertyValue(value: RawValue): RawValue; + enrichGeoJsonAndMbFeatureState( + featureCollection: FeatureCollection, + mbMap: MbMap, + mbSourceId: string + ): boolean; } export class DynamicStyleProperty<T> @@ -356,6 +351,12 @@ export class DynamicStyleProperty<T> ); } + // Returns the name that should be used for accessing the data from the mb-style rule + // Depending on + // - whether the field is used for labeling, icon-orientation, or other properties (color, size, ...), `feature-state` and or `get` is used + // - whether the field was run through a field-formatter, a new dynamic field is created with the formatted-value + // The combination of both will inform what field-name (e.g. the "raw" field name from the properties, the "computed field-name" for an on-the-fly created property (e.g. for feature-state or field-formatting). + // todo: There is an existing limitation to .mvt backed sources, where the field-formatters are not applied. Here, the raw-data needs to be accessed. getMbPropertyName() { if (!this._field) { return ''; @@ -385,6 +386,35 @@ export class DynamicStyleProperty<T> // Calling `isOrdinal` would be equivalent. return this.supportsMbFeatureState() ? getNumericalMbFeatureStateValue(rawValue) : rawValue; } + + enrichGeoJsonAndMbFeatureState( + featureCollection: FeatureCollection, + mbMap: MbMap, + mbSourceId: string + ): boolean { + const supportsFeatureState = this.supportsMbFeatureState(); + const featureIdentifier: FeatureIdentifier = { + source: mbSourceId, + id: undefined, + }; + const featureState: Record<string, RawValue> = {}; + const targetMbName = this.getMbPropertyName(); + for (let i = 0; i < featureCollection.features.length; i++) { + const feature = featureCollection.features[i]; + const rawValue = feature.properties ? feature.properties[this.getFieldName()] : undefined; + const targetMbValue = this.getMbPropertyValue(rawValue); + if (supportsFeatureState) { + featureState[targetMbName] = targetMbValue; // the same value will be potentially overridden multiple times, if the name remains identical + featureIdentifier.id = feature.id; + mbMap.setFeatureState(featureIdentifier, featureState); + } else { + if (feature.properties) { + feature.properties[targetMbName] = targetMbValue; + } + } + } + return supportsFeatureState; + } } export function getNumericalMbFeatureStateValue(value: RawValue) { diff --git a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx index 5d0d9712ef988f..acb158636e0b36 100644 --- a/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx +++ b/x-pack/plugins/maps/public/classes/styles/vector/vector_style.tsx @@ -641,7 +641,7 @@ export class VectorStyle implements IVectorStyle { featureCollection: FeatureCollection, mbMap: MbMap, mbSourceId: string - ) { + ): boolean { if (!featureCollection) { return false; } @@ -651,40 +651,24 @@ export class VectorStyle implements IVectorStyle { return false; } - const tmpFeatureIdentifier: FeatureIdentifier = { - source: '', - id: undefined, - }; - const tmpFeatureState: any = {}; - - for (let i = 0; i < featureCollection.features.length; i++) { - const feature = featureCollection.features[i]; - - for (let j = 0; j < dynamicStyleProps.length; j++) { - const dynamicStyleProp = dynamicStyleProps[j]; - const targetMbName = dynamicStyleProp.getMbPropertyName(); - const rawValue = feature.properties - ? feature.properties[dynamicStyleProp.getFieldName()] - : undefined; - const targetMbValue = dynamicStyleProp.getMbPropertyValue(rawValue); - if (dynamicStyleProp.supportsMbFeatureState()) { - tmpFeatureState[targetMbName] = targetMbValue; // the same value will be potentially overridden multiple times, if the name remains identical - } else { - if (feature.properties) { - feature.properties[targetMbName] = targetMbValue; - } - } + let shouldResetAllData = false; + for (let j = 0; j < dynamicStyleProps.length; j++) { + const dynamicStyleProp = dynamicStyleProps[j]; + const usedFeatureState = dynamicStyleProp.enrichGeoJsonAndMbFeatureState( + featureCollection, + mbMap, + mbSourceId + ); + if (!usedFeatureState) { + shouldResetAllData = true; } - tmpFeatureIdentifier.source = mbSourceId; - tmpFeatureIdentifier.id = feature.id; - mbMap.setFeatureState(tmpFeatureIdentifier, tmpFeatureState); } // returns boolean indicating if styles do not support feature-state and some values are stored in geojson properties // this return-value is used in an optimization for style-updates with mapbox-gl. // `true` indicates the entire data needs to reset on the source (otherwise the style-rules will not be reapplied) // `false` indicates the data does not need to be reset on the store, because styles are re-evaluated if they use featureState - return dynamicStyleProps.some((dynamicStyleProp) => !dynamicStyleProp.supportsMbFeatureState()); + return shouldResetAllData; } arePointsSymbolizedAsCircles() { From 819ccf124703f5da04bbe73e1a892b6a96e1c3e7 Mon Sep 17 00:00:00 2001 From: Robert Oskamp <robert.oskamp@elastic.co> Date: Fri, 2 Oct 2020 20:14:42 +0200 Subject: [PATCH 094/128] Adjust extend_es_archiver to handle additional cases (#79308) This PR enables the extend_es_archiver to recognize additional indices like .kibana_1 as well as a list of indices. --- test/common/services/kibana_server/extend_es_archiver.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/common/services/kibana_server/extend_es_archiver.js b/test/common/services/kibana_server/extend_es_archiver.js index 4efdfc4dddf775..f6e14061aed2aa 100644 --- a/test/common/services/kibana_server/extend_es_archiver.js +++ b/test/common/services/kibana_server/extend_es_archiver.js @@ -33,9 +33,15 @@ export function extendEsArchiver({ esArchiver, kibanaServer, retry, defaults }) // esArchiver methods return a stats object, with information about the indexes created const stats = await originalMethod.apply(esArchiver, args); + const statsKeys = Object.keys(stats); + const kibanaKeys = statsKeys.filter( + // this also matches stats keys like '.kibana_1' and '.kibana_2,.kibana_1' + (key) => key.includes(KIBANA_INDEX) && (stats[key].created || stats[key].deleted) + ); + // if the kibana index was created by the esArchiver then update the uiSettings // with the defaults to make sure that they are always in place initially - if (stats[KIBANA_INDEX] && (stats[KIBANA_INDEX].created || stats[KIBANA_INDEX].deleted)) { + if (kibanaKeys.length > 0) { await retry.try(async () => { await kibanaServer.uiSettings.update(defaults); }); From 92ff5178c6ee0b226c9e89aff00e3b3e8dcc7562 Mon Sep 17 00:00:00 2001 From: Jonathan Buttner <56361221+jonathan-buttner@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:15:03 -0400 Subject: [PATCH 095/128] [Security Solution][EPM] Reenabling the ingest and endpoint tests (#79290) * Reenabling the ingest and endpoint tests * Fixing list test and reenabling security functional tests --- .../apis/epm/list.ts | 28 ++++++++----------- .../apis/index.js | 2 +- .../ingest_manager_api_integration/config.ts | 2 +- .../apps/endpoint/index.ts | 2 +- .../apis/index.ts | 2 +- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts b/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts index bfe1954e46c9f9..732696433279f2 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/epm/list.ts @@ -6,21 +6,23 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../api_integration/ftr_provider_context'; -import { warnAndSkipTest } from '../../helpers'; +import { skipIfNoDockerRegistry } from '../../helpers'; +import { setupIngest } from '../fleet/agents/services'; -export default function ({ getService }: FtrProviderContext) { - const log = getService('log'); +export default function (providerContext: FtrProviderContext) { + const { getService } = providerContext; const supertest = getService('supertest'); - const dockerServers = getService('dockerServers'); - const server = dockerServers.get('registry'); // use function () {} and not () => {} here // because `this` has to point to the Mocha context // see https://mochajs.org/#arrow-functions describe('EPM - list', async function () { - it('lists all packages from the registry', async function () { - if (server.enabled) { + skipIfNoDockerRegistry(providerContext); + setupIngest(providerContext); + + describe('list api tests', async () => { + it('lists all packages from the registry', async function () { const fetchPackageList = async () => { const response = await supertest .get('/api/ingest_manager/epm/packages') @@ -30,13 +32,9 @@ export default function ({ getService }: FtrProviderContext) { }; const listResponse = await fetchPackageList(); expect(listResponse.response.length).not.to.be(0); - } else { - warnAndSkipTest(this, log); - } - }); + }); - it('lists all limited packages from the registry', async function () { - if (server.enabled) { + it('lists all limited packages from the registry', async function () { const fetchLimitedPackageList = async () => { const response = await supertest .get('/api/ingest_manager/epm/packages/limited') @@ -46,9 +44,7 @@ export default function ({ getService }: FtrProviderContext) { }; const listResponse = await fetchLimitedPackageList(); expect(listResponse.response).to.eql(['endpoint']); - } else { - warnAndSkipTest(this, log); - } + }); }); }); } diff --git a/x-pack/test/ingest_manager_api_integration/apis/index.js b/x-pack/test/ingest_manager_api_integration/apis/index.js index ec509e1485a9f8..7c1ebef337baa2 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/index.js +++ b/x-pack/test/ingest_manager_api_integration/apis/index.js @@ -5,7 +5,7 @@ */ export default function ({ loadTestFile }) { - describe.skip('Ingest Manager Endpoints', function () { + describe('Ingest Manager Endpoints', function () { this.tags('ciGroup7'); // Ingest Manager setup loadTestFile(require.resolve('./setup')); diff --git a/x-pack/test/ingest_manager_api_integration/config.ts b/x-pack/test/ingest_manager_api_integration/config.ts index 862ef732cb8b4a..d11884667c48a0 100644 --- a/x-pack/test/ingest_manager_api_integration/config.ts +++ b/x-pack/test/ingest_manager_api_integration/config.ts @@ -12,7 +12,7 @@ import { defineDockerServersConfig } from '@kbn/test'; // Docker image to use for Ingest Manager API integration tests. // This hash comes from the commit hash here: https://github.com/elastic/package-storage/commit export const dockerImage = - 'docker.elastic.co/package-registry/distribution:518a65a993ab7e9c77b1d8d20cd6f847921d04ec'; + 'docker.elastic.co/package-registry/distribution:a5132271ad37209d6978018bfe6e224546d719a8'; export default async function ({ readConfigFile }: FtrConfigProviderContext) { const xPackAPITestsConfig = await readConfigFile(require.resolve('../api_integration/config.ts')); diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts index c3862d130202d6..654aa18fba523c 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/index.ts @@ -13,7 +13,7 @@ import { export default function (providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe.skip('endpoint', function () { + describe('endpoint', function () { this.tags('ciGroup7'); const ingestManager = getService('ingestManager'); const log = getService('log'); diff --git a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts index 6c5764faed6316..3d344c1b3b51b6 100644 --- a/x-pack/test/security_solution_endpoint_api_int/apis/index.ts +++ b/x-pack/test/security_solution_endpoint_api_int/apis/index.ts @@ -10,7 +10,7 @@ import { getRegistryUrl as getRegistryUrlFromIngest } from '../../../plugins/ing export default function endpointAPIIntegrationTests(providerContext: FtrProviderContext) { const { loadTestFile, getService } = providerContext; - describe.skip('Endpoint plugin', function () { + describe('Endpoint plugin', function () { const ingestManager = getService('ingestManager'); this.tags('ciGroup7'); From 374ccfd66fcbfbbde29dd080bf805d58b7dc9d7a Mon Sep 17 00:00:00 2001 From: Wylie Conlon <william.conlon@elastic.co> Date: Fri, 2 Oct 2020 14:18:01 -0400 Subject: [PATCH 096/128] [Lens] Show runtime fields in field list and improve performance (#79167) * [Lens] Simplify request to determine existing fields * Remove duplicate values --- .../server/routes/existing_fields.test.ts | 131 ++-------------- .../lens/server/routes/existing_fields.ts | 142 ++---------------- .../apis/lens/existing_fields.ts | 5 +- .../logstash_functional/mappings.json | 15 ++ .../es_archives/visualize/default/data.json | 2 +- 5 files changed, 47 insertions(+), 248 deletions(-) diff --git a/x-pack/plugins/lens/server/routes/existing_fields.test.ts b/x-pack/plugins/lens/server/routes/existing_fields.test.ts index 9799dcf92ae411..c877e69d7b0dd7 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.test.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.test.ts @@ -14,99 +14,55 @@ describe('existingFields', () => { return { name, isScript: false, - isAlias: false, isMeta: false, - path: name.split('.'), ...obj, }; } - function indexPattern(_source: unknown, fields: unknown = {}) { - return { _source, fields }; + function searchResults(fields: Record<string, unknown[]> = {}) { + return { fields }; } it('should handle root level fields', () => { const result = existingFields( - [indexPattern({ foo: 'bar' }), indexPattern({ baz: 0 })], + [searchResults({ foo: ['bar'] }), searchResults({ baz: [0] })], [field('foo'), field('bar'), field('baz')] ); expect(result).toEqual(['foo', 'baz']); }); - it('should handle arrays of objects', () => { + it('should handle basic arrays, ignoring empty ones', () => { const result = existingFields( - [indexPattern({ stuff: [{ foo: 'bar' }, { baz: 0 }] })], - [field('stuff.foo'), field('stuff.bar'), field('stuff.baz')] + [searchResults({ stuff: ['heyo', 'there'], empty: [] })], + [field('stuff'), field('empty')] ); - expect(result).toEqual(['stuff.foo', 'stuff.baz']); - }); - - it('should handle basic arrays', () => { - const result = existingFields([indexPattern({ stuff: ['heyo', 'there'] })], [field('stuff')]); - expect(result).toEqual(['stuff']); }); - it('should handle deep object structures', () => { - const result = existingFields( - [indexPattern({ geo: { coordinates: { lat: 40, lon: -77 } } })], - [field('geo.coordinates')] - ); - - expect(result).toEqual(['geo.coordinates']); - }); - it('should handle objects with dotted fields', () => { const result = existingFields( - [indexPattern({ 'geo.country_name': 'US' })], + [searchResults({ 'geo.country_name': ['US'] })], [field('geo.country_name')] ); expect(result).toEqual(['geo.country_name']); }); - it('should handle arrays with dotted fields on both sides', () => { - const result = existingFields( - [indexPattern({ 'process.cpu': [{ 'user.pct': 50 }] })], - [field('process.cpu.user.pct')] - ); - - expect(result).toEqual(['process.cpu.user.pct']); - }); - - it('should be false if it hits a positive leaf before the end of the path', () => { - const result = existingFields( - [indexPattern({ geo: { coordinates: 32 } })], - [field('geo.coordinates.lat')] - ); - - expect(result).toEqual([]); - }); - - it('should use path, not name', () => { - const result = existingFields( - [indexPattern({ stuff: [{ foo: 'bar' }, { baz: 0 }] })], - [field({ name: 'goober', path: ['stuff', 'foo'] })] - ); - - expect(result).toEqual(['goober']); - }); - it('supports scripted fields', () => { const result = existingFields( - [indexPattern({}, { bar: 'scriptvalue' })], - [field({ name: 'baz', isScript: true, path: ['bar'] })] + [searchResults({ bar: ['scriptvalue'] })], + [field({ name: 'bar', isScript: true })] ); - expect(result).toEqual(['baz']); + expect(result).toEqual(['bar']); }); it('supports meta fields', () => { const result = existingFields( - [{ _mymeta: 'abc', ...indexPattern({}, { bar: 'scriptvalue' }) }], - [field({ name: '_mymeta', isMeta: true, path: ['_mymeta'] })] + [{ _mymeta: 'abc', ...searchResults({ bar: ['scriptvalue'] }) }], + [field({ name: '_mymeta', isMeta: true })] ); expect(result).toEqual(['_mymeta']); @@ -132,81 +88,22 @@ describe('buildFieldList', () => { references: [], }; - const mappings = { - testpattern: { - mappings: { - properties: { - '@bar': { - type: 'alias', - path: 'bar', - }, - }, - }, - }, - }; - - const fieldDescriptors = [ - { - name: 'baz', - subType: { multi: { parent: 'a.b.c' } }, - }, - ]; - - it('uses field descriptors to determine the path', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); - expect(fields.find((f) => f.name === 'baz')).toMatchObject({ - isAlias: false, - isScript: false, - name: 'baz', - path: ['a', 'b', 'c'], - }); - }); - - it('uses aliases to determine the path', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); - expect(fields.find((f) => f.isAlias)).toMatchObject({ - isAlias: true, - isScript: false, - name: '@bar', - path: ['bar'], - }); - }); - it('supports scripted fields', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, []); + const fields = buildFieldList(indexPattern, []); expect(fields.find((f) => f.isScript)).toMatchObject({ - isAlias: false, isScript: true, name: 'foo', - path: ['foo'], lang: 'painless', script: '2+2', }); }); it('supports meta fields', () => { - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, ['_mymeta']); + const fields = buildFieldList(indexPattern, ['_mymeta']); expect(fields.find((f) => f.isMeta)).toMatchObject({ - isAlias: false, isScript: false, isMeta: true, name: '_mymeta', - path: ['_mymeta'], - }); - }); - - it('handles missing mappings', () => { - const fields = buildFieldList(indexPattern, {}, fieldDescriptors, []); - expect(fields.every((f) => f.isAlias === false)).toEqual(true); - }); - - it('handles empty fieldDescriptors by skipping multi-mappings', () => { - const fields = buildFieldList(indexPattern, mappings, [], []); - expect(fields.find((f) => f.name === 'baz')).toMatchObject({ - isAlias: false, - isScript: false, - name: 'baz', - path: ['baz'], }); }); }); diff --git a/x-pack/plugins/lens/server/routes/existing_fields.ts b/x-pack/plugins/lens/server/routes/existing_fields.ts index 33fcafacfad733..c925517b572dae 100644 --- a/x-pack/plugins/lens/server/routes/existing_fields.ts +++ b/x-pack/plugins/lens/server/routes/existing_fields.ts @@ -9,36 +9,17 @@ import { schema } from '@kbn/config-schema'; import { ILegacyScopedClusterClient, SavedObject, RequestHandlerContext } from 'src/core/server'; import { CoreSetup } from 'src/core/server'; import { BASE_API_URL } from '../../common'; -import { - IndexPatternsFetcher, - IndexPatternAttributes, - UI_SETTINGS, -} from '../../../../../src/plugins/data/server'; +import { IndexPatternAttributes, UI_SETTINGS } from '../../../../../src/plugins/data/server'; /** * The number of docs to sample to determine field empty status. */ const SAMPLE_SIZE = 500; -interface MappingResult { - [indexPatternTitle: string]: { - mappings: { - properties: Record<string, { type: string; path: string }>; - }; - }; -} - -interface FieldDescriptor { - name: string; - subType?: { multi?: { parent?: string } }; -} - export interface Field { name: string; isScript: boolean; - isAlias: boolean; isMeta: boolean; - path: string[]; lang?: string; script?: string; } @@ -105,14 +86,12 @@ async function fetchFieldExistence({ timeFieldName?: string; }) { const metaFields: string[] = await context.core.uiSettings.client.get(UI_SETTINGS.META_FIELDS); - const { - indexPattern, - indexPatternTitle, - mappings, - fieldDescriptors, - } = await fetchIndexPatternDefinition(indexPatternId, context, metaFields); + const { indexPattern, indexPatternTitle } = await fetchIndexPatternDefinition( + indexPatternId, + context + ); - const fields = buildFieldList(indexPattern, mappings, fieldDescriptors, metaFields); + const fields = buildFieldList(indexPattern, metaFields); const docs = await fetchIndexPatternStats({ fromDate, toDate, @@ -129,51 +108,17 @@ async function fetchFieldExistence({ }; } -async function fetchIndexPatternDefinition( - indexPatternId: string, - context: RequestHandlerContext, - metaFields: string[] -) { +async function fetchIndexPatternDefinition(indexPatternId: string, context: RequestHandlerContext) { const savedObjectsClient = context.core.savedObjects.client; - const requestClient = context.core.elasticsearch.legacy.client; const indexPattern = await savedObjectsClient.get<IndexPatternAttributes>( 'index-pattern', indexPatternId ); const indexPatternTitle = indexPattern.attributes.title; - if (indexPatternTitle.includes(':')) { - // Cross cluster search patterns include a colon, and we aren't able to fetch - // mapping information. - return { - indexPattern, - indexPatternTitle, - mappings: {}, - fieldDescriptors: [], - }; - } - - // TODO: maybe don't use IndexPatternsFetcher at all, since we're only using it - // to look up field values in the resulting documents. We can accomplish the same - // using the mappings which we're also fetching here. - const indexPatternsFetcher = new IndexPatternsFetcher(requestClient.callAsCurrentUser); - const [mappings, fieldDescriptors] = await Promise.all([ - requestClient.callAsCurrentUser('indices.getMapping', { - index: indexPatternTitle, - }), - - indexPatternsFetcher.getFieldsForWildcard({ - pattern: indexPatternTitle, - // TODO: Pull this from kibana advanced settings - metaFields, - }), - ]); - return { indexPattern, indexPatternTitle, - mappings, - fieldDescriptors, }; } @@ -182,32 +127,13 @@ async function fetchIndexPatternDefinition( */ export function buildFieldList( indexPattern: SavedObject<IndexPatternAttributes>, - mappings: MappingResult | {}, - fieldDescriptors: FieldDescriptor[], metaFields: string[] ): Field[] { - const aliasMap = Object.entries(Object.values(mappings)[0]?.mappings.properties ?? {}) - .map(([name, v]) => ({ ...v, name })) - .filter((f) => f.type === 'alias') - .reduce((acc, f) => { - acc[f.name] = f.path; - return acc; - }, {} as Record<string, string>); - - const descriptorMap = fieldDescriptors.reduce((acc, f) => { - acc[f.name] = f; - return acc; - }, {} as Record<string, FieldDescriptor>); - return JSON.parse(indexPattern.attributes.fields).map( (field: { name: string; lang: string; scripted?: boolean; script?: string }) => { - const path = - aliasMap[field.name] || descriptorMap[field.name]?.subType?.multi?.parent || field.name; return { name: field.name, isScript: !!field.scripted, - isAlias: !!aliasMap[field.name], - path: path.split('.'), lang: field.lang, script: field.script, // id is a special case - it doesn't show up in the meta field list, @@ -263,8 +189,8 @@ async function fetchIndexPatternStats({ size: SAMPLE_SIZE, query, sort: timeFieldName && fromDate && toDate ? [{ [timeFieldName]: 'desc' }] : [], - // _source is required because we are also providing script fields. - _source: '*', + fields: ['*'], + _source: false, script_fields: scriptedFields.reduce((acc, field) => { acc[field.name] = { script: { @@ -279,49 +205,11 @@ async function fetchIndexPatternStats({ return result.hits.hits; } -// Recursive function to determine if the _source of a document -// contains a known path. -function exists(obj: unknown, path: string[], i = 0): boolean { - if (obj == null) { - return false; - } - - if (path.length === i) { - return true; - } - - if (Array.isArray(obj)) { - return obj.some((child) => exists(child, path, i)); - } - - if (typeof obj === 'object') { - // Because Elasticsearch flattens paths, dots in the field name are allowed - // as JSON keys. For example, { 'a.b': 10 } - const partialKeyMatches = Object.getOwnPropertyNames(obj) - .map((key) => key.split('.')) - .filter((keyPaths) => keyPaths.every((key, keyIndex) => key === path[keyIndex + i])); - - if (partialKeyMatches.length) { - return partialKeyMatches.every((keyPaths) => { - return exists( - (obj as Record<string, unknown>)[keyPaths.join('.')], - path, - i + keyPaths.length - ); - }); - } - - return exists((obj as Record<string, unknown>)[path[i]], path, i + 1); - } - - return path.length === i; -} - /** * Exported only for unit tests. */ export function existingFields( - docs: Array<{ _source: unknown; fields: unknown; [key: string]: unknown }>, + docs: Array<{ fields: Record<string, unknown[]>; [key: string]: unknown }>, fields: Field[] ): string[] { const missingFields = new Set(fields); @@ -332,14 +220,14 @@ export function existingFields( } missingFields.forEach((field) => { - let fieldStore = doc._source; - if (field.isScript) { - fieldStore = doc.fields; - } + let fieldStore: Record<string, unknown> = doc.fields; if (field.isMeta) { fieldStore = doc; } - if (exists(fieldStore, field.path)) { + const value = fieldStore[field.name]; + if (Array.isArray(value) && value.length) { + missingFields.delete(field); + } else if (!Array.isArray(value) && value) { missingFields.delete(field); } }); diff --git a/x-pack/test/api_integration/apis/lens/existing_fields.ts b/x-pack/test/api_integration/apis/lens/existing_fields.ts index 10ee7bc9b48ea1..08806df380f38a 100644 --- a/x-pack/test/api_integration/apis/lens/existing_fields.ts +++ b/x-pack/test/api_integration/apis/lens/existing_fields.ts @@ -22,7 +22,6 @@ const fieldsWithData = [ '@timestamp', '_id', '_index', - '_source', 'agent', 'agent.raw', 'bytes', @@ -60,6 +59,7 @@ const fieldsWithData = [ 'utc_time', 'xss', 'xss.raw', + 'runtime_number', 'relatedContent.article:modified_time', 'relatedContent.article:published_time', @@ -101,7 +101,6 @@ const metricBeatData = [ '@timestamp', '_id', '_index', - '_source', 'agent.ephemeral_id', 'agent.hostname', 'agent.id', @@ -193,7 +192,6 @@ export default ({ getService }: FtrProviderContext) => { '@timestamp', '_id', '_index', - '_source', 'agent', 'agent.raw', 'bytes', @@ -211,6 +209,7 @@ export default ({ getService }: FtrProviderContext) => { 'request.raw', 'response', 'response.raw', + 'runtime_number', 'spaces', 'spaces.raw', 'type', diff --git a/x-pack/test/functional/es_archives/logstash_functional/mappings.json b/x-pack/test/functional/es_archives/logstash_functional/mappings.json index dcfafa2612c5bf..ee7feedd775303 100644 --- a/x-pack/test/functional/es_archives/logstash_functional/mappings.json +++ b/x-pack/test/functional/es_archives/logstash_functional/mappings.json @@ -342,6 +342,11 @@ } }, "type": "text" + }, + "runtime_number": { + "type": "runtime", + "runtime_type" : "long", + "script" : { "source" : "emit(doc['bytes'].value)" } } } }, @@ -707,6 +712,11 @@ } }, "type": "text" + }, + "runtime_number": { + "type": "runtime", + "runtime_type" : "long", + "script" : { "source" : "emit(doc['bytes'].value)" } } } }, @@ -1072,6 +1082,11 @@ } }, "type": "text" + }, + "runtime_number": { + "type": "runtime", + "runtime_type" : "long", + "script" : { "source" : "emit(doc['bytes'].value)" } } } }, diff --git a/x-pack/test/functional/es_archives/visualize/default/data.json b/x-pack/test/functional/es_archives/visualize/default/data.json index f72a61c9e3b857..5b5ee355c7086b 100644 --- a/x-pack/test/functional/es_archives/visualize/default/data.json +++ b/x-pack/test/functional/es_archives/visualize/default/data.json @@ -152,7 +152,7 @@ "index-pattern": { "title": "logstash-*", "timeFieldName": "@timestamp", - "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true}]" + "fields": "[{\"name\":\"@message\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@message.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@tags\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"@tags.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"@timestamp\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"agent\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"agent.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bytes\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"clientip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extension\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extension.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.coordinates\",\"type\":\"geo_point\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.dest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.src\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geo.srcdest\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"headings\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"headings.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"host\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"host.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"index\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"index.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ip\",\"type\":\"ip\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"links\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"links.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.os\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"machine.os.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"machine.ram\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"memory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.char\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"meta.related\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.firstname\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"meta.user.lastname\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"phpmemory\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"referer\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:modified_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:published_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:section\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:section.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.article:tag\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.article:tag.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:height\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:height.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:image:width\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:image:width.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:site_name\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:site_name.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:type.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.og:url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.og:url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:card\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:card.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:description\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:description.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:image\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:image.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:site\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:site.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.twitter:title\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.twitter:title.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"relatedContent.url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"relatedContent.url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"request\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"request.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"response\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"response.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spaces\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"spaces.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"type\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"url\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"url.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"utc_time\",\"type\":\"date\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"xss\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xss.raw\",\"type\":\"string\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"runtime_number\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]" }, "type": "index-pattern", "migrationVersion": { From 7cfdeaeedeb1bd81b436c6ec1b22e1f033f509d4 Mon Sep 17 00:00:00 2001 From: Ryland Herrick <ryalnd@gmail.com> Date: Fri, 2 Oct 2020 13:19:02 -0500 Subject: [PATCH 097/128] [Security Solution][Detections] EQL Validation (#77493) * WIP: Adding new route for EQL Validation This is mostly boilerplate with some rough parameter definitions; the actual implementation of the validation is going to live in our validateEql function. A few tests are failing as the mocks haven't yet been implemented, I need to see the shape of the responses first. * Cherry-pick Marshall's EQL types * Implements actual EQL validation * Performs an EQL search * filters out non-parsing errors, and returns what remains in the response * Adds mocks for empty EQL responses (we don't yet have a need for mocked data, but we will when we unit-test validateEql) * Adds validation calls to the EQL form input * Adds EQL Validation response schema,mocks,tests * Adds frontend function to call our validation endpoint * Adds hook, useEqlValidation, to call the above function and return state * Adds labels/help text for EQL Query bar * EqlQueryBar consumes useEqlValidation and marks the field as invalid, but does not yet report errors. * Do not call the validation API if query is not present This causes a broader error that results in a 400 response; we can (and do) handle the case of a blank query in the form itself. * Remove EQL Help Text It doesn't add any information for the user, and it currently looks bad when combined with validation errors. * Flesh out and use our popover for displaying validation errors * Fixes issue where old errors were persisted after the user had made modifications * Include verification_exception errors as validation errors These include errors related to index fields and mappings. * Generalize our validation helpers We're concerned with validation errors; the source of those errors is an implementation detail of these functions. * Move error popover and EQL reference link to footer This more closely resembles the new Eui Markdown editor, which places errors and doc links in a footer. * Fix jest tests following additional prop * Add icon for EQL Rule card * Fixes existing EqlQueryBar tests These were broken by our use of useAppToasts and the EUI theme. * Add unit tests around error rendering on EQL Query Bar * Add tests for ErrorPopover * Remove unused schema type Decode doesn't do any additional processing, so we can use t.TypeOf here (the default for buildRouteValidation). * Remove duplicated header * Use ignore parameter to prevent EQL validations from logging errors Without `ignore: [400]` the ES client will log errors and then throw them. We can catch the error, but the log is undesirable. This updates the query to use the ignore parameter, along with updating the validation logic to work with the updated response. Adds some mocks and tests around these responses and helpers, since these will exist independent of the validation implementation. * Include mapping_exceptions during EQL query validation These include errors for inaccessible indexes, which should be useful to the rule writer in writing their EQL query. * Display toast messages for non-validation messages * fix type errors This type was renamed. * Do not request data in our validation request By not having the cluster retrieve/send any data, this should saves us a few CPU cycles. * Move EQL validation to an async form validator Rather than invoking a custom validation hook (useEqlValidation) at custom times (onBlur) in our EqlQueryBar component, we can instead move this functionality to a form validation function and have it be invoked automatically by our form when values change. However, because we still need to handle the validation messages slightly differently (place them in a popover as opposed to an EuiFormRow), we also need custom error retrieval in the form of getValidationResults. After much pain, it was determined that the default behavior of _.debounce does not work with async validator functions, as a debounced call will not "wait" for the eventual invocation but will instead return the most recently resolved value. This leads to stale validation results and terrible UX, so I wrote a custom function (debounceAsync) that behaves like we want/need; see tests for details. * Invalidate our query field when index patterns change Since EQL rules actually validate against the relevant indexes, changing said indexes should invalidate/revalidate the query. With the form lib, this is beautifully simple :) * Set a min-height on our EQL textarea * Remove unused prop from EqlQueryBar Index corresponds to the value from the index field; now that our EQL validation is performed by the form we have no need for it here. * Update EQL overview link to point to elasticsearch docs Adds an entry in our doclinks service, and uses that. * Remove unused prop from stale tests * Update docLinks documentation with new EQL link * Fix bug where saved query rules had no type selected on Edit * Wait for kibana requests to complete before moving between rule tabs With our new async validation, a user can quickly navigate away from the Definition tab before the validation has completed, resulting in the form being invalidated. Any subsequent user actions cause the form to correct itself, but until I can find a better solution here this really just gives the validation time to complete and sidesteps the issue. --- ...-plugin-core-public.doclinksstart.links.md | 1 + ...kibana-plugin-core-public.doclinksstart.md | 2 +- .../public/doc_links/doc_links_service.ts | 2 + src/core/public/public.api.md | 1 + .../security_solution/common/constants.ts | 1 + .../request/eql_validation_schema.mock.ts | 12 ++ .../request/eql_validation_schema.test.ts | 59 ++++++++++ .../schemas/request/eql_validation_schema.ts | 18 +++ .../response/eql_validation_schema.mock.ts | 17 +++ .../response/eql_validation_schema.test.ts | 59 ++++++++++ .../schemas/response/eql_validation_schema.ts | 16 +++ .../common/detection_engine/utils.ts | 3 +- .../alerts_detection_rules_custom.spec.ts | 3 +- .../cypress/screens/edit_rule.ts | 2 + .../cypress/tasks/edit_rule.ts | 6 +- .../public/common/hooks/eql/api.ts | 31 ++++++ .../rules/eql_query_bar/eql_overview_link.tsx | 26 +++++ .../eql_query_bar/eql_query_bar.test.tsx | 37 +++++- .../rules/eql_query_bar/eql_query_bar.tsx | 53 +++++++-- .../eql_query_bar/errors_popover.test.tsx | 50 +++++++++ .../rules/eql_query_bar/errors_popover.tsx | 55 +++++++++ .../components/rules/eql_query_bar/footer.tsx | 42 +++++++ .../rules/eql_query_bar/translations.ts | 42 +++++++ .../rules/eql_query_bar/validators.mock.ts | 19 ++++ .../rules/eql_query_bar/validators.test.ts | 52 +++++++++ .../rules/eql_query_bar/validators.ts | 105 ++++++++++++++++++ .../select_rule_type/eql_search_icon.svg | 6 + .../rules/select_rule_type/index.tsx | 5 +- .../rules/step_define_rule/index.tsx | 5 + .../rules/step_define_rule/schema.tsx | 13 +-- .../rules/step_define_rule/translations.tsx | 14 +++ .../public/shared_imports.ts | 1 + .../routes/__mocks__/request_context.ts | 5 +- .../routes/__mocks__/request_responses.ts | 27 ++++- .../routes/eql/helpers.mock.ts | 69 ++++++++++++ .../routes/eql/helpers.test.ts | 58 ++++++++++ .../detection_engine/routes/eql/helpers.ts | 35 ++++++ .../routes/eql/validate_eql.ts | 46 ++++++++ .../routes/eql/validation_route.test.ts | 53 +++++++++ .../routes/eql/validation_route.ts | 49 ++++++++ .../security_solution/server/routes/index.ts | 4 + 41 files changed, 1075 insertions(+), 29 deletions(-) create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.mock.ts create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.test.ts create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.ts create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.mock.ts create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.test.ts create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.ts create mode 100644 x-pack/plugins/security_solution/public/common/hooks/eql/api.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_overview_link.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/translations.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.mock.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.test.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.ts create mode 100644 x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/eql_search_icon.svg create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.mock.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validate_eql.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.test.ts create mode 100644 x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.ts 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 f7b55b0650d8b8..3afd5eaa6f1f7d 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 @@ -91,6 +91,7 @@ readonly links: { readonly gettingStarted: string; }; readonly query: { + readonly eql: string; readonly luceneQuerySyntax: string; readonly queryDsl: string; readonly kueryQuerySyntax: 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 3f58cf08ee6b6c..5249381969b98d 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) | <code>string</code> | | | [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | <code>string</code> | | -| [links](./kibana-plugin-core-public.doclinksstart.links.md) | <code>{</code><br/><code> readonly dashboard: {</code><br/><code> readonly drilldowns: string;</code><br/><code> readonly drilldownsTriggerPicker: string;</code><br/><code> readonly urlDrilldownTemplateSyntax: string;</code><br/><code> readonly urlDrilldownVariables: string;</code><br/><code> };</code><br/><code> readonly filebeat: {</code><br/><code> readonly base: string;</code><br/><code> readonly installation: string;</code><br/><code> readonly configuration: string;</code><br/><code> readonly elasticsearchOutput: string;</code><br/><code> readonly startup: string;</code><br/><code> readonly exportedFields: string;</code><br/><code> };</code><br/><code> readonly auditbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly metricbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly heartbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly logstash: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly functionbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly winlogbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly aggs: {</code><br/><code> readonly date_histogram: string;</code><br/><code> readonly date_range: string;</code><br/><code> readonly filter: string;</code><br/><code> readonly filters: string;</code><br/><code> readonly geohash_grid: string;</code><br/><code> readonly histogram: string;</code><br/><code> readonly ip_range: string;</code><br/><code> readonly range: string;</code><br/><code> readonly significant_terms: string;</code><br/><code> readonly terms: string;</code><br/><code> readonly avg: string;</code><br/><code> readonly avg_bucket: string;</code><br/><code> readonly max_bucket: string;</code><br/><code> readonly min_bucket: string;</code><br/><code> readonly sum_bucket: string;</code><br/><code> readonly cardinality: string;</code><br/><code> readonly count: string;</code><br/><code> readonly cumulative_sum: string;</code><br/><code> readonly derivative: string;</code><br/><code> readonly geo_bounds: string;</code><br/><code> readonly geo_centroid: string;</code><br/><code> readonly max: string;</code><br/><code> readonly median: string;</code><br/><code> readonly min: string;</code><br/><code> readonly moving_avg: string;</code><br/><code> readonly percentile_ranks: string;</code><br/><code> readonly serial_diff: string;</code><br/><code> readonly std_dev: string;</code><br/><code> readonly sum: string;</code><br/><code> readonly top_hits: string;</code><br/><code> };</code><br/><code> readonly scriptedFields: {</code><br/><code> readonly scriptFields: string;</code><br/><code> readonly scriptAggs: string;</code><br/><code> readonly painless: string;</code><br/><code> readonly painlessApi: string;</code><br/><code> readonly painlessSyntax: string;</code><br/><code> readonly luceneExpressions: string;</code><br/><code> };</code><br/><code> readonly indexPatterns: {</code><br/><code> readonly loadingData: string;</code><br/><code> readonly introduction: string;</code><br/><code> };</code><br/><code> readonly addData: string;</code><br/><code> readonly kibana: string;</code><br/><code> readonly siem: {</code><br/><code> readonly guide: string;</code><br/><code> readonly gettingStarted: string;</code><br/><code> };</code><br/><code> readonly query: {</code><br/><code> readonly luceneQuerySyntax: string;</code><br/><code> readonly queryDsl: string;</code><br/><code> readonly kueryQuerySyntax: string;</code><br/><code> };</code><br/><code> readonly date: {</code><br/><code> readonly dateMath: string;</code><br/><code> };</code><br/><code> readonly management: Record<string, string>;</code><br/><code> readonly visualize: Record<string, string>;</code><br/><code> }</code> | | +| [links](./kibana-plugin-core-public.doclinksstart.links.md) | <code>{</code><br/><code> readonly dashboard: {</code><br/><code> readonly drilldowns: string;</code><br/><code> readonly drilldownsTriggerPicker: string;</code><br/><code> readonly urlDrilldownTemplateSyntax: string;</code><br/><code> readonly urlDrilldownVariables: string;</code><br/><code> };</code><br/><code> readonly filebeat: {</code><br/><code> readonly base: string;</code><br/><code> readonly installation: string;</code><br/><code> readonly configuration: string;</code><br/><code> readonly elasticsearchOutput: string;</code><br/><code> readonly startup: string;</code><br/><code> readonly exportedFields: string;</code><br/><code> };</code><br/><code> readonly auditbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly metricbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly heartbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly logstash: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly functionbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly winlogbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly aggs: {</code><br/><code> readonly date_histogram: string;</code><br/><code> readonly date_range: string;</code><br/><code> readonly filter: string;</code><br/><code> readonly filters: string;</code><br/><code> readonly geohash_grid: string;</code><br/><code> readonly histogram: string;</code><br/><code> readonly ip_range: string;</code><br/><code> readonly range: string;</code><br/><code> readonly significant_terms: string;</code><br/><code> readonly terms: string;</code><br/><code> readonly avg: string;</code><br/><code> readonly avg_bucket: string;</code><br/><code> readonly max_bucket: string;</code><br/><code> readonly min_bucket: string;</code><br/><code> readonly sum_bucket: string;</code><br/><code> readonly cardinality: string;</code><br/><code> readonly count: string;</code><br/><code> readonly cumulative_sum: string;</code><br/><code> readonly derivative: string;</code><br/><code> readonly geo_bounds: string;</code><br/><code> readonly geo_centroid: string;</code><br/><code> readonly max: string;</code><br/><code> readonly median: string;</code><br/><code> readonly min: string;</code><br/><code> readonly moving_avg: string;</code><br/><code> readonly percentile_ranks: string;</code><br/><code> readonly serial_diff: string;</code><br/><code> readonly std_dev: string;</code><br/><code> readonly sum: string;</code><br/><code> readonly top_hits: string;</code><br/><code> };</code><br/><code> readonly scriptedFields: {</code><br/><code> readonly scriptFields: string;</code><br/><code> readonly scriptAggs: string;</code><br/><code> readonly painless: string;</code><br/><code> readonly painlessApi: string;</code><br/><code> readonly painlessSyntax: string;</code><br/><code> readonly luceneExpressions: string;</code><br/><code> };</code><br/><code> readonly indexPatterns: {</code><br/><code> readonly loadingData: string;</code><br/><code> readonly introduction: string;</code><br/><code> };</code><br/><code> readonly addData: string;</code><br/><code> readonly kibana: string;</code><br/><code> readonly siem: {</code><br/><code> readonly guide: string;</code><br/><code> readonly gettingStarted: string;</code><br/><code> };</code><br/><code> readonly query: {</code><br/><code> readonly eql: string;</code><br/><code> readonly luceneQuerySyntax: string;</code><br/><code> readonly queryDsl: string;</code><br/><code> readonly kueryQuerySyntax: string;</code><br/><code> };</code><br/><code> readonly date: {</code><br/><code> readonly dateMath: string;</code><br/><code> };</code><br/><code> readonly management: Record<string, string>;</code><br/><code> readonly visualize: Record<string, string>;</code><br/><code> }</code> | | diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts index 47f58a3a9fcbfb..629bf97c24887d 100644 --- a/src/core/public/doc_links/doc_links_service.ts +++ b/src/core/public/doc_links/doc_links_service.ts @@ -119,6 +119,7 @@ export class DocLinksService { gettingStarted: `${ELASTIC_WEBSITE_URL}guide/en/security/${DOC_LINK_VERSION}/index.html`, }, query: { + eql: `${ELASTICSEARCH_DOCS}eql.html`, luceneQuerySyntax: `${ELASTICSEARCH_DOCS}query-dsl-query-string-query.html#query-string-syntax`, queryDsl: `${ELASTICSEARCH_DOCS}query-dsl.html`, kueryQuerySyntax: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/kuery-query.html`, @@ -227,6 +228,7 @@ export interface DocLinksStart { readonly gettingStarted: string; }; readonly query: { + readonly eql: string; readonly luceneQuerySyntax: string; readonly queryDsl: string; readonly kueryQuerySyntax: string; diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 5970c9a8571c45..08491dc76cd275 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -539,6 +539,7 @@ export interface DocLinksStart { readonly gettingStarted: string; }; readonly query: { + readonly eql: string; readonly luceneQuerySyntax: string; readonly queryDsl: string; readonly kueryQuerySyntax: string; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 2910f02a187f48..e46bd9e28d8c45 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -117,6 +117,7 @@ export const DETECTION_ENGINE_PREPACKAGED_URL = `${DETECTION_ENGINE_RULES_URL}/p export const DETECTION_ENGINE_PRIVILEGES_URL = `${DETECTION_ENGINE_URL}/privileges`; export const DETECTION_ENGINE_INDEX_URL = `${DETECTION_ENGINE_URL}/index`; export const DETECTION_ENGINE_TAGS_URL = `${DETECTION_ENGINE_URL}/tags`; +export const DETECTION_ENGINE_EQL_VALIDATION_URL = `${DETECTION_ENGINE_URL}/validate_eql`; 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`; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.mock.ts new file mode 100644 index 00000000000000..96afc0c85df44b --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.mock.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EqlValidationSchema } from './eql_validation_schema'; + +export const getEqlValidationSchemaMock = (): EqlValidationSchema => ({ + index: ['index-123'], + query: 'process where process.name == "regsvr32.exe"', +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.test.ts new file mode 100644 index 00000000000000..84bb8e067bf754 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; +import { eqlValidationSchema, EqlValidationSchema } from './eql_validation_schema'; +import { getEqlValidationSchemaMock } from './eql_validation_schema.mock'; + +describe('EQL validation schema', () => { + it('requires a value for index', () => { + const payload = { + ...getEqlValidationSchemaMock(), + index: undefined, + }; + const decoded = eqlValidationSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "index"', + ]); + expect(message.schema).toEqual({}); + }); + + it('requires a value for query', () => { + const payload = { + ...getEqlValidationSchemaMock(), + query: undefined, + }; + const decoded = eqlValidationSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "query"', + ]); + expect(message.schema).toEqual({}); + }); + + it('validates a payload with index and query', () => { + const payload = getEqlValidationSchemaMock(); + const decoded = eqlValidationSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + const expected: EqlValidationSchema = { + index: ['index-123'], + query: 'process where process.name == "regsvr32.exe"', + }; + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(expected); + }); +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.ts new file mode 100644 index 00000000000000..abbbe33a32258b --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/request/eql_validation_schema.ts @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +import { index, query } from '../common/schemas'; + +export const eqlValidationSchema = t.exact( + t.type({ + index, + query, + }) +); + +export type EqlValidationSchema = t.TypeOf<typeof eqlValidationSchema>; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.mock.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.mock.ts new file mode 100644 index 00000000000000..98e5db47253fb2 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.mock.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EqlValidationSchema } from './eql_validation_schema'; + +export const getEqlValidationResponseMock = (): EqlValidationSchema => ({ + valid: false, + errors: ['line 3:52: token recognition error at: '], +}); + +export const getValidEqlValidationResponseMock = (): EqlValidationSchema => ({ + valid: true, + errors: [], +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.test.ts new file mode 100644 index 00000000000000..939238e340cff5 --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; + +import { exactCheck } from '../../../exact_check'; +import { foldLeftRight, getPaths } from '../../../test_utils'; +import { getEqlValidationResponseMock } from './eql_validation_schema.mock'; +import { eqlValidationSchema } from './eql_validation_schema'; + +describe('EQL validation response schema', () => { + it('validates a typical response', () => { + const payload = getEqlValidationResponseMock(); + const decoded = eqlValidationSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(getEqlValidationResponseMock()); + }); + + it('invalidates a response with extra properties', () => { + const payload = { ...getEqlValidationResponseMock(), extra: 'nope' }; + const decoded = eqlValidationSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual(['invalid keys "extra"']); + expect(message.schema).toEqual({}); + }); + + it('invalidates a response with missing properties', () => { + const payload = { ...getEqlValidationResponseMock(), valid: undefined }; + const decoded = eqlValidationSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "undefined" supplied to "valid"', + ]); + expect(message.schema).toEqual({}); + }); + + it('invalidates a response with properties of the wrong type', () => { + const payload = { ...getEqlValidationResponseMock(), errors: 'should be an array' }; + const decoded = eqlValidationSchema.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "should be an array" supplied to "errors"', + ]); + expect(message.schema).toEqual({}); + }); +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.ts new file mode 100644 index 00000000000000..e999e1dd273f8f --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/response/eql_validation_schema.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +export const eqlValidationSchema = t.exact( + t.type({ + valid: t.boolean, + errors: t.array(t.string), + }) +); + +export type EqlValidationSchema = t.TypeOf<typeof eqlValidationSchema>; diff --git a/x-pack/plugins/security_solution/common/detection_engine/utils.ts b/x-pack/plugins/security_solution/common/detection_engine/utils.ts index f76417099bb173..d7b23755699f50 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/utils.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/utils.ts @@ -19,5 +19,6 @@ export const hasNestedEntry = (entries: EntriesArray): boolean => { export const isEqlRule = (ruleType: Type | undefined): boolean => ruleType === 'eql'; export const isThresholdRule = (ruleType: Type | undefined): boolean => ruleType === 'threshold'; -export const isQueryRule = (ruleType: Type | undefined): boolean => ruleType === 'query'; +export const isQueryRule = (ruleType: Type | undefined): boolean => + ruleType === 'query' || ruleType === 'saved_query'; export const isThreatMatchRule = (ruleType: Type): boolean => ruleType === 'threat_match'; diff --git a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts index f999c5cecc3923..d8832dc4ee600f 100644 --- a/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts +++ b/x-pack/plugins/security_solution/cypress/integration/alerts_detection_rules_custom.spec.ts @@ -93,7 +93,7 @@ import { goToScheduleStepTab, waitForTheRuleToBeExecuted, } from '../tasks/create_new_rule'; -import { saveEditedRule } from '../tasks/edit_rule'; +import { saveEditedRule, waitForKibana } from '../tasks/edit_rule'; import { esArchiverLoad, esArchiverUnload } from '../tasks/es_archiver'; import { loginAndWaitForPageWithoutDateRange } from '../tasks/login'; import { refreshPage } from '../tasks/security_header'; @@ -290,6 +290,7 @@ describe('Custom detection rules deletion and edition', () => { context('Edition', () => { it('Allows a rule to be edited', () => { editFirstRule(); + waitForKibana(); // expect define step to populate cy.get(CUSTOM_QUERY_INPUT).should('have.text', existingRule.customQuery); diff --git a/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts b/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts index 1bf0ff34ebd941..e25eb7453c63cf 100644 --- a/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts +++ b/x-pack/plugins/security_solution/cypress/screens/edit_rule.ts @@ -5,3 +5,5 @@ */ export const EDIT_SUBMIT_BUTTON = '[data-test-subj="ruleEditSubmitButton"]'; +export const KIBANA_LOADING_INDICATOR = '[data-test-subj="globalLoadingIndicator"]'; +export const KIBANA_LOADING_COMPLETE_INDICATOR = '[data-test-subj="globalLoadingIndicator-hidden"]'; diff --git a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts index 690a36058ec333..2dc1318ccb81dd 100644 --- a/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts +++ b/x-pack/plugins/security_solution/cypress/tasks/edit_rule.ts @@ -4,9 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EDIT_SUBMIT_BUTTON } from '../screens/edit_rule'; +import { EDIT_SUBMIT_BUTTON, KIBANA_LOADING_COMPLETE_INDICATOR } from '../screens/edit_rule'; export const saveEditedRule = () => { cy.get(EDIT_SUBMIT_BUTTON).should('exist').click({ force: true }); cy.get(EDIT_SUBMIT_BUTTON).should('not.exist'); }; + +export const waitForKibana = () => { + cy.get(KIBANA_LOADING_COMPLETE_INDICATOR).should('exist'); +}; diff --git a/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts b/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts new file mode 100644 index 00000000000000..11fe79910bc876 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/eql/api.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { HttpStart } from '../../../../../../../src/core/public'; +import { DETECTION_ENGINE_EQL_VALIDATION_URL } from '../../../../common/constants'; +import { EqlValidationSchema as EqlValidationRequest } from '../../../../common/detection_engine/schemas/request/eql_validation_schema'; +import { EqlValidationSchema as EqlValidationResponse } from '../../../../common/detection_engine/schemas/response/eql_validation_schema'; + +interface ApiParams { + http: HttpStart; + signal: AbortSignal; +} + +export const validateEql = async ({ + http, + query, + index, + signal, +}: ApiParams & EqlValidationRequest) => { + return http.fetch<EqlValidationResponse>(DETECTION_ENGINE_EQL_VALIDATION_URL, { + method: 'POST', + body: JSON.stringify({ + query, + index, + }), + signal, + }); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_overview_link.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_overview_link.tsx new file mode 100644 index 00000000000000..e9891fc066ec2a --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_overview_link.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import styled from 'styled-components'; +import { EuiLink, EuiText } from '@elastic/eui'; + +import { useKibana } from '../../../../common/lib/kibana'; +import { EQL_OVERVIEW_LINK_TEXT } from './translations'; + +const InlineText = styled(EuiText)` + display: inline-block; +`; + +export const EqlOverviewLink = () => { + const overviewUrl = useKibana().services.docLinks.links.query.eql; + + return ( + <EuiLink external href={overviewUrl} target="_blank"> + <InlineText size="xs">{EQL_OVERVIEW_LINK_TEXT}</InlineText> + </EuiLink> + ); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.test.tsx index 331c0ba4c44914..5539e5eb2c294e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.test.tsx @@ -7,9 +7,12 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; -import { useFormFieldMock } from '../../../../common/mock'; +import { TestProviders, useFormFieldMock } from '../../../../common/mock'; import { mockQueryBar } from '../../../pages/detection_engine/rules/all/__mocks__/mock'; import { EqlQueryBar, EqlQueryBarProps } from './eql_query_bar'; +import { getEqlValidationError } from './validators.mock'; + +jest.mock('../../../../common/lib/kibana'); describe('EqlQueryBar', () => { let mockField: EqlQueryBarProps['field']; @@ -27,7 +30,11 @@ describe('EqlQueryBar', () => { }); it('sets the field value on input change', () => { - const wrapper = mount(<EqlQueryBar dataTestSubj="myQueryBar" field={mockField} />); + const wrapper = mount( + <TestProviders> + <EqlQueryBar dataTestSubj="myQueryBar" field={mockField} /> + </TestProviders> + ); wrapper .find('[data-test-subj="eqlQueryBarTextInput"]') @@ -44,4 +51,30 @@ describe('EqlQueryBar', () => { expect(mockField.setValue).toHaveBeenCalledWith(expected); }); + + it('does not render errors for a valid query', () => { + const wrapper = mount( + <TestProviders> + <EqlQueryBar dataTestSubj="myQueryBar" field={mockField} /> + </TestProviders> + ); + + expect(wrapper.find('[data-test-subj="eql-validation-errors-popover"]').exists()).toEqual( + false + ); + }); + + it('renders errors for an invalid query', () => { + const invalidMockField = useFormFieldMock({ + value: mockQueryBar, + errors: [getEqlValidationError()], + }); + const wrapper = mount( + <TestProviders> + <EqlQueryBar dataTestSubj="myQueryBar" field={invalidMockField} /> + </TestProviders> + ); + + expect(wrapper.find('[data-test-subj="eql-validation-errors-popover"]').exists()).toEqual(true); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx index e3f33ea9b9b870..f7ee5be18154cb 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/eql_query_bar.tsx @@ -4,11 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { FC, useCallback, ChangeEvent } from 'react'; +import React, { FC, useCallback, ChangeEvent, useEffect, useState } from 'react'; +import styled from 'styled-components'; import { EuiFormRow, EuiTextArea } from '@elastic/eui'; -import { FieldHook, getFieldValidityAndErrorMessage } from '../../../../shared_imports'; +import { FieldHook } from '../../../../shared_imports'; +import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { DefineStepRule } from '../../../pages/detection_engine/rules/types'; +import * as i18n from './translations'; +import { EqlQueryBarFooter } from './footer'; +import { getValidationResults } from './validators'; + +const TextArea = styled(EuiTextArea)` + display: block; + border: ${({ theme }) => theme.eui.euiBorderThin}; + border-bottom: 0; + box-shadow: none; + min-height: ${({ theme }) => theme.eui.euiFormControlHeight}; +`; export interface EqlQueryBarProps { dataTestSubj: string; @@ -17,14 +30,27 @@ export interface EqlQueryBarProps { } export const EqlQueryBar: FC<EqlQueryBarProps> = ({ dataTestSubj, field, idAria }) => { + const { addError } = useAppToasts(); + const [errorMessages, setErrorMessages] = useState<string[]>([]); const { setValue } = field; - const { isInvalid, errorMessage } = getFieldValidityAndErrorMessage(field); + const { isValid, message, messages, error } = getValidationResults(field); const fieldValue = field.value.query.query as string; + useEffect(() => { + setErrorMessages(messages ?? []); + }, [messages]); + + useEffect(() => { + if (error) { + addError(error, { title: i18n.EQL_VALIDATION_REQUEST_ERROR }); + } + }, [error, addError]); + const handleChange = useCallback( (e: ChangeEvent<HTMLTextAreaElement>) => { const newQuery = e.target.value; + setErrorMessages([]); setValue({ filters: [], query: { @@ -41,19 +67,22 @@ export const EqlQueryBar: FC<EqlQueryBarProps> = ({ dataTestSubj, field, idAria label={field.label} labelAppend={field.labelAppend} helpText={field.helpText} - error={errorMessage} - isInvalid={isInvalid} + error={message} + isInvalid={!isValid} fullWidth data-test-subj={dataTestSubj} describedByIds={idAria ? [idAria] : undefined} > - <EuiTextArea - data-test-subj="eqlQueryBarTextInput" - fullWidth - isInvalid={isInvalid} - value={fieldValue} - onChange={handleChange} - /> + <> + <TextArea + data-test-subj="eqlQueryBarTextInput" + fullWidth + isInvalid={!isValid} + value={fieldValue} + onChange={handleChange} + /> + <EqlQueryBarFooter errors={errorMessages} /> + </> </EuiFormRow> ); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.test.tsx new file mode 100644 index 00000000000000..01bd8afd0e4abf --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow, mount } from 'enzyme'; + +import { ErrorsPopover } from './errors_popover'; + +describe('ErrorsPopover', () => { + let mockErrors: string[]; + + beforeEach(() => { + mockErrors = []; + }); + + it('renders correctly', () => { + const wrapper = shallow(<ErrorsPopover errors={mockErrors} />); + + expect(wrapper.find('[data-test-subj="eql-validation-errors-popover"]')).toHaveLength(1); + }); + + it('renders the number of errors by default', () => { + mockErrors = ['error', 'other', 'third']; + const wrapper = mount(<ErrorsPopover errors={mockErrors} />); + expect( + wrapper.find('[data-test-subj="eql-validation-errors-popover"]').first().text() + ).toContain('3'); + }); + + it('renders the error messages if clicked', () => { + mockErrors = ['error', 'other']; + const wrapper = mount(<ErrorsPopover errors={mockErrors} />); + wrapper + .find('[data-test-subj="eql-validation-errors-popover-button"]') + .first() + .simulate('click'); + + expect( + wrapper.find('[data-test-subj="eql-validation-errors-popover"]').first().text() + ).toContain('2'); + const messagesContent = wrapper + .find('[data-test-subj="eql-validation-errors-popover-content"]') + .text(); + expect(messagesContent).toContain('error'); + expect(messagesContent).toContain('other'); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.tsx new file mode 100644 index 00000000000000..a7122b7dc65d87 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/errors_popover.tsx @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC, useCallback, useState } from 'react'; +import { EuiButtonEmpty, EuiPopover, EuiPopoverTitle, EuiText } from '@elastic/eui'; + +import * as i18n from './translations'; + +export interface ErrorsPopoverProps { + ariaLabel?: string; + errors: string[]; +} + +export const ErrorsPopover: FC<ErrorsPopoverProps> = ({ ariaLabel, errors }) => { + const [isOpen, setIsOpen] = useState(false); + + const handleToggle = useCallback(() => { + setIsOpen(!isOpen); + }, [isOpen]); + + const handleClose = useCallback(() => { + setIsOpen(false); + }, []); + + return ( + <EuiPopover + data-test-subj="eql-validation-errors-popover" + button={ + <EuiButtonEmpty + data-test-subj="eql-validation-errors-popover-button" + iconType="crossInACircleFilled" + size="s" + color="danger" + aria-label={ariaLabel} + onClick={handleToggle} + > + {errors.length} + </EuiButtonEmpty> + } + isOpen={isOpen} + closePopover={handleClose} + anchorPosition="downCenter" + > + <div data-test-subj="eql-validation-errors-popover-content"> + <EuiPopoverTitle>{i18n.EQL_VALIDATION_ERRORS_TITLE}</EuiPopoverTitle> + {errors.map((message, idx) => ( + <EuiText key={idx}>{message}</EuiText> + ))} + </div> + </EuiPopover> + ); +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.tsx new file mode 100644 index 00000000000000..19bab26f8aa589 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/footer.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC } from 'react'; +import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel } from '@elastic/eui'; + +import * as i18n from './translations'; +import { ErrorsPopover } from './errors_popover'; +import { EqlOverviewLink } from './eql_overview_link'; + +export interface Props { + errors: string[]; +} + +const Container = styled(EuiPanel)` + border-radius: 0; + background: ${({ theme }) => theme.eui.euiPageBackgroundColor}; + padding: ${({ theme }) => theme.eui.euiSizeXS}; +`; + +const FlexGroup = styled(EuiFlexGroup)` + min-height: ${({ theme }) => theme.eui.euiSizeXL}; +`; + +export const EqlQueryBarFooter: FC<Props> = ({ errors }) => ( + <Container> + <FlexGroup alignItems="center" justifyContent="spaceBetween" gutterSize="none"> + <EuiFlexItem> + {errors.length > 0 && ( + <ErrorsPopover ariaLabel={i18n.EQL_VALIDATION_ERROR_POPOVER_LABEL} errors={errors} /> + )} + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EqlOverviewLink /> + </EuiFlexItem> + </FlexGroup> + </Container> +); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/translations.ts b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/translations.ts new file mode 100644 index 00000000000000..8b016d9ad68cb6 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/translations.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const EQL_VALIDATION_REQUEST_ERROR = i18n.translate( + 'xpack.securitySolution.detectionEngine.eqlValidation.requestError', + { + defaultMessage: 'An error occurred while validating your EQL query', + } +); + +export const EQL_VALIDATION_ERRORS_TITLE = i18n.translate( + 'xpack.securitySolution.detectionEngine.eqlValidation.title', + { + defaultMessage: 'EQL Validation Errors', + } +); + +export const EQL_VALIDATION_ERROR_POPOVER_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.eqlValidation.showErrorsLabel', + { + defaultMessage: 'Show EQL Validation Errors', + } +); + +export const EQL_QUERY_BAR_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.eqlQueryBar.label', + { + defaultMessage: 'Enter an EQL Query', + } +); + +export const EQL_OVERVIEW_LINK_TEXT = i18n.translate( + 'xpack.securitySolution.detectionEngine.eqlOverViewLink.text', + { + defaultMessage: 'Event Query Language (EQL) Overview', + } +); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.mock.ts b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.mock.ts new file mode 100644 index 00000000000000..40e45b9c2d470c --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.mock.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ValidationError } from '../../../../shared_imports'; +import { ERROR_CODES } from './validators'; + +export const getEqlResponseError = (): ValidationError => ({ + code: ERROR_CODES.FAILED_REQUEST, + message: 'something went wrong', +}); + +export const getEqlValidationError = (): ValidationError => ({ + code: ERROR_CODES.INVALID_EQL, + messages: ['line 1: WRONG\nline 2: ALSO WRONG'], + message: '', +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.test.ts b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.test.ts new file mode 100644 index 00000000000000..24afce7bb18b02 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { debounceAsync } from './validators'; + +jest.useFakeTimers(); + +describe('debounceAsync', () => { + let fn: jest.Mock; + + beforeEach(() => { + fn = jest.fn().mockResolvedValueOnce('first'); + }); + + it('resolves with the underlying invocation result', async () => { + const debounced = debounceAsync(fn, 0); + const promise = debounced(); + jest.runOnlyPendingTimers(); + + expect(await promise).toEqual('first'); + }); + + it('resolves intermediate calls when the next invocation resolves', async () => { + const debounced = debounceAsync(fn, 200); + fn.mockResolvedValueOnce('second'); + + const promise = debounced(); + jest.runOnlyPendingTimers(); + expect(await promise).toEqual('first'); + + const promises = [debounced(), debounced()]; + jest.runOnlyPendingTimers(); + + expect(await Promise.all(promises)).toEqual(['second', 'second']); + }); + + it('debounces the function', async () => { + const debounced = debounceAsync(fn, 200); + + debounced(); + jest.runOnlyPendingTimers(); + + debounced(); + debounced(); + jest.runOnlyPendingTimers(); + + expect(fn).toHaveBeenCalledTimes(2); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.ts b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.ts new file mode 100644 index 00000000000000..165522c10916e9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/eql_query_bar/validators.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { isEmpty } from 'lodash'; + +import { FieldHook, ValidationError, ValidationFunc } from '../../../../shared_imports'; +import { isEqlRule } from '../../../../../common/detection_engine/utils'; +import { KibanaServices } from '../../../../common/lib/kibana'; +import { DefineStepRule } from '../../../pages/detection_engine/rules/types'; +import { validateEql } from '../../../../common/hooks/eql/api'; +import { FieldValueQueryBar } from '../query_bar'; +import * as i18n from './translations'; + +export enum ERROR_CODES { + FAILED_REQUEST = 'ERR_FAILED_REQUEST', + INVALID_EQL = 'ERR_INVALID_EQL', +} + +/** + * Unlike lodash's debounce, which resolves intermediate calls with the most + * recent value, this implementation waits to resolve intermediate calls until + * the next invocation resolves. + * + * @param fn an async function + * + * @returns A debounced async function that resolves on the next invocation + */ +export const debounceAsync = <Args extends unknown[], Result extends Promise<unknown>>( + fn: (...args: Args) => Result, + interval: number +): ((...args: Args) => Result) => { + let handle: ReturnType<typeof setTimeout> | undefined; + let resolves: Array<(value?: Result) => void> = []; + + return (...args: Args): Result => { + if (handle) { + clearTimeout(handle); + } + + handle = setTimeout(() => { + const result = fn(...args); + resolves.forEach((resolve) => resolve(result)); + resolves = []; + }, interval); + + return new Promise((resolve) => resolves.push(resolve)) as Result; + }; +}; + +export const eqlValidator = async ( + ...args: Parameters<ValidationFunc> +): Promise<ValidationError<ERROR_CODES> | void | undefined> => { + const [{ value, formData }] = args; + const { query: queryValue } = value as FieldValueQueryBar; + const query = queryValue.query as string; + const { index, ruleType } = formData as DefineStepRule; + + const needsValidation = isEqlRule(ruleType) && !isEmpty(query); + if (!needsValidation) { + return; + } + + try { + const { http } = KibanaServices.get(); + const signal = new AbortController().signal; + const response = await validateEql({ query, http, signal, index }); + + if (response?.valid === false) { + return { + code: ERROR_CODES.INVALID_EQL, + message: '', + messages: response.errors, + }; + } + } catch (error) { + return { + code: ERROR_CODES.FAILED_REQUEST, + message: i18n.EQL_VALIDATION_REQUEST_ERROR, + error, + }; + } +}; + +export const getValidationResults = <T = unknown>( + field: FieldHook<T> +): { isValid: boolean; message: string; messages?: string[]; error?: Error } => { + const hasErrors = field.errors.length > 0; + const isValid = !field.isChangingValue && !hasErrors; + + if (hasErrors) { + const [error] = field.errors; + const message = error.message; + + if (error.code === ERROR_CODES.INVALID_EQL) { + return { isValid, message, messages: error.messages }; + } else { + return { isValid, message, error: error.error }; + } + } else { + return { isValid, message: '' }; + } +}; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/eql_search_icon.svg b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/eql_search_icon.svg new file mode 100644 index 00000000000000..716fff726293c9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/eql_search_icon.svg @@ -0,0 +1,6 @@ +<svg width="26" height="24" viewBox="0 0 26 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M17.0699 15.5699C18.2787 14.0377 19 12.1031 19 10C19 5.02944 14.9706 1 10 1C5.02944 1 1 5.02944 1 10C1 14.9706 5.02944 19 10 19C11.7165 19 13.3208 18.5195 14.6856 17.6856L20 23H24.5L17.0699 15.5699Z" fill="white"/> + <path d="M17.0699 15.5699L16.6773 15.2602L16.402 15.6091L16.7163 15.9234L17.0699 15.5699ZM14.6856 17.6856L15.0392 17.332L14.7608 17.0537L14.4249 17.2589L14.6856 17.6856ZM20 23L19.6464 23.3536L19.7929 23.5H20V23ZM24.5 23V23.5H25.7071L24.8536 22.6464L24.5 23ZM18.5 10C18.5 11.9868 17.819 13.8131 16.6773 15.2602L17.4624 15.8796C18.7383 14.2623 19.5 12.2194 19.5 10H18.5ZM10 1.5C14.6944 1.5 18.5 5.30558 18.5 10H19.5C19.5 4.75329 15.2467 0.5 10 0.5V1.5ZM1.5 10C1.5 5.30558 5.30558 1.5 10 1.5V0.5C4.75329 0.5 0.5 4.75329 0.5 10H1.5ZM10 18.5C5.30558 18.5 1.5 14.6944 1.5 10H0.5C0.5 15.2467 4.75329 19.5 10 19.5V18.5ZM14.4249 17.2589C13.1363 18.0462 11.6219 18.5 10 18.5V19.5C11.8111 19.5 13.5052 18.9927 14.9463 18.1123L14.4249 17.2589ZM20.3536 22.6464L15.0392 17.332L14.332 18.0392L19.6464 23.3536L20.3536 22.6464ZM24.5 22.5H20V23.5H24.5V22.5ZM16.7163 15.9234L24.1464 23.3536L24.8536 22.6464L17.4234 15.2163L16.7163 15.9234Z" fill="black"/> + <circle cx="10" cy="10" r="5.5" stroke="black"/> + <path d="M8 11.2169V8.7831L10 7.5831L12 8.7831V11.2169L10 12.4169L8 11.2169Z" stroke="black"/> +</svg> diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx index 9a1d11a2dfe42c..4b96b8a0ad7bd1 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx @@ -7,6 +7,7 @@ import React, { useCallback, useMemo } from 'react'; import { EuiCard, EuiFlexGrid, EuiFlexItem, EuiFormRow, EuiIcon } from '@elastic/eui'; +import { Type } from '../../../../../common/detection_engine/schemas/common/schemas'; import { isMlRule } from '../../../../../common/machine_learning/helpers'; import { isThresholdRule, @@ -18,7 +19,7 @@ import { FieldHook } from '../../../../shared_imports'; import { useKibana } from '../../../../common/lib/kibana'; import * as i18n from './translations'; import { MlCardDescription } from './ml_card_description'; -import { Type } from '../../../../../common/detection_engine/schemas/common/schemas'; +import EqlSearchIcon from './eql_search_icon.svg'; interface SelectRuleTypeProps { describedByIds?: string[]; @@ -144,7 +145,7 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({ data-test-subj="eqlRuleType" title={i18n.EQL_TYPE_TITLE} description={i18n.EQL_TYPE_DESCRIPTION} - icon={<EuiIcon size="l" type="bullseye" />} + icon={<EuiIcon size="l" type={EqlSearchIcon} />} isDisabled={eqlSelectableConfig.isDisabled && !eqlSelectableConfig.isSelected} selectable={eqlSelectableConfig} /> 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 f728a508fef86b..9bd0e4fb4da5de 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 @@ -274,6 +274,10 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({ isLoading: indexPatternsLoading, dataTestSubj: 'detectionEngineStepDefineRuleEqlQueryBar', }} + config={{ + ...schema.queryBar, + label: i18n.EQL_QUERY_BAR_LABEL, + }} /> ) : ( <UseField @@ -281,6 +285,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({ path="queryBar" config={{ ...schema.queryBar, + label: i18n.QUERY_BAR_LABEL, labelAppend: ( <MyLabelButton data-test-subj="importQueryFromSavedTimeline" diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx index 9c2f8fd80f66e8..ebffb1abf47870 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/schema.tsx @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { isEmpty } from 'lodash'; import { i18n } from '@kbn/i18n'; import { EuiText } from '@elastic/eui'; -import { isEmpty } from 'lodash/fp'; import React from 'react'; import { @@ -25,6 +25,7 @@ import { ValidationFunc, } from '../../../../shared_imports'; import { DefineStepRule } from '../../../pages/detection_engine/rules/types'; +import { debounceAsync, eqlValidator } from '../eql_query_bar/validators'; import { CUSTOM_QUERY_REQUIRED, INVALID_CUSTOM_QUERY, @@ -36,6 +37,7 @@ import { export const schema: FormSchema<DefineStepRule> = { index: { + fieldsToValidateOnChange: ['index', 'queryBar'], type: FIELD_TYPES.COMBO_BOX, label: i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepAboutRule.fiedIndexPatternsLabel', @@ -69,12 +71,6 @@ export const schema: FormSchema<DefineStepRule> = { ], }, queryBar: { - label: i18n.translate( - 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldQuerBarLabel', - { - defaultMessage: 'Custom query', - } - ), validations: [ { validator: ( @@ -120,6 +116,9 @@ export const schema: FormSchema<DefineStepRule> = { } }, }, + { + validator: debounceAsync(eqlValidator, 300), + }, ], }, ruleType: { diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx index 8e0a3f9b8659e4..164b1df8463e6f 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/translations.tsx @@ -71,6 +71,20 @@ export const ENABLE_ML_JOB_WARNING = i18n.translate( } ); +export const QUERY_BAR_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.fieldQuerBarLabel', + { + defaultMessage: 'Custom query', + } +); + +export const EQL_QUERY_BAR_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.EqlQueryBarLabel', + { + defaultMessage: 'EQL query', + } +); + export const THREAT_MATCH_INDEX_HELPER_TEXT = i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepDefineRule.threatMatchingIcesHelperDescription', { diff --git a/x-pack/plugins/security_solution/public/shared_imports.ts b/x-pack/plugins/security_solution/public/shared_imports.ts index 08e9fb854e5a2c..f60fefc4f6dfa2 100644 --- a/x-pack/plugins/security_solution/public/shared_imports.ts +++ b/x-pack/plugins/security_solution/public/shared_imports.ts @@ -22,6 +22,7 @@ export { useForm, useFormContext, useFormData, + ValidationError, ValidationFunc, VALIDATION_TYPES, } from '../../../../src/plugins/es_ui_shared/static/forms/hook_form_lib'; 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 c45dd5bd8a2817..8e379e5caa89ec 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 @@ -18,6 +18,7 @@ const createMockClients = () => ({ alertsClient: alertsClientMock.create(), clusterClient: elasticsearchServiceMock.createLegacyScopedClusterClient(), licensing: { license: licensingMock.createLicenseMock() }, + newClusterClient: elasticsearchServiceMock.createScopedClusterClient(), savedObjectsClient: savedObjectsClientMock.create(), appClient: siemMock.createClient(), }); @@ -31,7 +32,9 @@ const createRequestContextMock = ( core: { ...coreContext, elasticsearch: { - legacy: { ...coreContext.elasticsearch, client: clients.clusterClient }, + ...coreContext.elasticsearch, + client: clients.newClusterClient, + legacy: { ...coreContext.elasticsearch.legacy, client: clients.clusterClient }, }, savedObjects: { client: clients.savedObjectsClient }, }, 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 894ac2e0bb703a..33da5a0c2322d2 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 @@ -15,8 +15,9 @@ import { INTERNAL_RULE_ID_KEY, INTERNAL_IMMUTABLE_KEY, DETECTION_ENGINE_PREPACKAGED_URL, + DETECTION_ENGINE_EQL_VALIDATION_URL, } from '../../../../../common/constants'; -import { ShardsResponse } from '../../../types'; +import { EqlSearchResponse, ShardsResponse } from '../../../types'; import { RuleAlertType, IRuleSavedAttributesSavedObjectAttributes, @@ -28,6 +29,7 @@ import { QuerySignalsSchemaDecoded } from '../../../../../common/detection_engin import { SetSignalsStatusSchemaDecoded } from '../../../../../common/detection_engine/schemas/request/set_signal_status_schema'; import { getCreateRulesSchemaMock } from '../../../../../common/detection_engine/schemas/request/create_rules_schema.mock'; import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock'; +import { getEqlValidationSchemaMock } from '../../../../../common/detection_engine/schemas/request/eql_validation_schema.mock'; export const typicalSetStatusSignalByIdsPayload = (): SetSignalsStatusSchemaDecoded => ({ signal_ids: ['somefakeid1', 'somefakeid2'], @@ -145,6 +147,13 @@ export const getPrepackagedRulesStatusRequest = () => path: `${DETECTION_ENGINE_PREPACKAGED_URL}/_status`, }); +export const eqlValidationRequest = () => + requestMock.create({ + method: 'post', + path: DETECTION_ENGINE_EQL_VALIDATION_URL, + body: getEqlValidationSchemaMock(), + }); + export interface FindHit<T = RuleAlertType> { page: number; perPage: number; @@ -577,6 +586,22 @@ export const getEmptySignalsResponse = (): SignalSearchResponse => ({ }, }); +export const getEmptyEqlSearchResponse = (): EqlSearchResponse<unknown> => ({ + hits: { total: { value: 0, relation: 'eq' }, events: [] }, + is_partial: false, + is_running: false, + took: 1, + timed_out: false, +}); + +export const getEmptyEqlSequencesResponse = (): EqlSearchResponse<unknown> => ({ + hits: { total: { value: 0, relation: 'eq' }, sequences: [] }, + is_partial: false, + is_running: false, + took: 1, + timed_out: false, +}); + export const getSuccessfulSignalUpdateResponse = () => ({ took: 18, timed_out: false, diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.mock.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.mock.ts new file mode 100644 index 00000000000000..4aa4c38802a929 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.mock.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ApiResponse } from '@elastic/elasticsearch'; +import { ErrorResponse } from './helpers'; + +export const getValidEqlResponse = (): ApiResponse['body'] => ({ + is_partial: false, + is_running: false, + took: 162, + timed_out: false, + hits: { + total: { + value: 1, + relation: 'eq', + }, + sequences: [], + }, +}); + +export const getEqlResponseWithValidationError = (): ErrorResponse => ({ + error: { + root_cause: [ + { + type: 'verification_exception', + reason: + 'Found 2 problems\nline 1:1: Unknown column [event.category]\nline 1:13: Unknown column [event.name]', + }, + ], + type: 'verification_exception', + reason: + 'Found 2 problems\nline 1:1: Unknown column [event.category]\nline 1:13: Unknown column [event.name]', + }, +}); + +export const getEqlResponseWithValidationErrors = (): ErrorResponse => ({ + error: { + root_cause: [ + { + type: 'verification_exception', + reason: + 'Found 2 problems\nline 1:1: Unknown column [event.category]\nline 1:13: Unknown column [event.name]', + }, + { + type: 'parsing_exception', + reason: "line 1:4: mismatched input '<EOF>' expecting 'where'", + }, + ], + type: 'verification_exception', + reason: + 'Found 2 problems\nline 1:1: Unknown column [event.category]\nline 1:13: Unknown column [event.name]', + }, +}); + +export const getEqlResponseWithNonValidationError = (): ApiResponse['body'] => ({ + error: { + root_cause: [ + { + type: 'other_error', + reason: 'some other reason', + }, + ], + type: 'other_error', + reason: 'some other reason', + }, +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.test.ts new file mode 100644 index 00000000000000..41a1ef0faf69f7 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { getValidationErrors, isErrorResponse, isValidationErrorResponse } from './helpers'; +import { + getEqlResponseWithNonValidationError, + getEqlResponseWithValidationError, + getEqlResponseWithValidationErrors, + getValidEqlResponse, +} from './helpers.mock'; + +describe('eql validation helpers', () => { + describe('isErrorResponse', () => { + it('is false for a regular response', () => { + expect(isErrorResponse(getValidEqlResponse())).toEqual(false); + }); + + it('is true for a response with non-validation errors', () => { + expect(isErrorResponse(getEqlResponseWithNonValidationError())).toEqual(true); + }); + + it('is true for a response with validation errors', () => { + expect(isErrorResponse(getEqlResponseWithValidationError())).toEqual(true); + }); + }); + + describe('isValidationErrorResponse', () => { + it('is false for a regular response', () => { + expect(isValidationErrorResponse(getValidEqlResponse())).toEqual(false); + }); + + it('is false for a response with non-validation errors', () => { + expect(isValidationErrorResponse(getEqlResponseWithNonValidationError())).toEqual(false); + }); + + it('is true for a response with validation errors', () => { + expect(isValidationErrorResponse(getEqlResponseWithValidationError())).toEqual(true); + }); + }); + + describe('getValidationErrors', () => { + it('returns a single error for a single root cause', () => { + expect(getValidationErrors(getEqlResponseWithValidationError())).toEqual([ + 'Found 2 problems\nline 1:1: Unknown column [event.category]\nline 1:13: Unknown column [event.name]', + ]); + }); + + it('returns multiple errors for multiple root causes', () => { + expect(getValidationErrors(getEqlResponseWithValidationErrors())).toEqual([ + 'Found 2 problems\nline 1:1: Unknown column [event.category]\nline 1:13: Unknown column [event.name]', + "line 1:4: mismatched input '<EOF>' expecting 'where'", + ]); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.ts new file mode 100644 index 00000000000000..71601574802cef --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/helpers.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import get from 'lodash/get'; +import has from 'lodash/has'; + +const PARSING_ERROR_TYPE = 'parsing_exception'; +const VERIFICATION_ERROR_TYPE = 'verification_exception'; +const MAPPING_ERROR_TYPE = 'mapping_exception'; + +interface ErrorCause { + type: string; + reason: string; +} + +export interface ErrorResponse { + error: ErrorCause & { root_cause: ErrorCause[] }; +} + +const isValidationErrorType = (type: unknown): boolean => + type === PARSING_ERROR_TYPE || type === VERIFICATION_ERROR_TYPE || type === MAPPING_ERROR_TYPE; + +export const isErrorResponse = (response: unknown): response is ErrorResponse => + has(response, 'error.type'); + +export const isValidationErrorResponse = (response: unknown): response is ErrorResponse => + isErrorResponse(response) && isValidationErrorType(get(response, 'error.type')); + +export const getValidationErrors = (response: ErrorResponse): string[] => + response.error.root_cause + .filter((cause) => isValidationErrorType(cause.type)) + .map((cause) => cause.reason); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validate_eql.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validate_eql.ts new file mode 100644 index 00000000000000..ab3bbc7b06cc9e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validate_eql.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ElasticsearchClient } from '../../../../../../../../src/core/server'; +import { getValidationErrors, isErrorResponse, isValidationErrorResponse } from './helpers'; + +export interface Validation { + isValid: boolean; + errors: string[]; +} + +export interface ValidateEqlParams { + client: ElasticsearchClient; + index: string[]; + query: string; +} + +export const validateEql = async ({ + client, + index, + query, +}: ValidateEqlParams): Promise<Validation> => { + const response = await client.eql.search( + { + // @ts-expect-error type is missing allow_no_indices + allow_no_indices: true, + index: index.join(','), + body: { + query, + size: 0, + }, + }, + { ignore: [400] } + ); + + if (isValidationErrorResponse(response.body)) { + return { isValid: false, errors: getValidationErrors(response.body) }; + } else if (isErrorResponse(response.body)) { + throw new Error(JSON.stringify(response.body)); + } else { + return { isValid: true, errors: [] }; + } +}; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.test.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.test.ts new file mode 100644 index 00000000000000..9fe2c13cff71a3 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ApiResponse, TransportRequestPromise } from '@elastic/elasticsearch/lib/Transport'; +import { serverMock, requestContextMock } from '../__mocks__'; +import { eqlValidationRequest, getEmptyEqlSearchResponse } from '../__mocks__/request_responses'; +import { eqlValidationRoute } from './validation_route'; + +describe('validate_eql route', () => { + let server: ReturnType<typeof serverMock.create>; + let { clients, context } = requestContextMock.createTools(); + + beforeEach(() => { + server = serverMock.create(); + ({ clients, context } = requestContextMock.createTools()); + + clients.newClusterClient.asCurrentUser.eql.search.mockResolvedValue( + (getEmptyEqlSearchResponse() as unknown) as TransportRequestPromise< + ApiResponse<unknown, unknown> + > + ); + eqlValidationRoute(server.router); + }); + + describe('normal status codes', () => { + test('returns 200 when doing a normal request', async () => { + const response = await server.inject(eqlValidationRequest(), context); + expect(response.status).toEqual(200); + }); + + test('returns the payload when doing a normal request', async () => { + const response = await server.inject(eqlValidationRequest(), context); + const expectedBody = { + valid: true, + errors: [], + }; + expect(response.status).toEqual(200); + expect(response.body).toEqual(expectedBody); + }); + + test('returns 500 when bad response from cluster', async () => { + clients.newClusterClient.asCurrentUser.eql.search.mockImplementation(() => { + throw new Error('Test error'); + }); + const response = await server.inject(eqlValidationRequest(), context); + expect(response.status).toEqual(500); + expect(response.body).toEqual({ message: 'Test error', status_code: 500 }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.ts new file mode 100644 index 00000000000000..478cefbc5ba8e5 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/routes/eql/validation_route.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter } from '../../../../../../../../src/core/server'; +import { eqlValidationSchema } from '../../../../../common/detection_engine/schemas/request/eql_validation_schema'; +import { DETECTION_ENGINE_EQL_VALIDATION_URL } from '../../../../../common/constants'; +import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; +import { transformError, buildSiemResponse } from '../utils'; +import { validateEql } from './validate_eql'; + +export const eqlValidationRoute = (router: IRouter) => { + router.post( + { + path: DETECTION_ENGINE_EQL_VALIDATION_URL, + validate: { + body: buildRouteValidation(eqlValidationSchema), + }, + options: { + tags: ['access:securitySolution'], + }, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + const { query, index } = request.body; + const esClient = context.core.elasticsearch.client.asCurrentUser; + + try { + const validation = await validateEql({ + client: esClient, + query, + index, + }); + + return response.ok({ + body: { valid: validation.isValid, errors: validation.errors }, + }); + } catch (err) { + const error = transformError(err); + return siemResponse.error({ + body: error.message, + statusCode: error.statusCode, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 000bd875930f9d..fff3cd4b2e391e 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -34,6 +34,7 @@ import { createTimelinesRoute } from '../lib/timeline/routes/create_timelines_ro import { updateTimelinesRoute } from '../lib/timeline/routes/update_timelines_route'; import { getDraftTimelinesRoute } from '../lib/timeline/routes/get_draft_timelines_route'; import { cleanDraftTimelinesRoute } from '../lib/timeline/routes/clean_draft_timelines_route'; +import { eqlValidationRoute } from '../lib/detection_engine/routes/eql/validation_route'; import { SetupPlugins } from '../plugin'; import { ConfigType } from '../config'; import { installPrepackedTimelinesRoute } from '../lib/timeline/routes/install_prepacked_timelines_route'; @@ -94,4 +95,7 @@ export const initRoutes = ( // Privileges API to get the generic user privileges readPrivilegesRoute(router, security, usingEphemeralEncryptionKey); + + // Route used by the UI for form validation + eqlValidationRoute(router); }; From 9973667f4cff97c5253c84fdc6b8800af683cafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20Haro?= <alejandro.haro@elastic.co> Date: Fri, 2 Oct 2020 19:24:08 +0100 Subject: [PATCH 098/128] [Usage Collection] [schema] `apm` (#79000) --- .../src/tools/serializer.test.ts | 18 + .../src/tools/serializer.ts | 33 +- .../kbn-telemetry-tools/src/tools/utils.ts | 4 +- .../telemetry_collectors/constants.ts | 4 + x-pack/.telemetryrc.json | 4 +- .../__snapshots__/apm_telemetry.test.ts.snap | 1192 ++++----- x-pack/plugins/apm/common/apm_telemetry.ts | 279 +-- .../apm/server/lib/apm_telemetry/index.ts | 13 +- .../apm/server/lib/apm_telemetry/schema.ts | 206 ++ .../apm/server/lib/apm_telemetry/types.ts | 6 +- .../schema/xpack_plugins.json | 2180 +++++++++++++++++ 11 files changed, 2982 insertions(+), 957 deletions(-) create mode 100644 x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts index 652b26c8edb23f..85fb84c714e200 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.test.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.test.ts @@ -138,4 +138,22 @@ describe('getDescriptor', () => { }, }); }); + + it('serializes RecordWithKnownProps', () => { + const usageInterface = usageInterfaces.get('RecordWithKnownProps')!; + const descriptor = getDescriptor(usageInterface, tsProgram); + expect(descriptor).toEqual({ + prop1: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + prop2: { kind: ts.SyntaxKind.NumberKeyword, type: 'NumberKeyword' }, + }); + }); + + it('serializes IndexedAccessType', () => { + const usageInterface = usageInterfaces.get('IndexedAccessType')!; + const descriptor = getDescriptor(usageInterface, tsProgram); + expect(descriptor).toEqual({ + prop1: { kind: ts.SyntaxKind.StringKeyword, type: 'StringKeyword' }, + prop2: { kind: ts.SyntaxKind.StringKeyword, type: 'StringKeyword' }, + }); + }); }); diff --git a/packages/kbn-telemetry-tools/src/tools/serializer.ts b/packages/kbn-telemetry-tools/src/tools/serializer.ts index cd845a680ad06c..ea5f184008026b 100644 --- a/packages/kbn-telemetry-tools/src/tools/serializer.ts +++ b/packages/kbn-telemetry-tools/src/tools/serializer.ts @@ -18,7 +18,7 @@ */ import * as ts from 'typescript'; -import { uniqBy } from 'lodash'; +import { uniqBy, pick } from 'lodash'; import { getResolvedModuleSourceFile, getIdentifierDeclarationFromSource, @@ -95,7 +95,16 @@ export function getConstraints(node: ts.Node, program: ts.Program): any { return node.literal.text; } - throw Error(`Unsupported constraint`); + if (ts.isImportSpecifier(node)) { + const source = node.getSourceFile(); + const importedModuleName = getModuleSpecifier(node); + + const declarationSource = getResolvedModuleSourceFile(source, program, importedModuleName); + const declarationNode = getIdentifierDeclarationFromSource(node.name, declarationSource); + return getConstraints(declarationNode, program); + } + + throw Error(`Unsupported constraint of kind ${node.kind} [${ts.SyntaxKind[node.kind]}]`); } export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | DescriptorValue { @@ -157,9 +166,25 @@ export function getDescriptor(node: ts.Node, program: ts.Program): Descriptor | return { kind: TelemetryKinds.Date, type: 'Date' }; } // Support `Record<string, SOMETHING>` - if (symbolName === 'Record' && node.typeArguments![0].kind === ts.SyntaxKind.StringKeyword) { - return { '@@INDEX@@': getDescriptor(node.typeArguments![1], program) }; + if (symbolName === 'Record') { + const descriptor = getDescriptor(node.typeArguments![1], program); + if (node.typeArguments![0].kind === ts.SyntaxKind.StringKeyword) { + return { '@@INDEX@@': descriptor }; + } + const constraints = getConstraints(node.typeArguments![0], program); + const constraintsArray = Array.isArray(constraints) ? constraints : [constraints]; + if (typeof constraintsArray[0] === 'string') { + return constraintsArray.reduce((acc, c) => ({ ...acc, [c]: descriptor }), {}); + } + } + + // Support `Pick<SOMETHING, 'prop1' | 'prop2'>` + if (symbolName === 'Pick') { + const parentDescriptor = getDescriptor(node.typeArguments![0], program); + const pickPropNames = getConstraints(node.typeArguments![1], program); + return pick(parentDescriptor, pickPropNames); } + const declaration = (symbol?.getDeclarations() || [])[0]; if (declaration) { return getDescriptor(declaration, program); diff --git a/packages/kbn-telemetry-tools/src/tools/utils.ts b/packages/kbn-telemetry-tools/src/tools/utils.ts index 947a4f66908f64..90ba7f4d9168f2 100644 --- a/packages/kbn-telemetry-tools/src/tools/utils.ts +++ b/packages/kbn-telemetry-tools/src/tools/utils.ts @@ -78,14 +78,14 @@ export function getIdentifierDeclarationFromSource(node: ts.Node, source: ts.Sou const identifierName = node.getText(); const identifierDefinition: ts.Node = (source as any).locals.get(identifierName); if (!identifierDefinition) { - throw new Error(`Unable to fine identifier in source ${identifierName}`); + throw new Error(`Unable to find identifier in source ${identifierName}`); } const declarations = (identifierDefinition as any).declarations as ts.Node[]; const latestDeclaration: ts.Node | false | undefined = Array.isArray(declarations) && declarations[declarations.length - 1]; if (!latestDeclaration) { - throw new Error(`Unable to fine declaration for identifier ${identifierName}`); + throw new Error(`Unable to find declaration for identifier ${identifierName}`); } return latestDeclaration; diff --git a/src/fixtures/telemetry_collectors/constants.ts b/src/fixtures/telemetry_collectors/constants.ts index 60df05bac2aeb1..8896c294676c47 100644 --- a/src/fixtures/telemetry_collectors/constants.ts +++ b/src/fixtures/telemetry_collectors/constants.ts @@ -66,3 +66,7 @@ export interface MappedTypes { [key in 'prop3']: number; }; } + +export type RecordWithKnownProps = Record<MappedTypeProps, number>; + +export type IndexedAccessType = Pick<WithUnion, 'prop1' | 'prop2'>; diff --git a/x-pack/.telemetryrc.json b/x-pack/.telemetryrc.json index 706decfc93e9cb..ae85efcda32d53 100644 --- a/x-pack/.telemetryrc.json +++ b/x-pack/.telemetryrc.json @@ -1,7 +1,5 @@ { "output": "plugins/telemetry_collection_xpack/schema/xpack_plugins.json", "root": "plugins/", - "exclude": [ - "plugins/apm/server/lib/apm_telemetry/index.ts" - ] + "exclude": [] } diff --git a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap index 663411dff76ff3..9f7a911bf21c76 100644 --- a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap @@ -11,6 +11,67 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "apm": { "properties": { + "services_per_agent": { + "properties": { + "dotnet": { + "type": "long" + }, + "go": { + "type": "long" + }, + "java": { + "type": "long" + }, + "js-base": { + "type": "long" + }, + "nodejs": { + "type": "long" + }, + "python": { + "type": "long" + }, + "ruby": { + "type": "long" + }, + "rum-js": { + "type": "long" + }, + "otlp": { + "type": "long" + }, + "opentelemetry/cpp": { + "type": "long" + }, + "opentelemetry/dotnet": { + "type": "long" + }, + "opentelemetry/erlang": { + "type": "long" + }, + "opentelemetry/go": { + "type": "long" + }, + "opentelemetry/java": { + "type": "long" + }, + "opentelemetry/nodejs": { + "type": "long" + }, + "opentelemetry/php": { + "type": "long" + }, + "opentelemetry/python": { + "type": "long" + }, + "opentelemetry/ruby": { + "type": "long" + }, + "opentelemetry/webjs": { + "type": "long" + } + } + }, "agents": { "properties": { "dotnet": { @@ -18,8 +79,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -27,49 +87,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -82,8 +133,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -91,49 +141,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -146,8 +187,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -155,49 +195,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -210,8 +241,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -219,49 +249,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -274,8 +295,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -283,49 +303,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -338,8 +349,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -347,49 +357,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -402,8 +403,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -411,49 +411,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -466,8 +457,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -475,49 +465,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -530,8 +511,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -539,49 +519,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -594,8 +565,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -603,49 +573,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -658,8 +619,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -667,49 +627,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -722,8 +673,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -731,49 +681,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -786,8 +727,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -795,49 +735,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -850,8 +781,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -859,49 +789,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -914,8 +835,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -923,49 +843,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -978,8 +889,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -987,49 +897,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -1042,8 +943,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -1051,49 +951,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 - } + "type": "keyword" + }, + "composite": { + "type": "keyword" + } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -1106,8 +997,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -1115,49 +1005,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -1170,8 +1051,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "agent": { "properties": { "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, @@ -1179,49 +1059,40 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "framework": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "language": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } }, "runtime": { "properties": { - "composite": { - "type": "keyword", - "ignore_above": 1024 - }, "name": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "version": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" + }, + "composite": { + "type": "keyword" } } } @@ -1231,6 +1102,39 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, + "has_any_services": { + "type": "boolean" + }, + "version": { + "properties": { + "apm_server": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + }, + "patch": { + "type": "long" + } + } + } + } + }, + "environments": { + "properties": { + "services_without_environments": { + "type": "long" + }, + "services_with_multiple_environments": { + "type": "long" + }, + "top_environments": { + "type": "keyword" + } + } + }, "aggregated_transactions": { "properties": { "current_implementation": { @@ -1240,9 +1144,6 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the }, "transaction_count": { "type": "long" - }, - "ratio": { - "type": "float" } } }, @@ -1253,67 +1154,77 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the }, "transaction_count": { "type": "long" - }, - "ratio": { - "type": "float" } } }, - "with_country": { + "no_rum": { "properties": { "expected_metric_document_count": { "type": "long" }, "transaction_count": { "type": "long" + } + } + }, + "no_rum_no_observer_name": { + "properties": { + "expected_metric_document_count": { + "type": "long" }, - "ratio": { - "type": "float" + "transaction_count": { + "type": "long" } } - } - } - }, - "environments": { - "properties": { - "services_without_environment": { - "type": "long" }, - "services_with_multiple_environments": { - "type": "long" + "only_rum": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } }, - "top_enviroments": { - "type": "keyword", - "ignore_above": 1024 + "only_rum_no_observer_name": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } } } }, "cloud": { "properties": { "availability_zone": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "provider": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" }, "region": { - "type": "keyword", - "ignore_above": 1024 + "type": "keyword" } } }, "counts": { "properties": { - "agent_configuration": { + "transaction": { "properties": { + "1d": { + "type": "long" + }, "all": { "type": "long" } } }, - "error": { + "span": { "properties": { "1d": { "type": "long" @@ -1323,21 +1234,27 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "max_error_groups_per_service": { + "error": { "properties": { "1d": { "type": "long" + }, + "all": { + "type": "long" } } }, - "max_transaction_groups_per_service": { + "metric": { "properties": { "1d": { "type": "long" + }, + "all": { + "type": "long" } } }, - "metric": { + "sourcemap": { "properties": { "1d": { "type": "long" @@ -1357,14 +1274,14 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "services": { + "agent_configuration": { "properties": { - "1d": { + "all": { "type": "long" } } }, - "sourcemap": { + "max_transaction_groups_per_service": { "properties": { "1d": { "type": "long" @@ -1374,7 +1291,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "span": { + "max_error_groups_per_service": { "properties": { "1d": { "type": "long" @@ -1388,10 +1305,13 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "properties": { "1d": { "type": "long" + }, + "all": { + "type": "long" } } }, - "transaction": { + "services": { "properties": { "1d": { "type": "long" @@ -1470,55 +1390,22 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "has_any_services": { - "type": "boolean" - }, - "indices": { + "retainment": { "properties": { - "all": { - "properties": { - "total": { - "properties": { - "docs": { - "properties": { - "count": { - "type": "long" - } - } - }, - "store": { - "properties": { - "size_in_bytes": { - "type": "long" - } - } - } - } - } - } - }, - "shards": { + "span": { "properties": { - "total": { + "ms": { "type": "long" } } - } - } - }, - "integrations": { - "properties": { - "ml": { + }, + "transaction": { "properties": { - "all_jobs_count": { + "ms": { "type": "long" } } - } - } - }, - "retainment": { - "properties": { + }, "error": { "properties": { "ms": { @@ -1533,106 +1420,63 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "onboarding": { + "sourcemap": { "properties": { "ms": { "type": "long" } } }, - "span": { + "onboarding": { "properties": { "ms": { "type": "long" } } - }, - "transaction": { + } + } + }, + "integrations": { + "properties": { + "ml": { "properties": { - "ms": { + "all_jobs_count": { "type": "long" } } } } }, - "services_per_agent": { + "indices": { "properties": { - "dotnet": { - "type": "long", - "null_value": 0 - }, - "go": { - "type": "long", - "null_value": 0 - }, - "java": { - "type": "long", - "null_value": 0 - }, - "js-base": { - "type": "long", - "null_value": 0 - }, - "nodejs": { - "type": "long", - "null_value": 0 - }, - "python": { - "type": "long", - "null_value": 0 - }, - "ruby": { - "type": "long", - "null_value": 0 - }, - "rum-js": { - "type": "long", - "null_value": 0 - }, - "otlp": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/cpp": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/dotnet": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/erlang": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/go": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/java": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/nodejs": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/php": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/python": { - "type": "long", - "null_value": 0 - }, - "opentelemetry/ruby": { - "type": "long", - "null_value": 0 + "shards": { + "properties": { + "total": { + "type": "long" + } + } }, - "opentelemetry/webjs": { - "type": "long", - "null_value": 0 + "all": { + "properties": { + "total": { + "properties": { + "docs": { + "properties": { + "count": { + "type": "long" + } + } + }, + "store": { + "properties": { + "size_in_bytes": { + "type": "long" + } + } + } + } + } + } } } }, @@ -1649,7 +1493,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "agent_configuration": { + "cloud": { "properties": { "took": { "properties": { @@ -1660,7 +1504,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "agents": { + "processor_events": { "properties": { "took": { "properties": { @@ -1671,7 +1515,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "cardinality": { + "agent_configuration": { "properties": { "took": { "properties": { @@ -1682,7 +1526,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "cloud": { + "services": { "properties": { "took": { "properties": { @@ -1693,7 +1537,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "environments": { + "versions": { "properties": { "took": { "properties": { @@ -1715,17 +1559,6 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "indices_stats": { - "properties": { - "took": { - "properties": { - "ms": { - "type": "long" - } - } - } - } - }, "integrations": { "properties": { "took": { @@ -1737,7 +1570,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "processor_events": { + "agents": { "properties": { "took": { "properties": { @@ -1748,7 +1581,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "services": { + "indices_stats": { "properties": { "took": { "properties": { @@ -1759,7 +1592,7 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } }, - "versions": { + "cardinality": { "properties": { "took": { "properties": { @@ -1771,23 +1604,6 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the } } } - }, - "version": { - "properties": { - "apm_server": { - "properties": { - "major": { - "type": "long" - }, - "minor": { - "type": "long" - }, - "patch": { - "type": "long" - } - } - } - } } } } diff --git a/x-pack/plugins/apm/common/apm_telemetry.ts b/x-pack/plugins/apm/common/apm_telemetry.ts index 874cee05553d4c..faf5b21d69c7bf 100644 --- a/x-pack/plugins/apm/common/apm_telemetry.ts +++ b/x-pack/plugins/apm/common/apm_telemetry.ts @@ -4,260 +4,39 @@ * you may not use this file except in compliance with the Elastic License. */ import { produce } from 'immer'; -import { AGENT_NAMES } from './agent_name'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { apmSchema } from '../server/lib/apm_telemetry/schema'; + +function schemaToMapping(schemaLeaf: any): any { + // convert "array" definition to mapping + if (schemaLeaf.type === 'array') { + return schemaToMapping(schemaLeaf.items); + } + + if (typeof schemaLeaf.type === 'string') { + return schemaLeaf; + } + + return Object.entries<any>(schemaLeaf).reduce((acc, [key, value]) => { + const propMapping = schemaToMapping(value); + + return { + ...acc, + [key]: + typeof propMapping.type === 'string' + ? propMapping + : { properties: propMapping }, + }; + }, {}); +} /** - * Generate an object containing the mapping used for APM telemetry. Can be used - * with the `upload-telemetry-data` script or to update the mapping in the - * telemetry repository. - * - * This function breaks things up to make the mapping easier to understand. + * Generate an object containing the mapping used for APM telemetry based on the schema specified + * in the usage collector. Can be used with the `upload-telemetry-data` script or to update the + * mapping in the telemetry repository. */ export function getApmTelemetryMapping() { - const keyword = { - type: 'keyword', - ignore_above: 1024, - }; - - const float = { - type: 'float', - }; - - const long = { - type: 'long', - }; - - const allProperties = { - properties: { - all: long, - }, - }; - - const oneDayProperties = { - properties: { - '1d': long, - }, - }; - - const oneDayAllProperties = { - properties: { - '1d': long, - all: long, - }, - }; - - const msProperties = { - properties: { - ms: long, - }, - }; - - const tookProperties = { - properties: { - took: msProperties, - }, - }; - - const compositeNameVersionProperties = { - properties: { - composite: keyword, - name: keyword, - version: keyword, - }, - }; - - const agentProperties = { - properties: { version: keyword }, - }; - - const serviceProperties = { - properties: { - framework: compositeNameVersionProperties, - language: compositeNameVersionProperties, - runtime: compositeNameVersionProperties, - }, - }; - - const aggregatedTransactionsProperties = { - properties: { - expected_metric_document_count: long, - transaction_count: long, - ratio: float, - }, - }; - - return { - properties: { - agents: { - properties: AGENT_NAMES.reduce<Record<string, any>>( - (previousValue, currentValue) => { - previousValue[currentValue] = { - properties: { - agent: agentProperties, - service: serviceProperties, - }, - }; - - return previousValue; - }, - {} - ), - }, - aggregated_transactions: { - properties: { - current_implementation: aggregatedTransactionsProperties, - no_observer_name: aggregatedTransactionsProperties, - with_country: aggregatedTransactionsProperties, - }, - }, - environments: { - properties: { - services_without_environment: long, - services_with_multiple_environments: long, - top_enviroments: keyword, - }, - }, - cloud: { - properties: { - availability_zone: keyword, - provider: keyword, - region: keyword, - }, - }, - counts: { - properties: { - agent_configuration: allProperties, - error: oneDayAllProperties, - max_error_groups_per_service: oneDayProperties, - max_transaction_groups_per_service: oneDayProperties, - metric: oneDayAllProperties, - onboarding: oneDayAllProperties, - services: oneDayProperties, - sourcemap: oneDayAllProperties, - span: oneDayAllProperties, - traces: oneDayProperties, - transaction: oneDayAllProperties, - }, - }, - cardinality: { - properties: { - client: { - properties: { - geo: { - properties: { - country_iso_code: { properties: { rum: oneDayProperties } }, - }, - }, - }, - }, - user_agent: { - properties: { - original: { - properties: { - all_agents: oneDayProperties, - rum: oneDayProperties, - }, - }, - }, - }, - transaction: { - properties: { - name: { - properties: { - all_agents: oneDayProperties, - rum: oneDayProperties, - }, - }, - }, - }, - }, - }, - has_any_services: { - type: 'boolean', - }, - indices: { - properties: { - all: { - properties: { - total: { - properties: { - docs: { - properties: { - count: long, - }, - }, - store: { - properties: { - size_in_bytes: long, - }, - }, - }, - }, - }, - }, - shards: { - properties: { - total: long, - }, - }, - }, - }, - integrations: { - properties: { - ml: { - properties: { - all_jobs_count: long, - }, - }, - }, - }, - retainment: { - properties: { - error: msProperties, - metric: msProperties, - onboarding: msProperties, - span: msProperties, - transaction: msProperties, - }, - }, - services_per_agent: { - properties: AGENT_NAMES.reduce<Record<string, any>>( - (previousValue, currentValue) => { - previousValue[currentValue] = { ...long, null_value: 0 }; - return previousValue; - }, - {} - ), - }, - tasks: { - properties: { - aggregated_transactions: tookProperties, - agent_configuration: tookProperties, - agents: tookProperties, - cardinality: tookProperties, - cloud: tookProperties, - environments: tookProperties, - groupings: tookProperties, - indices_stats: tookProperties, - integrations: tookProperties, - processor_events: tookProperties, - services: tookProperties, - versions: tookProperties, - }, - }, - version: { - properties: { - apm_server: { - properties: { - major: long, - minor: long, - patch: long, - }, - }, - }, - }, - }, - }; + return { properties: schemaToMapping(apmSchema) }; } /** diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts index f78280aa7428e0..c93fdfc15fe3ce 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/index.ts @@ -6,7 +6,6 @@ import { Observable } from 'rxjs'; import { take } from 'rxjs/operators'; import { UsageCollectionSetup } from 'src/plugins/usage_collection/server'; -import { DeepRequired } from 'utility-types'; import { CoreSetup, Logger, @@ -21,14 +20,14 @@ import { APM_TELEMETRY_SAVED_OBJECT_ID, APM_TELEMETRY_SAVED_OBJECT_TYPE, } from '../../../common/apm_saved_object_constants'; -import { getApmTelemetryMapping } from '../../../common/apm_telemetry'; import { getInternalSavedObjectsClient } from '../helpers/get_internal_saved_objects_client'; import { getApmIndices } from '../settings/apm_indices/get_apm_indices'; import { collectDataTelemetry, CollectTelemetryParams, } from './collect_data_telemetry'; -import { APMDataTelemetry } from './types'; +import { APMUsage } from './types'; +import { apmSchema } from './schema'; const APM_TELEMETRY_TASK_NAME = 'apm-telemetry-task'; @@ -107,9 +106,9 @@ export async function createApmTelemetry({ ); }; - const collector = usageCollector.makeUsageCollector({ + const collector = usageCollector.makeUsageCollector<APMUsage | {}>({ type: 'apm', - schema: getApmTelemetryMapping(), + schema: apmSchema, fetch: async () => { try { const { kibanaVersion: storedKibanaVersion, ...data } = ( @@ -117,9 +116,7 @@ export async function createApmTelemetry({ APM_TELEMETRY_SAVED_OBJECT_TYPE, APM_TELEMETRY_SAVED_OBJECT_ID ) - ).attributes as { kibanaVersion: string } & DeepRequired< - APMDataTelemetry - >; + ).attributes as { kibanaVersion: string } & APMUsage; return data; } catch (err) { diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts new file mode 100644 index 00000000000000..4bbda9add0fdbb --- /dev/null +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts @@ -0,0 +1,206 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { MakeSchemaFrom } from 'src/plugins/usage_collection/server'; +import { + AggregatedTransactionsCounts, + APMUsage, + TimeframeMap, + TimeframeMap1d, + TimeframeMapAll, +} from './types'; +import { AgentName } from '../../../typings/es_schemas/ui/fields/agent'; + +const long: { type: 'long' } = { type: 'long' }; + +const aggregatedTransactionCountSchema: MakeSchemaFrom<AggregatedTransactionsCounts> = { + expected_metric_document_count: long, + transaction_count: long, +}; + +const timeframeMap1dSchema: MakeSchemaFrom<TimeframeMap1d> = { + '1d': long, +}; + +const timeframeMapAllSchema: MakeSchemaFrom<TimeframeMapAll> = { + all: long, +}; + +const timeframeMapSchema: MakeSchemaFrom<TimeframeMap> = { + ...timeframeMap1dSchema, + ...timeframeMapAllSchema, +}; + +const agentSchema: MakeSchemaFrom<APMUsage>['agents'][AgentName] = { + agent: { + version: { type: 'array', items: { type: 'keyword' } }, + }, + service: { + framework: { + name: { type: 'array', items: { type: 'keyword' } }, + version: { type: 'array', items: { type: 'keyword' } }, + composite: { type: 'array', items: { type: 'keyword' } }, + }, + language: { + name: { type: 'array', items: { type: 'keyword' } }, + version: { type: 'array', items: { type: 'keyword' } }, + composite: { type: 'array', items: { type: 'keyword' } }, + }, + runtime: { + name: { type: 'array', items: { type: 'keyword' } }, + version: { type: 'array', items: { type: 'keyword' } }, + composite: { type: 'array', items: { type: 'keyword' } }, + }, + }, +}; + +const apmPerAgentSchema: Pick< + MakeSchemaFrom<APMUsage>, + 'services_per_agent' | 'agents' +> = { + // services_per_agent: AGENT_NAMES.reduce( + // (acc, name) => ({ ...acc, [name]: long }), + // {} as Record<AgentName, typeof long> + // ), + // agents: AGENT_NAMES.reduce( + // (acc, name) => ({ ...acc, [name]: agentSchema }), + // {} as Record<AgentName, typeof agentSchema> + // ), + // TODO: Find a way for `@kbn/telemetry-tools` to understand and evaluate expressions. + // In the meanwhile, we'll have to maintain these lists up to date (TS will remind us to update) + services_per_agent: { + dotnet: long, + go: long, + java: long, + 'js-base': long, + nodejs: long, + python: long, + ruby: long, + 'rum-js': long, + otlp: long, + 'opentelemetry/cpp': long, + 'opentelemetry/dotnet': long, + 'opentelemetry/erlang': long, + 'opentelemetry/go': long, + 'opentelemetry/java': long, + 'opentelemetry/nodejs': long, + 'opentelemetry/php': long, + 'opentelemetry/python': long, + 'opentelemetry/ruby': long, + 'opentelemetry/webjs': long, + }, + agents: { + dotnet: agentSchema, + go: agentSchema, + java: agentSchema, + 'js-base': agentSchema, + nodejs: agentSchema, + python: agentSchema, + ruby: agentSchema, + 'rum-js': agentSchema, + otlp: agentSchema, + 'opentelemetry/cpp': agentSchema, + 'opentelemetry/dotnet': agentSchema, + 'opentelemetry/erlang': agentSchema, + 'opentelemetry/go': agentSchema, + 'opentelemetry/java': agentSchema, + 'opentelemetry/nodejs': agentSchema, + 'opentelemetry/php': agentSchema, + 'opentelemetry/python': agentSchema, + 'opentelemetry/ruby': agentSchema, + 'opentelemetry/webjs': agentSchema, + }, +}; + +export const apmSchema: MakeSchemaFrom<APMUsage> = { + ...apmPerAgentSchema, + has_any_services: { type: 'boolean' }, + version: { + apm_server: { + major: long, + minor: long, + patch: long, + }, + }, + environments: { + services_without_environments: long, + services_with_multiple_environments: long, + top_environments: { type: 'array', items: { type: 'keyword' } }, + }, + aggregated_transactions: { + current_implementation: aggregatedTransactionCountSchema, + no_observer_name: aggregatedTransactionCountSchema, + no_rum: aggregatedTransactionCountSchema, + no_rum_no_observer_name: aggregatedTransactionCountSchema, + only_rum: aggregatedTransactionCountSchema, + only_rum_no_observer_name: aggregatedTransactionCountSchema, + }, + cloud: { + availability_zone: { type: 'array', items: { type: 'keyword' } }, + provider: { type: 'array', items: { type: 'keyword' } }, + region: { type: 'array', items: { type: 'keyword' } }, + }, + counts: { + transaction: timeframeMapSchema, + span: timeframeMapSchema, + error: timeframeMapSchema, + metric: timeframeMapSchema, + sourcemap: timeframeMapSchema, + onboarding: timeframeMapSchema, + agent_configuration: timeframeMapAllSchema, + max_transaction_groups_per_service: timeframeMapSchema, + max_error_groups_per_service: timeframeMapSchema, + traces: timeframeMapSchema, + services: timeframeMapSchema, + }, + cardinality: { + client: { geo: { country_iso_code: { rum: timeframeMap1dSchema } } }, + user_agent: { + original: { + all_agents: timeframeMap1dSchema, + rum: timeframeMap1dSchema, + }, + }, + transaction: { + name: { + all_agents: timeframeMap1dSchema, + rum: timeframeMap1dSchema, + }, + }, + }, + retainment: { + span: { ms: long }, + transaction: { ms: long }, + error: { ms: long }, + metric: { ms: long }, + sourcemap: { ms: long }, + onboarding: { ms: long }, + }, + integrations: { ml: { all_jobs_count: long } }, + + indices: { + shards: { total: long }, + all: { + total: { + docs: { count: long }, + store: { size_in_bytes: long }, + }, + }, + }, + tasks: { + aggregated_transactions: { took: { ms: long } }, + cloud: { took: { ms: long } }, + processor_events: { took: { ms: long } }, + agent_configuration: { took: { ms: long } }, + services: { took: { ms: long } }, + versions: { took: { ms: long } }, + groupings: { took: { ms: long } }, + integrations: { took: { ms: long } }, + agents: { took: { ms: long } }, + indices_stats: { took: { ms: long } }, + cardinality: { took: { ms: long } }, + }, +}; diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts index c7af292e817c7a..7ed79752b43c48 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/types.ts @@ -20,7 +20,7 @@ export interface AggregatedTransactionsCounts { transaction_count: number; } -export type APMDataTelemetry = DeepPartial<{ +export interface APMUsage { has_any_services: boolean; services_per_agent: Record<AgentName, number>; version: { @@ -139,6 +139,8 @@ export type APMDataTelemetry = DeepPartial<{ | 'cardinality', { took: { ms: number } } >; -}>; +} + +export type APMDataTelemetry = DeepPartial<APMUsage>; export type APMTelemetry = APMDataTelemetry; 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 98230f143d3d6b..478c7e42a0c16c 100644 --- a/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json +++ b/x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @@ -270,6 +270,2186 @@ } } }, + "apm": { + "properties": { + "services_per_agent": { + "properties": { + "dotnet": { + "type": "long" + }, + "go": { + "type": "long" + }, + "java": { + "type": "long" + }, + "js-base": { + "type": "long" + }, + "nodejs": { + "type": "long" + }, + "python": { + "type": "long" + }, + "ruby": { + "type": "long" + }, + "rum-js": { + "type": "long" + }, + "otlp": { + "type": "long" + }, + "opentelemetry/cpp": { + "type": "long" + }, + "opentelemetry/dotnet": { + "type": "long" + }, + "opentelemetry/erlang": { + "type": "long" + }, + "opentelemetry/go": { + "type": "long" + }, + "opentelemetry/java": { + "type": "long" + }, + "opentelemetry/nodejs": { + "type": "long" + }, + "opentelemetry/php": { + "type": "long" + }, + "opentelemetry/python": { + "type": "long" + }, + "opentelemetry/ruby": { + "type": "long" + }, + "opentelemetry/webjs": { + "type": "long" + } + } + }, + "agents": { + "properties": { + "dotnet": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "go": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "java": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "js-base": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "nodejs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "python": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "ruby": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "rum-js": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "otlp": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/cpp": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/dotnet": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/erlang": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/go": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/java": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/nodejs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/php": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/python": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/ruby": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + }, + "opentelemetry/webjs": { + "properties": { + "agent": { + "properties": { + "version": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "service": { + "properties": { + "framework": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "language": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "runtime": { + "properties": { + "name": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "version": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "composite": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + } + } + } + } + } + } + }, + "has_any_services": { + "type": "boolean" + }, + "version": { + "properties": { + "apm_server": { + "properties": { + "major": { + "type": "long" + }, + "minor": { + "type": "long" + }, + "patch": { + "type": "long" + } + } + } + } + }, + "environments": { + "properties": { + "services_without_environments": { + "type": "long" + }, + "services_with_multiple_environments": { + "type": "long" + }, + "top_environments": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "aggregated_transactions": { + "properties": { + "current_implementation": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } + }, + "no_observer_name": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } + }, + "no_rum": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } + }, + "no_rum_no_observer_name": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } + }, + "only_rum": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } + }, + "only_rum_no_observer_name": { + "properties": { + "expected_metric_document_count": { + "type": "long" + }, + "transaction_count": { + "type": "long" + } + } + } + } + }, + "cloud": { + "properties": { + "availability_zone": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "provider": { + "type": "array", + "items": { + "type": "keyword" + } + }, + "region": { + "type": "array", + "items": { + "type": "keyword" + } + } + } + }, + "counts": { + "properties": { + "transaction": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "span": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "error": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "metric": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "sourcemap": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "onboarding": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "agent_configuration": { + "properties": { + "all": { + "type": "long" + } + } + }, + "max_transaction_groups_per_service": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "max_error_groups_per_service": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "traces": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + }, + "services": { + "properties": { + "1d": { + "type": "long" + }, + "all": { + "type": "long" + } + } + } + } + }, + "cardinality": { + "properties": { + "client": { + "properties": { + "geo": { + "properties": { + "country_iso_code": { + "properties": { + "rum": { + "properties": { + "1d": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "user_agent": { + "properties": { + "original": { + "properties": { + "all_agents": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "rum": { + "properties": { + "1d": { + "type": "long" + } + } + } + } + } + } + }, + "transaction": { + "properties": { + "name": { + "properties": { + "all_agents": { + "properties": { + "1d": { + "type": "long" + } + } + }, + "rum": { + "properties": { + "1d": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "retainment": { + "properties": { + "span": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "transaction": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "error": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "metric": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "sourcemap": { + "properties": { + "ms": { + "type": "long" + } + } + }, + "onboarding": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "integrations": { + "properties": { + "ml": { + "properties": { + "all_jobs_count": { + "type": "long" + } + } + } + } + }, + "indices": { + "properties": { + "shards": { + "properties": { + "total": { + "type": "long" + } + } + }, + "all": { + "properties": { + "total": { + "properties": { + "docs": { + "properties": { + "count": { + "type": "long" + } + } + }, + "store": { + "properties": { + "size_in_bytes": { + "type": "long" + } + } + } + } + } + } + } + } + }, + "tasks": { + "properties": { + "aggregated_transactions": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "cloud": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "processor_events": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "agent_configuration": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "services": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "versions": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "groupings": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "integrations": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "agents": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "indices_stats": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + }, + "cardinality": { + "properties": { + "took": { + "properties": { + "ms": { + "type": "long" + } + } + } + } + } + } + } + } + }, "canvas": { "properties": { "workpads": { From 54fa55d69197c6f9fefb3069869df588c91d48f1 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet <nicolas.chaulet@elastic.co> Date: Fri, 2 Oct 2020 14:25:41 -0400 Subject: [PATCH 099/128] [Ingest Manager] Set the default timeout to 5 minute with a 20 seconds margin and improve the rate limitter (#79307) --- .../ingest_manager/common/constants/agent.ts | 6 +++-- .../ingest_manager/server/constants/index.ts | 1 + .../agents/checkin/rxjs_utils.test.ts | 8 +++++-- .../services/agents/checkin/rxjs_utils.ts | 8 ++++--- .../agents/checkin/state_new_actions.ts | 22 ++++++++++++------- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/x-pack/plugins/ingest_manager/common/constants/agent.ts b/x-pack/plugins/ingest_manager/common/constants/agent.ts index 82d2ad712ef023..30b8a6b7406090 100644 --- a/x-pack/plugins/ingest_manager/common/constants/agent.ts +++ b/x-pack/plugins/ingest_manager/common/constants/agent.ts @@ -13,10 +13,12 @@ export const AGENT_TYPE_EPHEMERAL = 'EPHEMERAL'; export const AGENT_TYPE_TEMPORARY = 'TEMPORARY'; export const AGENT_POLLING_REQUEST_TIMEOUT_MS = 300000; // 5 minutes +export const AGENT_POLLING_REQUEST_TIMEOUT_MARGIN_MS = 20000; // 20s + export const AGENT_POLLING_THRESHOLD_MS = 30000; export const AGENT_POLLING_INTERVAL = 1000; export const AGENT_UPDATE_LAST_CHECKIN_INTERVAL_MS = 30000; export const AGENT_UPDATE_ACTIONS_INTERVAL_MS = 5000; -export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS = 5000; -export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL = 25; +export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS = 1000; +export const AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL = 5; diff --git a/x-pack/plugins/ingest_manager/server/constants/index.ts b/x-pack/plugins/ingest_manager/server/constants/index.ts index 3965e27da05427..c69ee7e4b60925 100644 --- a/x-pack/plugins/ingest_manager/server/constants/index.ts +++ b/x-pack/plugins/ingest_manager/server/constants/index.ts @@ -8,6 +8,7 @@ export { AGENT_TYPE_EPHEMERAL, AGENT_TYPE_TEMPORARY, AGENT_POLLING_THRESHOLD_MS, + AGENT_POLLING_REQUEST_TIMEOUT_MARGIN_MS, AGENT_POLLING_INTERVAL, AGENT_UPDATE_LAST_CHECKIN_INTERVAL_MS, AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL, diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.test.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.test.ts index 5e84e3a50bb440..2909899418ec24 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.test.ts @@ -15,9 +15,13 @@ describe('createRateLimiter', () => { scheduler.run(({ expectObservable, cold }) => { const source = cold('a-b-c-d-e-f|'); - const rateLimiter = createRateLimiter(10, 1, 2, scheduler); + const intervalMs = 10; + const perInterval = 1; + const maxDelayMs = 50; + const rateLimiter = createRateLimiter(intervalMs, perInterval, maxDelayMs, scheduler); const obs = source.pipe(rateLimiter()); - const results = 'a 9ms b 9ms c 9ms d 9ms e 9ms (f|)'; + // f should be dropped because of maxDelay + const results = 'a 9ms b 9ms c 9ms d 9ms (e|)'; expectObservable(obs).toBe(results); }); }); diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts index 3bbfbbd4ec1bfb..bbdaa9975eeacd 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/rxjs_utils.ts @@ -54,6 +54,8 @@ export function createRateLimiter( let countInCurrentInterval = 0; function createRateLimitOperator<T>(): Rx.OperatorFunction<T, T> { + const maxIntervalEnd = scheduler.now() + maxDelay; + return Rx.pipe( concatMap(function rateLimit(value: T) { const now = scheduler.now(); @@ -61,9 +63,9 @@ export function createRateLimiter( countInCurrentInterval = 1; intervalEnd = now + ratelimitIntervalMs; return Rx.of(value); - } else if (intervalEnd >= now + maxDelay) { - // re-rate limit in the future to avoid to schedule too far in the future as some observer can unsubscribe - return Rx.of(value).pipe(delay(maxDelay, scheduler), createRateLimitOperator<T>()); + } else if (intervalEnd >= maxIntervalEnd) { + // drop the value as it's never going to success as long polling timeout is going to happen before we can send the policy + return Rx.EMPTY; } else { if (++countInCurrentInterval > ratelimitRequestPerInterval) { countInCurrentInterval = 1; diff --git a/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts index 51ccdc8eb1c7c9..8ae151577fefa2 100644 --- a/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts +++ b/x-pack/plugins/ingest_manager/server/services/agents/checkin/state_new_actions.ts @@ -27,6 +27,7 @@ import * as APIKeysService from '../../api_keys'; import { AGENT_SAVED_OBJECT_TYPE, AGENT_UPDATE_ACTIONS_INTERVAL_MS, + AGENT_POLLING_REQUEST_TIMEOUT_MARGIN_MS, AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS, AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL, } from '../../../constants'; @@ -38,8 +39,6 @@ import { import { appContextService } from '../../app_context'; import { toPromiseAbortable, AbortError, createRateLimiter } from './rxjs_utils'; -const RATE_LIMIT_MAX_DELAY_MS = 5 * 60 * 1000; // 5 minutes - function getInternalUserSOClient() { const fakeRequest = ({ headers: {}, @@ -166,19 +165,29 @@ export async function createAgentActionFromPolicyAction( return [newAgentAction]; } +function getPollingTimeoutMs() { + const pollingTimeoutMs = appContextService.getConfig()?.fleet.pollingRequestTimeout ?? 0; + // Set a timeout 20s before the real timeout to have a chance to respond an empty response before socket timeout + return Math.max( + pollingTimeoutMs - AGENT_POLLING_REQUEST_TIMEOUT_MARGIN_MS, + AGENT_POLLING_REQUEST_TIMEOUT_MARGIN_MS + ); +} + export function agentCheckinStateNewActionsFactory() { // Shared Observables const agentPolicies$ = new Map<string, Observable<AgentPolicyAction>>(); const newActions$ = createNewActionsSharedObservable(); // Rx operators - const pollingTimeoutMs = appContextService.getConfig()?.fleet.pollingRequestTimeout ?? 0; + const pollingTimeoutMs = getPollingTimeoutMs(); + const rateLimiterIntervalMs = appContextService.getConfig()?.fleet.agentPolicyRolloutRateLimitIntervalMs ?? AGENT_POLICY_ROLLOUT_RATE_LIMIT_INTERVAL_MS; const rateLimiterRequestPerInterval = appContextService.getConfig()?.fleet.agentPolicyRolloutRateLimitRequestPerInterval ?? AGENT_POLICY_ROLLOUT_RATE_LIMIT_REQUEST_PER_INTERVAL; - const rateLimiterMaxDelay = Math.min(RATE_LIMIT_MAX_DELAY_MS, pollingTimeoutMs); + const rateLimiterMaxDelay = pollingTimeoutMs; const rateLimiter = createRateLimiter( rateLimiterIntervalMs, @@ -204,10 +213,7 @@ export function agentCheckinStateNewActionsFactory() { } const stream$ = agentPolicy$.pipe( - timeout( - // Set a timeout 3s before the real timeout to have a chance to respond an empty response before socket timeout - Math.max(pollingTimeoutMs - 3000, 3000) - ), + timeout(pollingTimeoutMs), filter( (action) => agent.policy_id !== undefined && From 95102ab1321060e75711f94661c4fe7d89ca6f83 Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola <michael.olorunnisola@elastic.co> Date: Fri, 2 Oct 2020 14:52:50 -0400 Subject: [PATCH 100/128] [Security Solution][Resolver] Panel state tests (#78975) --- .../data_access_layer/mocks/emptify_mock.ts | 27 +- .../data_access_layer/mocks/pausify_mock.ts | 65 ++++- .../current_related_event_fetcher.ts | 2 +- .../resolver/view/panels/event_detail.tsx | 2 +- .../resolver/view/panels/node_detail.tsx | 12 +- .../view/panels/node_events_of_type.tsx | 12 +- .../view/panels/panel_content_error.tsx | 7 +- .../resolver/view/panels/panel_loading.tsx | 20 +- .../view/panels/panel_states.test.tsx | 243 ++++++++++++++++++ 9 files changed, 370 insertions(+), 20 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/resolver/view/panels/panel_states.test.tsx diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts index 88a3052a61f743..540430695b6f51 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/emptify_mock.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { SafeResolverEvent } from './../../../../common/endpoint/types/index'; + import { ResolverRelatedEvents, ResolverTree, @@ -12,7 +14,12 @@ import { import { mockTreeWithNoProcessEvents } from '../../mocks/resolver_tree'; import { DataAccessLayer } from '../../types'; -type EmptiableRequests = 'relatedEvents' | 'resolverTree' | 'entities'; +type EmptiableRequests = + | 'relatedEvents' + | 'resolverTree' + | 'entities' + | 'eventsWithEntityIDAndCategory' + | 'event'; interface Metadata<T> { /** @@ -58,6 +65,24 @@ export function emptifyMock<T>( : dataAccessLayer.relatedEvents(...args); }, + async eventsWithEntityIDAndCategory( + ...args + ): Promise<{ + events: SafeResolverEvent[]; + nextEvent: string | null; + }> { + return dataShouldBeEmpty.includes('eventsWithEntityIDAndCategory') + ? { + events: [], + nextEvent: null, + } + : dataAccessLayer.eventsWithEntityIDAndCategory(...args); + }, + + async event(...args): Promise<SafeResolverEvent | null> { + return dataShouldBeEmpty.includes('event') ? null : dataAccessLayer.event(...args); + }, + /** * Fetch a ResolverTree for a entityID */ diff --git a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts index a3ec667385470a..6832affa3e511f 100644 --- a/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts +++ b/x-pack/plugins/security_solution/public/resolver/data_access_layer/mocks/pausify_mock.ts @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +import { SafeResolverEvent } from './../../../../common/endpoint/types/index'; + import { ResolverRelatedEvents, ResolverTree, @@ -11,7 +13,12 @@ import { } from '../../../../common/endpoint/types'; import { DataAccessLayer } from '../../types'; -type PausableRequests = 'relatedEvents' | 'resolverTree' | 'entities'; +type PausableRequests = + | 'relatedEvents' + | 'resolverTree' + | 'entities' + | 'eventsWithEntityIDAndCategory' + | 'event'; interface Metadata<T> { /** @@ -40,10 +47,14 @@ export function pausifyMock<T>({ resume: (pausableRequests: PausableRequests[]) => void; } { let relatedEventsPromise = Promise.resolve(); + let eventsWithEntityIDAndCategoryPromise = Promise.resolve(); + let eventPromise = Promise.resolve(); let resolverTreePromise = Promise.resolve(); let entitiesPromise = Promise.resolve(); let relatedEventsResolver: (() => void) | null; + let eventsWithEntityIDAndCategoryResolver: (() => void) | null; + let eventResolver: (() => void) | null; let resolverTreeResolver: (() => void) | null; let entitiesResolver: (() => void) | null; @@ -53,7 +64,26 @@ export function pausifyMock<T>({ const pauseRelatedEventsRequest = pausableRequests.includes('relatedEvents'); const pauseResolverTreeRequest = pausableRequests.includes('resolverTree'); const pauseEntitiesRequest = pausableRequests.includes('entities'); + const pauseEventsWithEntityIDAndCategoryRequest = pausableRequests.includes( + 'eventsWithEntityIDAndCategory' + ); + const pauseEventRequest = pausableRequests.includes('event'); + if (pauseRelatedEventsRequest && !relatedEventsResolver) { + relatedEventsPromise = new Promise((resolve) => { + relatedEventsResolver = resolve; + }); + } + if (pauseEventsWithEntityIDAndCategoryRequest && !eventsWithEntityIDAndCategoryResolver) { + eventsWithEntityIDAndCategoryPromise = new Promise((resolve) => { + eventsWithEntityIDAndCategoryResolver = resolve; + }); + } + if (pauseEventRequest && !eventResolver) { + eventPromise = new Promise((resolve) => { + eventResolver = resolve; + }); + } if (pauseRelatedEventsRequest && !relatedEventsResolver) { relatedEventsPromise = new Promise((resolve) => { relatedEventsResolver = resolve; @@ -74,6 +104,10 @@ export function pausifyMock<T>({ const resumeEntitiesRequest = pausableRequests.includes('entities'); const resumeResolverTreeRequest = pausableRequests.includes('resolverTree'); const resumeRelatedEventsRequest = pausableRequests.includes('relatedEvents'); + const resumeEventsWithEntityIDAndCategoryRequest = pausableRequests.includes( + 'eventsWithEntityIDAndCategory' + ); + const resumeEventRequest = pausableRequests.includes('event'); if (resumeEntitiesRequest && entitiesResolver) { entitiesResolver(); @@ -87,6 +121,14 @@ export function pausifyMock<T>({ relatedEventsResolver(); relatedEventsResolver = null; } + if (resumeEventsWithEntityIDAndCategoryRequest && eventsWithEntityIDAndCategoryResolver) { + eventsWithEntityIDAndCategoryResolver(); + eventsWithEntityIDAndCategoryResolver = null; + } + if (resumeEventRequest && eventResolver) { + eventResolver(); + eventResolver = null; + } }, dataAccessLayer: { ...dataAccessLayer, @@ -98,6 +140,27 @@ export function pausifyMock<T>({ return dataAccessLayer.relatedEvents(...args); }, + /** + * Fetch related events for an entity ID + */ + async eventsWithEntityIDAndCategory( + ...args + ): Promise<{ + events: SafeResolverEvent[]; + nextEvent: string | null; + }> { + await eventsWithEntityIDAndCategoryPromise; + return dataAccessLayer.eventsWithEntityIDAndCategory(...args); + }, + + /** + * Fetch related events for an entity ID + */ + async event(...args): Promise<SafeResolverEvent | null> { + await eventPromise; + return dataAccessLayer.event(...args); + }, + /** * Fetch a ResolverTree for a entityID */ diff --git a/x-pack/plugins/security_solution/public/resolver/store/middleware/current_related_event_fetcher.ts b/x-pack/plugins/security_solution/public/resolver/store/middleware/current_related_event_fetcher.ts index 9ff9c35dbac3c7..7f83ef7bf2aa86 100644 --- a/x-pack/plugins/security_solution/public/resolver/store/middleware/current_related_event_fetcher.ts +++ b/x-pack/plugins/security_solution/public/resolver/store/middleware/current_related_event_fetcher.ts @@ -43,7 +43,7 @@ export function CurrentRelatedEventFetcher( type: 'appRequestedCurrentRelatedEventData', }); - let result: SafeResolverEvent | undefined | null; + let result: SafeResolverEvent | null = null; try { result = await dataAccessLayer.event(currentEventID); } catch (error) { diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx index 27ab343a882a61..039287d04e9213 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/event_detail.tsx @@ -100,7 +100,7 @@ const EventDetailContents = memo(function ({ const nodeName = processEvent ? eventModel.processNameSafeVersion(processEvent) : null; return ( - <StyledPanel> + <StyledPanel data-test-subj="resolver:panel:event-detail"> <EventDetailBreadcrumbs nodeID={nodeID} nodeName={nodeName} diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx index 181c9ac8ab8a08..011614e8eb9b5e 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/node_detail.tsx @@ -38,13 +38,17 @@ export const NodeDetail = memo(function ({ nodeID }: { nodeID: string }) { selectors.processEventForID(state)(nodeID) ); return ( - <StyledPanel> + <> {processEvent === null ? ( - <PanelLoading /> + <StyledPanel> + <PanelLoading /> + </StyledPanel> ) : ( - <NodeDetailView nodeID={nodeID} processEvent={processEvent} /> + <StyledPanel data-test-subj="resolver:panel:node-detail"> + <NodeDetailView nodeID={nodeID} processEvent={processEvent} /> + </StyledPanel> )} - </StyledPanel> + </> ); }); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx index 0265770fbb4a5a..7f635097d8ac91 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/node_events_of_type.tsx @@ -45,11 +45,13 @@ export const NodeEventsInCategory = memo(function ({ const events = useSelector((state: ResolverState) => selectors.nodeEventsInCategory(state)); return ( - <StyledPanel> + <> {eventCount === undefined || processEvent === null ? ( - <PanelLoading /> + <StyledPanel> + <PanelLoading /> + </StyledPanel> ) : ( - <> + <StyledPanel data-test-subj="resolver:panel:events-in-category"> <NodeEventsInCategoryBreadcrumbs nodeName={eventModel.processNameSafeVersion(processEvent)} eventCategory={eventCategory} @@ -59,9 +61,9 @@ export const NodeEventsInCategory = memo(function ({ /> <EuiSpacer size="l" /> <NodeEventList eventCategory={eventCategory} nodeID={nodeID} events={events} /> - </> + </StyledPanel> )} - </StyledPanel> + </> ); }); diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx index c7cc420fcc0d4a..b6905043023a94 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_content_error.tsx @@ -44,11 +44,14 @@ export const PanelContentError = memo(function ({ <> <Breadcrumbs breadcrumbs={crumbs} /> <EuiSpacer size="l" /> - <EuiText textAlign="center">{translatedErrorMessage}</EuiText> + <EuiText textAlign="center" data-test-subj="resolver:panel:error"> + {translatedErrorMessage} + </EuiText> <EuiSpacer size="l" /> + <EuiButtonEmpty {...nodesLinkNavProps}> {i18n.translate('xpack.securitySolution.endpoint.resolver.panel.error.goBack', { - defaultMessage: 'Click here to return to the list of all processes.', + defaultMessage: 'View all processes', })} </EuiButtonEmpty> </> diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_loading.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_loading.tsx index 2de0bf5d320ea8..377c19ee5e71c9 100644 --- a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_loading.tsx +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_loading.tsx @@ -6,15 +6,20 @@ import React, { useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiSpacer, EuiTitle } from '@elastic/eui'; +import styled from 'styled-components'; +import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiText, EuiSpacer } from '@elastic/eui'; import { Breadcrumbs } from './breadcrumbs'; import { useLinkProps } from '../use_link_props'; +const StyledSpinnerFlexItem = styled.span` + margin-right: 5px; +`; + export function PanelLoading() { const waitingString = i18n.translate( 'xpack.securitySolution.endpoint.resolver.panel.relatedDetail.wait', { - defaultMessage: 'Waiting For Events...', + defaultMessage: 'Loading Events...', } ); const eventsString = i18n.translate( @@ -38,9 +43,14 @@ export function PanelLoading() { <> <Breadcrumbs breadcrumbs={waitCrumbs} /> <EuiSpacer size="l" /> - <EuiTitle> - <h4>{waitingString}</h4> - </EuiTitle> + <EuiFlexGroup alignItems="center" direction="row" gutterSize="none"> + <StyledSpinnerFlexItem> + <EuiLoadingSpinner size="m" /> + </StyledSpinnerFlexItem> + <EuiFlexItem grow={false}> + <EuiText data-test-subj="resolver:panel:loading">{waitingString}</EuiText> + </EuiFlexItem> + </EuiFlexGroup> </> ); } diff --git a/x-pack/plugins/security_solution/public/resolver/view/panels/panel_states.test.tsx b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_states.test.tsx new file mode 100644 index 00000000000000..39a5130ecaf681 --- /dev/null +++ b/x-pack/plugins/security_solution/public/resolver/view/panels/panel_states.test.tsx @@ -0,0 +1,243 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createMemoryHistory, History as HistoryPackageHistoryInterface } from 'history'; +import { Simulator } from '../../test_utilities/simulator'; +import { pausifyMock } from '../../data_access_layer/mocks/pausify_mock'; +import { emptifyMock } from '../../data_access_layer/mocks/emptify_mock'; +import { noAncestorsTwoChildrenWithRelatedEventsOnOrigin } from '../../data_access_layer/mocks/no_ancestors_two_children_with_related_events_on_origin'; +import { firstRelatedEventID } from '../../mocks/resolver_tree'; +import { urlSearch } from '../../test_utilities/url_search'; +import '../../test_utilities/extend_jest'; + +describe('Resolver: panel loading and resolution states', () => { + let simulator: Simulator; + const resolverComponentInstanceID = 'resolverPanelStates'; + let memoryHistory: HistoryPackageHistoryInterface<never>; + + const queryStringWithEventDetailSelected = urlSearch(resolverComponentInstanceID, { + panelParameters: { + nodeID: 'origin', + eventCategory: 'registry', + eventID: firstRelatedEventID, + }, + panelView: 'eventDetail', + }); + + const queryStringWithEventsInCategorySelected = urlSearch(resolverComponentInstanceID, { + panelParameters: { + nodeID: 'origin', + eventCategory: 'registry', + }, + panelView: 'nodeEventsInCategory', + }); + + const queryStringWithNodeDetailSelected = urlSearch(resolverComponentInstanceID, { + panelParameters: { + nodeID: 'origin', + }, + panelView: 'nodeDetail', + }); + + describe('When analyzing a tree with no ancestors and two children with related events on the origin', () => { + describe('when navigating to the event detail panel', () => { + let resumeRequest: (pausableRequest: ['event']) => void; + beforeEach(() => { + const { + metadata: { databaseDocumentID }, + dataAccessLayer, + pause, + resume, + } = pausifyMock(noAncestorsTwoChildrenWithRelatedEventsOnOrigin()); + + resumeRequest = resume; + memoryHistory = createMemoryHistory(); + pause(['event']); + + simulator = new Simulator({ + dataAccessLayer, + databaseDocumentID, + history: memoryHistory, + resolverComponentInstanceID, + indices: [], + }); + + memoryHistory.push({ + search: queryStringWithEventDetailSelected, + }); + }); + + it('should display a loading state in the panel', async () => { + await expect( + simulator.map(() => ({ + resolverPanelLoading: simulator.testSubject('resolver:panel:loading').length, + resolverPanelEventDetail: simulator.testSubject('resolver:panel:event-detail').length, + })) + ).toYieldEqualTo({ + resolverPanelLoading: 1, + resolverPanelEventDetail: 0, + }); + }); + + it('should successfully load the event detail panel', async () => { + await resumeRequest(['event']); + await expect( + simulator.map(() => ({ + resolverPanelLoading: simulator.testSubject('resolver:panel:loading').length, + resolverPanelEventDetail: simulator.testSubject('resolver:panel:event-detail').length, + })) + ).toYieldEqualTo({ + resolverPanelLoading: 0, + resolverPanelEventDetail: 1, + }); + }); + }); + + describe('when the server fails to return event data on the event detail panel', () => { + beforeEach(() => { + const { + metadata: { databaseDocumentID }, + dataAccessLayer, + } = emptifyMock(noAncestorsTwoChildrenWithRelatedEventsOnOrigin(), ['event']); + memoryHistory = createMemoryHistory(); + simulator = new Simulator({ + dataAccessLayer, + databaseDocumentID, + history: memoryHistory, + resolverComponentInstanceID, + indices: [], + }); + memoryHistory.push({ + search: queryStringWithEventDetailSelected, + }); + }); + + it('should display an error state in the panel', async () => { + await expect( + simulator.map(() => ({ + resolverPanelError: simulator.testSubject('resolver:panel:error').length, + resolverPanelEventDetail: simulator.testSubject('resolver:panel:event-detail').length, + })) + ).toYieldEqualTo({ + resolverPanelError: 1, + resolverPanelEventDetail: 0, + }); + }); + }); + + describe('when navigating to the event categories panel', () => { + let resumeRequest: (pausableRequest: ['entities']) => void; + beforeEach(() => { + const { + metadata: { databaseDocumentID }, + dataAccessLayer, + pause, + resume, + } = pausifyMock(noAncestorsTwoChildrenWithRelatedEventsOnOrigin()); + + resumeRequest = resume; + memoryHistory = createMemoryHistory(); + pause(['entities']); + + simulator = new Simulator({ + dataAccessLayer, + databaseDocumentID, + history: memoryHistory, + resolverComponentInstanceID, + indices: [], + }); + + memoryHistory.push({ + search: queryStringWithEventsInCategorySelected, + }); + }); + + it('should display a loading state in the panel', async () => { + await expect( + simulator.map(() => ({ + resolverPanelLoading: simulator.testSubject('resolver:panel:loading').length, + resolverPanelEventsInCategory: simulator.testSubject( + 'resolver:panel:events-in-category' + ).length, + })) + ).toYieldEqualTo({ + resolverPanelLoading: 1, + resolverPanelEventsInCategory: 0, + }); + }); + + it('should successfully load the events in category panel', async () => { + await resumeRequest(['entities']); + await expect( + simulator.map(() => ({ + resolverPanelLoading: simulator.testSubject('resolver:panel:loading').length, + resolverPanelEventsInCategory: simulator.testSubject( + 'resolver:panel:events-in-category' + ).length, + })) + ).toYieldEqualTo({ + resolverPanelLoading: 0, + resolverPanelEventsInCategory: 1, + }); + }); + }); + + describe('when navigating to the node detail panel', () => { + let resumeRequest: (pausableRequest: ['entities']) => void; + beforeEach(() => { + const { + metadata: { databaseDocumentID }, + dataAccessLayer, + pause, + resume, + } = pausifyMock(noAncestorsTwoChildrenWithRelatedEventsOnOrigin()); + + resumeRequest = resume; + memoryHistory = createMemoryHistory(); + pause(['entities']); + + simulator = new Simulator({ + dataAccessLayer, + databaseDocumentID, + history: memoryHistory, + resolverComponentInstanceID, + indices: [], + }); + + memoryHistory.push({ + search: queryStringWithNodeDetailSelected, + }); + }); + + it('should display a loading state in the panel', async () => { + await expect( + simulator.map(() => ({ + resolverPanelLoading: simulator.testSubject('resolver:panel:loading').length, + resolverPanelEventsInCategory: simulator.testSubject('resolver:panel:node-detail') + .length, + })) + ).toYieldEqualTo({ + resolverPanelLoading: 1, + resolverPanelEventsInCategory: 0, + }); + }); + + it('should successfully load the events in category panel', async () => { + await resumeRequest(['entities']); + await expect( + simulator.map(() => ({ + resolverPanelLoading: simulator.testSubject('resolver:panel:loading').length, + resolverPanelEventsInCategory: simulator.testSubject('resolver:panel:node-detail') + .length, + })) + ).toYieldEqualTo({ + resolverPanelLoading: 0, + resolverPanelEventsInCategory: 1, + }); + }); + }); + }); +}); From 952bb0730fbe4a218123eabeb802ef7b01de7134 Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet <pierre.gayvallet@elastic.co> Date: Fri, 2 Oct 2020 21:00:41 +0200 Subject: [PATCH 101/128] prepend basePath when navigating from home links (#79318) --- .../public/application/components/app_navigation_handler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/home/public/application/components/app_navigation_handler.ts b/src/plugins/home/public/application/components/app_navigation_handler.ts index 91407ffcaf2265..b6230bc9f1e38b 100644 --- a/src/plugins/home/public/application/components/app_navigation_handler.ts +++ b/src/plugins/home/public/application/components/app_navigation_handler.ts @@ -24,6 +24,7 @@ export const createAppNavigationHandler = (targetUrl: string) => (event: MouseEv if (event.altKey || event.metaKey || event.ctrlKey) { return; } + const { application, addBasePath } = getServices(); event.preventDefault(); - getServices().application.navigateToUrl(targetUrl); + application.navigateToUrl(addBasePath(targetUrl)); }; From 9021c836cd24ed0cbe97d8aaa9e666e42bc2b909 Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Fri, 2 Oct 2020 12:05:59 -0700 Subject: [PATCH 102/128] [core/server/plugins] don't run discovery in dev server parent process (#79235) Co-authored-by: spalger <spalger@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/plugins/plugins_service.test.ts | 81 +++++++++++++------ src/core/server/plugins/plugins_service.ts | 18 +++-- 2 files changed, 69 insertions(+), 30 deletions(-) diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index d36fd2251176a2..64a382e539fb0f 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -102,35 +102,42 @@ const createPlugin = ( }); }; -describe('PluginsService', () => { - beforeEach(async () => { - mockPackage.raw = { - branch: 'feature-v1', - version: 'v1', - build: { - distributable: true, - number: 100, - sha: 'feature-v1-build-sha', - }, - }; +async function testSetup(options: { isDevClusterMaster?: boolean } = {}) { + mockPackage.raw = { + branch: 'feature-v1', + version: 'v1', + build: { + distributable: true, + number: 100, + sha: 'feature-v1-build-sha', + }, + }; - coreId = Symbol('core'); - env = Env.createDefault(REPO_ROOT, getEnvOptions()); + coreId = Symbol('core'); + env = Env.createDefault(REPO_ROOT, { + ...getEnvOptions(), + isDevClusterMaster: options.isDevClusterMaster ?? false, + }); - config$ = new BehaviorSubject<Record<string, any>>({ plugins: { initialize: true } }); - const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); - configService = new ConfigService(rawConfigService, env, logger); - await configService.setSchema(config.path, config.schema); - pluginsService = new PluginsService({ coreId, env, logger, configService }); + config$ = new BehaviorSubject<Record<string, any>>({ plugins: { initialize: true } }); + const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); + configService = new ConfigService(rawConfigService, env, logger); + await configService.setSchema(config.path, config.schema); + pluginsService = new PluginsService({ coreId, env, logger, configService }); - [mockPluginSystem] = MockPluginsSystem.mock.instances as any; - mockPluginSystem.uiPlugins.mockReturnValue(new Map()); + [mockPluginSystem] = MockPluginsSystem.mock.instances as any; + mockPluginSystem.uiPlugins.mockReturnValue(new Map()); - environmentSetup = environmentServiceMock.createSetupContract(); - }); + environmentSetup = environmentServiceMock.createSetupContract(); +} - afterEach(() => { - jest.clearAllMocks(); +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('PluginsService', () => { + beforeEach(async () => { + await testSetup(); }); describe('#discover()', () => { @@ -613,3 +620,29 @@ describe('PluginsService', () => { }); }); }); + +describe('PluginService when isDevClusterMaster is true', () => { + beforeEach(async () => { + await testSetup({ + isDevClusterMaster: true, + }); + }); + + describe('#discover()', () => { + it('does not try to run discovery', async () => { + await expect(pluginsService.discover({ environment: environmentSetup })).resolves + .toMatchInlineSnapshot(` + Object { + "pluginTree": undefined, + "uiPlugins": Object { + "browserConfigs": Map {}, + "internal": Map {}, + "public": Map {}, + }, + } + `); + + expect(mockDiscover).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index e8fe42ee491ca5..a1062bde7765f0 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -18,7 +18,7 @@ */ import Path from 'path'; -import { Observable } from 'rxjs'; +import { Observable, EMPTY } from 'rxjs'; import { filter, first, map, mergeMap, tap, toArray } from 'rxjs/operators'; import { pick } from '@kbn/std'; @@ -86,9 +86,11 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS private readonly config$: Observable<PluginsConfig>; private readonly pluginConfigDescriptors = new Map<PluginName, PluginConfigDescriptor>(); private readonly uiPluginInternalInfo = new Map<PluginName, InternalPluginInfo>(); + private readonly discoveryDisabled: boolean; constructor(private readonly coreContext: CoreContext) { this.log = coreContext.logger.get('plugins-service'); + this.discoveryDisabled = coreContext.env.isDevClusterMaster; this.pluginsSystem = new PluginsSystem(coreContext); this.configService = coreContext.configService; this.config$ = coreContext.configService @@ -97,13 +99,17 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS } public async discover({ environment }: PluginsServiceDiscoverDeps) { - this.log.debug('Discovering plugins'); - const config = await this.config$.pipe(first()).toPromise(); - const { error$, plugin$ } = discover(config, this.coreContext, { - uuid: environment.instanceUuid, - }); + const { error$, plugin$ } = this.discoveryDisabled + ? { + error$: EMPTY, + plugin$: EMPTY, + } + : discover(config, this.coreContext, { + uuid: environment.instanceUuid, + }); + await this.handleDiscoveryErrors(error$); await this.handleDiscoveredPlugins(plugin$); From 388a905b29e8c812e31ead8ccc82a8bfc94e8b37 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin <aleh.zasypkin@gmail.com> Date: Fri, 2 Oct 2020 21:08:39 +0200 Subject: [PATCH 103/128] Add support for the encryption key rotation to the encrypted saved objects. (#72420) --- docs/setup/production.asciidoc | 1 + .../resources/bin/kibana-docker | 1 + .../server/config.test.ts | 108 +- .../encrypted_saved_objects/server/config.ts | 71 +- .../encrypted_saved_objects_service.test.ts | 387 ++- .../crypto/encrypted_saved_objects_service.ts | 146 +- .../encryption_key_rotation_service.mocks.ts | 15 + .../encryption_key_rotation_service.test.ts | 502 ++++ .../crypto/encryption_key_rotation_service.ts | 268 ++ .../server/crypto/index.mock.ts | 1 + .../server/crypto/index.ts | 1 + .../server/plugin.test.ts | 9 +- .../encrypted_saved_objects/server/plugin.ts | 61 +- .../server/routes/index.mock.ts | 19 + .../server/routes/index.ts | 25 + .../server/routes/key_rotation.test.ts | 172 ++ .../server/routes/key_rotation.ts | 75 + .../get_descriptor_namespace.test.ts | 11 + .../saved_objects/get_descriptor_namespace.ts | 8 +- .../config.ts | 4 + .../es_archiver/key_rotation/data.json | 186 ++ .../es_archiver/key_rotation/mappings.json | 2417 +++++++++++++++++ .../services.ts | 8 +- .../tests/encrypted_saved_objects_api.ts | 143 +- 24 files changed, 4486 insertions(+), 153 deletions(-) create mode 100644 x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.mocks.ts create mode 100644 x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.test.ts create mode 100644 x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts create mode 100644 x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts create mode 100644 x-pack/plugins/encrypted_saved_objects/server/routes/index.ts create mode 100644 x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts create mode 100644 x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts create mode 100644 x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/data.json create mode 100644 x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json diff --git a/docs/setup/production.asciidoc b/docs/setup/production.asciidoc index 3075220e3a47c5..e097704e05d406 100644 --- a/docs/setup/production.asciidoc +++ b/docs/setup/production.asciidoc @@ -135,6 +135,7 @@ Settings that must be the same: xpack.security.encryptionKey //decrypting session information xpack.reporting.encryptionKey //decrypting reports xpack.encryptedSavedObjects.encryptionKey // decrypting saved objects +xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys // saved objects encryption key rotation, if any -------- Separate configuration files can be used from the command line by using the `-c` flag: diff --git a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker index 2770f288b6af8f..959e1f8dc3e729 100755 --- a/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker +++ b/src/dev/build/tasks/os_packages/docker_generator/resources/bin/kibana-docker @@ -159,6 +159,7 @@ kibana_vars=( xpack.code.security.gitHostWhitelist xpack.code.security.gitProtocolWhitelist xpack.encryptedSavedObjects.encryptionKey + xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys xpack.graph.enabled xpack.graph.canEditDrillDownUrls xpack.graph.savePolicy diff --git a/x-pack/plugins/encrypted_saved_objects/server/config.test.ts b/x-pack/plugins/encrypted_saved_objects/server/config.test.ts index 3f8074eb15c0c3..cbe987830717fc 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/config.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/config.test.ts @@ -6,9 +6,8 @@ jest.mock('crypto', () => ({ randomBytes: jest.fn() })); -import { first } from 'rxjs/operators'; -import { loggingSystemMock, coreMock } from 'src/core/server/mocks'; -import { createConfig$, ConfigSchema } from './config'; +import { loggingSystemMock } from 'src/core/server/mocks'; +import { createConfig, ConfigSchema } from './config'; describe('config schema', () => { it('generates proper defaults', () => { @@ -16,6 +15,9 @@ describe('config schema', () => { Object { "enabled": true, "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "keyRotation": Object { + "decryptionOnlyKeys": Array [], + }, } `); @@ -23,12 +25,41 @@ describe('config schema', () => { Object { "enabled": true, "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "keyRotation": Object { + "decryptionOnlyKeys": Array [], + }, } `); expect(ConfigSchema.validate({}, { dist: true })).toMatchInlineSnapshot(` Object { "enabled": true, + "keyRotation": Object { + "decryptionOnlyKeys": Array [], + }, + } + `); + }); + + it('properly validates config', () => { + expect( + ConfigSchema.validate( + { + encryptionKey: 'a'.repeat(32), + keyRotation: { decryptionOnlyKeys: ['b'.repeat(32), 'c'.repeat(32)] }, + }, + { dist: true } + ) + ).toMatchInlineSnapshot(` + Object { + "enabled": true, + "encryptionKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "keyRotation": Object { + "decryptionOnlyKeys": Array [ + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "cccccccccccccccccccccccccccccccc", + ], + }, } `); }); @@ -46,21 +77,65 @@ describe('config schema', () => { `"[encryptionKey]: value has length [3] but it must have a minimum length of [32]."` ); }); + + it('should throw error if any of the xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys is less than 32 characters', () => { + expect(() => + ConfigSchema.validate({ + keyRotation: { decryptionOnlyKeys: ['a'.repeat(32), 'b'.repeat(31)] }, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"[keyRotation.decryptionOnlyKeys.1]: value has length [31] but it must have a minimum length of [32]."` + ); + + expect(() => + ConfigSchema.validate( + { keyRotation: { decryptionOnlyKeys: ['a'.repeat(32), 'b'.repeat(31)] } }, + { dist: true } + ) + ).toThrowErrorMatchingInlineSnapshot( + `"[keyRotation.decryptionOnlyKeys.1]: value has length [31] but it must have a minimum length of [32]."` + ); + }); + + it('should throw error if any of the xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys is equal to xpack.encryptedSavedObjects.encryptionKey', () => { + expect(() => + ConfigSchema.validate({ + encryptionKey: 'a'.repeat(32), + keyRotation: { decryptionOnlyKeys: ['a'.repeat(32)] }, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"\`keyRotation.decryptionOnlyKeys\` cannot contain primary encryption key specified in \`encryptionKey\`."` + ); + + expect(() => + ConfigSchema.validate( + { + encryptionKey: 'a'.repeat(32), + keyRotation: { decryptionOnlyKeys: ['a'.repeat(32)] }, + }, + { dist: true } + ) + ).toThrowErrorMatchingInlineSnapshot( + `"\`keyRotation.decryptionOnlyKeys\` cannot contain primary encryption key specified in \`encryptionKey\`."` + ); + }); }); -describe('createConfig$()', () => { - it('should log a warning, set xpack.encryptedSavedObjects.encryptionKey and usingEphemeralEncryptionKey=true when encryptionKey is not set', async () => { +describe('createConfig()', () => { + it('should log a warning, set xpack.encryptedSavedObjects.encryptionKey and usingEphemeralEncryptionKey=true when encryptionKey is not set', () => { const mockRandomBytes = jest.requireMock('crypto').randomBytes; mockRandomBytes.mockReturnValue('ab'.repeat(16)); - const contextMock = coreMock.createPluginInitializerContext({}); - const config = await createConfig$(contextMock).pipe(first()).toPromise(); + const logger = loggingSystemMock.create().get(); + const config = createConfig(ConfigSchema.validate({}, { dist: true }), logger); expect(config).toEqual({ - config: { encryptionKey: 'ab'.repeat(16) }, + enabled: true, + encryptionKey: 'ab'.repeat(16), + keyRotation: { decryptionOnlyKeys: [] }, usingEphemeralEncryptionKey: true, }); - expect(loggingSystemMock.collect(contextMock.logger).warn).toMatchInlineSnapshot(` + expect(loggingSystemMock.collect(logger).warn).toMatchInlineSnapshot(` Array [ Array [ "Generating a random key for xpack.encryptedSavedObjects.encryptionKey. To be able to decrypt encrypted saved objects attributes after restart, please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml", @@ -70,15 +145,18 @@ describe('createConfig$()', () => { }); it('should not log a warning and set usingEphemeralEncryptionKey=false when encryptionKey is set', async () => { - const contextMock = coreMock.createPluginInitializerContext({ - encryptionKey: 'supersecret', - }); - const config = await createConfig$(contextMock).pipe(first()).toPromise(); + const logger = loggingSystemMock.create().get(); + const config = createConfig( + ConfigSchema.validate({ encryptionKey: 'supersecret'.repeat(3) }, { dist: true }), + logger + ); expect(config).toEqual({ - config: { encryptionKey: 'supersecret' }, + enabled: true, + encryptionKey: 'supersecret'.repeat(3), + keyRotation: { decryptionOnlyKeys: [] }, usingEphemeralEncryptionKey: false, }); - expect(loggingSystemMock.collect(contextMock.logger).warn).toEqual([]); + expect(loggingSystemMock.collect(logger).warn).toEqual([]); }); }); diff --git a/x-pack/plugins/encrypted_saved_objects/server/config.ts b/x-pack/plugins/encrypted_saved_objects/server/config.ts index 9c751a9c67f523..f06c6fa1823ba6 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/config.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/config.ts @@ -5,41 +5,50 @@ */ import crypto from 'crypto'; -import { map } from 'rxjs/operators'; import { schema, TypeOf } from '@kbn/config-schema'; -import { PluginInitializerContext } from 'src/core/server'; +import { Logger } from 'src/core/server'; -export const ConfigSchema = schema.object({ - enabled: schema.boolean({ defaultValue: true }), - encryptionKey: schema.conditional( - schema.contextRef('dist'), - true, - schema.maybe(schema.string({ minLength: 32 })), - schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) }) - ), -}); +export type ConfigType = ReturnType<typeof createConfig>; -export function createConfig$(context: PluginInitializerContext) { - return context.config.create<TypeOf<typeof ConfigSchema>>().pipe( - map((config) => { - const logger = context.logger.get('config'); +export const ConfigSchema = schema.object( + { + enabled: schema.boolean({ defaultValue: true }), + encryptionKey: schema.conditional( + schema.contextRef('dist'), + true, + schema.maybe(schema.string({ minLength: 32 })), + schema.string({ minLength: 32, defaultValue: 'a'.repeat(32) }) + ), + keyRotation: schema.object({ + decryptionOnlyKeys: schema.arrayOf(schema.string({ minLength: 32 }), { defaultValue: [] }), + }), + }, + { + validate(value) { + const decryptionOnlyKeys = value.keyRotation?.decryptionOnlyKeys ?? []; + if (value.encryptionKey && decryptionOnlyKeys.includes(value.encryptionKey)) { + return '`keyRotation.decryptionOnlyKeys` cannot contain primary encryption key specified in `encryptionKey`.'; + } + }, + } +); - let encryptionKey = config.encryptionKey; - const usingEphemeralEncryptionKey = encryptionKey === undefined; - if (encryptionKey === undefined) { - logger.warn( - 'Generating a random key for xpack.encryptedSavedObjects.encryptionKey. ' + - 'To be able to decrypt encrypted saved objects attributes after restart, ' + - 'please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml' - ); +export function createConfig(config: TypeOf<typeof ConfigSchema>, logger: Logger) { + let encryptionKey = config.encryptionKey; + const usingEphemeralEncryptionKey = encryptionKey === undefined; + if (encryptionKey === undefined) { + logger.warn( + 'Generating a random key for xpack.encryptedSavedObjects.encryptionKey. ' + + 'To be able to decrypt encrypted saved objects attributes after restart, ' + + 'please set xpack.encryptedSavedObjects.encryptionKey in kibana.yml' + ); - encryptionKey = crypto.randomBytes(16).toString('hex'); - } + encryptionKey = crypto.randomBytes(16).toString('hex'); + } - return { - config: { ...config, encryptionKey }, - usingEphemeralEncryptionKey, - }; - }) - ); + return { + ...config, + encryptionKey, + usingEphemeralEncryptionKey, + }; } diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts index 42d2e2ffd15163..88d57072697fe6 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts @@ -14,40 +14,44 @@ import { EncryptionError } from './encryption_error'; import { loggingSystemMock } from 'src/core/server/mocks'; import { encryptedSavedObjectsAuditLoggerMock } from '../audit/index.mock'; -const crypto = nodeCrypto({ encryptionKey: 'encryption-key-abc' }); +function createNodeCryptMock(encryptionKey: string) { + const crypto = nodeCrypto({ encryptionKey }); + const nodeCryptoMock: jest.Mocked<Crypto> = { + encrypt: jest.fn(), + decrypt: jest.fn(), + encryptSync: jest.fn(), + decryptSync: jest.fn(), + }; -const mockNodeCrypto: jest.Mocked<Crypto> = { - encrypt: jest.fn(), - decrypt: jest.fn(), - encryptSync: jest.fn(), - decryptSync: jest.fn(), -}; - -let service: EncryptedSavedObjectsService; -let mockAuditLogger: jest.Mocked<EncryptedSavedObjectsAuditLogger>; - -beforeEach(() => { // Call actual `@elastic/node-crypto` by default, but allow to override implementation in tests. - mockNodeCrypto.encrypt.mockImplementation(async (input: any, aad?: string) => + nodeCryptoMock.encrypt.mockImplementation(async (input: any, aad?: string) => crypto.encrypt(input, aad) ); - mockNodeCrypto.decrypt.mockImplementation( + nodeCryptoMock.decrypt.mockImplementation( async (encryptedOutput: string | Buffer, aad?: string) => crypto.decrypt(encryptedOutput, aad) ); - mockNodeCrypto.encryptSync.mockImplementation((input: any, aad?: string) => + nodeCryptoMock.encryptSync.mockImplementation((input: any, aad?: string) => crypto.encryptSync(input, aad) ); - mockNodeCrypto.decryptSync.mockImplementation((encryptedOutput: string | Buffer, aad?: string) => + nodeCryptoMock.decryptSync.mockImplementation((encryptedOutput: string | Buffer, aad?: string) => crypto.decryptSync(encryptedOutput, aad) ); + return nodeCryptoMock; +} + +let mockNodeCrypto: jest.Mocked<Crypto>; +let service: EncryptedSavedObjectsService; +let mockAuditLogger: jest.Mocked<EncryptedSavedObjectsAuditLogger>; +beforeEach(() => { + mockNodeCrypto = createNodeCryptMock('encryption-key-abc'); mockAuditLogger = encryptedSavedObjectsAuditLoggerMock.create(); - service = new EncryptedSavedObjectsService( - mockNodeCrypto, - loggingSystemMock.create().get(), - mockAuditLogger - ); + service = new EncryptedSavedObjectsService({ + primaryCrypto: mockNodeCrypto, + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); }); afterEach(() => jest.resetAllMocks()); @@ -229,11 +233,11 @@ describe('#encryptAttributes', () => { async (valueToEncrypt, aad) => `|${valueToEncrypt}|${aad}|` ); - service = new EncryptedSavedObjectsService( - mockNodeCrypto, - loggingSystemMock.create().get(), - mockAuditLogger - ); + service = new EncryptedSavedObjectsService({ + primaryCrypto: mockNodeCrypto, + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); }); it('does not encrypt attributes for unknown types', async () => { @@ -304,6 +308,34 @@ describe('#encryptAttributes', () => { ); }); + it('encrypts only using primary crypto', async () => { + const attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }; + + const decryptionOnlyCrypto = createNodeCryptMock('some-key'); + service = new EncryptedSavedObjectsService({ + primaryCrypto: mockNodeCrypto, + decryptionOnlyCryptos: [decryptionOnlyCrypto], + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); + service.registerType({ + type: 'known-type-1', + attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']), + }); + + await expect( + service.encryptAttributes({ type: 'known-type-1', id: 'object-id' }, attributes) + ).resolves.toEqual({ + attrOne: '|one|["known-type-1","object-id",{"attrTwo":"two"}]|', + attrTwo: 'two', + attrThree: '|three|["known-type-1","object-id",{"attrTwo":"two"}]|', + attrFour: null, + }); + + expect(decryptionOnlyCrypto.encrypt).not.toHaveBeenCalled(); + expect(decryptionOnlyCrypto.encryptSync).not.toHaveBeenCalled(); + }); + it('encrypts only attributes that are supposed to be encrypted even if not all provided', async () => { const attributes = { attrTwo: 'two', attrThree: 'three' }; @@ -923,11 +955,11 @@ describe('#decryptAttributes', () => { }); it('fails if encrypted with another encryption key', async () => { - service = new EncryptedSavedObjectsService( - nodeCrypto({ encryptionKey: 'encryption-key-abc*' }), - loggingSystemMock.create().get(), - mockAuditLogger - ); + service = new EncryptedSavedObjectsService({ + primaryCrypto: nodeCrypto({ encryptionKey: 'encryption-key-abc*' }), + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); service.registerType({ type: 'known-type-1', @@ -949,6 +981,123 @@ describe('#decryptAttributes', () => { ); }); }); + + describe('with decryption only keys', () => { + function getService(primaryCrypto: Crypto, decryptionOnlyCryptos?: Readonly<Crypto[]>) { + const esoService = new EncryptedSavedObjectsService({ + primaryCrypto, + decryptionOnlyCryptos, + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); + + esoService.registerType({ + type: 'known-type-1', + attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']), + }); + + return esoService; + } + + const attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }; + + let decryptionOnlyCryptoOne: jest.Mocked<Crypto>; + let decryptionOnlyCryptoTwo: jest.Mocked<Crypto>; + beforeEach(() => { + decryptionOnlyCryptoOne = createNodeCryptMock('old-key-one'); + decryptionOnlyCryptoTwo = createNodeCryptMock('old-key-two'); + + service = getService(mockNodeCrypto, [decryptionOnlyCryptoOne, decryptionOnlyCryptoTwo]); + }); + + it('does not use decryption only keys if we can decrypt using primary key', async () => { + const encryptedAttributes = await service.encryptAttributes( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + await expect( + service.decryptAttributes({ type: 'known-type-1', id: 'object-id' }, encryptedAttributes) + ).resolves.toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + expect(decryptionOnlyCryptoOne.decrypt).not.toHaveBeenCalled(); + expect(decryptionOnlyCryptoTwo.decrypt).not.toHaveBeenCalled(); + }); + + it('uses decryption only keys if cannot decrypt using primary key', async () => { + const encryptedAttributes = await getService(decryptionOnlyCryptoOne).encryptAttributes( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + await expect( + service.decryptAttributes({ type: 'known-type-1', id: 'object-id' }, encryptedAttributes) + ).resolves.toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + // One call per attributes, we have 2 of them. + expect(mockNodeCrypto.decrypt).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoOne.decrypt).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoTwo.decrypt).not.toHaveBeenCalled(); + }); + + it('uses all available decryption only keys if needed', async () => { + const encryptedAttributes = await getService(decryptionOnlyCryptoTwo).encryptAttributes( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + await expect( + service.decryptAttributes({ type: 'known-type-1', id: 'object-id' }, encryptedAttributes) + ).resolves.toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + // One call per attributes, we have 2 of them. + expect(mockNodeCrypto.decrypt).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoOne.decrypt).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoTwo.decrypt).toHaveBeenCalledTimes(2); + }); + + it('does not use primary encryption key if `omitPrimaryEncryptionKey` is specified', async () => { + const encryptedAttributes = await getService(decryptionOnlyCryptoOne).encryptAttributes( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + await expect( + service.decryptAttributes({ type: 'known-type-1', id: 'object-id' }, encryptedAttributes, { + omitPrimaryEncryptionKey: true, + }) + ).resolves.toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + // One call per attributes, we have 2 of them. + expect(mockNodeCrypto.decrypt).not.toHaveBeenCalled(); + expect(decryptionOnlyCryptoOne.decrypt).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoTwo.decrypt).not.toHaveBeenCalled(); + }); + }); }); describe('#encryptAttributesSync', () => { @@ -957,11 +1106,11 @@ describe('#encryptAttributesSync', () => { (valueToEncrypt, aad) => `|${valueToEncrypt}|${aad}|` ); - service = new EncryptedSavedObjectsService( - mockNodeCrypto, - loggingSystemMock.create().get(), - mockAuditLogger - ); + service = new EncryptedSavedObjectsService({ + primaryCrypto: mockNodeCrypto, + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); }); it('does not encrypt attributes that are not supposed to be encrypted', () => { @@ -996,6 +1145,34 @@ describe('#encryptAttributesSync', () => { }); }); + it('encrypts only using primary crypto', async () => { + const attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }; + + const decryptionOnlyCrypto = createNodeCryptMock('some-key'); + service = new EncryptedSavedObjectsService({ + primaryCrypto: mockNodeCrypto, + decryptionOnlyCryptos: [decryptionOnlyCrypto], + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); + service.registerType({ + type: 'known-type-1', + attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']), + }); + + expect( + service.encryptAttributesSync({ type: 'known-type-1', id: 'object-id' }, attributes) + ).toEqual({ + attrOne: '|one|["known-type-1","object-id",{"attrTwo":"two"}]|', + attrTwo: 'two', + attrThree: '|three|["known-type-1","object-id",{"attrTwo":"two"}]|', + attrFour: null, + }); + + expect(decryptionOnlyCrypto.encrypt).not.toHaveBeenCalled(); + expect(decryptionOnlyCrypto.encryptSync).not.toHaveBeenCalled(); + }); + it('encrypts only attributes that are supposed to be encrypted even if not all provided', () => { const attributes = { attrTwo: 'two', attrThree: 'three' }; @@ -1459,11 +1636,11 @@ describe('#decryptAttributesSync', () => { }); it('fails if encrypted with another encryption key', () => { - service = new EncryptedSavedObjectsService( - nodeCrypto({ encryptionKey: 'encryption-key-abc*' }), - loggingSystemMock.create().get(), - mockAuditLogger - ); + service = new EncryptedSavedObjectsService({ + primaryCrypto: nodeCrypto({ encryptionKey: 'encryption-key-abc*' }), + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); service.registerType({ type: 'known-type-1', @@ -1478,4 +1655,132 @@ describe('#decryptAttributesSync', () => { ).toThrowError(EncryptionError); }); }); + + describe('with decryption only keys', () => { + function getService(primaryCrypto: Crypto, decryptionOnlyCryptos?: Readonly<Crypto[]>) { + const esoService = new EncryptedSavedObjectsService({ + primaryCrypto, + decryptionOnlyCryptos, + logger: loggingSystemMock.create().get(), + audit: mockAuditLogger, + }); + + esoService.registerType({ + type: 'known-type-1', + attributesToEncrypt: new Set(['attrOne', 'attrThree', 'attrFour']), + }); + + return esoService; + } + + const attributes = { attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }; + + let decryptionOnlyCryptoOne: jest.Mocked<Crypto>; + let decryptionOnlyCryptoTwo: jest.Mocked<Crypto>; + beforeEach(() => { + decryptionOnlyCryptoOne = createNodeCryptMock('old-key-one'); + decryptionOnlyCryptoTwo = createNodeCryptMock('old-key-two'); + + service = getService(mockNodeCrypto, [decryptionOnlyCryptoOne, decryptionOnlyCryptoTwo]); + }); + + it('does not use decryption only keys if we can decrypt using primary key', () => { + const encryptedAttributes = service.encryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + expect( + service.decryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + encryptedAttributes + ) + ).toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + expect(decryptionOnlyCryptoOne.decryptSync).not.toHaveBeenCalled(); + expect(decryptionOnlyCryptoTwo.decryptSync).not.toHaveBeenCalled(); + }); + + it('uses decryption only keys if cannot decrypt using primary key', () => { + const encryptedAttributes = getService(decryptionOnlyCryptoOne).encryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + expect( + service.decryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + encryptedAttributes + ) + ).toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + // One call per attributes, we have 2 of them. + expect(mockNodeCrypto.decryptSync).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoOne.decryptSync).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoTwo.decryptSync).not.toHaveBeenCalled(); + }); + + it('uses all available decryption only keys if needed', () => { + const encryptedAttributes = getService(decryptionOnlyCryptoTwo).encryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + expect( + service.decryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + encryptedAttributes + ) + ).toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + // One call per attributes, we have 2 of them. + expect(mockNodeCrypto.decryptSync).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoOne.decryptSync).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoTwo.decryptSync).toHaveBeenCalledTimes(2); + }); + + it('does not use primary encryption key if `omitPrimaryEncryptionKey` is specified', () => { + const encryptedAttributes = getService(decryptionOnlyCryptoOne).encryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + attributes + ); + + expect( + service.decryptAttributesSync( + { type: 'known-type-1', id: 'object-id' }, + encryptedAttributes, + { omitPrimaryEncryptionKey: true } + ) + ).toEqual({ attrOne: 'one', attrTwo: 'two', attrThree: 'three', attrFour: null }); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledTimes(1); + expect(mockAuditLogger.decryptAttributesSuccess).toHaveBeenCalledWith( + ['attrOne', 'attrThree'], + { type: 'known-type-1', id: 'object-id' }, + undefined + ); + + // One call per attributes, we have 2 of them. + expect(mockNodeCrypto.decryptSync).not.toHaveBeenCalled(); + expect(decryptionOnlyCryptoOne.decryptSync).toHaveBeenCalledTimes(2); + expect(decryptionOnlyCryptoTwo.decryptSync).not.toHaveBeenCalled(); + }); + }); }); diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts index 82d6bb9be15f67..1f1093a179538c 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts @@ -52,6 +52,38 @@ interface CommonParameters { user?: AuthenticatedUser; } +/** + * Describes parameters for the decrypt methods. + */ +interface DecryptParameters extends CommonParameters { + /** + * Indicates whether decryption should only be performed using secondary decryption-only keys. + */ + omitPrimaryEncryptionKey?: boolean; +} + +interface EncryptedSavedObjectsServiceOptions { + /** + * Service logger instance. + */ + logger: Logger; + + /** + * Audit logger instance. + */ + audit: EncryptedSavedObjectsAuditLogger; + + /** + * NodeCrypto instance used for both encryption and decryption. + */ + primaryCrypto: Crypto; + + /** + * NodeCrypto instances used ONLY for decryption (i.e. rotated encryption keys). + */ + decryptionOnlyCryptos?: Readonly<Crypto[]>; +} + /** * Utility function that gives array representation of the saved object descriptor respecting * optional `namespace` property. @@ -79,16 +111,7 @@ export class EncryptedSavedObjectsService { EncryptedSavedObjectAttributesDefinition > = new Map(); - /** - * @param crypto nodeCrypto instance. - * @param logger Ordinary logger instance. - * @param audit Audit logger instance. - */ - constructor( - private readonly crypto: Readonly<Crypto>, - private readonly logger: Logger, - private readonly audit: EncryptedSavedObjectsAuditLogger - ) {} + constructor(private readonly options: EncryptedSavedObjectsServiceOptions) {} /** * Registers saved object type as the one that contains attributes that should be encrypted. @@ -136,7 +159,7 @@ export class EncryptedSavedObjectsService { descriptor: SavedObjectDescriptor, attributes: T, originalAttributes?: T, - params?: CommonParameters + params?: DecryptParameters ) { const typeDefinition = this.typeDefinitions.get(descriptor.type); if (typeDefinition === undefined) { @@ -174,7 +197,7 @@ export class EncryptedSavedObjectsService { Object.fromEntries( Object.entries(attributes).filter(([key]) => !typeDefinition.shouldBeStripped(key)) ) as T, - { user: params?.user } + params ); } catch (err) { decryptionError = err; @@ -210,10 +233,10 @@ export class EncryptedSavedObjectsService { try { encryptedAttributes[attributeName] = (yield [attributeValue, encryptionAAD])!; } catch (err) { - this.logger.error( + this.options.logger.error( `Failed to encrypt "${attributeName}" attribute: ${err.message || err}` ); - this.audit.encryptAttributeFailure(attributeName, descriptor, params?.user); + this.options.audit.encryptAttributeFailure(attributeName, descriptor, params?.user); throw new EncryptionError( `Unable to encrypt attribute "${attributeName}"`, @@ -229,7 +252,7 @@ export class EncryptedSavedObjectsService { // not the case we should collect and log them to make troubleshooting easier. const encryptedAttributesKeys = Object.keys(encryptedAttributes); if (encryptedAttributesKeys.length !== typeDefinition.attributesToEncrypt.size) { - this.logger.debug( + this.options.logger.debug( `The following attributes of saved object "${descriptorToArray( descriptor )}" should have been encrypted: ${Array.from( @@ -242,7 +265,7 @@ export class EncryptedSavedObjectsService { return attributes; } - this.audit.encryptAttributesSuccess(encryptedAttributesKeys, descriptor, params?.user); + this.options.audit.encryptAttributesSuccess(encryptedAttributesKeys, descriptor, params?.user); return { ...attributes, @@ -270,7 +293,9 @@ export class EncryptedSavedObjectsService { while (!iteratorResult.done) { const [attributeValue, encryptionAAD] = iteratorResult.value; try { - iteratorResult = iterator.next(await this.crypto.encrypt(attributeValue, encryptionAAD)); + iteratorResult = iterator.next( + await this.options.primaryCrypto.encrypt(attributeValue, encryptionAAD) + ); } catch (err) { iterator.throw!(err); } @@ -299,7 +324,9 @@ export class EncryptedSavedObjectsService { while (!iteratorResult.done) { const [attributeValue, encryptionAAD] = iteratorResult.value; try { - iteratorResult = iterator.next(this.crypto.encryptSync(attributeValue, encryptionAAD)); + iteratorResult = iterator.next( + this.options.primaryCrypto.encryptSync(attributeValue, encryptionAAD) + ); } catch (err) { iterator.throw!(err); } @@ -321,19 +348,31 @@ export class EncryptedSavedObjectsService { public async decryptAttributes<T extends Record<string, unknown>>( descriptor: SavedObjectDescriptor, attributes: T, - params?: CommonParameters + params?: DecryptParameters ): Promise<T> { + const decrypters = this.getDecrypters(params?.omitPrimaryEncryptionKey); const iterator = this.attributesToDecryptIterator<T>(descriptor, attributes, params); let iteratorResult = iterator.next(); while (!iteratorResult.done) { const [attributeValue, encryptionAAD] = iteratorResult.value; - try { - iteratorResult = iterator.next( - (await this.crypto.decrypt(attributeValue, encryptionAAD)) as string - ); - } catch (err) { - iterator.throw!(err); + + let decryptionError; + for (const decrypter of decrypters) { + try { + iteratorResult = iterator.next(await decrypter.decrypt(attributeValue, encryptionAAD)); + decryptionError = undefined; + break; + } catch (err) { + // Remember the error thrown when we tried to decrypt with the primary key. + if (!decryptionError) { + decryptionError = err; + } + } + } + + if (decryptionError) { + iterator.throw!(decryptionError); } } @@ -353,17 +392,31 @@ export class EncryptedSavedObjectsService { public decryptAttributesSync<T extends Record<string, unknown>>( descriptor: SavedObjectDescriptor, attributes: T, - params?: CommonParameters + params?: DecryptParameters ): T { + const decrypters = this.getDecrypters(params?.omitPrimaryEncryptionKey); const iterator = this.attributesToDecryptIterator<T>(descriptor, attributes, params); let iteratorResult = iterator.next(); while (!iteratorResult.done) { const [attributeValue, encryptionAAD] = iteratorResult.value; - try { - iteratorResult = iterator.next(this.crypto.decryptSync(attributeValue, encryptionAAD)); - } catch (err) { - iterator.throw!(err); + + let decryptionError; + for (const decrypter of decrypters) { + try { + iteratorResult = iterator.next(decrypter.decryptSync(attributeValue, encryptionAAD)); + decryptionError = undefined; + break; + } catch (err) { + // Remember the error thrown when we tried to decrypt with the primary key. + if (!decryptionError) { + decryptionError = err; + } + } + } + + if (decryptionError) { + iterator.throw!(decryptionError); } } @@ -388,7 +441,7 @@ export class EncryptedSavedObjectsService { } if (typeof attributeValue !== 'string') { - this.audit.decryptAttributeFailure(attributeName, descriptor, params?.user); + this.options.audit.decryptAttributeFailure(attributeName, descriptor, params?.user); throw new Error( `Encrypted "${attributeName}" attribute should be a string, but found ${typeDetect( attributeValue @@ -401,8 +454,10 @@ export class EncryptedSavedObjectsService { try { decryptedAttributes[attributeName] = (yield [attributeValue, encryptionAAD])!; } catch (err) { - this.logger.error(`Failed to decrypt "${attributeName}" attribute: ${err.message || err}`); - this.audit.decryptAttributeFailure(attributeName, descriptor, params?.user); + this.options.logger.error( + `Failed to decrypt "${attributeName}" attribute: ${err.message || err}` + ); + this.options.audit.decryptAttributeFailure(attributeName, descriptor, params?.user); throw new EncryptionError( `Unable to decrypt attribute "${attributeName}"`, @@ -417,7 +472,7 @@ export class EncryptedSavedObjectsService { // not the case we should collect and log them to make troubleshooting easier. const decryptedAttributesKeys = Object.keys(decryptedAttributes); if (decryptedAttributesKeys.length !== typeDefinition.attributesToEncrypt.size) { - this.logger.debug( + this.options.logger.debug( `The following attributes of saved object "${descriptorToArray( descriptor )}" should have been decrypted: ${Array.from( @@ -430,7 +485,7 @@ export class EncryptedSavedObjectsService { return attributes; } - this.audit.decryptAttributesSuccess(decryptedAttributesKeys, descriptor, params?.user); + this.options.audit.decryptAttributesSuccess(decryptedAttributesKeys, descriptor, params?.user); return { ...attributes, @@ -459,7 +514,7 @@ export class EncryptedSavedObjectsService { } if (Object.keys(attributesAAD).length === 0) { - this.logger.debug( + this.options.logger.debug( `The AAD for saved object "${descriptorToArray( descriptor )}" does not include any attributes.` @@ -468,4 +523,23 @@ export class EncryptedSavedObjectsService { return stringify([...descriptorToArray(descriptor), attributesAAD]); } + + /** + * Returns list of NodeCrypto instances used for decryption. + * @param omitPrimaryEncryptionKey Specifies whether returned decrypters shouldn't include primary + * encryption/decryption crypto. + */ + private getDecrypters(omitPrimaryEncryptionKey?: boolean) { + if (omitPrimaryEncryptionKey) { + if (!this.options.decryptionOnlyCryptos || this.options.decryptionOnlyCryptos.length === 0) { + throw new Error( + `"omitPrimaryEncryptionKey" cannot be set when secondary keys aren't configured.` + ); + } + + return this.options.decryptionOnlyCryptos; + } + + return [this.options.primaryCrypto, ...(this.options.decryptionOnlyCryptos ?? [])]; + } } diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.mocks.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.mocks.ts new file mode 100644 index 00000000000000..2d14577f915676 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.mocks.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EncryptionKeyRotationService } from './encryption_key_rotation_service'; + +function createEncryptionKeyRotationServiceMock() { + return ({ rotate: jest.fn() } as unknown) as jest.Mocked<EncryptionKeyRotationService>; +} + +export const encryptionKeyRotationServiceMock = { + create: createEncryptionKeyRotationServiceMock, +}; diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.test.ts new file mode 100644 index 00000000000000..8607b81e7205ec --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.test.ts @@ -0,0 +1,502 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + SavedObject, + SavedObjectsClientContract, + SavedObjectsServiceStart, +} from '../../../../../src/core/server'; +import { EncryptionError, EncryptionErrorOperation } from './encryption_error'; +import { EncryptionKeyRotationService } from './encryption_key_rotation_service'; +import { EncryptedSavedObjectsService } from './encrypted_saved_objects_service'; + +import { + coreMock, + httpServerMock, + loggingSystemMock, + savedObjectsClientMock, + savedObjectsTypeRegistryMock, +} from '../../../../../src/core/server/mocks'; +import { encryptedSavedObjectsServiceMock } from './index.mock'; + +function getMockSavedObject(savedObject?: Partial<SavedObject<any>>) { + const id = savedObject?.id ?? `id-1`; + return { + id, + type: `type-${id}`, + references: [], + attributes: { attr: `attr-${id}` }, + score: 0, + ...savedObject, + }; +} + +let mockEncryptionService: jest.Mocked<EncryptedSavedObjectsService>; +let mockRetrieveClient: jest.Mocked<SavedObjectsClientContract>; +let mockUpdateClient: jest.Mocked<SavedObjectsClientContract>; +let mockSavedObjects: jest.Mocked<SavedObjectsServiceStart>; +let service: EncryptionKeyRotationService; +beforeEach(() => { + mockEncryptionService = encryptedSavedObjectsServiceMock.create(); + mockEncryptionService.isRegistered.mockImplementation( + (type) => type !== 'type-id-3' && type !== 'type-id-6' + ); + mockEncryptionService.decryptAttributes.mockImplementation(async (descriptor, { attr }) => ({ + attr: `decrypted-${attr}`, + })); + + const coreSetupMock = coreMock.createSetup(); + const coreStartMock = coreMock.createStart(); + coreSetupMock.getStartServices.mockResolvedValue([coreStartMock, {}, {}]); + + mockSavedObjects = coreStartMock.savedObjects; + const typeRegistryMock = savedObjectsTypeRegistryMock.create(); + typeRegistryMock.getAllTypes.mockReturnValue([ + { name: 'type-id-1', namespaceType: 'single', mappings: { properties: {} }, hidden: false }, + { name: 'type-id-2', namespaceType: 'single', mappings: { properties: {} }, hidden: true }, + { name: 'type-id-3', namespaceType: 'single', mappings: { properties: {} }, hidden: false }, + { name: 'type-id-4', namespaceType: 'multiple', mappings: { properties: {} }, hidden: true }, + { name: 'type-id-5', namespaceType: 'single', mappings: { properties: {} }, hidden: false }, + { name: 'type-id-6', namespaceType: 'single', mappings: { properties: {} }, hidden: true }, + ]); + typeRegistryMock.isSingleNamespace.mockImplementation((type) => type !== 'type-id-4'); + mockSavedObjects.getTypeRegistry.mockReturnValue(typeRegistryMock); + + mockRetrieveClient = savedObjectsClientMock.create(); + mockRetrieveClient.find.mockResolvedValue({ total: 0, saved_objects: [], per_page: 0, page: 0 }); + mockUpdateClient = savedObjectsClientMock.create(); + mockSavedObjects.getScopedClient.mockImplementation((request, params) => + params?.excludedWrappers?.[0] === 'encryptedSavedObjects' + ? mockRetrieveClient + : mockUpdateClient + ); + + service = new EncryptionKeyRotationService({ + logger: loggingSystemMock.create().get(), + service: mockEncryptionService, + getStartServices: coreSetupMock.getStartServices, + }); +}); + +it('correctly setups Saved Objects clients', async () => { + const mockRequest = httpServerMock.createKibanaRequest(); + await service.rotate(mockRequest, { batchSize: 10000 }); + + expect(mockSavedObjects.getScopedClient).toHaveBeenCalledTimes(2); + expect(mockSavedObjects.getScopedClient).toHaveBeenCalledWith(mockRequest, { + includedHiddenTypes: ['type-id-2', 'type-id-4'], + excludedWrappers: ['encryptedSavedObjects'], + }); + expect(mockSavedObjects.getScopedClient).toHaveBeenCalledWith(mockRequest, { + includedHiddenTypes: ['type-id-2', 'type-id-4'], + }); +}); + +it('bails out if specified type is not registered', async () => { + mockEncryptionService.isRegistered.mockImplementation((type) => type !== 'type-unknown'); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { + batchSize: 10000, + type: 'type-unknown', + }) + ).resolves.toEqual({ + total: 0, + successful: 0, + failed: 0, + }); + + expect(mockSavedObjects.getScopedClient).not.toHaveBeenCalled(); +}); + +it('does not perform rotation if there are no Saved Objects to process', async () => { + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 12345 }) + ).resolves.toEqual({ + total: 0, + successful: 0, + failed: 0, + }); + + expect(mockRetrieveClient.find).toHaveBeenCalledTimes(1); + expect(mockRetrieveClient.find).toHaveBeenCalledWith({ + type: ['type-id-1', 'type-id-2', 'type-id-4', 'type-id-5'], + perPage: 12345, + namespaces: ['*'], + sortField: 'updated_at', + sortOrder: 'asc', + }); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 54321, type: 'type-id-2' }) + ).resolves.toEqual({ + total: 0, + successful: 0, + failed: 0, + }); + + expect(mockRetrieveClient.find).toHaveBeenCalledTimes(2); + expect(mockRetrieveClient.find).toHaveBeenCalledWith({ + type: ['type-id-2'], + perPage: 54321, + namespaces: ['*'], + sortField: 'updated_at', + sortOrder: 'asc', + }); + + expect(mockEncryptionService.decryptAttributes).not.toHaveBeenCalled(); + expect(mockUpdateClient.bulkUpdate).not.toHaveBeenCalled(); +}); + +it('throws if Saved Object attributes cannot be decrypted because of unknown reason', async () => { + mockRetrieveClient.find.mockResolvedValue({ + total: 2, + saved_objects: [getMockSavedObject({ id: 'id-1' }), getMockSavedObject({ id: 'id-2' })], + per_page: 2, + page: 0, + }); + + const decryptionFailure = new Error('Oh no!'); + mockEncryptionService.decryptAttributes.mockRejectedValue(decryptionFailure); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 12345 }) + ).rejects.toBe(decryptionFailure); + + expect(mockUpdateClient.bulkUpdate).not.toHaveBeenCalled(); +}); + +it('does not perform rotation if Saved Object attributes cannot be decrypted', async () => { + mockRetrieveClient.find.mockResolvedValue({ + total: 2, + saved_objects: [getMockSavedObject({ id: 'id-1' }), getMockSavedObject({ id: 'id-2' })], + per_page: 2, + page: 0, + }); + + mockEncryptionService.decryptAttributes.mockRejectedValue( + new EncryptionError('some-message', 'attr', EncryptionErrorOperation.Decryption) + ); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 12345 }) + ).resolves.toEqual({ + total: 2, + successful: 0, + failed: 0, + }); + + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(2); + expect(mockUpdateClient.bulkUpdate).not.toHaveBeenCalled(); +}); + +it('properly rotates encryption key', async () => { + const savedObjects = [ + getMockSavedObject({ id: 'id-1' }), + getMockSavedObject({ id: 'id-2', namespaces: ['ns-1'] }), + getMockSavedObject({ id: 'id-4', namespaces: ['ns-2', 'ns-3'] }), + ]; + mockRetrieveClient.find.mockResolvedValue({ + total: 3, + saved_objects: savedObjects, + per_page: 3, + page: 0, + }); + mockUpdateClient.bulkUpdate.mockResolvedValue({ + saved_objects: savedObjects.map((object) => ({ ...object, attributes: {} })), + }); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 12345 }) + ).resolves.toEqual({ + total: 3, + successful: 3, + failed: 0, + }); + + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(3); + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledWith( + { type: 'type-id-1', id: 'id-1' }, + { attr: 'attr-id-1' }, + { omitPrimaryEncryptionKey: true } + ); + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledWith( + { type: 'type-id-2', id: 'id-2', namespace: 'ns-1' }, + { attr: 'attr-id-2' }, + { omitPrimaryEncryptionKey: true } + ); + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledWith( + { type: 'type-id-4', id: 'id-4' }, + { attr: 'attr-id-4' }, + { omitPrimaryEncryptionKey: true } + ); + + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledTimes(1); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith([ + { ...savedObjects[0], attributes: { attr: 'decrypted-attr-id-1' } }, + { ...savedObjects[1], namespace: 'ns-1', attributes: { attr: 'decrypted-attr-id-2' } }, + { ...savedObjects[2], namespace: 'ns-2', attributes: { attr: 'decrypted-attr-id-4' } }, + ]); +}); + +it('skips objects that cannot be decrypted', async () => { + const savedObjects = [ + getMockSavedObject({ id: 'id-1' }), + getMockSavedObject({ id: 'id-2', namespaces: ['ns-1'] }), + getMockSavedObject({ id: 'id-4', namespaces: ['ns-2', 'ns-3'] }), + ]; + mockRetrieveClient.find.mockResolvedValue({ + total: 3, + saved_objects: savedObjects, + per_page: 3, + page: 0, + }); + mockUpdateClient.bulkUpdate.mockResolvedValue({ + saved_objects: [ + { ...savedObjects[0], attributes: {} }, + { ...savedObjects[2], attributes: {} }, + ], + }); + + mockEncryptionService.decryptAttributes.mockImplementation(async ({ type }, { attr }) => { + if (type === 'type-id-2') { + throw new EncryptionError('some-message', 'attr', EncryptionErrorOperation.Decryption); + } + + return { attr: `decrypted-${attr}` }; + }); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 12345 }) + ).resolves.toEqual({ + total: 3, + successful: 2, + failed: 0, + }); + + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(3); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledTimes(1); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith([ + { ...savedObjects[0], attributes: { attr: 'decrypted-attr-id-1' } }, + { ...savedObjects[2], namespace: 'ns-2', attributes: { attr: 'decrypted-attr-id-4' } }, + ]); +}); + +it('marks object that we could not update as failed', async () => { + const savedObjects = [ + getMockSavedObject({ id: 'id-1' }), + getMockSavedObject({ id: 'id-2', namespaces: ['ns-1'] }), + getMockSavedObject({ id: 'id-4', namespaces: ['ns-2', 'ns-3'] }), + ]; + mockRetrieveClient.find.mockResolvedValue({ + total: 3, + saved_objects: savedObjects, + per_page: 3, + page: 0, + }); + mockUpdateClient.bulkUpdate.mockResolvedValue({ + saved_objects: [{ ...savedObjects[0], attributes: {} }, { error: new Error('Oh no!') } as any], + }); + + mockEncryptionService.decryptAttributes.mockImplementation(async ({ type }, { attr }) => { + if (type === 'type-id-2') { + throw new EncryptionError('some-message', 'attr', EncryptionErrorOperation.Decryption); + } + + return { attr: `decrypted-${attr}` }; + }); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 12345 }) + ).resolves.toEqual({ + total: 3, + successful: 1, + failed: 1, + }); + + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(3); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledTimes(1); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith([ + { ...savedObjects[0], attributes: { attr: 'decrypted-attr-id-1' } }, + { ...savedObjects[2], namespace: 'ns-2', attributes: { attr: 'decrypted-attr-id-4' } }, + ]); +}); + +it('iterates until number of returned results less than batch size', async () => { + const savedObjectsBatch0 = [ + getMockSavedObject({ id: 'id-1', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-2', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-3', type: 'type-id-1' }), + ]; + + const savedObjectsBatch1 = [ + getMockSavedObject({ id: 'id-4', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-5', type: 'type-id-1' }), + ]; + + // During first request we had 100 objects in total. + mockRetrieveClient.find.mockResolvedValueOnce({ + total: 100, + saved_objects: savedObjectsBatch0, + per_page: 3, + page: 0, + }); + mockUpdateClient.bulkUpdate.mockResolvedValueOnce({ + saved_objects: [ + { ...savedObjectsBatch0[0], attributes: {} }, + { ...savedObjectsBatch0[1], attributes: {} }, + { ...savedObjectsBatch0[2], attributes: {} }, + ], + }); + + // But when we fetch data for the second time we have just two objects left (e.g. they were removed). + mockRetrieveClient.find.mockResolvedValueOnce({ + total: 2, + saved_objects: savedObjectsBatch1, + per_page: 2, + page: 0, + }); + mockUpdateClient.bulkUpdate.mockResolvedValueOnce({ + saved_objects: [ + { ...savedObjectsBatch1[0], attributes: {} }, + { ...savedObjectsBatch1[1], attributes: {} }, + ], + }); + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 3 }) + ).resolves.toEqual({ + total: 100, + successful: 5, + failed: 0, + }); + + expect(mockRetrieveClient.find).toHaveBeenCalledTimes(2); + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(5); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledTimes(2); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith([ + { ...savedObjectsBatch0[0], attributes: { attr: 'decrypted-attr-id-1' } }, + { ...savedObjectsBatch0[1], attributes: { attr: 'decrypted-attr-id-2' } }, + { ...savedObjectsBatch0[2], attributes: { attr: 'decrypted-attr-id-3' } }, + ]); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith([ + { ...savedObjectsBatch1[0], attributes: { attr: 'decrypted-attr-id-4' } }, + { ...savedObjectsBatch1[1], attributes: { attr: 'decrypted-attr-id-5' } }, + ]); +}); + +it('iterates until no new objects are returned', async () => { + const savedObjectBatches = [ + [ + getMockSavedObject({ id: 'id-1', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-2', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-3', type: 'type-id-1' }), + ], + [ + getMockSavedObject({ id: 'id-4', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-5', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-6', type: 'type-id-1' }), + ], + [ + getMockSavedObject({ id: 'id-7', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-8', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-9', type: 'type-id-1' }), + ], + [ + getMockSavedObject({ id: 'id-1', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-2', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-3', type: 'type-id-1' }), + ], + ]; + + for (const batch of savedObjectBatches) { + mockRetrieveClient.find.mockResolvedValueOnce({ + total: 100, + saved_objects: batch, + per_page: 3, + page: 0, + }); + mockUpdateClient.bulkUpdate.mockResolvedValueOnce({ + saved_objects: batch.map((object) => ({ ...object, attributes: {} })), + }); + } + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 3 }) + ).resolves.toEqual({ + total: 100, + successful: 9, + failed: 0, + }); + + expect(mockRetrieveClient.find).toHaveBeenCalledTimes(4); + // We don't decrypt\update same object twice, so neither object from the last batch is decrypted or updated. + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(9); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledTimes(3); + for (const batch of savedObjectBatches.slice(0, 3)) { + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith( + batch.map((object) => ({ + ...object, + attributes: { attr: `decrypted-${object.attributes.attr}` }, + })) + ); + } +}); + +it('iterates until max number of batches is reached', async () => { + // Simulate the scenario when we're getting more records then was indicated by the `total` field + // returned with the first batch, and every such batch includes documents we haven't processed yet. + const savedObjectBatches = [ + [ + getMockSavedObject({ id: 'id-1', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-2', type: 'type-id-1' }), + ], + [ + getMockSavedObject({ id: 'id-3', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-4', type: 'type-id-1' }), + ], + [ + getMockSavedObject({ id: 'id-5', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-6', type: 'type-id-1' }), + ], + [ + getMockSavedObject({ id: 'id-7', type: 'type-id-1' }), + getMockSavedObject({ id: 'id-8', type: 'type-id-1' }), + ], + ]; + + for (const batch of savedObjectBatches) { + mockRetrieveClient.find.mockResolvedValueOnce({ + total: 3, + saved_objects: batch, + per_page: 2, + page: 0, + }); + mockUpdateClient.bulkUpdate.mockResolvedValueOnce({ + saved_objects: batch.map((object) => ({ ...object, attributes: {} })), + }); + } + + await expect( + service.rotate(httpServerMock.createKibanaRequest(), { batchSize: 2 }) + ).resolves.toEqual({ + total: 3, + successful: 6, + failed: 0, + }); + + expect(mockRetrieveClient.find).toHaveBeenCalledTimes(3); + expect(mockEncryptionService.decryptAttributes).toHaveBeenCalledTimes(6); + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledTimes(3); + for (const batch of savedObjectBatches.slice(0, 3)) { + expect(mockUpdateClient.bulkUpdate).toHaveBeenCalledWith( + batch.map((object) => ({ + ...object, + attributes: { attr: `decrypted-${object.attributes.attr}` }, + })) + ); + } +}); diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts new file mode 100644 index 00000000000000..fb1b6db45e762d --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encryption_key_rotation_service.ts @@ -0,0 +1,268 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + ISavedObjectTypeRegistry, + KibanaRequest, + Logger, + SavedObject, + SavedObjectsBulkUpdateObject, + StartServicesAccessor, +} from 'src/core/server'; +import { AuthenticatedUser, SecurityPluginSetup } from '../../../security/server'; +import { getDescriptorNamespace } from '../saved_objects/get_descriptor_namespace'; +import { EncryptedSavedObjectsService } from './encrypted_saved_objects_service'; +import { EncryptionError } from './encryption_error'; + +interface EncryptionKeyRotationServiceOptions { + logger: Logger; + service: PublicMethodsOf<EncryptedSavedObjectsService>; + getStartServices: StartServicesAccessor; + security?: SecurityPluginSetup; +} + +interface EncryptionKeyRotationParams { + /** + * The maximum number of the objects we fetch and process in one iteration. + */ + batchSize: number; + + /** + * Optionally allows to limit key rotation to only specified Saved Object type. + */ + type?: string; +} + +interface EncryptionKeyRotationResult { + /** + * The total number of the Saved Objects encrypted by the Encrypted Saved Objects plugin. + */ + total: number; + + /** + * The number of the Saved Objects that were still encrypted with one of the secondary encryption + * keys and were successfully re-encrypted with the primary key. + */ + successful: number; + + /** + * The number of the Saved Objects that were still encrypted with one of the secondary encryption + * keys that we failed to re-encrypt with the primary key. + */ + failed: number; +} + +/** + * Service that deals with encryption key rotation matters. + */ +export class EncryptionKeyRotationService { + constructor(private readonly options: EncryptionKeyRotationServiceOptions) {} + + public async rotate( + request: KibanaRequest, + { batchSize, type }: EncryptionKeyRotationParams + ): Promise<EncryptionKeyRotationResult> { + const [{ savedObjects }] = await this.options.getStartServices(); + const typeRegistry = savedObjects.getTypeRegistry(); + + // We need to retrieve all SavedObject types which have encrypted attributes, specifically + // collecting those that are hidden as they are ignored by the Saved Objects client by default. + this.options.logger.debug('Retrieving Saved Object types that require encryption.'); + const registeredSavedObjectTypes = []; + const registeredHiddenSavedObjectTypes = []; + for (const knownType of typeRegistry.getAllTypes()) { + if (this.options.service.isRegistered(knownType.name) && (!type || knownType.name === type)) { + registeredSavedObjectTypes.push(knownType.name); + + if (knownType.hidden) { + registeredHiddenSavedObjectTypes.push(knownType.name); + } + } + } + + const result = { total: 0, successful: 0, failed: 0 }; + if (registeredSavedObjectTypes.length === 0) { + this.options.logger.info( + type + ? `Saved Object type "${type}" is not registered, encryption key rotation is not needed.` + : 'There are no registered Saved Object types that can have encrypted attributes, encryption key rotation is not needed.' + ); + return result; + } + + this.options.logger.info( + `Saved Objects with the following types [${registeredSavedObjectTypes}] will be processed.` + ); + + // We need two separate Saved Objects clients for the retrieval and update. For retrieval we + // don't want to have Encrypted Saved Objects wrapper so that it doesn't strip encrypted + // attributes. But for the update we want to have it so that it automatically re-encrypts + // attributes with the new primary encryption key. + const user = this.options.security?.authc.getCurrentUser(request) ?? undefined; + const retrieveClient = savedObjects.getScopedClient(request, { + includedHiddenTypes: registeredHiddenSavedObjectTypes, + excludedWrappers: ['encryptedSavedObjects'], + }); + const updateClient = savedObjects.getScopedClient(request, { + includedHiddenTypes: registeredHiddenSavedObjectTypes, + }); + + // Keeps track of object IDs that have been processed already. + const processedObjectIDs = new Set<string>(); + + // Until we get scroll/search_after support in Saved Objects client we have to retrieve as much objects as allowed + // by the `batchSize` parameter. Instead of using paging functionality (size/from or page/perPage parameters) that + // has certain performance issues and is also limited by the maximum result window setting on .kibana index + // (10,000 by default) we always fetch the first page of the results sorted by the `updated_at` field. This way we + // can prioritize "old" objects that have a higher chance to have been encrypted with the old encryption keys, since + // all newly created or updated objects are always encrypted with the current primary key. Re-encryption of the + // "old" objects with the primary key implicitly bumps up their `updated_at` field so that these objects won't be + // included into the first page of the results during next iteration. Additionally we track IDs of all processed + // objects so that eventually we can detect that first page consists of only objects encrypted with the current + // primary key and stop iterating. + // + // LIMITATION: if we have a lot of "old" objects encrypted with the _unknown_ encryption key it may either + // significantly slow down rotation or prevent it from happening completely since such objects will be included into + // every batch we fetch and if their number is equal to or greater than `batchSize` we won't be able to process any + // object. Another and more complex case when we can be hit by this limitation is when users have multiple Kibana + // instances configured with different primary encryption keys, these time even "new" objects may require rotation, + // but they may be included into 2+ page of the results. We can potentially detect such cases and issue a warning, + // but it's not an easy task: if we detect a case when none of the objects from the very first batch cannot be + // decrypted with the decryption only keys we'll need to check how many of them can be decrypted at all using all + // available keys including the current primary one. + // + // Also theoretically if `batchSize` is less than `index.max_result_window` we could try to rely on the paging + // functionality and switch to the second page, but the issue here is that objects can be deleted in the meantime + // so that unprocessed objects may get into the first page and we'll miss them. We can of course oscillate between + // the first and the second pages or do multiple rotation passes, but it'd complicate code significantly. + let batch = 0; + let maxBatches = 0; + while (true) { + this.options.logger.debug(`Fetching ${batchSize} objects (batch #${batch}).`); + const savedObjectsToDecrypt = await retrieveClient.find({ + type: registeredSavedObjectTypes, + perPage: batchSize, + namespaces: ['*'], + sortField: 'updated_at', + sortOrder: 'asc', + }); + + // We use `total` only from the first batch just as an approximate indicator for the consumer since total number + // can change from batch to batch, but it won't affect the actual processing logic. + if (batch === 0) { + this.options.logger.debug(`Found ${savedObjectsToDecrypt.total} objects.`); + result.total = savedObjectsToDecrypt.total; + // Since we process live data there is a theoretical chance that we may be getting new + // objects in every batch effectively making this loop infinite. To prevent this we want to + // limit a number of batches we process during single rotation request giving enough room + // for the Saved Objects occasionally created during rotation. + maxBatches = Math.ceil((savedObjectsToDecrypt.total * 2) / batchSize); + } + + this.options.logger.debug( + `Decrypting ${savedObjectsToDecrypt.saved_objects.length} objects (batch #${batch}).` + ); + const savedObjectsToEncrypt = await this.getSavedObjectsToReEncrypt( + savedObjectsToDecrypt.saved_objects, + processedObjectIDs, + typeRegistry, + user + ); + if (savedObjectsToEncrypt.length === 0) { + break; + } + + this.options.logger.debug( + `Re-encrypting ${savedObjectsToEncrypt.length} objects (batch #${batch}).` + ); + try { + const succeeded = ( + await updateClient.bulkUpdate(savedObjectsToEncrypt) + ).saved_objects.filter((savedObject) => !savedObject.error).length; + + this.options.logger.debug( + `Successfully re-encrypted ${succeeded} out of ${savedObjectsToEncrypt.length} objects (batch #${batch}).` + ); + + result.successful += succeeded; + result.failed += savedObjectsToEncrypt.length - succeeded; + } catch (err) { + this.options.logger.error( + `Failed to re-encrypt saved objects (batch #${batch}): ${err.message}` + ); + result.failed += savedObjectsToEncrypt.length; + } + + if (savedObjectsToDecrypt.total <= batchSize || ++batch >= maxBatches) { + break; + } + } + + this.options.logger.info( + `Encryption key rotation is completed. ${result.successful} objects out ouf ${result.total} were successfully re-encrypted with the primary encryption key and ${result.failed} objects failed.` + ); + + return result; + } + + /** + * Takes a list of Saved Objects and tries to decrypt their attributes with the secondary encryption + * keys, silently skipping those that cannot be decrypted. The objects that were decrypted with the + * decryption-only keys will be returned and grouped by the namespace. + * @param savedObjects Saved Objects to decrypt attributes for. + * @param processedObjectIDs Set of Saved Object IDs that were already processed. + * @param typeRegistry Saved Objects type registry. + * @param user The user that initiated decryption. + */ + private async getSavedObjectsToReEncrypt( + savedObjects: SavedObject[], + processedObjectIDs: Set<string>, + typeRegistry: ISavedObjectTypeRegistry, + user?: AuthenticatedUser + ) { + const decryptedSavedObjects: SavedObjectsBulkUpdateObject[] = []; + for (const savedObject of savedObjects) { + // We shouldn't process objects that we already processed during previous iterations. + if (processedObjectIDs.has(savedObject.id)) { + continue; + } else { + processedObjectIDs.add(savedObject.id); + } + + let decryptedAttributes; + try { + decryptedAttributes = await this.options.service.decryptAttributes( + { + type: savedObject.type, + id: savedObject.id, + namespace: getDescriptorNamespace( + typeRegistry, + savedObject.type, + savedObject.namespaces + ), + }, + savedObject.attributes as Record<string, unknown>, + { omitPrimaryEncryptionKey: true, user } + ); + } catch (err) { + if (!(err instanceof EncryptionError)) { + throw err; + } + + continue; + } + + decryptedSavedObjects.push({ + ...savedObject, + attributes: decryptedAttributes, + // `bulkUpdate` expects objects with a single `namespace`. + namespace: savedObject.namespaces?.[0], + }); + } + + return decryptedSavedObjects; + } +} diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/index.mock.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.mock.ts index 3e4983deca6255..4410cbac7beb90 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/index.mock.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.mock.ts @@ -5,3 +5,4 @@ */ export { encryptedSavedObjectsServiceMock } from './encrypted_saved_objects_service.mocks'; +export { encryptionKeyRotationServiceMock } from './encryption_key_rotation_service.mocks'; diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts index 75445bd24eba84..ff5e5fdc010596 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/index.ts @@ -12,3 +12,4 @@ export { } from './encrypted_saved_objects_service'; export { EncryptionError } from './encryption_error'; export { EncryptedSavedObjectAttributesDefinition } from './encrypted_saved_object_type_definition'; +export { EncryptionKeyRotationService } from './encryption_key_rotation_service'; diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts index 57108954f2568d..8d8f1a51f68021 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts @@ -5,6 +5,7 @@ */ import { Plugin } from './plugin'; +import { ConfigSchema } from './config'; import { coreMock } from 'src/core/server/mocks'; import { securityMock } from '../../security/server/mocks'; @@ -12,7 +13,9 @@ import { securityMock } from '../../security/server/mocks'; describe('EncryptedSavedObjects Plugin', () => { describe('setup()', () => { it('exposes proper contract', async () => { - const plugin = new Plugin(coreMock.createPluginInitializerContext()); + const plugin = new Plugin( + coreMock.createPluginInitializerContext(ConfigSchema.validate({}, { dist: true })) + ); await expect(plugin.setup(coreMock.createSetup(), { security: securityMock.createSetup() })) .resolves.toMatchInlineSnapshot(` Object { @@ -26,7 +29,9 @@ describe('EncryptedSavedObjects Plugin', () => { describe('start()', () => { it('exposes proper contract', async () => { - const plugin = new Plugin(coreMock.createPluginInitializerContext()); + const plugin = new Plugin( + coreMock.createPluginInitializerContext(ConfigSchema.validate({}, { dist: true })) + ); await plugin.setup(coreMock.createSetup(), { security: securityMock.createSetup() }); const startContract = plugin.start(); diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts index 69777798ddf192..6e3724fa3fe588 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts @@ -4,19 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ +import { first, map } from 'rxjs/operators'; import nodeCrypto from '@elastic/node-crypto'; import { Logger, PluginInitializerContext, CoreSetup } from 'src/core/server'; -import { first } from 'rxjs/operators'; +import { TypeOf } from '@kbn/config-schema'; import { SecurityPluginSetup } from '../../security/server'; -import { createConfig$ } from './config'; +import { createConfig, ConfigSchema } from './config'; import { EncryptedSavedObjectsService, EncryptedSavedObjectTypeRegistration, EncryptionError, + EncryptionKeyRotationService, } from './crypto'; import { EncryptedSavedObjectsAuditLogger } from './audit'; import { setupSavedObjects, ClientInstanciator } from './saved_objects'; import { getCreateMigration, CreateEncryptedSavedObjectsMigrationFn } from './create_migration'; +import { defineRoutes } from './routes'; export interface PluginsSetup { security?: SecurityPluginSetup; @@ -48,18 +51,29 @@ export class Plugin { core: CoreSetup, deps: PluginsSetup ): Promise<EncryptedSavedObjectsPluginSetup> { - const { - config: { encryptionKey }, - usingEphemeralEncryptionKey, - } = await createConfig$(this.initializerContext).pipe(first()).toPromise(); - - const crypto = nodeCrypto({ encryptionKey }); - + const config = await this.initializerContext.config + .create<TypeOf<typeof ConfigSchema>>() + .pipe( + map((rawConfig) => createConfig(rawConfig, this.initializerContext.logger.get('config'))) + ) + .pipe(first()) + .toPromise(); const auditLogger = new EncryptedSavedObjectsAuditLogger( deps.security?.audit.getLogger('encryptedSavedObjects') ); + + const primaryCrypto = nodeCrypto({ encryptionKey: config.encryptionKey }); + const decryptionOnlyCryptos = config.keyRotation.decryptionOnlyKeys.map((decryptionKey) => + nodeCrypto({ encryptionKey: decryptionKey }) + ); + const service = Object.freeze( - new EncryptedSavedObjectsService(crypto, this.logger, auditLogger) + new EncryptedSavedObjectsService({ + primaryCrypto, + decryptionOnlyCryptos, + logger: this.logger, + audit: auditLogger, + }) ); this.savedObjectsSetup = setupSavedObjects({ @@ -69,18 +83,33 @@ export class Plugin { getStartServices: core.getStartServices, }); + defineRoutes({ + router: core.http.createRouter(), + logger: this.initializerContext.logger.get('routes'), + encryptionKeyRotationService: Object.freeze( + new EncryptionKeyRotationService({ + logger: this.logger.get('key-rotation-service'), + service, + getStartServices: core.getStartServices, + security: deps.security, + }) + ), + config, + }); + return { registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => service.registerType(typeRegistration), - usingEphemeralEncryptionKey, + usingEphemeralEncryptionKey: config.usingEphemeralEncryptionKey, createMigration: getCreateMigration( service, (typeRegistration: EncryptedSavedObjectTypeRegistration) => { - const serviceForMigration = new EncryptedSavedObjectsService( - crypto, - this.logger, - auditLogger - ); + const serviceForMigration = new EncryptedSavedObjectsService({ + primaryCrypto, + decryptionOnlyCryptos, + logger: this.logger, + audit: auditLogger, + }); serviceForMigration.registerType(typeRegistration); return serviceForMigration; } diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.ts new file mode 100644 index 00000000000000..b3d54c7f1ecac9 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/index.mock.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ConfigSchema, createConfig } from '../config'; + +import { httpServiceMock, loggingSystemMock } from '../../../../../src/core/server/mocks'; +import { encryptionKeyRotationServiceMock } from '../crypto/index.mock'; + +export const routeDefinitionParamsMock = { + create: (config: Record<string, unknown> = {}) => ({ + router: httpServiceMock.createRouter(), + logger: loggingSystemMock.create().get(), + config: createConfig(ConfigSchema.validate(config), loggingSystemMock.create().get()), + encryptionKeyRotationService: encryptionKeyRotationServiceMock.create(), + }), +}; diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/index.ts new file mode 100644 index 00000000000000..72af8060de8270 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/index.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { IRouter, Logger } from '../../../../../src/core/server'; +import { ConfigType } from '../config'; +import { EncryptionKeyRotationService } from '../crypto'; + +import { defineKeyRotationRoutes } from './key_rotation'; + +/** + * Describes parameters used to define HTTP routes. + */ +export interface RouteDefinitionParams { + router: IRouter; + logger: Logger; + config: ConfigType; + encryptionKeyRotationService: PublicMethodsOf<EncryptionKeyRotationService>; +} + +export function defineRoutes(params: RouteDefinitionParams) { + defineKeyRotationRoutes(params); +} diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts new file mode 100644 index 00000000000000..ced4dda48fcd2a --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Type } from '@kbn/config-schema'; +import { + IRouter, + kibanaResponseFactory, + RequestHandler, + RequestHandlerContext, + RouteConfig, +} from '../../../../../src/core/server'; +import { defineKeyRotationRoutes } from './key_rotation'; + +import { httpServerMock } from '../../../../../src/core/server/mocks'; +import { routeDefinitionParamsMock } from './index.mock'; +import { EncryptionKeyRotationService } from '../crypto'; + +describe('Key rotation routes', () => { + let router: jest.Mocked<IRouter>; + let mockContext: RequestHandlerContext; + let mockEncryptionKeyRotationService: jest.Mocked<EncryptionKeyRotationService>; + beforeEach(() => { + const routeParamsMock = routeDefinitionParamsMock.create({ + keyRotation: { decryptionOnlyKeys: ['b'.repeat(32)] }, + }); + router = routeParamsMock.router; + mockEncryptionKeyRotationService = routeParamsMock.encryptionKeyRotationService; + + mockContext = ({} as unknown) as RequestHandlerContext; + + defineKeyRotationRoutes(routeParamsMock); + }); + + describe('rotate', () => { + let routeHandler: RequestHandler<any, any, any>; + let routeConfig: RouteConfig<any, any, any, any>; + beforeEach(() => { + const [rotateRouteConfig, rotateRouteHandler] = router.post.mock.calls.find( + ([{ path }]) => path === '/api/encrypted_saved_objects/_rotate_key' + )!; + + routeConfig = rotateRouteConfig; + routeHandler = rotateRouteHandler; + }); + + it('correctly defines route.', () => { + expect(routeConfig.options).toEqual({ tags: ['access:rotateEncryptionKey'] }); + expect(routeConfig.validate).toEqual({ + body: undefined, + query: expect.any(Type), + params: undefined, + }); + + const queryValidator = (routeConfig.validate as any).query as Type<any>; + expect( + queryValidator.validate({ + batchSize: 100, + type: 'some-type', + }) + ).toEqual({ + batchSize: 100, + type: 'some-type', + }); + expect(queryValidator.validate({ batchSize: 1 })).toEqual({ batchSize: 1 }); + expect(queryValidator.validate({ batchSize: 10000 })).toEqual({ batchSize: 10000 }); + expect(queryValidator.validate({})).toEqual({ batchSize: 10000 }); + + expect(() => queryValidator.validate({ batchSize: 0 })).toThrowErrorMatchingInlineSnapshot( + `"[batchSize]: Value must be equal to or greater than [1]."` + ); + expect(() => + queryValidator.validate({ batchSize: 10001 }) + ).toThrowErrorMatchingInlineSnapshot( + `"[batchSize]: Value must be equal to or lower than [10000]."` + ); + + expect(() => queryValidator.validate({ type: 100 })).toThrowErrorMatchingInlineSnapshot( + `"[type]: expected value of type [string] but got [number]"` + ); + }); + + it('returns 400 if decryption only keys are not specified.', async () => { + const routeParamsMock = routeDefinitionParamsMock.create(); + defineKeyRotationRoutes(routeParamsMock); + const [, rotateRouteHandler] = routeParamsMock.router.post.mock.calls.find( + ([{ path }]) => path === '/api/encrypted_saved_objects/_rotate_key' + )!; + + await expect( + rotateRouteHandler(mockContext, httpServerMock.createKibanaRequest(), kibanaResponseFactory) + ).resolves.toEqual({ + status: 400, + payload: + 'Kibana is not configured to support encryption key rotation. Update `kibana.yml` to include `xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys` to rotate your encryption keys.', + options: { + body: + 'Kibana is not configured to support encryption key rotation. Update `kibana.yml` to include `xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys` to rotate your encryption keys.', + }, + }); + }); + + it('returns 500 if `rotate` throws unhandled exception.', async () => { + const unhandledException = new Error('Something went wrong.'); + mockEncryptionKeyRotationService.rotate.mockRejectedValue(unhandledException); + + const mockRequest = httpServerMock.createKibanaRequest({ query: { batchSize: 1234 } }); + const response = await routeHandler(mockContext, mockRequest, kibanaResponseFactory); + + expect(response.status).toBe(500); + expect(response.payload).toEqual(unhandledException); + expect(mockEncryptionKeyRotationService.rotate).toHaveBeenCalledWith(mockRequest, { + batchSize: 1234, + }); + }); + + it('returns whatever `rotate` returns.', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ query: { batchSize: 1234 } }); + mockEncryptionKeyRotationService.rotate.mockResolvedValue({ + total: 3, + successful: 6, + failed: 0, + }); + + await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ + status: 200, + payload: { total: 3, successful: 6, failed: 0 }, + options: { body: { total: 3, successful: 6, failed: 0 } }, + }); + }); + + it('returns 429 if called while rotation is in progress.', async () => { + const mockRequest = httpServerMock.createKibanaRequest({ query: { batchSize: 1234 } }); + mockEncryptionKeyRotationService.rotate.mockResolvedValue({ + total: 3, + successful: 6, + failed: 0, + }); + + // Run rotation, but don't wait until it's complete. + const firstRequestPromise = routeHandler(mockContext, mockRequest, kibanaResponseFactory); + + // Try to run rotation once again. + await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ + status: 429, + payload: + 'Encryption key rotation is in progress already. Please wait until it is completed and try again.', + options: { + statusCode: 429, + body: + 'Encryption key rotation is in progress already. Please wait until it is completed and try again.', + }, + }); + + // Initial request properly resolves. + await expect(firstRequestPromise).resolves.toEqual({ + status: 200, + payload: { total: 3, successful: 6, failed: 0 }, + options: { body: { total: 3, successful: 6, failed: 0 } }, + }); + + // And subsequent requests resolve properly too. + await expect(routeHandler(mockContext, mockRequest, kibanaResponseFactory)).resolves.toEqual({ + status: 200, + payload: { total: 3, successful: 6, failed: 0 }, + options: { body: { total: 3, successful: 6, failed: 0 } }, + }); + }); + }); +}); diff --git a/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts new file mode 100644 index 00000000000000..48b29387106ee3 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/routes/key_rotation.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { RouteDefinitionParams } from '.'; + +/** + * The default maximum value of from + size for searches to .kibana index. Since we cannot use scroll + * or search_after functionality with the .kibana index we limit maximum batch size with this value. + */ +const DEFAULT_MAX_RESULT_WINDOW = 10000; + +/** + * Defines routes that are used for encryption key rotation. + */ +export function defineKeyRotationRoutes({ + encryptionKeyRotationService, + router, + logger, + config, +}: RouteDefinitionParams) { + let rotationInProgress = false; + router.post( + { + path: '/api/encrypted_saved_objects/_rotate_key', + validate: { + query: schema.object({ + batchSize: schema.number({ + min: 1, + max: DEFAULT_MAX_RESULT_WINDOW, + defaultValue: DEFAULT_MAX_RESULT_WINDOW, + }), + type: schema.maybe(schema.string()), + }), + }, + options: { + tags: ['access:rotateEncryptionKey'], + }, + }, + async (context, request, response) => { + if (config.keyRotation.decryptionOnlyKeys.length === 0) { + return response.badRequest({ + body: + 'Kibana is not configured to support encryption key rotation. Update `kibana.yml` to include `xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys` to rotate your encryption keys.', + }); + } + + if (rotationInProgress) { + return response.customError({ + body: + 'Encryption key rotation is in progress already. Please wait until it is completed and try again.', + statusCode: 429, + }); + } + + rotationInProgress = true; + try { + return response.ok({ + body: await encryptionKeyRotationService.rotate(request, { + batchSize: request.query.batchSize, + type: request.query.type, + }), + }); + } catch (err) { + logger.error(err); + return response.customError({ body: err, statusCode: 500 }); + } finally { + rotationInProgress = false; + } + } + ); +} diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.test.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.test.ts index 7ba90a5a76ab32..33ea1d8c3acec8 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.test.ts @@ -66,5 +66,16 @@ describe('getDescriptorNamespace', () => { 'foo-namespace' ); }); + + it('returns the provided namespace if it is in array format', () => { + const mockBaseTypeRegistry = savedObjectsTypeRegistryMock.create(); + mockBaseTypeRegistry.isSingleNamespace.mockReturnValue(true); + mockBaseTypeRegistry.isMultiNamespace.mockReturnValue(false); + mockBaseTypeRegistry.isNamespaceAgnostic.mockReturnValue(false); + + expect(getDescriptorNamespace(mockBaseTypeRegistry, 'singletype', ['foo-namespace'])).toEqual( + 'foo-namespace' + ); + }); }); }); diff --git a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts index 7201f13fb930b6..7c237b82cbb151 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/get_descriptor_namespace.ts @@ -9,9 +9,13 @@ import { ISavedObjectTypeRegistry, SavedObjectsUtils } from '../../../../../src/ export const getDescriptorNamespace = ( typeRegistry: ISavedObjectTypeRegistry, type: string, - namespace?: string + namespace?: string | string[] ) => { - const descriptorNamespace = typeRegistry.isSingleNamespace(type) ? namespace : undefined; + const descriptorNamespace = typeRegistry.isSingleNamespace(type) + ? Array.isArray(namespace) + ? namespace[0] + : namespace + : undefined; return normalizeNamespace(descriptorNamespace); }; diff --git a/x-pack/test/encrypted_saved_objects_api_integration/config.ts b/x-pack/test/encrypted_saved_objects_api_integration/config.ts index f061a38b72ce6d..8f1aff337a3de7 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/config.ts +++ b/x-pack/test/encrypted_saved_objects_api_integration/config.ts @@ -27,6 +27,10 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { serverArgs: [ ...xPackAPITestsConfig.get('kbnTestServer.serverArgs'), '--xpack.encryptedSavedObjects.encryptionKey="wuGNaIhoMpk5sO4UBxgr3NyW1sFcLgIf"', + `--xpack.encryptedSavedObjects.keyRotation.decryptionOnlyKeys=${JSON.stringify([ + 'a'.repeat(32), + 'b'.repeat(32), + ])}`, `--plugin-path=${path.resolve(__dirname, './fixtures/api_consumer_plugin')}`, ], }, diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/data.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/data.json new file mode 100644 index 00000000000000..a98bf5ca8bd1bd --- /dev/null +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/data.json @@ -0,0 +1,186 @@ +{ + "type": "doc", + "value": { + "id": "config:8.0.0", + "index": ".kibana_1", + "source": { + "config": { + "buildNum": 9007199254740991 + }, + "migrationVersion": { + "config": "7.9.0" + }, + "references": [ + ], + "type": "config", + "updated_at": "2020-06-17T15:03:14.532Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "space:default", + "index": ".kibana_1", + "source": { + "migrationVersion": { + "space": "6.6.0" + }, + "references": [ + ], + "space": { + "_reserved": true, + "color": "#00bfb3", + "description": "This is your default space!", + "disabledFeatures": [ + ], + "name": "Default" + }, + "type": "space", + "updated_at": "2020-06-17T15:03:27.426Z" + } + } +} + +{ + "type": "doc", + "value": { + "id" : "space:a", + "index" : ".kibana_1", + "source" : { + "space" : { + "name" : "a", + "disabledFeatures" : [ ] + }, + "type" : "space", + "references" : [ ], + "migrationVersion" : { + "space" : "6.6.0" + }, + "updated_at" : "2020-10-01T06:13:23.484Z" + } + } +} + +{ + "type": "doc", + "value": { + "id": "saved-object-with-secret:cd9272b2-6a15-4295-bb7b-15f6347e267b", + "index": ".kibana_1", + "source": { + "saved-object-with-secret": { + "publicProperty": "some-public-property-0", + "publicPropertyExcludedFromAAD": "some-public-property-excluded-from-aad-0", + "publicPropertyStoredEncrypted": "DNrREm+906YK6Dt3P9NzBmZ+pSYmNYd7dyxw5Q97lIquROGRgqC84EW2/B2eVNfUhYYYcFlvtDrdZQXYmTmKNfZ5z8/hSwhRIXEhGPYiBplm1TiOQNp/P31FLp4l26YKt/ygVjQY/QFVWhDoA5sd+58TtzhxTtzx2FythwYgW6jJNQ==", + "privateProperty": "BdPvHwNJGgrXHQkSwKPJAtAvBMgfbapmSkthDjFeuoT7kJVfVujxvIZ47ZrUTfXOj1S67yx8R14A0B1VMdk6mITEeQpnIKgRbtqrevqzU522oTKdRZ/D99DL3ZEymmXMKqzafXwNny4pBZY4IMJsP4TRiMr2" + }, + "type": "saved-object-with-secret", + "references": [], + "updated_at": "2020-10-01T06:13:47.268Z" + } + } +} + +{ + "type": "doc", + "value": { + "id" : "saved-object-with-secret-and-multiple-spaces:6b388d9b-6d8f-4e20-ba38-d9477370729d", + "index" : ".kibana_1", + "source" : { + "saved-object-with-secret-and-multiple-spaces" : { + "publicProperty" : "some-public-property-0", + "publicPropertyExcludedFromAAD" : "some-public-property-excluded-from-aad-0", + "publicPropertyStoredEncrypted" : "iA3mnPhS4k1I9SOhguDGb52dq/VsFNlNepxdpAdjXUeHyxV9tXkdX98wDRwSMRgD5esGcdaA8A/QdbAP/pSUnGwMXNAAwmyVrDlC/vAPrZStm/nNVhI6ibl+ItjBEGmV0sueZhU49K8uctrrnctWcSk4u0ZFijQbMvN53Obgeg7p9g==", + "privateProperty" : "oAvCdNvgs4B/BJT5Rc6pNM1uSZ9EeMbjQPK10jAFWQE+hheZHpbXjTqaYxStZC+L/kr5ahRtWyWmOguissMLanx8vhfUWMvpZTxR8VG5rTgnXJ/XNwyRWsBCiIzpRmQHyBQgZJ84jeh/kaRskM1atv+Rm8Sc" + }, + "type" : "saved-object-with-secret-and-multiple-spaces", + "references" : [ ], + "namespaces" : [ + "default", + "a" + ], + "updated_at" : "2020-10-01T06:14:33.842Z" + } + } +} + +{ + "type": "doc", + "value": { + "id" : "hidden-saved-object-with-secret:8d90daef-f992-443c-ab78-053c6a7b0e9c", + "index" : ".kibana_1", + "source" : { + "hidden-saved-object-with-secret" : { + "publicProperty" : "some-public-property-0", + "publicPropertyExcludedFromAAD" : "some-public-property-excluded-from-aad-0", + "publicPropertyStoredEncrypted" : "raJcA4T4lOKuNMFOQC26dp6ToA7iBhUURh7ep4E6ZWGBbot/dpAvjccVBqYcLNCk7Qub2MKY2w7B4dhibJoVaBks8bl5EesKcxFIQ2llU+t8rtKk+x08kB5pOl2IPk4jXkNgkv/zZvwKMM+ot2lE4JG8IKbmuJmrbjmKbGLglIs2mg==", + "privateProperty" : "rYnHH52YWJWPv1jJ++oQAzVK2UUVJr6LSn+IU1LszAzhE3+pC7uAnHnW5KzOqaKtf71Gtc+yxwqu0w8Pnh1/aZqE7GVNVbRYAKDoJS0rpG/0SQXH8Inwl0Q+pTle6UJUvfS3PnwBVBkHGCtIiX4Oo+jXZIku" + }, + "type" : "hidden-saved-object-with-secret", + "references" : [ ], + "updated_at" : "2020-10-01T06:26:55.367Z" + } + } +} + +{ + "type": "doc", + "value": { + "id" : "saved-object-with-secret:4336f782-450f-4142-aecd-b46ed092af52", + "index" : ".kibana_1", + "source" : { + "saved-object-with-secret" : { + "publicProperty" : "some-public-property-0", + "publicPropertyExcludedFromAAD" : "some-public-property-excluded-from-aad-0", + "publicPropertyStoredEncrypted" : "ZzRNcMUBDo59TxZxhfvsIx5fNdwHKUXuqBzRJTuGCeiHzTETxX5/uM/L8oUfKRW4BqslRrqnbKMoew6v3dmd+8q5M1OGikX+hDLF06enuZTJi2OD8rcvvBds716hVxoE6aZvWQ4oybO06617wCPwaI2JUV6peA4/4Tz4wnony3TbCA==", + "privateProperty" : "NhxZiOueUmizr9QOxnJqgUIkQvM7NbARU9cGl7J6aoB5BZPQRGASHekFHfyy2PAiQX7bAVOo/C4XYxSgAmgzs6sSK4SqMKljZzRkh1IUss2hPZoEGq3yuyGlJusSPRMTuIgnzbvdSmSqlDoew6or+nRXOD55" + }, + "type" : "saved-object-with-secret", + "references" : [ ], + "updated_at" : "2020-10-01T06:31:36.578Z" + } + } +} + +{ + "type": "doc", + "value": { + "id" : "saved-object-with-secret-and-multiple-spaces:2ee1bede-400d-4767-b3f0-09a7064bec14", + "index" : ".kibana_1", + "source" : { + "saved-object-with-secret-and-multiple-spaces" : { + "publicProperty" : "some-public-property-0", + "publicPropertyExcludedFromAAD" : "some-public-property-excluded-from-aad-0", + "publicPropertyStoredEncrypted" : "eRXGrN833HNDpyw4aglw9RVyaVnOZo4BK6sAIWjbH49kITuGlmWBNPTBXyXR59VqgEtxeKJFcoCndkYMlYTc/NRNtoOBfLIIYoyfrPNMLwJb7MShRL/z4ykq/be+lU5mi6Uylnt3XXJSVv6pfQJYdTdZUMULQVUhSzn0DSj3i9k2qg==", + "privateProperty" : "ro8iLGcH4i4/XqadjfuKBET7+fdso8+IXMSWyyq0F+OS1t7oH7MsRuTS2xnraN4eXMF0cfj+jJPSKD7o5oD9539H4GdXXwBg6cEWMy1bTff/g8ixSS9BcuEG0JFX+Id10CoUVq0oY0O7acf1b/yRnpB7C9yi" + }, + "type" : "saved-object-with-secret-and-multiple-spaces", + "references" : [ ], + "namespaces" : [ + "default", + "a" + ], + "updated_at" : "2020-10-01T06:31:53.192Z" + } + } +} + +{ + "type": "doc", + "value": { + "id" : "hidden-saved-object-with-secret:506038a1-ec71-42b5-bce2-99661b29c62b", + "index" : ".kibana_1", + "source" : { + "hidden-saved-object-with-secret" : { + "publicProperty" : "some-public-property-0", + "publicPropertyExcludedFromAAD" : "some-public-property-excluded-from-aad-0", + "publicPropertyStoredEncrypted" : "nkYREB/Uqtc1btk1ieaoFvtU/sZPlHVuCICkDkBrIzkK/K6kaaDAV1AephRH9m3xpyQkhQjdE0PO7OvR0vxjIl4D+huteM0W8Iezzov0MT4r9xfhFodLamsAPAMwRlUmxSnTq6T0LiiNdRIF+LdSc0tb4Qi6mZuY7B8cI5t+uklRcQ==", + "privateProperty" : "vHce4ivOenygNl50dpuXAuBmKGoGMFoDOCvIEQXj02sYrzW8RjbV9xvrZo9PQNMlJOZeedseqJcjRO54J1fJ6MkQ0KmMtkRaMJSWLdvO23OstTmgOyaHQ6z6pg4REdtTG56vEotq6NzBE0w2PPIo/aNW5rl+" + }, + "type" : "hidden-saved-object-with-secret", + "references" : [ ], + "updated_at" : "2020-10-01T06:32:01.476Z" + } + } +} diff --git a/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json new file mode 100644 index 00000000000000..e24ab1c7b63f2f --- /dev/null +++ b/x-pack/test/encrypted_saved_objects_api_integration/fixtures/es_archiver/key_rotation/mappings.json @@ -0,0 +1,2417 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "dynamic": "strict", + "_meta": { + "migrationMappingPropertyHashes": { + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "visualization": "44d6bd48a1a653bcb60ea01614b9e3c9", + "endpoint:user-artifact": "4a11183eee21e6fbad864f7a30b39ad0", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "epm-packages": "386dc9996a3b74607de64c2ab2171582", + "type": "2f4316de49999235636386fe51dc06c1", + "space": "c5ca8acafa0beaa4d08d014a97b6bc6b", + "infrastructure-ui-source": "3d1b76c39bfb2cc8296b024d73854724", + "ingest_manager_settings": "02a03095f0e05b7a538fa801b88a217f", + "saved-object-with-migration": "7b67d1471e47144478618055293b45d3", + "application_usage_totals": "3d1b76c39bfb2cc8296b024d73854724", + "saved-object-with-secret-and-multiple-spaces": "bdf31541e7d2f348b5e21a7769c022ba", + "action": "6e96ac5e648f57523879661ea72525b7", + "dashboard": "74eb4b909f81222fa1ddeaba2881a37e", + "metrics-explorer-view": "3d1b76c39bfb2cc8296b024d73854724", + "siem-detection-engine-rule-actions": "6569b288c169539db10cb262bf79de18", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "application_usage_transactional": "3d1b76c39bfb2cc8296b024d73854724", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "fleet-agent-events": "e20a508b6e805189356be381dbfac8db", + "saved-object-with-secret": "bdf31541e7d2f348b5e21a7769c022ba", + "apm-indices": "9bb9b2bf1fa636ed8619cbab5ce6a1dd", + "inventory-view": "3d1b76c39bfb2cc8296b024d73854724", + "upgrade-assistant-reindex-operation": "215107c281839ea9b3ad5f6419819763", + "cases-comments": "c2061fb929f585df57425102fa928b4b", + "canvas-workpad-template": "ae2673f678281e2c055d764b153e9715", + "fleet-enrollment-api-keys": "a69ef7ae661dab31561d6c6f052ef2a7", + "canvas-element": "7390014e1091044523666d97247392fc", + "ingest-outputs": "8aa988c376e65443fefc26f1075e93a3", + "telemetry": "36a616f7026dfa617d6655df850fe16d", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "lens-ui-telemetry": "509bfa5978586998e05f9e303c07a327", + "namespaces": "2f4316de49999235636386fe51dc06c1", + "application_usage_daily": "43b8830d5d0df85a6823d290885fc9fd", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "saved-object-without-secret": "f9ee0ffffd41159d7b9942dbfd22ce6d", + "lens": "52346cfec69ff7b47d5f0c12361a2797", + "exception-list-agnostic": "497afa2f881a675d72d58e20057f3d8b", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "fleet-agent-actions": "9511b565b1cc6441a42033db3d5de8e9", + "exception-list": "497afa2f881a675d72d58e20057f3d8b", + "monitoring-telemetry": "2669d5ec15e82391cf58df4294ee9c68", + "app_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "search": "7f9e077078cab612f6a58e3bfdedb71a", + "originId": "2f4316de49999235636386fe51dc06c1", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "cases-configure": "42711cbb311976c0687853f4c1354572", + "search-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "alert": "3685ae5bd1c7ad4a73a2c37b1819e89e", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "siem-detection-engine-rule-status": "ae783f41c6937db6b7a2ef5c93a9e9b0", + "ingest-package-policies": "f74dfe498e1849267cda41580b2be110", + "map": "4a05b35c3a3a58fbc72dd0202dc3487f", + "uptime-dynamic-settings": "3d1b76c39bfb2cc8296b024d73854724", + "cases": "32aa96a6d3855ddda53010ae2048ac22", + "apm-telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "siem-ui-timeline": "d12c5474364d737d17252acf1dc4585c", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "enterprise_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724", + "hidden-saved-object-with-secret": "bdf31541e7d2f348b5e21a7769c022ba", + "endpoint:user-artifact-manifest": "4b9c0e7cfaf86d82a7ee9ed68065e50d", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "index-pattern": "45915a1ad866812242df474eb0479052", + "fleet-agents": "cb661e8ede2b640c42c8e5ef99db0683", + "maps-telemetry": "5ef305b18111b77789afefbd36b66171", + "namespace": "2f4316de49999235636386fe51dc06c1", + "cases-user-actions": "32277330ec6b721abe3b846cfd939a71", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "config": "c63748b75f39d0c54de12d12c1ccbc20", + "ingest-agent-policies": "8b0733cce189659593659dad8db426f0", + "tsvb-validation-telemetry": "3a37ef6c8700ae6fc97d5c7da00e9215", + "workplace_search_telemetry": "3d1b76c39bfb2cc8296b024d73854724" + } + }, + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "type": "object", + "enabled": false + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "type": "object", + "enabled": false + } + } + }, + "alert": { + "properties": { + "actions": { + "type": "nested", + "properties": { + "actionRef": { + "type": "keyword" + }, + "actionTypeId": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "type": "object", + "enabled": false + } + } + }, + "alertTypeId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "consumer": { + "type": "keyword" + }, + "createdAt": { + "type": "date" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "meta": { + "properties": { + "versionApiKeyLastmodified": { + "type": "keyword" + } + } + }, + "muteAll": { + "type": "boolean" + }, + "mutedInstanceIds": { + "type": "keyword" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + }, + "params": { + "type": "object", + "enabled": false + }, + "schedule": { + "properties": { + "interval": { + "type": "keyword" + } + } + }, + "scheduledTaskId": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "throttle": { + "type": "keyword" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "apm-indices": { + "properties": { + "apm_oss": { + "properties": { + "errorIndices": { + "type": "keyword" + }, + "metricsIndices": { + "type": "keyword" + }, + "onboardingIndices": { + "type": "keyword" + }, + "sourcemapIndices": { + "type": "keyword" + }, + "spanIndices": { + "type": "keyword" + }, + "transactionIndices": { + "type": "keyword" + } + } + } + } + }, + "apm-telemetry": { + "type": "object", + "dynamic": "false" + }, + "app_search_telemetry": { + "type": "object", + "dynamic": "false" + }, + "application_usage_daily": { + "dynamic": "false", + "properties": { + "timestamp": { + "type": "date" + } + } + }, + "application_usage_totals": { + "type": "object", + "dynamic": "false" + }, + "application_usage_transactional": { + "type": "object", + "dynamic": "false" + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + } + } + }, + "canvas-workpad-template": { + "dynamic": "false", + "properties": { + "help": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + }, + "tags": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } + }, + "template_key": { + "type": "keyword" + } + } + }, + "cases": { + "properties": { + "closed_at": { + "type": "date" + }, + "closed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "connector_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "description": { + "type": "text" + }, + "external_service": { + "properties": { + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "external_id": { + "type": "keyword" + }, + "external_title": { + "type": "text" + }, + "external_url": { + "type": "text" + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "status": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-comments": { + "properties": { + "comment": { + "type": "text" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "pushed_at": { + "type": "date" + }, + "pushed_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-configure": { + "properties": { + "closure_type": { + "type": "keyword" + }, + "connector_id": { + "type": "keyword" + }, + "connector_name": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "created_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + } + } + }, + "cases-user-actions": { + "properties": { + "action": { + "type": "keyword" + }, + "action_at": { + "type": "date" + }, + "action_by": { + "properties": { + "email": { + "type": "keyword" + }, + "full_name": { + "type": "keyword" + }, + "username": { + "type": "keyword" + } + } + }, + "action_field": { + "type": "keyword" + }, + "new_value": { + "type": "text" + }, + "old_value": { + "type": "text" + } + } + }, + "config": { + "dynamic": "false", + "properties": { + "buildNum": { + "type": "keyword" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer", + "index": false, + "doc_values": false + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text", + "index": false + } + } + }, + "optionsJSON": { + "type": "text", + "index": false + }, + "panelsJSON": { + "type": "text", + "index": false + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "pause": { + "type": "boolean", + "doc_values": false, + "index": false + }, + "section": { + "type": "integer", + "index": false, + "doc_values": false + }, + "value": { + "type": "integer", + "index": false, + "doc_values": false + } + } + }, + "timeFrom": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "timeRestore": { + "type": "boolean", + "doc_values": false, + "index": false + }, + "timeTo": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "endpoint:user-artifact": { + "properties": { + "body": { + "type": "binary" + }, + "compressionAlgorithm": { + "type": "keyword", + "index": false + }, + "created": { + "type": "date", + "index": false + }, + "decodedSha256": { + "type": "keyword", + "index": false + }, + "decodedSize": { + "type": "long", + "index": false + }, + "encodedSha256": { + "type": "keyword" + }, + "encodedSize": { + "type": "long", + "index": false + }, + "encryptionAlgorithm": { + "type": "keyword", + "index": false + }, + "identifier": { + "type": "keyword" + } + } + }, + "endpoint:user-artifact-manifest": { + "properties": { + "created": { + "type": "date", + "index": false + }, + "ids": { + "type": "keyword", + "index": false + }, + "schemaVersion": { + "type": "keyword" + }, + "semanticVersion": { + "type": "keyword", + "index": false + } + } + }, + "enterprise_search_telemetry": { + "type": "object", + "dynamic": "false" + }, + "epm-packages": { + "properties": { + "es_index_patterns": { + "type": "object", + "enabled": false + }, + "install_started_at": { + "type": "date" + }, + "install_status": { + "type": "keyword" + }, + "install_version": { + "type": "keyword" + }, + "installed_es": { + "type": "nested", + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "installed_kibana": { + "type": "nested", + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "internal": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "removable": { + "type": "boolean" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "keyword", + "fields": { + "text": { + "type": "text" + } + } + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "keyword", + "fields": { + "text": { + "type": "text" + } + } + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "exception-list-agnostic": { + "properties": { + "_tags": { + "type": "keyword" + }, + "comments": { + "properties": { + "comment": { + "type": "keyword" + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "updated_at": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "created_at": { + "type": "keyword" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "keyword" + }, + "entries": { + "properties": { + "entries": { + "properties": { + "field": { + "type": "keyword" + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "keyword", + "fields": { + "text": { + "type": "text" + } + } + } + } + }, + "field": { + "type": "keyword" + }, + "list": { + "properties": { + "id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "operator": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "keyword", + "fields": { + "text": { + "type": "text" + } + } + } + } + }, + "immutable": { + "type": "boolean" + }, + "item_id": { + "type": "keyword" + }, + "list_id": { + "type": "keyword" + }, + "list_type": { + "type": "keyword" + }, + "meta": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "tags": { + "type": "keyword" + }, + "tie_breaker_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_by": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "fleet-agent-actions": { + "properties": { + "ack_data": { + "type": "text" + }, + "agent_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "data": { + "type": "binary" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "sent_at": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agent-events": { + "properties": { + "action_id": { + "type": "keyword" + }, + "agent_id": { + "type": "keyword" + }, + "data": { + "type": "text" + }, + "message": { + "type": "text" + }, + "payload": { + "type": "text" + }, + "policy_id": { + "type": "keyword" + }, + "stream_id": { + "type": "keyword" + }, + "subtype": { + "type": "keyword" + }, + "timestamp": { + "type": "date" + }, + "type": { + "type": "keyword" + } + } + }, + "fleet-agents": { + "properties": { + "access_api_key_id": { + "type": "keyword" + }, + "active": { + "type": "boolean" + }, + "current_error_events": { + "type": "text", + "index": false + }, + "default_api_key": { + "type": "binary" + }, + "default_api_key_id": { + "type": "keyword" + }, + "enrolled_at": { + "type": "date" + }, + "last_checkin": { + "type": "date" + }, + "last_checkin_status": { + "type": "keyword" + }, + "last_updated": { + "type": "date" + }, + "local_metadata": { + "type": "flattened" + }, + "packages": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "policy_revision": { + "type": "integer" + }, + "shared_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "unenrolled_at": { + "type": "date" + }, + "unenrollment_started_at": { + "type": "date" + }, + "updated_at": { + "type": "date" + }, + "upgrade_started_at": { + "type": "date" + }, + "upgraded_at": { + "type": "date" + }, + "user_provided_metadata": { + "type": "flattened" + }, + "version": { + "type": "keyword" + } + } + }, + "fleet-enrollment-api-keys": { + "properties": { + "active": { + "type": "boolean" + }, + "api_key": { + "type": "binary" + }, + "api_key_id": { + "type": "keyword" + }, + "created_at": { + "type": "date" + }, + "expire_at": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "policy_id": { + "type": "keyword" + }, + "type": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "hidden-saved-object-with-secret": { + "properties": { + "privateProperty": { + "type": "binary" + }, + "publicProperty": { + "type": "keyword" + }, + "publicPropertyExcludedFromAAD": { + "type": "keyword" + }, + "publicPropertyStoredEncrypted": { + "type": "binary" + } + } + }, + "index-pattern": { + "dynamic": "false", + "properties": { + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "type": "object", + "dynamic": "false" + }, + "ingest-agent-policies": { + "properties": { + "description": { + "type": "text" + }, + "is_default": { + "type": "boolean" + }, + "monitoring_enabled": { + "type": "keyword", + "index": false + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "package_policies": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "status": { + "type": "keyword" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest-outputs": { + "properties": { + "ca_sha256": { + "type": "keyword", + "index": false + }, + "config": { + "type": "flattened" + }, + "fleet_enroll_password": { + "type": "binary" + }, + "fleet_enroll_username": { + "type": "binary" + }, + "hosts": { + "type": "keyword" + }, + "is_default": { + "type": "boolean" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "ingest-package-policies": { + "properties": { + "created_at": { + "type": "date" + }, + "created_by": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "enabled": { + "type": "boolean" + }, + "inputs": { + "type": "nested", + "enabled": false, + "properties": { + "config": { + "type": "flattened" + }, + "enabled": { + "type": "boolean" + }, + "streams": { + "type": "nested", + "properties": { + "compiled_stream": { + "type": "flattened" + }, + "config": { + "type": "flattened" + }, + "data_stream": { + "properties": { + "dataset": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "enabled": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + } + }, + "type": { + "type": "keyword" + }, + "vars": { + "type": "flattened" + } + } + }, + "name": { + "type": "keyword" + }, + "namespace": { + "type": "keyword" + }, + "output_id": { + "type": "keyword" + }, + "package": { + "properties": { + "name": { + "type": "keyword" + }, + "title": { + "type": "keyword" + }, + "version": { + "type": "keyword" + } + } + }, + "policy_id": { + "type": "keyword" + }, + "revision": { + "type": "integer" + }, + "updated_at": { + "type": "date" + }, + "updated_by": { + "type": "keyword" + } + } + }, + "ingest_manager_settings": { + "properties": { + "agent_auto_upgrade": { + "type": "keyword" + }, + "has_seen_add_data_notice": { + "type": "boolean", + "index": false + }, + "kibana_ca_sha256": { + "type": "keyword" + }, + "kibana_urls": { + "type": "keyword" + }, + "package_auto_upgrade": { + "type": "keyword" + } + } + }, + "inventory-view": { + "type": "object", + "dynamic": "false" + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "description": { + "type": "text" + }, + "expression": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "state": { + "type": "flattened" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "lens-ui-telemetry": { + "properties": { + "count": { + "type": "integer" + }, + "date": { + "type": "date" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "type": "object", + "enabled": false + }, + "metrics-explorer-view": { + "type": "object", + "dynamic": "false" + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "config": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "space": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "monitoring-telemetry": { + "properties": { + "reportedClusterUuids": { + "type": "keyword" + } + } + }, + "namespace": { + "type": "keyword" + }, + "namespaces": { + "type": "keyword" + }, + "originId": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "type": "object", + "enabled": false + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "type": "keyword", + "index": false + } + } + }, + "timefilter": { + "type": "object", + "enabled": false + }, + "title": { + "type": "text" + } + } + }, + "references": { + "type": "nested", + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + } + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "saved-object-with-migration": { + "properties": { + "additionalEncryptedAttribute": { + "type": "keyword" + }, + "encryptedAttribute": { + "type": "binary" + }, + "nonEncryptedAttribute": { + "type": "keyword" + } + } + }, + "saved-object-with-secret": { + "properties": { + "privateProperty": { + "type": "binary" + }, + "publicProperty": { + "type": "keyword" + }, + "publicPropertyExcludedFromAAD": { + "type": "keyword" + }, + "publicPropertyStoredEncrypted": { + "type": "binary" + } + } + }, + "saved-object-with-secret-and-multiple-spaces": { + "properties": { + "privateProperty": { + "type": "binary" + }, + "publicProperty": { + "type": "keyword" + }, + "publicPropertyExcludedFromAAD": { + "type": "keyword" + }, + "publicPropertyStoredEncrypted": { + "type": "binary" + } + } + }, + "saved-object-without-secret": { + "properties": { + "publicProperty": { + "type": "keyword" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer", + "index": false, + "doc_values": false + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text", + "index": false + } + } + }, + "sort": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "search-telemetry": { + "type": "object", + "dynamic": "false" + }, + "siem-detection-engine-rule-actions": { + "properties": { + "actions": { + "properties": { + "action_type_id": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "id": { + "type": "keyword" + }, + "params": { + "type": "object", + "enabled": false + } + } + }, + "alertThrottle": { + "type": "keyword" + }, + "ruleAlertId": { + "type": "keyword" + }, + "ruleThrottle": { + "type": "keyword" + } + } + }, + "siem-detection-engine-rule-status": { + "properties": { + "alertId": { + "type": "keyword" + }, + "bulkCreateTimeDurations": { + "type": "float" + }, + "gap": { + "type": "text" + }, + "lastFailureAt": { + "type": "date" + }, + "lastFailureMessage": { + "type": "text" + }, + "lastLookBackDate": { + "type": "date" + }, + "lastSuccessAt": { + "type": "date" + }, + "lastSuccessMessage": { + "type": "text" + }, + "searchAfterTimeDurations": { + "type": "float" + }, + "status": { + "type": "keyword" + }, + "statusDate": { + "type": "date" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + }, + "type": { + "type": "text" + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "eventType": { + "type": "keyword" + }, + "excludedRowRendererIds": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "filters": { + "properties": { + "exists": { + "type": "text" + }, + "match_all": { + "type": "text" + }, + "meta": { + "properties": { + "alias": { + "type": "text" + }, + "controlledBy": { + "type": "text" + }, + "disabled": { + "type": "boolean" + }, + "field": { + "type": "text" + }, + "formattedValue": { + "type": "text" + }, + "index": { + "type": "keyword" + }, + "key": { + "type": "keyword" + }, + "negate": { + "type": "boolean" + }, + "params": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "value": { + "type": "text" + } + } + }, + "missing": { + "type": "text" + }, + "query": { + "type": "text" + }, + "range": { + "type": "text" + }, + "script": { + "type": "text" + } + } + }, + "indexNames": { + "type": "text" + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "savedQueryId": { + "type": "keyword" + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "status": { + "type": "keyword" + }, + "templateTimelineId": { + "type": "text" + }, + "templateTimelineVersion": { + "type": "integer" + }, + "timelineType": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "imageUrl": { + "type": "text", + "index": false + }, + "initials": { + "type": "keyword" + }, + "name": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 2048 + } + } + } + } + }, + "telemetry": { + "properties": { + "allowChangingOptInStatus": { + "type": "boolean" + }, + "enabled": { + "type": "boolean" + }, + "lastReported": { + "type": "date" + }, + "lastVersionChecked": { + "type": "keyword" + }, + "reportFailureCount": { + "type": "integer" + }, + "reportFailureVersion": { + "type": "keyword" + }, + "sendUsageFrom": { + "type": "keyword" + }, + "userHasSeenNotice": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "tsvb-validation-telemetry": { + "properties": { + "failedRequests": { + "type": "long" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "properties": { + "errorMessage": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "indexName": { + "type": "keyword" + }, + "lastCompletedStep": { + "type": "long" + }, + "locked": { + "type": "date" + }, + "newIndexName": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "reindexOptions": { + "properties": { + "openAndClose": { + "type": "boolean" + }, + "queueSettings": { + "properties": { + "queuedAt": { + "type": "long" + }, + "startedAt": { + "type": "long" + } + } + } + } + }, + "reindexTaskId": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 256 + } + } + }, + "reindexTaskPercComplete": { + "type": "float" + }, + "runningReindexCount": { + "type": "integer" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "type": "boolean", + "null_value": true + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "type": "long", + "null_value": 0 + }, + "indices": { + "type": "long", + "null_value": 0 + }, + "overview": { + "type": "long", + "null_value": 0 + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "type": "long", + "null_value": 0 + }, + "open": { + "type": "long", + "null_value": 0 + }, + "start": { + "type": "long", + "null_value": 0 + }, + "stop": { + "type": "long", + "null_value": 0 + } + } + } + } + }, + "uptime-dynamic-settings": { + "type": "object", + "dynamic": "false" + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "type": "text", + "fields": { + "keyword": { + "type": "keyword", + "ignore_above": 2048 + } + } + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text", + "index": false + } + } + }, + "savedSearchRefName": { + "type": "keyword", + "index": false, + "doc_values": false + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text", + "index": false + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text", + "index": false + } + } + }, + "workplace_search_telemetry": { + "type": "object", + "dynamic": "false" + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "1", + "number_of_shards": "1" + } + } + } +} diff --git a/x-pack/test/encrypted_saved_objects_api_integration/services.ts b/x-pack/test/encrypted_saved_objects_api_integration/services.ts index b7398349cce5da..a8d8048462693d 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/services.ts +++ b/x-pack/test/encrypted_saved_objects_api_integration/services.ts @@ -4,4 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -export { services } from '../api_integration/services'; +import { services as commonServices } from '../common/services'; +import { services as apiIntegrationServices } from '../api_integration/services'; + +export const services = { + ...commonServices, + supertestWithoutAuth: apiIntegrationServices.supertestWithoutAuth, +}; diff --git a/x-pack/test/encrypted_saved_objects_api_integration/tests/encrypted_saved_objects_api.ts b/x-pack/test/encrypted_saved_objects_api_integration/tests/encrypted_saved_objects_api.ts index 8bdc1715bf487b..637bb7c2022419 100644 --- a/x-pack/test/encrypted_saved_objects_api_integration/tests/encrypted_saved_objects_api.ts +++ b/x-pack/test/encrypted_saved_objects_api_integration/tests/encrypted_saved_objects_api.ts @@ -13,6 +13,7 @@ export default function ({ getService }: FtrProviderContext) { const randomness = getService('randomness'); const supertest = getService('supertest'); const esArchiver = getService('esArchiver'); + const security = getService('security'); const SAVED_OBJECT_WITH_SECRET_TYPE = 'saved-object-with-secret'; const HIDDEN_SAVED_OBJECT_WITH_SECRET_TYPE = 'hidden-saved-object-with-secret'; @@ -28,10 +29,10 @@ export default function ({ getService }: FtrProviderContext) { async function getRawSavedObjectAttributes({ id, type }: SavedObject) { const { _source: { [type]: savedObject }, - } = await es.get({ + } = await es.get<Record<string, any>>({ id: generateRawID(id, type), index: '.kibana', - }); + } as any); return savedObject; } @@ -437,15 +438,15 @@ export default function ({ getService }: FtrProviderContext) { }:${id}`; } - afterEach(async () => { - await es.deleteByQuery({ - index: '.kibana', - q: `type:${SAVED_OBJECT_WITH_SECRET_TYPE} OR type:${HIDDEN_SAVED_OBJECT_WITH_SECRET_TYPE} OR type:${SAVED_OBJECT_WITH_SECRET_AND_MULTIPLE_SPACES_TYPE} OR type:${SAVED_OBJECT_WITHOUT_SECRET_TYPE}`, - refresh: true, + describe('within a default space', () => { + afterEach(async () => { + await es.deleteByQuery({ + index: '.kibana', + q: `type:${SAVED_OBJECT_WITH_SECRET_TYPE} OR type:${HIDDEN_SAVED_OBJECT_WITH_SECRET_TYPE} OR type:${SAVED_OBJECT_WITH_SECRET_AND_MULTIPLE_SPACES_TYPE} OR type:${SAVED_OBJECT_WITHOUT_SECRET_TYPE}`, + refresh: true, + }); }); - }); - describe('within a default space', () => { describe('with `single` namespace saved object', () => { runTests( SAVED_OBJECT_WITH_SECRET_TYPE, @@ -486,6 +487,14 @@ export default function ({ getService }: FtrProviderContext) { await supertest.delete(`/api/spaces/space/${SPACE_ID}`).set('kbn-xsrf', 'xxx').expect(204); }); + afterEach(async () => { + await es.deleteByQuery({ + index: '.kibana', + q: `type:${SAVED_OBJECT_WITH_SECRET_TYPE} OR type:${HIDDEN_SAVED_OBJECT_WITH_SECRET_TYPE} OR type:${SAVED_OBJECT_WITH_SECRET_AND_MULTIPLE_SPACES_TYPE} OR type:${SAVED_OBJECT_WITHOUT_SECRET_TYPE}`, + refresh: true, + }); + }); + describe('with `single` namespace saved object', () => { runTests( SAVED_OBJECT_WITH_SECRET_TYPE, @@ -529,5 +538,121 @@ export default function ({ getService }: FtrProviderContext) { }); }); }); + + describe('key rotation', () => { + const supertestWithoutAuth = getService('supertestWithoutAuth'); + const savedObjectsEncryptedWithLegacyKeys: Array<[string, string, string[], boolean]> = [ + [SAVED_OBJECT_WITH_SECRET_TYPE, 'cd9272b2-6a15-4295-bb7b-15f6347e267b', ['default'], false], + [ + SAVED_OBJECT_WITH_SECRET_AND_MULTIPLE_SPACES_TYPE, + '6b388d9b-6d8f-4e20-ba38-d9477370729d', + ['a', 'default'], + false, + ], + [ + HIDDEN_SAVED_OBJECT_WITH_SECRET_TYPE, + '8d90daef-f992-443c-ab78-053c6a7b0e9c', + ['default'], + true, + ], + [SAVED_OBJECT_WITH_SECRET_TYPE, '4336f782-450f-4142-aecd-b46ed092af52', ['default'], false], + [ + SAVED_OBJECT_WITH_SECRET_AND_MULTIPLE_SPACES_TYPE, + '2ee1bede-400d-4767-b3f0-09a7064bec14', + ['a', 'default'], + false, + ], + [ + HIDDEN_SAVED_OBJECT_WITH_SECRET_TYPE, + '506038a1-ec71-42b5-bce2-99661b29c62b', + ['default'], + true, + ], + ]; + + const KIBANA_ADMIN_USERNAME = 'admin'; + const KIBANA_ADMIN_PASSWORD = 'changeme'; + before(async () => { + await security.user.create(KIBANA_ADMIN_USERNAME, { + password: KIBANA_ADMIN_PASSWORD, + roles: ['kibana_admin'], + full_name: 'a kibana admin', + }); + await esArchiver.load('key_rotation'); + }); + + after(async () => { + await esArchiver.unload('key_rotation'); + await security.user.delete('admin'); + }); + + it('#get can properly retrieve objects encrypted with the legacy keys', async () => { + // Hidden objects cannot be retrieved with standard Saved Objects APIs. + for (const [type, id, namespaces] of savedObjectsEncryptedWithLegacyKeys.filter( + ([, , , hiddenSavedObject]) => !hiddenSavedObject + )) { + const { body: decryptedResponse } = await supertest + .get(`/api/saved_objects/${type}/${id}`) + .expect(200); + + expect(decryptedResponse.namespaces.sort()).to.eql(namespaces); + expect(decryptedResponse.attributes).to.eql({ + publicProperty: 'some-public-property-0', + publicPropertyExcludedFromAAD: 'some-public-property-excluded-from-aad-0', + publicPropertyStoredEncrypted: 'some-public-but-encrypted-property-0', + }); + } + }); + + it('#get-decrypted-as-internal-user can properly retrieve objects encrypted with the legacy keys', async () => { + for (const [type, id, namespaces, hidden] of savedObjectsEncryptedWithLegacyKeys) { + const url = hidden + ? `/api/hidden_saved_objects/get-decrypted-as-internal-user/${type}/${id}` + : `/api/saved_objects/get-decrypted-as-internal-user/${type}/${id}`; + const { body: decryptedResponse } = await supertest.get(url).expect(200); + + expect(decryptedResponse.namespaces.sort()).to.eql(namespaces); + expect(decryptedResponse.attributes).to.eql({ + privateProperty: 'some-private-property-0', + publicProperty: 'some-public-property-0', + publicPropertyExcludedFromAAD: 'some-public-property-excluded-from-aad-0', + publicPropertyStoredEncrypted: 'some-public-but-encrypted-property-0', + }); + } + }); + + it('non-super user cannot rotate encryption key', async () => { + await supertestWithoutAuth + .post('/api/encrypted_saved_objects/_rotate_key') + .set('kbn-xsrf', 'xxx') + .auth(KIBANA_ADMIN_USERNAME, KIBANA_ADMIN_PASSWORD) + .send() + .expect(404); + }); + + // Since this test re-encrypts objects it should always go last in this suite. + it('encryption key can be properly rotated by the superuser', async () => { + await supertest + .post('/api/encrypted_saved_objects/_rotate_key') + .set('kbn-xsrf', 'xxx') + .send() + .expect(200, { total: 6, successful: 6, failed: 0 }); + + for (const [type, id, namespaces, hidden] of savedObjectsEncryptedWithLegacyKeys) { + const url = hidden + ? `/api/hidden_saved_objects/get-decrypted-as-internal-user/${type}/${id}` + : `/api/saved_objects/get-decrypted-as-internal-user/${type}/${id}`; + const { body: decryptedResponse } = await supertest.get(url).expect(200); + + expect(decryptedResponse.namespaces.sort()).to.eql(namespaces); + expect(decryptedResponse.attributes).to.eql({ + privateProperty: 'some-private-property-0', + publicProperty: 'some-public-property-0', + publicPropertyExcludedFromAAD: 'some-public-property-excluded-from-aad-0', + publicPropertyStoredEncrypted: 'some-public-but-encrypted-property-0', + }); + } + }); + }); }); } From 00d2105650b2a3a22135cbfdcb4aee9310979b0b Mon Sep 17 00:00:00 2001 From: Bohdan Tsymbala <bohdan.tsymbala@elastic.co> Date: Fri, 2 Oct 2020 21:11:23 +0200 Subject: [PATCH 104/128] Added view type parameter to routing in trusted apps (#79297) * Refactored store code to group properties related to location so that would be easy to introduce a new view type parameter. * Added view type to the location and routing. * Fixed type errors. * Fixed and completed tests. --- .../public/management/common/routing.test.ts | 96 +++++++++++++++++-- .../public/management/common/routing.ts | 8 +- .../state/trusted_apps_list_page_state.ts | 3 + .../trusted_apps/store/middleware.test.ts | 10 +- .../pages/trusted_apps/store/reducer.test.ts | 19 ++-- .../pages/trusted_apps/store/reducer.ts | 1 + .../trusted_apps/store/selectors.test.ts | 28 ++++-- 7 files changed, 136 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/common/routing.test.ts b/x-pack/plugins/security_solution/public/management/common/routing.test.ts index 7082ab0ce5c4f4..47f2f729446bd8 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.test.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.test.ts @@ -6,6 +6,7 @@ import { extractTrustedAppsListPageLocation, getTrustedAppsListPath } from './routing'; import { MANAGEMENT_DEFAULT_PAGE, MANAGEMENT_DEFAULT_PAGE_SIZE } from './constants'; +import { TrustedAppsListPageLocation } from '../pages/trusted_apps/state'; describe('routing', () => { describe('extractListPaginationParams()', () => { @@ -56,6 +57,42 @@ describe('routing', () => { it('extracts proper page size when single valid value provided', () => { expect(extractTrustedAppsListPageLocation({ page_size: '20' }).page_size).toBe(20); }); + + it('extracts proper "show" when single valid value provided', () => { + expect(extractTrustedAppsListPageLocation({ show: 'create' }).show).toBe('create'); + }); + + it('extracts only last "show" when multiple values provided', () => { + expect(extractTrustedAppsListPageLocation({ show: ['invalid', 'create'] }).show).toBe( + 'create' + ); + }); + + it('extracts default "show" when no value provided', () => { + expect(extractTrustedAppsListPageLocation({}).show).toBeUndefined(); + }); + + it('extracts default "show" when single invalid value provided', () => { + expect(extractTrustedAppsListPageLocation({ show: 'invalid' }).show).toBeUndefined(); + }); + + it('extracts proper view type when single valid value provided', () => { + expect(extractTrustedAppsListPageLocation({ view_type: 'list' }).view_type).toBe('list'); + }); + + it('extracts only last view type when multiple values provided', () => { + expect(extractTrustedAppsListPageLocation({ view_type: ['grid', 'list'] }).view_type).toBe( + 'list' + ); + }); + + it('extracts default view type when no value provided', () => { + expect(extractTrustedAppsListPageLocation({}).view_type).toBe('grid'); + }); + + it('extracts default view type when single invalid value provided', () => { + expect(extractTrustedAppsListPageLocation({ view_type: 'invalid' }).view_type).toBe('grid'); + }); }); describe('getTrustedAppsListPath()', () => { @@ -67,17 +104,32 @@ describe('routing', () => { expect(getTrustedAppsListPath({})).toEqual('/trusted_apps'); }); - it('builds proper path when no page index provided', () => { + it('builds proper path when only page size provided', () => { expect(getTrustedAppsListPath({ page_size: 20 })).toEqual('/trusted_apps?page_size=20'); }); - it('builds proper path when no page size provided', () => { + it('builds proper path when only page index provided', () => { expect(getTrustedAppsListPath({ page_index: 2 })).toEqual('/trusted_apps?page_index=2'); }); - it('builds proper path when both page index and size provided', () => { - expect(getTrustedAppsListPath({ page_index: 2, page_size: 20 })).toEqual( - '/trusted_apps?page_index=2&page_size=20' + it('builds proper path when only "show" provided', () => { + expect(getTrustedAppsListPath({ show: 'create' })).toEqual('/trusted_apps?show=create'); + }); + + it('builds proper path when only view type provided', () => { + expect(getTrustedAppsListPath({ view_type: 'list' })).toEqual('/trusted_apps?view_type=list'); + }); + + it('builds proper path when all params provided', () => { + const location: TrustedAppsListPageLocation = { + page_index: 2, + page_size: 20, + show: 'create', + view_type: 'list', + }; + + expect(getTrustedAppsListPath(location)).toEqual( + '/trusted_apps?page_index=2&page_size=20&view_type=list&show=create' ); }); @@ -85,24 +137,52 @@ describe('routing', () => { const path = getTrustedAppsListPath({ page_index: MANAGEMENT_DEFAULT_PAGE, page_size: 20, + show: 'create', + view_type: 'list', }); - expect(path).toEqual('/trusted_apps?page_size=20'); + expect(path).toEqual('/trusted_apps?page_size=20&view_type=list&show=create'); }); it('builds proper path when page size is equal to default', () => { const path = getTrustedAppsListPath({ page_index: 2, page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, + show: 'create', + view_type: 'list', + }); + + expect(path).toEqual('/trusted_apps?page_index=2&view_type=list&show=create'); + }); + + it('builds proper path when "show" is equal to default', () => { + const path = getTrustedAppsListPath({ + page_index: 2, + page_size: 20, + show: undefined, + view_type: 'list', + }); + + expect(path).toEqual('/trusted_apps?page_index=2&page_size=20&view_type=list'); + }); + + it('builds proper path when view type is equal to default', () => { + const path = getTrustedAppsListPath({ + page_index: 2, + page_size: 20, + show: 'create', + view_type: 'grid', }); - expect(path).toEqual('/trusted_apps?page_index=2'); + expect(path).toEqual('/trusted_apps?page_index=2&page_size=20&show=create'); }); - it('builds proper path when both page index and size are equal to default', () => { + it('builds proper path when params are equal to default', () => { const path = getTrustedAppsListPath({ page_index: MANAGEMENT_DEFAULT_PAGE, page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, + show: undefined, + view_type: 'grid', }); expect(path).toEqual('/trusted_apps'); diff --git a/x-pack/plugins/security_solution/public/management/common/routing.ts b/x-pack/plugins/security_solution/public/management/common/routing.ts index 9acf4a1613c0b2..b3162a8708a5f8 100644 --- a/x-pack/plugins/security_solution/public/management/common/routing.ts +++ b/x-pack/plugins/security_solution/public/management/common/routing.ts @@ -105,6 +105,7 @@ const normalizeTrustedAppsPageLocation = ( ...(!isDefaultOrMissing(location.page_size, MANAGEMENT_DEFAULT_PAGE_SIZE) ? { page_size: location.page_size } : {}), + ...(!isDefaultOrMissing(location.view_type, 'grid') ? { view_type: location.view_type } : {}), ...(!isDefaultOrMissing(location.show, undefined) ? { show: location.show } : {}), }; } else { @@ -144,13 +145,16 @@ export const extractTrustedAppsListPageLocation = ( query: querystring.ParsedUrlQuery ): TrustedAppsListPageLocation => ({ ...extractListPaginationParams(query), + view_type: extractFirstParamValue(query, 'view_type') === 'list' ? 'list' : 'grid', show: extractFirstParamValue(query, 'show') === 'create' ? 'create' : undefined, }); -export const getTrustedAppsListPath = (params?: Partial<TrustedAppsListPageLocation>): string => { +export const getTrustedAppsListPath = (location?: Partial<TrustedAppsListPageLocation>): string => { const path = generatePath(MANAGEMENT_ROUTING_TRUSTED_APPS_PATH, { tabName: AdministrationSubTab.trustedApps, }); - return `${path}${appendSearch(querystring.stringify(normalizeTrustedAppsPageLocation(params)))}`; + return `${path}${appendSearch( + querystring.stringify(normalizeTrustedAppsPageLocation(location)) + )}`; }; diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts index a98ec03a006f53..e58de80c354a86 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/state/trusted_apps_list_page_state.ts @@ -38,9 +38,12 @@ export interface TrustedAppCreateFailure { data: ServerApiError; } +export type ViewType = 'list' | 'grid'; + export interface TrustedAppsListPageLocation { page_index: number; page_size: number; + view_type: ViewType; show?: 'create'; } diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts index 2143b5135c5753..e19731f28d9b9c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/middleware.test.ts @@ -75,7 +75,7 @@ describe('middleware', () => { describe('refreshing list resource state', () => { it('refreshes the list when location changes and data gets outdated', async () => { const pagination = { index: 2, size: 50 }; - const location = { page_index: 2, page_size: 50, show: undefined }; + const location = { page_index: 2, page_size: 50, show: undefined, view_type: 'grid' }; const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); @@ -110,7 +110,7 @@ describe('middleware', () => { it('does not refresh the list when location changes and data does not get outdated', async () => { const pagination = { index: 2, size: 50 }; - const location = { page_index: 2, page_size: 50, show: undefined }; + const location = { page_index: 2, page_size: 50, show: undefined, view_type: 'grid' }; const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); @@ -136,7 +136,7 @@ describe('middleware', () => { it('refreshes the list when data gets outdated with and outdate action', async () => { const newNow = 222222; const pagination = { index: 0, size: 10 }; - const location = { page_index: 0, page_size: 10, show: undefined }; + const location = { page_index: 0, page_size: 10, show: undefined, view_type: 'grid' }; const service = createTrustedAppsServiceMock(); const { store, spyMiddleware } = createStoreSetup(service); @@ -196,7 +196,7 @@ describe('middleware', () => { freshDataTimestamp: initialNow, }, active: true, - location: { page_index: 2, page_size: 50, show: undefined }, + location: { page_index: 2, page_size: 50, show: undefined, view_type: 'grid' }, }); const infiniteLoopTest = async () => { @@ -212,7 +212,7 @@ describe('middleware', () => { const entry = createSampleTrustedApp(3); const notFoundError = createServerApiError('Not Found'); const pagination = { index: 0, size: 10 }; - const location = { page_index: 0, page_size: 10, show: undefined }; + const location = { page_index: 0, page_size: 10, show: undefined, view_type: 'grid' }; const getTrustedAppsListResponse = createGetTrustedListAppsResponse(pagination, 500); const listView = createLoadedListViewWithPagination(initialNow, pagination, 500); const listViewNew = createLoadedListViewWithPagination(newNow, pagination, 500); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts index 94fcdb39bb1692..1c1d609fb16e8f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.test.ts @@ -24,31 +24,34 @@ const initialState = initialTrustedAppsPageState(); describe('reducer', () => { describe('UserChangedUrl', () => { - it('makes page state active and extracts pagination parameters', () => { + it('makes page state active and extracts all parameters', () => { const result = trustedAppsPageReducer( initialState, - createUserChangedUrlAction('/trusted_apps', '?page_index=5&page_size=50') + createUserChangedUrlAction( + '/trusted_apps', + '?page_index=5&page_size=50&show=create&view_type=list' + ) ); expect(result).toStrictEqual({ ...initialState, - location: { page_index: 5, page_size: 50, show: undefined }, + location: { page_index: 5, page_size: 50, show: 'create', view_type: 'list' }, active: true, }); }); - it('extracts default pagination parameters when none provided', () => { + it('extracts default pagination parameters when invalid provided', () => { const result = trustedAppsPageReducer( - { ...initialState, location: { page_index: 5, page_size: 50 } }, - createUserChangedUrlAction('/trusted_apps', '?page_index=b&page_size=60') + { ...initialState, location: { page_index: 5, page_size: 50, view_type: 'grid' } }, + createUserChangedUrlAction('/trusted_apps', '?page_index=b&page_size=60&show=a&view_type=c') ); expect(result).toStrictEqual({ ...initialState, active: true }); }); - it('extracts default pagination parameters when invalid provided', () => { + it('extracts default pagination parameters when none provided', () => { const result = trustedAppsPageReducer( - { ...initialState, location: { page_index: 5, page_size: 50 } }, + { ...initialState, location: { page_index: 5, page_size: 50, view_type: 'grid' } }, createUserChangedUrlAction('/trusted_apps') ); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts index f4056f02a41404..efabbf20e629e0 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/reducer.ts @@ -152,6 +152,7 @@ export const initialTrustedAppsPageState = (): TrustedAppsListPageState => ({ page_index: MANAGEMENT_DEFAULT_PAGE, page_size: MANAGEMENT_DEFAULT_PAGE_SIZE, show: undefined, + view_type: 'grid', }, active: false, }); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts index 01fe3e5bf202ea..8706333cb7bf6b 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/store/selectors.test.ts @@ -4,7 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AsyncResourceState, TrustedAppsListPageState } from '../state'; +import { + AsyncResourceState, + TrustedAppsListPageLocation, + TrustedAppsListPageState, +} from '../state'; import { initialTrustedAppsPageState } from './reducer'; import { getListResourceState, @@ -84,7 +88,11 @@ describe('selectors', () => { it('returns false when current loaded data is up to date', () => { const listView = createLoadedListViewWithPagination(initialNow); - const location = { page_index: 0, page_size: 10 }; + const location: TrustedAppsListPageLocation = { + page_index: 0, + page_size: 10, + view_type: 'grid', + }; expect(needsRefreshOfListData({ ...initialState, listView, active: true, location })).toBe( false @@ -166,17 +174,25 @@ describe('selectors', () => { describe('getListCurrentPageIndex()', () => { it('returns page index', () => { - const state = { ...initialState, location: { page_index: 3, page_size: 10 } }; + const location: TrustedAppsListPageLocation = { + page_index: 3, + page_size: 10, + view_type: 'grid', + }; - expect(getCurrentLocationPageIndex(state)).toBe(3); + expect(getCurrentLocationPageIndex({ ...initialState, location })).toBe(3); }); }); describe('getListCurrentPageSize()', () => { it('returns page size', () => { - const state = { ...initialState, location: { page_index: 0, page_size: 20 } }; + const location: TrustedAppsListPageLocation = { + page_index: 0, + page_size: 20, + view_type: 'grid', + }; - expect(getCurrentLocationPageSize(state)).toBe(20); + expect(getCurrentLocationPageSize({ ...initialState, location })).toBe(20); }); }); From 1310ac61fcde46e3a61a47bc22cf1833b9d9deda Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Fri, 2 Oct 2020 15:13:28 -0400 Subject: [PATCH 105/128] [SECURITY_SOLUTION][ENDPOINT] Trusted apps create form UX mocks sync (#79155) * Make flyout size `m` + condition entry value should be 100% wide * Condition entry and group components support for small screens * Adjust spacing below each entry in the condition group * Move `AND` button to the condition group + style it to design mock --- .../create_trusted_app_form.test.tsx | 2 +- .../components/condition_entry.tsx | 2 + .../components/condition_group.tsx | 107 ++++++++++++++---- .../logical_condition_builder.tsx | 27 +---- .../trusted_apps/view/trusted_apps_page.tsx | 2 +- 5 files changed, 92 insertions(+), 48 deletions(-) diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx index dd4f389f047052..211fc9ec3371e1 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/create_trusted_app_form.test.tsx @@ -76,7 +76,7 @@ describe('When showing the Trusted App Create Form', () => { dataTestSub: string = dataTestSubjForForm ): HTMLButtonElement => { return renderResult.getByTestId( - `${dataTestSub}-conditionsBuilder-AndButton` + `${dataTestSub}-conditionsBuilder-group1-AndButton` ) as HTMLButtonElement; }; const getConditionBuilderAndConnectorBadge = ( diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx index 7d30e81898cf22..25d5d757759310 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx @@ -130,6 +130,7 @@ export const ConditionEntry = memo<ConditionEntryProps>( alignItems="center" direction="row" data-test-subj={dataTestSubj} + responsive={false} > <EuiFlexItem grow={2}> <ConditionEntryCell @@ -176,6 +177,7 @@ export const ConditionEntry = memo<ConditionEntryProps>( <EuiFieldText name="value" value={entry.value} + fullWidth required onChange={handleValueUpdate} onBlur={handleValueOnBlur} diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_group.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_group.tsx index 9359011486c45c..5c8a55653a84f7 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_group.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_group.tsx @@ -5,14 +5,39 @@ */ import React, { memo, useCallback } from 'react'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiHideFor, EuiSpacer } from '@elastic/eui'; import styled from 'styled-components'; +import { FormattedMessage } from '@kbn/i18n/react'; import { NewTrustedApp, TrustedApp } from '../../../../../../../../common/endpoint/types'; import { ConditionEntry, ConditionEntryProps } from './condition_entry'; import { AndOrBadge } from '../../../../../../../common/components/and_or_badge'; -const AndBadgeFlexItem = styled(EuiFlexItem)` - padding-top: 20px; +const ConditionGroupFlexGroup = styled(EuiFlexGroup)` + // The positioning of the 'and-badge' is done by using the EuiButton's height and adding on to it + // the amount of padding used to space out each of the entries (times 2 because a spacer is also + // used above the Button), and then we adjust it with 3px + .and-badge { + padding-top: 20px; + padding-bottom: ${({ theme }) => { + return `calc(${theme.eui.euiButtonHeightSmall} + (${theme.eui.paddingSizes.s} * 2) + 3px);`; + }}; + } + + .group-entries { + margin-bottom: ${({ theme }) => theme.eui.paddingSizes.s}; + + & > * { + margin-bottom: ${({ theme }) => theme.eui.paddingSizes.s}; + + &:last-child { + margin-bottom: 0; + } + } + } + + .and-button { + min-width: 95px; + } `; export interface ConditionGroupProps { @@ -20,12 +45,23 @@ export interface ConditionGroupProps { entries: TrustedApp['entries']; onEntryRemove: ConditionEntryProps['onRemove']; onEntryChange: ConditionEntryProps['onChange']; + onAndClicked: () => void; + isAndDisabled?: boolean; /** called when any of the entries is visited (triggered via `onBlur` DOM event) */ onVisited?: ConditionEntryProps['onVisited']; 'data-test-subj'?: string; } export const ConditionGroup = memo<ConditionGroupProps>( - ({ os, entries, onEntryRemove, onEntryChange, onVisited, 'data-test-subj': dataTestSubj }) => { + ({ + os, + entries, + onEntryRemove, + onEntryChange, + onAndClicked, + isAndDisabled, + onVisited, + 'data-test-subj': dataTestSubj, + }) => { const getTestId = useCallback( (suffix: string): string | undefined => { if (dataTestSubj) { @@ -35,28 +71,53 @@ export const ConditionGroup = memo<ConditionGroupProps>( [dataTestSubj] ); return ( - <EuiFlexGroup gutterSize="xs" data-test-subj={dataTestSubj}> + <ConditionGroupFlexGroup gutterSize="xs" data-test-subj={dataTestSubj}> {entries.length > 1 && ( - <AndBadgeFlexItem grow={false} data-test-subj={getTestId('andConnector')}> - <AndOrBadge type={'and'} includeAntennas={true} /> - </AndBadgeFlexItem> + <EuiHideFor sizes={['xs', 's']}> + <EuiFlexItem + grow={false} + data-test-subj={getTestId('andConnector')} + className="and-badge" + > + <AndOrBadge type={'and'} includeAntennas={true} /> + </EuiFlexItem> + </EuiHideFor> )} - <EuiFlexItem grow={1} data-test-subj={getTestId('entries')}> - {(entries as (NewTrustedApp & { os: 'windows' })['entries']).map((entry, index) => ( - <ConditionEntry - key={index} - os={os} - entry={entry} - showLabels={index === 0} - isRemoveDisabled={index === 0 && entries.length <= 1} - onRemove={onEntryRemove} - onChange={onEntryChange} - onVisited={onVisited} - data-test-subj={getTestId(`entry${index}`)} - /> - ))} + <EuiFlexItem grow={1}> + <div data-test-subj={getTestId('entries')} className="group-entries"> + {(entries as (NewTrustedApp & { os: 'windows' })['entries']).map((entry, index) => ( + <ConditionEntry + key={index} + os={os} + entry={entry} + showLabels={index === 0} + isRemoveDisabled={index === 0 && entries.length <= 1} + onRemove={onEntryRemove} + onChange={onEntryChange} + onVisited={onVisited} + data-test-subj={getTestId(`entry${index}`)} + /> + ))} + </div> + <div> + <EuiSpacer size="s" /> + <EuiButton + fill + size="s" + iconType="plusInCircle" + onClick={onAndClicked} + data-test-subj={getTestId('AndButton')} + isDisabled={isAndDisabled} + className="and-button" + > + <FormattedMessage + id="xpack.securitySolution.trustedapps.logicalConditionBuilder.group.andOperator" + defaultMessage="AND" + /> + </EuiButton> + </div> </EuiFlexItem> - </EuiFlexGroup> + </ConditionGroupFlexGroup> ); } ); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/logical_condition_builder.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/logical_condition_builder.tsx index 84bfe1c1d2dc91..3ae2b13326b23f 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/logical_condition_builder.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/logical_condition_builder.tsx @@ -5,17 +5,11 @@ */ import React, { memo, useCallback } from 'react'; -import { EuiButton, CommonProps, EuiText, EuiSpacer, EuiPanel } from '@elastic/eui'; +import { CommonProps, EuiText, EuiPanel } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ConditionGroup, ConditionGroupProps } from './components/condition_group'; -const BUTTON_MIN_WIDTH = Object.freeze({ minWidth: '95px' }); - -export type LogicalConditionBuilderProps = CommonProps & - ConditionGroupProps & { - onAndClicked: () => void; - isAndDisabled?: boolean; - }; +export type LogicalConditionBuilderProps = CommonProps & ConditionGroupProps; export const LogicalConditionBuilder = memo<LogicalConditionBuilderProps>( ({ entries, @@ -47,26 +41,13 @@ export const LogicalConditionBuilder = memo<LogicalConditionBuilderProps>( entries={entries} onEntryRemove={onEntryRemove} onEntryChange={onEntryChange} + onAndClicked={onAndClicked} + isAndDisabled={isAndDisabled} onVisited={onVisited} data-test-subj={getTestId('group1')} /> )} </div> - <EuiSpacer size="s" /> - <EuiButton - fill - size="s" - iconType="plusInCircle" - onClick={onAndClicked} - data-test-subj={getTestId('AndButton')} - isDisabled={isAndDisabled} - style={BUTTON_MIN_WIDTH} - > - <FormattedMessage - id="xpack.securitySolution.trustedapps.logicalConditionBuilder.andOperator" - defaultMessage="AND" - /> - </EuiButton> </div> ); } diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx index d63cda5b513dc1..c3add8cd5a9df5 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/trusted_apps_page.tsx @@ -84,7 +84,7 @@ export const TrustedAppsPage = memo(() => { {location.show === 'create' && ( <CreateTrustedAppFlyout onClose={handleAddFlyoutClose} - size="s" + size="m" data-test-subj="addTrustedAppFlyout" /> )} From 66878b4285d1ee623aa3e62194ed0781c438e0f5 Mon Sep 17 00:00:00 2001 From: spalger <spalger@users.noreply.github.com> Date: Fri, 2 Oct 2020 12:31:47 -0700 Subject: [PATCH 106/128] Revert "[core/server/plugins] don't run discovery in dev server parent process (#79235)" This reverts commit 9021c836cd24ed0cbe97d8aaa9e666e42bc2b909. --- .../server/plugins/plugins_service.test.ts | 81 ++++++------------- src/core/server/plugins/plugins_service.ts | 18 ++--- 2 files changed, 30 insertions(+), 69 deletions(-) diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index 64a382e539fb0f..d36fd2251176a2 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -102,42 +102,35 @@ const createPlugin = ( }); }; -async function testSetup(options: { isDevClusterMaster?: boolean } = {}) { - mockPackage.raw = { - branch: 'feature-v1', - version: 'v1', - build: { - distributable: true, - number: 100, - sha: 'feature-v1-build-sha', - }, - }; - - coreId = Symbol('core'); - env = Env.createDefault(REPO_ROOT, { - ...getEnvOptions(), - isDevClusterMaster: options.isDevClusterMaster ?? false, - }); +describe('PluginsService', () => { + beforeEach(async () => { + mockPackage.raw = { + branch: 'feature-v1', + version: 'v1', + build: { + distributable: true, + number: 100, + sha: 'feature-v1-build-sha', + }, + }; - config$ = new BehaviorSubject<Record<string, any>>({ plugins: { initialize: true } }); - const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); - configService = new ConfigService(rawConfigService, env, logger); - await configService.setSchema(config.path, config.schema); - pluginsService = new PluginsService({ coreId, env, logger, configService }); + coreId = Symbol('core'); + env = Env.createDefault(REPO_ROOT, getEnvOptions()); - [mockPluginSystem] = MockPluginsSystem.mock.instances as any; - mockPluginSystem.uiPlugins.mockReturnValue(new Map()); + config$ = new BehaviorSubject<Record<string, any>>({ plugins: { initialize: true } }); + const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); + configService = new ConfigService(rawConfigService, env, logger); + await configService.setSchema(config.path, config.schema); + pluginsService = new PluginsService({ coreId, env, logger, configService }); - environmentSetup = environmentServiceMock.createSetupContract(); -} + [mockPluginSystem] = MockPluginsSystem.mock.instances as any; + mockPluginSystem.uiPlugins.mockReturnValue(new Map()); -afterEach(() => { - jest.clearAllMocks(); -}); + environmentSetup = environmentServiceMock.createSetupContract(); + }); -describe('PluginsService', () => { - beforeEach(async () => { - await testSetup(); + afterEach(() => { + jest.clearAllMocks(); }); describe('#discover()', () => { @@ -620,29 +613,3 @@ describe('PluginsService', () => { }); }); }); - -describe('PluginService when isDevClusterMaster is true', () => { - beforeEach(async () => { - await testSetup({ - isDevClusterMaster: true, - }); - }); - - describe('#discover()', () => { - it('does not try to run discovery', async () => { - await expect(pluginsService.discover({ environment: environmentSetup })).resolves - .toMatchInlineSnapshot(` - Object { - "pluginTree": undefined, - "uiPlugins": Object { - "browserConfigs": Map {}, - "internal": Map {}, - "public": Map {}, - }, - } - `); - - expect(mockDiscover).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index a1062bde7765f0..e8fe42ee491ca5 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -18,7 +18,7 @@ */ import Path from 'path'; -import { Observable, EMPTY } from 'rxjs'; +import { Observable } from 'rxjs'; import { filter, first, map, mergeMap, tap, toArray } from 'rxjs/operators'; import { pick } from '@kbn/std'; @@ -86,11 +86,9 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS private readonly config$: Observable<PluginsConfig>; private readonly pluginConfigDescriptors = new Map<PluginName, PluginConfigDescriptor>(); private readonly uiPluginInternalInfo = new Map<PluginName, InternalPluginInfo>(); - private readonly discoveryDisabled: boolean; constructor(private readonly coreContext: CoreContext) { this.log = coreContext.logger.get('plugins-service'); - this.discoveryDisabled = coreContext.env.isDevClusterMaster; this.pluginsSystem = new PluginsSystem(coreContext); this.configService = coreContext.configService; this.config$ = coreContext.configService @@ -99,17 +97,13 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS } public async discover({ environment }: PluginsServiceDiscoverDeps) { - const config = await this.config$.pipe(first()).toPromise(); + this.log.debug('Discovering plugins'); - const { error$, plugin$ } = this.discoveryDisabled - ? { - error$: EMPTY, - plugin$: EMPTY, - } - : discover(config, this.coreContext, { - uuid: environment.instanceUuid, - }); + const config = await this.config$.pipe(first()).toPromise(); + const { error$, plugin$ } = discover(config, this.coreContext, { + uuid: environment.instanceUuid, + }); await this.handleDiscoveryErrors(error$); await this.handleDiscoveredPlugins(plugin$); From b66de2ce1d311ee803c838c9b5f59d372a361a01 Mon Sep 17 00:00:00 2001 From: Joe Reuter <johannes.reuter@elastic.co> Date: Fri, 2 Oct 2020 21:50:25 +0200 Subject: [PATCH 107/128] improve lens lazy loading (#79292) --- x-pack/plugins/lens/public/async_services.ts | 1 + .../editor_frame/index.ts | 2 + .../embeddable/embeddable.test.tsx | 74 +++++++++++++++---- .../embeddable/embeddable.tsx | 5 +- .../embeddable/embeddable_factory.ts | 7 +- .../editor_frame_service/embeddable/index.ts | 7 ++ .../public/editor_frame_service/service.tsx | 6 +- 7 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 x-pack/plugins/lens/public/editor_frame_service/embeddable/index.ts diff --git a/x-pack/plugins/lens/public/async_services.ts b/x-pack/plugins/lens/public/async_services.ts index 5a88b47c0e8944..09b9233197d2f0 100644 --- a/x-pack/plugins/lens/public/async_services.ts +++ b/x-pack/plugins/lens/public/async_services.ts @@ -21,4 +21,5 @@ export * from './xy_visualization/xy_visualization'; export * from './indexpattern_datasource/indexpattern'; export * from './editor_frame_service/editor_frame'; +export * from './editor_frame_service/embeddable'; export * from './app_plugin/mounter'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.ts b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.ts index 41558caafc64cf..04d4bc9c25de53 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/index.ts @@ -5,3 +5,5 @@ */ export * from './editor_frame'; +export * from './state_helpers'; +export * from './state_management'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx index d48f9ed713caff..151f85e817c70d 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.test.tsx @@ -26,7 +26,6 @@ import { VIS_EVENT_TO_TRIGGER } from '../../../../../../src/plugins/visualizatio import { coreMock, httpServiceMock } from '../../../../../../src/core/public/mocks'; import { IBasePath } from '../../../../../../src/core/public'; import { AttributeService } from '../../../../../../src/plugins/dashboard/public'; -import { Ast } from '@kbn/interpreter/common'; import { LensAttributeService } from '../../lens_attribute_service'; jest.mock('../../../../../../src/plugins/inspector/public/', () => ({ @@ -103,8 +102,14 @@ describe('embeddable', () => { indexPatternService: {} as IndexPatternsContract, editable: true, getTrigger, - documentToExpression: () => Promise.resolve({} as Ast), - toExpressionString: () => 'my | expression', + documentToExpression: () => + Promise.resolve({ + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }), }, {} as LensEmbeddableInput ); @@ -112,7 +117,8 @@ describe('embeddable', () => { embeddable.render(mountpoint); expect(expressionRenderer).toHaveBeenCalledTimes(1); - expect(expressionRenderer.mock.calls[0][0]!.expression).toEqual('my | expression'); + expect(expressionRenderer.mock.calls[0][0]!.expression).toEqual(`my +| expression`); }); it('should re-render if new input is pushed', async () => { @@ -129,8 +135,14 @@ describe('embeddable', () => { indexPatternService: {} as IndexPatternsContract, editable: true, getTrigger, - documentToExpression: () => Promise.resolve({} as Ast), - toExpressionString: () => 'my | expression', + documentToExpression: () => + Promise.resolve({ + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }), }, { id: '123' } as LensEmbeddableInput ); @@ -162,8 +174,14 @@ describe('embeddable', () => { indexPatternService: {} as IndexPatternsContract, editable: true, getTrigger, - documentToExpression: () => Promise.resolve({} as Ast), - toExpressionString: () => 'my | expression', + documentToExpression: () => + Promise.resolve({ + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }), }, input ); @@ -208,8 +226,14 @@ describe('embeddable', () => { indexPatternService: {} as IndexPatternsContract, editable: true, getTrigger, - documentToExpression: () => Promise.resolve({} as Ast), - toExpressionString: () => 'my | expression', + documentToExpression: () => + Promise.resolve({ + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }), }, input ); @@ -237,8 +261,14 @@ describe('embeddable', () => { indexPatternService: {} as IndexPatternsContract, editable: true, getTrigger, - documentToExpression: () => Promise.resolve({} as Ast), - toExpressionString: () => 'my | expression', + documentToExpression: () => + Promise.resolve({ + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }), }, { id: '123' } as LensEmbeddableInput ); @@ -270,8 +300,14 @@ describe('embeddable', () => { indexPatternService: {} as IndexPatternsContract, editable: true, getTrigger, - documentToExpression: () => Promise.resolve({} as Ast), - toExpressionString: () => 'my | expression', + documentToExpression: () => + Promise.resolve({ + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }), }, { id: '123', timeRange, query, filters } as LensEmbeddableInput ); @@ -311,8 +347,14 @@ describe('embeddable', () => { indexPatternService: {} as IndexPatternsContract, editable: true, getTrigger, - documentToExpression: () => Promise.resolve({} as Ast), - toExpressionString: () => 'my | expression', + documentToExpression: () => + Promise.resolve({ + type: 'expression', + chain: [ + { type: 'function', function: 'my', arguments: {} }, + { type: 'function', function: 'expression', arguments: {} }, + ], + }), }, { id: '123', timeRange, query, filters } as LensEmbeddableInput ); diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx index 16b19ca0af8497..1297c1da6e1b65 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable.tsx @@ -18,7 +18,7 @@ import { import { ExecutionContextSearch } from 'src/plugins/expressions'; import { Subscription } from 'rxjs'; -import { Ast } from '@kbn/interpreter/common'; +import { toExpression, Ast } from '@kbn/interpreter/common'; import { ExpressionRendererEvent, ReactExpressionRendererType, @@ -59,7 +59,6 @@ export interface LensEmbeddableOutput extends EmbeddableOutput { export interface LensEmbeddableDeps { attributeService: LensAttributeService; documentToExpression: (doc: Document) => Promise<Ast | null>; - toExpressionString: (astObj: Ast, type?: string) => string; editable: boolean; indexPatternService: IndexPatternsContract; expressionRenderer: ReactExpressionRendererType; @@ -135,7 +134,7 @@ export class Embeddable savedObjectId: (input as LensByReferenceInput)?.savedObjectId, }; const expression = await this.deps.documentToExpression(this.savedVis); - this.expression = expression ? this.deps.toExpressionString(expression) : null; + this.expression = expression ? toExpression(expression) : null; await this.initializeOutput(); this.isInitialized = true; if (this.domNode) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts index 8771d1ebaddb1b..35d120e5c4f45f 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/embeddable_factory.ts @@ -7,7 +7,7 @@ import { Capabilities, HttpSetup } from 'kibana/public'; import { i18n } from '@kbn/i18n'; import { RecursiveReadonly } from '@kbn/utility-types'; -import { toExpression, Ast } from '@kbn/interpreter/target/common'; +import { Ast } from '@kbn/interpreter/target/common'; import { IndexPatternsContract, TimefilterContract, @@ -17,7 +17,7 @@ import { EmbeddableFactoryDefinition, IContainer, } from '../../../../../../src/plugins/embeddable/public'; -import { Embeddable, LensByReferenceInput, LensEmbeddableInput } from './embeddable'; +import { LensByReferenceInput, LensEmbeddableInput } from './embeddable'; import { DOC_TYPE } from '../../persistence'; import { UiActionsStart } from '../../../../../../src/plugins/ui_actions/public'; import { Document } from '../../persistence/saved_object_store'; @@ -83,6 +83,8 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { indexPatternService, } = await this.getStartServices(); + const { Embeddable } = await import('../../async_services'); + return new Embeddable( { attributeService, @@ -93,7 +95,6 @@ export class EmbeddableFactory implements EmbeddableFactoryDefinition { basePath: coreHttp.basePath, getTrigger: uiActions?.getTrigger, documentToExpression, - toExpressionString: toExpression, }, input, parent diff --git a/x-pack/plugins/lens/public/editor_frame_service/embeddable/index.ts b/x-pack/plugins/lens/public/editor_frame_service/embeddable/index.ts new file mode 100644 index 00000000000000..460341365094e1 --- /dev/null +++ b/x-pack/plugins/lens/public/editor_frame_service/embeddable/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './embeddable'; diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index 8892217f5d51d8..e6d7f78f5ad071 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -25,10 +25,8 @@ import { Document } from '../persistence/saved_object_store'; import { mergeTables } from './merge_tables'; import { formatColumn } from './format_column'; import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; -import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { DashboardStart } from '../../../../../src/plugins/dashboard/public'; -import { persistedStateToExpression } from './editor_frame/state_helpers'; import { LensAttributeService } from '../lens_attribute_service'; export interface EditorFrameSetupPlugins { @@ -77,6 +75,8 @@ export class EditorFrameService { collectAsyncDefinitions(this.visualizations), ]); + const { persistedStateToExpression } = await import('../async_services'); + return await persistedStateToExpression(resolvedDatasources, resolvedVisualizations, doc); } @@ -133,7 +133,7 @@ export class EditorFrameService { const firstDatasourceId = Object.keys(resolvedDatasources)[0]; const firstVisualizationId = Object.keys(resolvedVisualizations)[0]; - const { EditorFrame } = await import('../async_services'); + const { EditorFrame, getActiveDatasourceIdFromDoc } = await import('../async_services'); render( <I18nProvider> From c456f64a7e4d4feca2b54ea76e9851095b249520 Mon Sep 17 00:00:00 2001 From: Madison Caldwell <madison.rey.caldwell@gmail.com> Date: Fri, 2 Oct 2020 15:54:43 -0400 Subject: [PATCH 108/128] [Security Solution][Exceptions] Add lowercase normalizer for case-insensitivity + deprecate _tags field (new OS field) (#77379) * Finish adding .lower to exceptionable fields * Add back migrations * .lower -> .caseless * Add separate field for os type * updates * Type updates * Switch over to osTypes * get rid of _tags * Add tests for schema validation * Remove remaining references to _tags * Another round of test fixes * DefaultArray tests * More test fixes * Fix remaining test failures * types / tests * more test updates * lowercase os values * Address feedback + fix test failure * tests * Fix integration test * process.executable.path -> process.executable.caseless Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../elasticsearch/template/template.test.ts | 31 ++++ .../epm/elasticsearch/template/template.ts | 3 + .../server/services/epm/fields/field.ts | 1 + x-pack/plugins/lists/README.md | 20 +-- x-pack/plugins/lists/common/constants.mock.ts | 3 +- .../common/schemas/common/schemas.test.ts | 33 +++++ .../lists/common/schemas/common/schemas.ts | 20 ++- .../create_endpoint_list_item_schema.mock.ts | 4 +- .../create_endpoint_list_item_schema.test.ts | 13 -- .../create_endpoint_list_item_schema.ts | 10 +- .../create_exception_list_item_schema.mock.ts | 6 +- .../create_exception_list_item_schema.test.ts | 13 -- .../create_exception_list_item_schema.ts | 10 +- .../create_exception_list_schema.mock.ts | 2 +- .../create_exception_list_schema.test.ts | 13 -- .../request/create_exception_list_schema.ts | 10 +- .../update_endpoint_list_item_schema.mock.ts | 4 +- .../update_endpoint_list_item_schema.test.ts | 12 -- .../update_endpoint_list_item_schema.ts | 10 +- .../update_exception_list_item_schema.mock.ts | 5 +- .../update_exception_list_item_schema.test.ts | 12 -- .../update_exception_list_item_schema.ts | 10 +- .../update_exception_list_schema.mock.ts | 4 +- .../update_exception_list_schema.test.ts | 12 -- .../request/update_exception_list_schema.ts | 10 +- .../create_endpoint_list_schema.test.ts | 2 +- .../exception_list_item_schema.mock.ts | 5 +- .../response/exception_list_item_schema.ts | 4 +- .../response/exception_list_schema.mock.ts | 4 +- .../schemas/response/exception_list_schema.ts | 4 +- .../exceptions_list_so_schema.ts | 4 +- x-pack/plugins/lists/common/shared_exports.ts | 2 + x-pack/plugins/lists/common/shared_imports.ts | 1 + .../routes/create_endpoint_list_item_route.ts | 4 +- .../create_exception_list_item_route.ts | 4 +- .../routes/create_exception_list_route.ts | 2 - .../routes/update_endpoint_list_item_route.ts | 4 +- .../update_exception_list_item_route.ts | 4 +- .../routes/update_exception_list_route.ts | 4 +- .../server/saved_objects/exception_list.ts | 7 + .../server/saved_objects/migrations.test.ts | 132 ++++++++++++++++++ .../lists/server/saved_objects/migrations.ts | 66 +++++++++ .../new/endpoint_list_item.json | 2 +- .../exception_lists/new/exception_list.json | 1 - .../new/exception_list_agnostic.json | 1 - .../new/exception_list_detection.json | 1 - .../new/exception_list_item.json | 2 +- .../new/exception_list_item_agnostic.json | 2 +- .../new/exception_list_item_auto_id.json | 2 +- ...exception_list_item_detection_auto_id.json | 1 - .../exception_list_item_with_bad_ip_list.json | 2 +- .../new/exception_list_item_with_list.json | 2 +- .../new/trusted_app_list_item_agnostic.json | 2 +- .../updates/simple_update.json | 2 +- .../updates/simple_update_agnostic.json | 2 +- .../updates/simple_update_item.json | 1 - .../exception_lists/create_endpoint_list.ts | 2 +- .../create_endpoint_trusted_apps_list.ts | 2 +- .../exception_lists/create_exception_list.ts | 5 +- .../create_exception_list_item.ts | 8 +- .../exception_lists/exception_list_client.ts | 22 ++- .../exception_list_client_types.ts | 14 +- .../exception_lists/update_exception_list.ts | 6 +- .../update_exception_list_item.ts | 8 +- .../server/services/exception_lists/utils.ts | 16 +-- .../schemas/types/default_array.test.ts | 80 +++++++++++ .../schemas/types/default_array.ts | 25 ++++ .../detection_engine/schemas/types/index.ts | 1 + .../endpoint/schema/trusted_apps.test.ts | 4 +- .../common/endpoint/schema/trusted_apps.ts | 2 +- .../common/endpoint/types/trusted_apps.ts | 2 +- .../common/shared_exports.ts | 1 + .../common/shared_imports.ts | 2 + .../add_exception_modal/index.test.tsx | 2 +- .../exceptions/add_exception_modal/index.tsx | 23 ++- .../exceptions/builder/helpers.test.tsx | 8 +- .../components/exceptions/builder/index.tsx | 3 +- .../exceptions/edit_exception_modal/index.tsx | 4 +- .../exceptions/exceptionable_fields.json | 40 +++--- .../components/exceptions/helpers.test.tsx | 119 +++------------- .../common/components/exceptions/helpers.tsx | 46 +----- ...se_fetch_or_create_rule_exception_list.tsx | 1 - .../viewer/exception_item/index.stories.tsx | 3 - .../exceptions/viewer/helpers.test.tsx | 10 +- .../components/exceptions/viewer/helpers.tsx | 9 +- .../components/condition_entry.tsx | 2 +- .../trusted_app_card/index.stories.tsx | 4 +- .../scripts/endpoint/trusted_apps/index.ts | 2 +- .../endpoint/lib/artifacts/lists.test.ts | 42 +++--- .../server/endpoint/lib/artifacts/lists.ts | 8 +- .../routes/trusted_apps/trusted_apps.test.ts | 19 +-- .../endpoint/routes/trusted_apps/utils.ts | 23 +-- 92 files changed, 636 insertions(+), 479 deletions(-) create mode 100644 x-pack/plugins/lists/server/saved_objects/migrations.test.ts create mode 100644 x-pack/plugins/lists/server/saved_objects/migrations.ts create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.test.ts create mode 100644 x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts index 99e568bf771f83..cc1aa79c7491ce 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.test.ts @@ -212,6 +212,37 @@ test('tests processing keyword field with multi fields with analyzed text field' expect(mappings).toEqual(keywordWithAnalyzedMultiFieldsMapping); }); +test('tests processing keyword field with multi fields with normalized keyword field', () => { + const keywordWithNormalizedMultiFieldsLiteralYml = ` + - name: keywordWithNormalizedMultiField + type: keyword + multi_fields: + - name: normalized + type: keyword + normalizer: lowercase + `; + + const keywordWithNormalizedMultiFieldsMapping = { + properties: { + keywordWithNormalizedMultiField: { + ignore_above: 1024, + type: 'keyword', + fields: { + normalized: { + type: 'keyword', + ignore_above: 1024, + normalizer: 'lowercase', + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(keywordWithNormalizedMultiFieldsLiteralYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(keywordWithNormalizedMultiFieldsMapping); +}); + test('tests processing object field with no other attributes', () => { const objectFieldLiteralYml = ` - name: objectField diff --git a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts index 00c2e873ba1295..e0fea59107c267 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/elasticsearch/template/template.ts @@ -189,6 +189,9 @@ function generateKeywordMapping(field: Field): IndexTemplateMapping { if (field.ignore_above) { mapping.ignore_above = field.ignore_above; } + if (field.normalizer) { + mapping.normalizer = field.normalizer; + } return mapping; } diff --git a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts index a44e5e4221f9fb..5913302e77ba69 100644 --- a/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts +++ b/x-pack/plugins/ingest_manager/server/services/epm/fields/field.ts @@ -20,6 +20,7 @@ export interface Field { index?: boolean; required?: boolean; multi_fields?: Fields; + normalizer?: string; doc_values?: boolean; copy_to?: string; analyzer?: string; diff --git a/x-pack/plugins/lists/README.md b/x-pack/plugins/lists/README.md index dac6e8bb78fa57..02be7573034175 100644 --- a/x-pack/plugins/lists/README.md +++ b/x-pack/plugins/lists/README.md @@ -113,12 +113,6 @@ You should see the new exception list created like so: ```sh { - "_tags": [ - "endpoint", - "process", - "malware", - "os:linux" - ], "created_at": "2020-05-28T19:16:31.052Z", "created_by": "yo", "description": "This is a sample endpoint type exception", @@ -141,12 +135,6 @@ And you can attach exception list items like so: ```ts { - "_tags": [ - "endpoint", - "process", - "malware", - "os:linux" - ], "comments": [], "created_at": "2020-05-28T19:17:21.099Z", "created_by": "yo", @@ -173,6 +161,7 @@ And you can attach exception list items like so: "list_id": "endpoint_list", "name": "Sample Endpoint Exception List", "namespace_type": "single", + "os_types": ["linux"], "tags": [ "user added string for a tag", "malware" @@ -222,12 +211,6 @@ or for finding exception lists: { "data": [ { - "_tags": [ - "endpoint", - "process", - "malware", - "os:linux" - ], "created_at": "2020-05-28T19:16:31.052Z", "created_by": "yo", "description": "This is a sample endpoint type exception", @@ -235,6 +218,7 @@ or for finding exception lists: "list_id": "endpoint_list", "name": "Sample Endpoint Exception List", "namespace_type": "single", + "os_types": ["linux"], "tags": [ "user added string for a tag", "malware" diff --git a/x-pack/plugins/lists/common/constants.mock.ts b/x-pack/plugins/lists/common/constants.mock.ts index 46ed524ff33e34..c712af83dd9b16 100644 --- a/x-pack/plugins/lists/common/constants.mock.ts +++ b/x-pack/plugins/lists/common/constants.mock.ts @@ -5,6 +5,7 @@ */ import moment from 'moment'; +import { OsTypeArray } from './schemas/common'; import { EntriesArray } from './schemas/types'; import { EndpointEntriesArray } from './schemas/types/endpoint'; export const DATE_NOW = '2020-04-20T15:25:31.830Z'; @@ -68,7 +69,7 @@ export const ENDPOINT_ENTRIES: EndpointEntriesArray = [ { field: 'some.not.nested.field', operator: 'included', type: 'match', value: 'some value' }, ]; export const ITEM_TYPE = 'simple'; -export const _TAGS = []; +export const OS_TYPES: OsTypeArray = ['windows']; export const TAGS = []; export const COMMENTS = []; export const FILTER = 'name:Nicolas Bourbaki'; diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.test.ts b/x-pack/plugins/lists/common/schemas/common/schemas.test.ts index ec3871b6738883..04bdf037c556ef 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.test.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.test.ts @@ -27,6 +27,8 @@ import { esDataTypeUnion, exceptionListType, operator, + osType, + osTypeArrayOrUndefined, type, } from './schemas'; @@ -379,4 +381,35 @@ describe('Common schemas', () => { expect(message.schema).toEqual({}); }); }); + + describe('osType', () => { + test('it will validate a correct osType', () => { + const payload = 'windows'; + const decoded = osType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it will fail to validate an incorrect osType', () => { + const payload = 'foo'; + const decoded = osType.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "foo" supplied to ""linux" | "macos" | "windows""', + ]); + expect(message.schema).toEqual({}); + }); + + test('it will default to an empty array when osTypeArrayOrUndefined is used', () => { + const payload = undefined; + const decoded = osTypeArrayOrUndefined.decode(payload); + const checked = exactCheck(payload, decoded); + const message = pipe(checked, foldLeftRight); + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); + }); }); diff --git a/x-pack/plugins/lists/common/schemas/common/schemas.ts b/x-pack/plugins/lists/common/schemas/common/schemas.ts index 37da5fbcd1a1b0..7497b81fbe91ca 100644 --- a/x-pack/plugins/lists/common/schemas/common/schemas.ts +++ b/x-pack/plugins/lists/common/schemas/common/schemas.ts @@ -9,7 +9,7 @@ import * as t from 'io-ts'; import { DefaultNamespace } from '../types/default_namespace'; -import { DefaultStringArray, NonEmptyString } from '../../shared_imports'; +import { DefaultArray, DefaultStringArray, NonEmptyString } from '../../shared_imports'; export const name = t.string; export type Name = t.TypeOf<typeof name>; @@ -211,11 +211,6 @@ export type Tags = t.TypeOf<typeof tags>; export const tagsOrUndefined = t.union([tags, t.undefined]); export type TagsOrUndefined = t.TypeOf<typeof tagsOrUndefined>; -export const _tags = DefaultStringArray; -export type _Tags = t.TypeOf<typeof _tags>; -export const _tagsOrUndefined = t.union([_tags, t.undefined]); -export type _TagsOrUndefined = t.TypeOf<typeof _tagsOrUndefined>; - export const exceptionListType = t.keyof({ detection: null, endpoint: null }); export const exceptionListTypeOrUndefined = t.union([exceptionListType, t.undefined]); export type ExceptionListType = t.TypeOf<typeof exceptionListType>; @@ -317,3 +312,16 @@ export type Immutable = t.TypeOf<typeof immutable>; export const immutableOrUndefined = t.union([immutable, t.undefined]); export type ImmutableOrUndefined = t.TypeOf<typeof immutableOrUndefined>; + +export const osType = t.keyof({ + linux: null, + macos: null, + windows: null, +}); +export type OsType = t.TypeOf<typeof osType>; + +export const osTypeArray = DefaultArray(osType); +export type OsTypeArray = t.TypeOf<typeof osTypeArray>; + +export const osTypeArrayOrUndefined = t.union([osTypeArray, t.undefined]); +export type OsTypeArrayOrUndefined = t.OutputOf<typeof osTypeArray>; diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts index 529e173618f15b..f292b7c5bc9459 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.mock.ts @@ -11,20 +11,20 @@ import { ITEM_TYPE, META, NAME, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { CreateEndpointListItemSchema } from './create_endpoint_list_item_schema'; export const getCreateEndpointListItemSchemaMock = (): CreateEndpointListItemSchema => ({ - _tags: _TAGS, comments: COMMENTS, description: DESCRIPTION, entries: ENDPOINT_ENTRIES, item_id: undefined, meta: META, name: NAME, + os_types: OS_TYPES, tags: TAGS, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts index 624de2fb30d17b..afb0454a79667b 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.test.ts @@ -174,19 +174,6 @@ describe('create_endpoint_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateEndpointListItemSchemaMock(); - const outputPayload = getCreateEndpointListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = createEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateEndpointListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { const inputPayload = getCreateEndpointListItemSchemaMock(); delete inputPayload.item_id; diff --git a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts index d1fc167f5a92b6..611d9a83befc74 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_endpoint_list_item_schema.ts @@ -8,13 +8,13 @@ import * as t from 'io-ts'; import { ItemId, + OsTypeArray, Tags, - _Tags, - _tags, description, exceptionListItemType, meta, name, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -34,10 +34,10 @@ export const createEndpointListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode meta, // defaults to undefined if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -48,11 +48,11 @@ export type CreateEndpointListItemSchema = t.OutputOf<typeof createEndpointListI // This type is used after a decode since some things are defaults after a decode. export type CreateEndpointListItemSchemaDecoded = Omit< RequiredKeepUndefined<t.TypeOf<typeof createEndpointListItemSchema>>, - '_tags' | 'tags' | 'item_id' | 'entries' | 'comments' + 'tags' | 'item_id' | 'entries' | 'comments' | 'os_types' > & { - _tags: _Tags; comments: CreateCommentsArray; tags: Tags; item_id: ItemId; entries: EntriesArray; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts index da22e33dc7b524..9a55e88a7a8fa3 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.mock.ts @@ -14,14 +14,13 @@ import { META, NAME, NAMESPACE_TYPE, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { CreateExceptionListItemSchema } from './create_exception_list_item_schema'; export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemSchema => ({ - _tags: _TAGS, comments: COMMENTS, description: DESCRIPTION, entries: ENTRIES, @@ -30,6 +29,7 @@ export const getCreateExceptionListItemSchemaMock = (): CreateExceptionListItemS meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: OS_TYPES, tags: TAGS, type: ITEM_TYPE, }); @@ -43,6 +43,7 @@ export const getCreateExceptionListItemMinimalSchemaMock = (): CreateExceptionLi item_id: ITEM_ID, list_id: LIST_ID, name: NAME, + os_types: OS_TYPES, type: ITEM_TYPE, }); @@ -54,5 +55,6 @@ export const getCreateExceptionListItemMinimalSchemaMockWithoutId = (): CreateEx entries: ENTRIES, list_id: LIST_ID, name: NAME, + os_types: OS_TYPES, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts index 4a4c3972dc1e35..e83b2e30107852 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.test.ts @@ -176,19 +176,6 @@ describe('create_exception_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should pass validation when supplied an undefined for "_tags" but return an array and generate a correct body not counting the auto generated uuid', () => { - const inputPayload = getCreateExceptionListItemSchemaMock(); - const outputPayload = getCreateExceptionListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = createExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListItemSchema).item_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should pass validation when supplied an undefined for "item_id" and auto generate a uuid', () => { const inputPayload = getCreateExceptionListItemSchemaMock(); delete inputPayload.item_id; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts index fd3390721d41e9..642a6c549e7faf 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_item_schema.ts @@ -8,15 +8,15 @@ import * as t from 'io-ts'; import { ItemId, + OsTypeArray, Tags, - _Tags, - _tags, description, exceptionListItemType, list_id, meta, name, namespace_type, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -41,11 +41,11 @@ export const createExceptionListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode comments: DefaultCreateCommentsArray, // defaults to empty array if not set during decode item_id: DefaultUuid, // defaults to GUID (uuid v4) if not set during decode meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -56,12 +56,12 @@ export type CreateExceptionListItemSchema = t.OutputOf<typeof createExceptionLis // This type is used after a decode since some things are defaults after a decode. export type CreateExceptionListItemSchemaDecoded = Omit< RequiredKeepUndefined<t.TypeOf<typeof createExceptionListItemSchema>>, - '_tags' | 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' + 'tags' | 'item_id' | 'entries' | 'namespace_type' | 'comments' > & { - _tags: _Tags; comments: CreateCommentsArray; tags: Tags; item_id: ItemId; entries: EntriesArray; namespace_type: NamespaceType; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts index f8431fcce1bf76..3150cb9975f216 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.mock.ts @@ -17,12 +17,12 @@ import { import { CreateExceptionListSchema } from './create_exception_list_schema'; export const getCreateExceptionListSchemaMock = (): CreateExceptionListSchema => ({ - _tags: [], description: DESCRIPTION, list_id: undefined, meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: [], tags: [], type: ENDPOINT_TYPE, version: VERSION, diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts index c9e2aa37a132bf..6bcd3bc15a9752 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.test.ts @@ -50,19 +50,6 @@ describe('create_exception_list_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array and generate a correct body not counting the uuid', () => { - const inputPayload = getCreateExceptionListSchemaMock(); - const outputPayload = getCreateExceptionListSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = createExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - delete (message.schema as CreateExceptionListSchema).list_id; - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should accept an undefined for "list_id" and auto generate a uuid', () => { const inputPayload = getCreateExceptionListSchemaMock(); delete inputPayload.list_id; diff --git a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts index ffec974602714a..4eae11081454c4 100644 --- a/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/create_exception_list_schema.ts @@ -8,14 +8,14 @@ import * as t from 'io-ts'; import { ListId, + OsTypeArray, Tags, - _Tags, - _tags, description, exceptionListType, meta, name, namespace_type, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -36,10 +36,10 @@ export const createExceptionListSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode list_id: DefaultUuid, // defaults to a GUID (UUID v4) string if not set during decode meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode version: DefaultVersionNumber, // defaults to numerical 1 if not set during decode }) @@ -51,11 +51,11 @@ export type CreateExceptionListSchema = t.OutputOf<typeof createExceptionListSch // This type is used after a decode since some things are defaults after a decode. export type CreateExceptionListSchemaDecoded = Omit< RequiredKeepUndefined<t.TypeOf<typeof createExceptionListSchema>>, - '_tags' | 'tags' | 'list_id' | 'namespace_type' + 'tags' | 'list_id' | 'namespace_type' | 'os_types' > & { - _tags: _Tags; tags: Tags; list_id: ListId; namespace_type: NamespaceType; + os_types: OsTypeArray; version: DefaultVersionNumberDecoded; }; diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts index 0847389dac922c..8c999332e88936 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.mock.ts @@ -13,14 +13,13 @@ import { LIST_ITEM_ID, META, NAME, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { UpdateEndpointListItemSchema } from './update_endpoint_list_item_schema'; export const getUpdateEndpointListItemSchemaMock = (): UpdateEndpointListItemSchema => ({ - _tags: _TAGS, _version: undefined, comments: COMMENTS, description: DESCRIPTION, @@ -29,6 +28,7 @@ export const getUpdateEndpointListItemSchemaMock = (): UpdateEndpointListItemSch item_id: LIST_ITEM_ID, meta: META, name: NAME, + os_types: OS_TYPES, tags: TAGS, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts index 671e38ceb52661..c7be8b78d54a6f 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.test.ts @@ -127,18 +127,6 @@ describe('update_endpoint_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array', () => { - const inputPayload = getUpdateEndpointListItemSchemaMock(); - const outputPayload = getUpdateEndpointListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = updateEndpointListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should not allow an extra key to be sent in', () => { const payload: UpdateEndpointListItemSchema & { extraKey?: string; diff --git a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts index 6ce5ad7858b788..f6ced91cd40106 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_endpoint_list_item_schema.ts @@ -7,15 +7,15 @@ import * as t from 'io-ts'; import { + OsTypeArray, Tags, - _Tags, - _tags, _version, description, exceptionListItemType, id, meta, name, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -37,12 +37,12 @@ export const updateEndpointListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode _version, // defaults to undefined if not set during decode comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode id, // defaults to undefined if not set during decode item_id: t.union([t.string, t.undefined]), meta, // defaults to undefined if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -53,10 +53,10 @@ export type UpdateEndpointListItemSchema = t.OutputOf<typeof updateEndpointListI // This type is used after a decode since some things are defaults after a decode. export type UpdateEndpointListItemSchemaDecoded = Omit< RequiredKeepUndefined<t.TypeOf<typeof updateEndpointListItemSchema>>, - '_tags' | 'tags' | 'entries' | 'comments' + 'tags' | 'entries' | 'comments' > & { - _tags: _Tags; comments: UpdateCommentsArray; tags: Tags; entries: EntriesArray; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts index 4673c0fe7629d2..e65b37b48545e1 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.mock.ts @@ -15,14 +15,13 @@ import { META, NAME, NAMESPACE_TYPE, + OS_TYPES, TAGS, - _TAGS, } from '../../constants.mock'; import { UpdateExceptionListItemSchema } from './update_exception_list_item_schema'; export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemSchema => ({ - _tags: _TAGS, _version: undefined, comments: COMMENTS, description: DESCRIPTION, @@ -32,6 +31,7 @@ export const getUpdateExceptionListItemSchemaMock = (): UpdateExceptionListItemS meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: ['linux'], tags: TAGS, type: ITEM_TYPE, }); @@ -45,5 +45,6 @@ export const getUpdateMinimalExceptionListItemSchemaMock = (): UpdateExceptionLi entries: ENTRIES, item_id: ITEM_ID, name: NAME, + os_types: OS_TYPES, type: ITEM_TYPE, }); diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts index da320a4983de37..387c29ad2d190c 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.test.ts @@ -139,18 +139,6 @@ describe('update_exception_list_item_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array', () => { - const inputPayload = getUpdateExceptionListItemSchemaMock(); - const outputPayload = getUpdateExceptionListItemSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = updateExceptionListItemSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should accept an undefined for "item_id" and generate a correct body not counting the uuid', () => { const inputPayload = getUpdateExceptionListItemSchemaMock(); delete inputPayload.item_id; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts index 659dde0b5b533e..14cac2bb93fe0c 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_item_schema.ts @@ -7,9 +7,8 @@ import * as t from 'io-ts'; import { + OsTypeArray, Tags, - _Tags, - _tags, _version, description, exceptionListItemType, @@ -17,6 +16,7 @@ import { meta, name, namespace_type, + osTypeArrayOrUndefined, tags, } from '../common/schemas'; import { RequiredKeepUndefined } from '../../types'; @@ -39,13 +39,13 @@ export const updateExceptionListItemSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode _version, // defaults to undefined if not set during decode comments: DefaultUpdateCommentsArray, // defaults to empty array if not set during decode id, // defaults to undefined if not set during decode item_id: t.union([t.string, t.undefined]), meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode }) ), @@ -56,11 +56,11 @@ export type UpdateExceptionListItemSchema = t.OutputOf<typeof updateExceptionLis // This type is used after a decode since some things are defaults after a decode. export type UpdateExceptionListItemSchemaDecoded = Omit< RequiredKeepUndefined<t.TypeOf<typeof updateExceptionListItemSchema>>, - '_tags' | 'tags' | 'entries' | 'namespace_type' | 'comments' + 'tags' | 'entries' | 'namespace_type' | 'comments' | 'os_types' > & { - _tags: _Tags; comments: UpdateCommentsArray; tags: Tags; entries: EntriesArray; namespace_type: NamespaceType; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts index b7dc2d9e0c9487..fdefa6fe9b2c51 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.mock.ts @@ -4,12 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ -import { DESCRIPTION, ID, LIST_ID, META, NAME, NAMESPACE_TYPE, _TAGS } from '../../constants.mock'; +import { DESCRIPTION, ID, LIST_ID, META, NAME, NAMESPACE_TYPE } from '../../constants.mock'; import { UpdateExceptionListSchema } from './update_exception_list_schema'; export const getUpdateExceptionListSchemaMock = (): UpdateExceptionListSchema => ({ - _tags: _TAGS, _version: undefined, description: DESCRIPTION, id: ID, @@ -17,6 +16,7 @@ export const getUpdateExceptionListSchemaMock = (): UpdateExceptionListSchema => meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: [], tags: ['malware'], type: 'endpoint', }); diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts index 32f114ae34d8e9..4afd1aa442aa7e 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.test.ts @@ -100,18 +100,6 @@ describe('update_exception_list_schema', () => { expect(message.schema).toEqual(outputPayload); }); - test('it should accept an undefined for "_tags" but return an array', () => { - const inputPayload = getUpdateExceptionListSchemaMock(); - const outputPayload = getUpdateExceptionListSchemaMock(); - delete inputPayload._tags; - outputPayload._tags = []; - const decoded = updateExceptionListSchema.decode(inputPayload); - const checked = exactCheck(inputPayload, decoded); - const message = pipe(checked, foldLeftRight); - expect(getPaths(left(message.errors))).toEqual([]); - expect(message.schema).toEqual(outputPayload); - }); - test('it should accept an undefined for "list_id" and generate a correct body not counting the uuid', () => { const inputPayload = getUpdateExceptionListSchemaMock(); delete inputPayload.list_id; diff --git a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts index 54e0bbafe49812..37ba21bcfc4244 100644 --- a/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/request/update_exception_list_schema.ts @@ -7,9 +7,8 @@ import * as t from 'io-ts'; import { + OsTypeArray, Tags, - _Tags, - _tags, _version, description, exceptionListType, @@ -18,6 +17,7 @@ import { meta, name, namespace_type, + osTypeArrayOrUndefined, tags, version, } from '../common/schemas'; @@ -34,12 +34,12 @@ export const updateExceptionListSchema = t.intersection([ ), t.exact( t.partial({ - _tags, // defaults to empty array if not set during decode _version, // defaults to undefined if not set during decode id, // defaults to undefined if not set during decode list_id, // defaults to undefined if not set during decode meta, // defaults to undefined if not set during decode namespace_type, // defaults to 'single' if not set during decode + os_types: osTypeArrayOrUndefined, // defaults to empty array if not set during decode tags, // defaults to empty array if not set during decode version, // defaults to undefined if not set during decode }) @@ -51,9 +51,9 @@ export type UpdateExceptionListSchema = t.OutputOf<typeof updateExceptionListSch // This type is used after a decode since the arrays turn into defaults of empty arrays. export type UpdateExceptionListSchemaDecoded = Omit< RequiredKeepUndefined<t.TypeOf<typeof updateExceptionListSchema>>, - '_tags | tags | namespace_type' + 'tags | namespace_type' | 'os_types' > & { - _tags: _Tags; tags: Tags; namespace_type: NamespaceType; + os_types: OsTypeArray; }; diff --git a/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts b/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts index 380a1e3a4cfd55..ebae189ca7d06f 100644 --- a/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts +++ b/x-pack/plugins/lists/common/schemas/response/create_endpoint_list_schema.test.ts @@ -42,7 +42,7 @@ describe('create_endpoint_list_schema', () => { const message = pipe(checked, foldLeftRight); expect(getPaths(left(message.errors))).toEqual([ - 'invalid keys "_tags,["endpoint","process","malware","os:linux"],_version,created_at,created_by,description,id,immutable,meta,{},name,namespace_type,tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by,version"', + 'invalid keys "_version,created_at,created_by,description,id,immutable,meta,{},name,namespace_type,os_types,["linux"],tags,["user added string for a tag","malware"],tie_breaker_id,type,updated_at,updated_by,version"', ]); expect(message.schema).toEqual({}); }); diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts index 1a8f21a5232f80..c2a751c03ee132 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.mock.ts @@ -15,6 +15,7 @@ import { META, NAME, NAMESPACE_TYPE, + OS_TYPES, TIE_BREAKER, USER, } from '../../constants.mock'; @@ -22,7 +23,6 @@ import { import { ExceptionListItemSchema } from './exception_list_item_schema'; export const getExceptionListItemSchemaMock = (): ExceptionListItemSchema => ({ - _tags: ['endpoint', 'process', 'malware', 'os:linux'], _version: undefined, comments: COMMENTS, created_at: DATE_NOW, @@ -35,6 +35,7 @@ export const getExceptionListItemSchemaMock = (): ExceptionListItemSchema => ({ meta: META, name: NAME, namespace_type: NAMESPACE_TYPE, + os_types: ['linux'], tags: ['user added string for a tag', 'malware'], tie_breaker_id: TIE_BREAKER, type: ITEM_TYPE, @@ -49,7 +50,6 @@ export const getExceptionListItemSchemaMock = (): ExceptionListItemSchema => ({ export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = (): Partial< ExceptionListItemSchema > => ({ - _tags: [], comments: [], created_by: ELASTIC_USER, description: DESCRIPTION, @@ -58,6 +58,7 @@ export const getExceptionListItemResponseMockWithoutAutoGeneratedValues = (): Pa list_id: LIST_ID, name: NAME, namespace_type: 'single', + os_types: OS_TYPES, tags: [], type: ITEM_TYPE, updated_by: ELASTIC_USER, diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts index 65a1a26eaa6227..f5ee12e098d176 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_item_schema.ts @@ -7,7 +7,6 @@ import * as t from 'io-ts'; import { - _tags, _versionOrUndefined, created_at, created_by, @@ -19,6 +18,7 @@ import { metaOrUndefined, name, namespace_type, + osTypeArray, tags, tie_breaker_id, updated_at, @@ -28,7 +28,6 @@ import { commentsArray, entriesArray } from '../types'; export const exceptionListItemSchema = t.exact( t.type({ - _tags, _version: _versionOrUndefined, comments: commentsArray, created_at, @@ -41,6 +40,7 @@ export const exceptionListItemSchema = t.exact( meta: metaOrUndefined, name, namespace_type, + os_types: osTypeArray, tags, tie_breaker_id, type: exceptionListItemType, diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts index 6df051e83b97c2..7371a9d16fd4d9 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.mock.ts @@ -28,7 +28,6 @@ import { import { ExceptionListSchema } from './exception_list_schema'; export const getExceptionListSchemaMock = (): ExceptionListSchema => ({ - _tags: ['endpoint', 'process', 'malware', 'os:linux'], _version: _VERSION, created_at: DATE_NOW, created_by: USER, @@ -39,6 +38,7 @@ export const getExceptionListSchemaMock = (): ExceptionListSchema => ({ meta: META, name: 'Sample Endpoint Exception List', namespace_type: 'agnostic', + os_types: ['linux'], tags: ['user added string for a tag', 'malware'], tie_breaker_id: TIE_BREAKER, type: ENDPOINT_TYPE, @@ -63,13 +63,13 @@ export const getTrustedAppsListSchemaMock = (): ExceptionListSchema => { export const getExceptionResponseMockWithoutAutoGeneratedValues = (): Partial< ExceptionListSchema > => ({ - _tags: [], created_by: ELASTIC_USER, description: DESCRIPTION, immutable: IMMUTABLE, list_id: LIST_ID, name: NAME, namespace_type: 'single', + os_types: [], tags: [], type: ENDPOINT_TYPE, updated_by: ELASTIC_USER, diff --git a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts index 6597cb20508cab..ec03467c64e5c1 100644 --- a/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts +++ b/x-pack/plugins/lists/common/schemas/response/exception_list_schema.ts @@ -7,7 +7,6 @@ import * as t from 'io-ts'; import { - _tags, _versionOrUndefined, created_at, created_by, @@ -19,6 +18,7 @@ import { metaOrUndefined, name, namespace_type, + osTypeArray, tags, tie_breaker_id, updated_at, @@ -28,7 +28,6 @@ import { export const exceptionListSchema = t.exact( t.type({ - _tags, _version: _versionOrUndefined, created_at, created_by, @@ -39,6 +38,7 @@ export const exceptionListSchema = t.exact( meta: metaOrUndefined, name, namespace_type, + os_types: osTypeArray, tags, tie_breaker_id, type: exceptionListType, diff --git a/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts index f4db77f4ee0579..16c43e4611edb6 100644 --- a/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts +++ b/x-pack/plugins/lists/common/schemas/saved_objects/exceptions_list_so_schema.ts @@ -8,7 +8,6 @@ import * as t from 'io-ts'; import { commentsArrayOrUndefined, entriesArrayOrUndefined } from '../types'; import { - _tags, created_at, created_by, description, @@ -20,6 +19,7 @@ import { list_type, metaOrUndefined, name, + osTypeArray, tags, tie_breaker_id, updated_by, @@ -31,7 +31,6 @@ import { */ export const exceptionListSoSchema = t.exact( t.type({ - _tags, comments: commentsArrayOrUndefined, created_at, created_by, @@ -43,6 +42,7 @@ export const exceptionListSoSchema = t.exact( list_type, meta: metaOrUndefined, name, + os_types: osTypeArray, tags, tie_breaker_id, type: t.union([exceptionListType, exceptionListItemType]), diff --git a/x-pack/plugins/lists/common/shared_exports.ts b/x-pack/plugins/lists/common/shared_exports.ts index 361837bdef2291..ec9358c2cb503e 100644 --- a/x-pack/plugins/lists/common/shared_exports.ts +++ b/x-pack/plugins/lists/common/shared_exports.ts @@ -41,6 +41,8 @@ export { namespaceType, ExceptionListType, Type, + osTypeArray, + OsTypeArray, } from './schemas'; export { ENDPOINT_LIST_ID } from './constants'; diff --git a/x-pack/plugins/lists/common/shared_imports.ts b/x-pack/plugins/lists/common/shared_imports.ts index e5302b5cd5d88a..9fe37465519ea8 100644 --- a/x-pack/plugins/lists/common/shared_imports.ts +++ b/x-pack/plugins/lists/common/shared_imports.ts @@ -6,6 +6,7 @@ export { NonEmptyString, + DefaultArray, DefaultUuid, DefaultStringArray, DefaultVersionNumber, diff --git a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts index 7fd07ed5fb8cd0..cce4038ff48d68 100644 --- a/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_endpoint_list_item_route.ts @@ -37,13 +37,13 @@ export const createEndpointListItemRoute = (router: IRouter): void => { try { const { name, - _tags, tags, meta, comments, description, entries, item_id: itemId, + os_types: osTypes, type, } = request.body; const exceptionLists = getExceptionListClient(context); @@ -58,13 +58,13 @@ export const createEndpointListItemRoute = (router: IRouter): void => { }); } else { const createdList = await exceptionLists.createEndpointListItem({ - _tags, comments, description, entries, itemId, meta, name, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts index e51e113239f20c..afcb0f99c8a35a 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_item_route.ts @@ -39,7 +39,6 @@ export const createExceptionListItemRoute = (router: IRouter): void => { const { namespace_type: namespaceType, name, - _tags, tags, meta, comments, @@ -47,6 +46,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => { entries, item_id: itemId, list_id: listId, + os_types: osTypes, type, } = request.body; const exceptionLists = getExceptionListClient(context); @@ -87,7 +87,6 @@ export const createExceptionListItemRoute = (router: IRouter): void => { } } const createdList = await exceptionLists.createExceptionListItem({ - _tags, comments, description, entries, @@ -96,6 +95,7 @@ export const createExceptionListItemRoute = (router: IRouter): void => { meta, name, namespaceType, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts index 08db0825e07bd9..fd2ba6340009ce 100644 --- a/x-pack/plugins/lists/server/routes/create_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/create_exception_list_route.ts @@ -36,7 +36,6 @@ export const createExceptionListRoute = (router: IRouter): void => { try { const { name, - _tags, tags, meta, namespace_type: namespaceType, @@ -58,7 +57,6 @@ export const createExceptionListRoute = (router: IRouter): void => { }); } else { const createdList = await exceptionLists.createExceptionList({ - _tags, description, immutable: false, listId, diff --git a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts index e0d6a0ffffa6b8..8312f2fc87b985 100644 --- a/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_endpoint_list_item_route.ts @@ -38,9 +38,9 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { description, id, name, + os_types: osTypes, meta, type, - _tags, _version, comments, entries, @@ -49,7 +49,6 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { } = request.body; const exceptionLists = getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateEndpointListItem({ - _tags, _version, comments, description, @@ -58,6 +57,7 @@ export const updateEndpointListItemRoute = (router: IRouter): void => { itemId, meta, name, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts index 745ad0735a1747..9ad563724b860d 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_item_route.ts @@ -46,12 +46,12 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { name, meta, type, - _tags, _version, comments, entries, item_id: itemId, namespace_type: namespaceType, + os_types: osTypes, tags, } = request.body; if (id == null && itemId == null) { @@ -62,7 +62,6 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { } else { const exceptionLists = getExceptionListClient(context); const exceptionListItem = await exceptionLists.updateExceptionListItem({ - _tags, _version, comments, description, @@ -72,6 +71,7 @@ export const updateExceptionListItemRoute = (router: IRouter): void => { meta, name, namespaceType, + osTypes, tags, type, }); diff --git a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts index 1903d0f601d1d1..47008e3b78fae9 100644 --- a/x-pack/plugins/lists/server/routes/update_exception_list_route.ts +++ b/x-pack/plugins/lists/server/routes/update_exception_list_route.ts @@ -35,7 +35,6 @@ export const updateExceptionListRoute = (router: IRouter): void => { const siemResponse = buildSiemResponse(response); try { const { - _tags, _version, tags, name, @@ -44,6 +43,7 @@ export const updateExceptionListRoute = (router: IRouter): void => { list_id: listId, meta, namespace_type: namespaceType, + os_types: osTypes, type, version, } = request.body; @@ -55,7 +55,6 @@ export const updateExceptionListRoute = (router: IRouter): void => { }); } else { const list = await exceptionLists.updateExceptionList({ - _tags, _version, description, id, @@ -63,6 +62,7 @@ export const updateExceptionListRoute = (router: IRouter): void => { meta, name, namespaceType, + osTypes, tags, type, version, 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 f9e408833e0697..b3fd2c0eced981 100644 --- a/x-pack/plugins/lists/server/saved_objects/exception_list.ts +++ b/x-pack/plugins/lists/server/saved_objects/exception_list.ts @@ -6,6 +6,8 @@ import { SavedObjectsType } from 'kibana/server'; +import { migrations } from './migrations'; + export const exceptionListSavedObjectType = 'exception-list'; export const exceptionListAgnosticSavedObjectType = 'exception-list-agnostic'; export type SavedObjectType = 'exception-list' | 'exception-list-agnostic'; @@ -149,6 +151,9 @@ export const exceptionListItemMapping: SavedObjectsType['mappings'] = { item_id: { type: 'keyword', }, + os_types: { + type: 'keyword', + }, }, }; @@ -163,6 +168,7 @@ const combinedMappings: SavedObjectsType['mappings'] = { export const exceptionListType: SavedObjectsType = { hidden: false, mappings: combinedMappings, + migrations, name: exceptionListSavedObjectType, namespaceType: 'single', }; @@ -170,6 +176,7 @@ export const exceptionListType: SavedObjectsType = { export const exceptionListAgnosticType: SavedObjectsType = { hidden: false, mappings: combinedMappings, + migrations, name: exceptionListAgnosticSavedObjectType, namespaceType: 'agnostic', }; diff --git a/x-pack/plugins/lists/server/saved_objects/migrations.test.ts b/x-pack/plugins/lists/server/saved_objects/migrations.test.ts new file mode 100644 index 00000000000000..cd7ef0f37d505b --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/migrations.test.ts @@ -0,0 +1,132 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SavedObjectUnsanitizedDoc } from 'kibana/server'; + +import { ENDPOINT_LIST_ID } from '../../common/constants'; + +import { OldExceptionListSoSchema, migrations } from './migrations'; + +describe('7.10.0 lists migrations', () => { + const migration = migrations['7.10.0']; + + test('properly converts .text fields to .caseless', () => { + const doc = { + attributes: { + entries: [ + { + field: 'file.path.text', + operator: 'included', + type: 'match', + value: 'C:\\Windows\\explorer.exe', + }, + { + field: 'host.os.name', + operator: 'included', + type: 'match', + value: 'my-host', + }, + { + entries: [ + { + field: 'process.command_line.text', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + { + field: 'process.parent.command_line.text', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + ], + field: 'nested.field', + type: 'nested', + }, + ], + list_id: ENDPOINT_LIST_ID, + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }; + expect( + migration((doc as unknown) as SavedObjectUnsanitizedDoc<OldExceptionListSoSchema>) + ).toEqual({ + attributes: { + entries: [ + { + field: 'file.path.caseless', + operator: 'included', + type: 'match', + value: 'C:\\Windows\\explorer.exe', + }, + { + field: 'host.os.name', + operator: 'included', + type: 'match', + value: 'my-host', + }, + { + entries: [ + { + field: 'process.command_line.caseless', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + { + field: 'process.parent.command_line.caseless', + operator: 'included', + type: 'match', + value: '/usr/bin/bash', + }, + ], + field: 'nested.field', + type: 'nested', + }, + ], + list_id: ENDPOINT_LIST_ID, + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }); + }); + + test('properly copies os tags to os_types', () => { + const doc = { + attributes: { + _tags: ['1234', 'os:windows'], + comments: [], + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }; + expect( + migration((doc as unknown) as SavedObjectUnsanitizedDoc<OldExceptionListSoSchema>) + ).toEqual({ + attributes: { + _tags: ['1234', 'os:windows'], + comments: [], + os_types: ['windows'], + }, + id: 'abcd', + migrationVersion: {}, + references: [], + type: 'so-type', + updated_at: '2020-06-09T20:18:20.349Z', + }); + }); +}); diff --git a/x-pack/plugins/lists/server/saved_objects/migrations.ts b/x-pack/plugins/lists/server/saved_objects/migrations.ts new file mode 100644 index 00000000000000..2e9792cd8eb3c6 --- /dev/null +++ b/x-pack/plugins/lists/server/saved_objects/migrations.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { SavedObjectSanitizedDoc, SavedObjectUnsanitizedDoc } from 'kibana/server'; + +import { ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID } from '../../common/constants'; +import { + EntriesArray, + ExceptionListSoSchema, + NonEmptyNestedEntriesArray, + OsTypeArray, + entriesNested, + entry, +} from '../../common/schemas'; + +const entryType = t.union([entry, entriesNested]); +type EntryType = t.TypeOf<typeof entryType>; + +const migrateEntry = (entryToMigrate: EntryType): EntryType => { + const newEntry = entryToMigrate; + if (entriesNested.is(entryToMigrate) && entriesNested.is(newEntry)) { + newEntry.entries = entryToMigrate.entries.map((nestedEntry) => + migrateEntry(nestedEntry) + ) as NonEmptyNestedEntriesArray; + } + newEntry.field = entryToMigrate.field.replace('.text', '.caseless'); + return newEntry; +}; + +const reduceOsTypes = (acc: string[], tag: string): string[] => { + if (tag.startsWith('os:')) { + // TODO: check OS against type + return [...acc, tag.replace('os:', '')]; + } + return [...acc]; +}; + +export type OldExceptionListSoSchema = ExceptionListSoSchema & { + _tags: string[]; +}; + +export const migrations = { + '7.10.0': ( + doc: SavedObjectUnsanitizedDoc<OldExceptionListSoSchema> + ): SavedObjectSanitizedDoc<ExceptionListSoSchema> => ({ + ...doc, + ...{ + attributes: { + ...doc.attributes, + ...(doc.attributes.entries && + [ENDPOINT_LIST_ID, ENDPOINT_TRUSTED_APPS_LIST_ID].includes(doc.attributes.list_id) && { + entries: (doc.attributes.entries as EntriesArray).map<EntryType>(migrateEntry), + }), + ...(doc.attributes._tags && + doc.attributes._tags.reduce(reduceOsTypes, []).length > 0 && { + os_types: doc.attributes._tags.reduce(reduceOsTypes, []) as OsTypeArray, + }), + }, + }, + references: doc.references || [], + }), +}; diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json index 6999441d21941b..5e7dee83776bfb 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/endpoint_list_item.json @@ -1,10 +1,10 @@ { "item_id": "simple_list_item", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample endpoint type exception", "name": "Sample Endpoint Exception List", + "os_types": ["linux"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json index 19027ac189a47b..73271514269da2 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list.json @@ -1,6 +1,5 @@ { "list_id": "simple_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "detection", "description": "This is a sample endpoint type exception", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json index 4121b138806604..9987f5d46af1b8 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_agnostic.json @@ -1,6 +1,5 @@ { "list_id": "endpoint_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "endpoint", "description": "This is a sample agnostic endpoint type exception", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json index 306195f4226e39..986c368bd2de3a 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_detection.json @@ -1,6 +1,5 @@ { "list_id": "detection_list", - "_tags": ["detection"], "tags": ["detection", "sample_tag"], "type": "detection", "description": "This is a sample detection type exception list", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json index eede855aab199f..e7eed0a56cb6dd 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item.json @@ -1,11 +1,11 @@ { "list_id": "simple_list", "item_id": "simple_list_item", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample endpoint type exception", "name": "Sample Endpoint Exception List", + "os_types": ["linux"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json index 9cda9c12d6b30a..d57fb19955e340 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_agnostic.json @@ -1,12 +1,12 @@ { "list_id": "endpoint_list", "item_id": "endpoint_list_item", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample agnostic endpoint type exception", "name": "Sample Endpoint Exception List", "namespace_type": "agnostic", + "os_types": ["linux"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json index f1281e2ea0560c..9cc73577818c56 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_auto_id.json @@ -1,10 +1,10 @@ { "list_id": "simple_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample endpoint type exception that has no item_id so it creates a new id each time", "name": "Sample Endpoint Exception List", + "os_types": ["linux"], "comments": [], "entries": [ { diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json index 833f6c023c5d97..e65f818c1df850 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_detection_auto_id.json @@ -1,6 +1,5 @@ { "list_id": "detection_list", - "_tags": ["detection"], "tags": ["test_tag", "detection", "no_more_bad_guys"], "type": "simple", "description": "This is a sample detection type exception that has no item_id so it creates a new id each time", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json index bab435487ec255..9a5f6e888e6e48 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_bad_ip_list.json @@ -1,11 +1,11 @@ { "list_id": "endpoint_list", "item_id": "endpoint_list_item_good_rock01", - "_tags": ["endpoint", "process", "malware", "os:windows"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "Don't signal when agent.name is rock01 and source.ip is in the goodguys.txt list", "name": "Filter out good guys ip and agent.name rock01", + "os_types": ["windows"], "comments": [], "entries": [ { diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json index e0d401eff92694..d0756b990aad00 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/exception_list_item_with_list.json @@ -1,11 +1,11 @@ { "list_id": "endpoint_list", "item_id": "endpoint_list_item_lg_val_list", - "_tags": ["endpoint", "process", "malware", "os:windows"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample exception list item with a large value list included", "name": "Sample Endpoint Exception List Item with large value list", + "os_types": ["windows"], "comments": [], "entries": [ { diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json index 9f0c306a408f0d..293ca14d323f76 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/new/trusted_app_list_item_agnostic.json @@ -1,12 +1,12 @@ { "list_id": "endpoint_trusted_apps", "item_id": "endpoint_trusted_apps_item", - "_tags": ["endpoint", "os:linux", "os:windows", "os:macos", "trusted-app"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample agnostic endpoint trusted app entry", "name": "Sample Endpoint Trusted App Entry", "namespace_type": "agnostic", + "os_types": ["linux", "windows", "macos"], "entries": [ { "field": "actingProcess.file.signer", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json index 8d07b29d7b428e..15a6f495b7a8fe 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update.json @@ -1,8 +1,8 @@ { "list_id": "simple_list", - "_tags": ["endpoint", "process", "malware", "os:linux"], "tags": ["user added string for a tag", "malware"], "type": "endpoint", + "os_types": ["linux"], "description": "Different description", "name": "Sample Endpoint Exception List" } diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json index 90d5e0846e53a2..fe29ca80c632ef 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_agnostic.json @@ -1,11 +1,11 @@ { "item_id": "endpoint_list_item", - "_tags": ["endpoint", "process", "malware", "os:windows"], "tags": ["user added string for a tag", "malware"], "type": "simple", "description": "This is a sample agnostic change here this list", "name": "Sample Endpoint Exception List update change", "namespace_type": "agnostic", + "os_types": ["windows"], "entries": [ { "field": "event.category", diff --git a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json index 81db9092775955..d55f121253406c 100644 --- a/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json +++ b/x-pack/plugins/lists/server/scripts/exception_lists/updates/simple_update_item.json @@ -1,5 +1,4 @@ { - "_tags": ["detection"], "comments": [], "description": "Test comments - exception list item", "entries": [ diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts index 2e9bb1325632e8..fb2b637657bb65 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_list.ts @@ -35,7 +35,6 @@ export const createEndpointList = async ({ const savedObject = await savedObjectsClient.create<ExceptionListSoSchema>( savedObjectType, { - _tags: [], comments: undefined, created_at: dateNow, created_by: user, @@ -47,6 +46,7 @@ export const createEndpointList = async ({ list_type: 'list', meta: undefined, name: ENDPOINT_LIST_NAME, + os_types: [], tags: [], tie_breaker_id: tieBreaker ?? uuid.v4(), type: 'endpoint', diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts index c782cdd3026661..d9eedb0af4e77b 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_endpoint_trusted_apps_list.ts @@ -43,7 +43,6 @@ export const createEndpointTrustedAppsList = async ({ const savedObject = await savedObjectsClient.create<ExceptionListSoSchema>( savedObjectType, { - _tags: [], comments: undefined, created_at: dateNow, created_by: user, @@ -55,6 +54,7 @@ export const createEndpointTrustedAppsList = async ({ list_type: 'list', meta: undefined, name: ENDPOINT_TRUSTED_APPS_LIST_NAME, + os_types: [], tags: [], tie_breaker_id: tieBreaker ?? uuid.v4(), type: 'endpoint', diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts index c8d709ca340ad7..91a0506ad06e32 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list.ts @@ -19,13 +19,11 @@ import { NamespaceType, Tags, Version, - _Tags, } from '../../../common/schemas'; import { getSavedObjectType, transformSavedObjectToExceptionList } from './utils'; interface CreateExceptionListOptions { - _tags: _Tags; listId: ListId; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; @@ -41,7 +39,6 @@ interface CreateExceptionListOptions { } export const createExceptionList = async ({ - _tags, listId, immutable, savedObjectsClient, @@ -58,7 +55,6 @@ export const createExceptionList = async ({ const savedObjectType = getSavedObjectType({ namespaceType }); const dateNow = new Date().toISOString(); const savedObject = await savedObjectsClient.create<ExceptionListSoSchema>(savedObjectType, { - _tags, comments: undefined, created_at: dateNow, created_by: user, @@ -70,6 +66,7 @@ export const createExceptionList = async ({ list_type: 'list', meta, name, + os_types: [], tags, tie_breaker_id: tieBreaker ?? uuid.v4(), type, diff --git a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts index 47c21735b45f44..9f331362cdd446 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/create_exception_list_item.ts @@ -19,8 +19,8 @@ import { MetaOrUndefined, Name, NamespaceType, + OsTypeArray, Tags, - _Tags, } from '../../../common/schemas'; import { @@ -30,7 +30,6 @@ import { } from './utils'; interface CreateExceptionListItemOptions { - _tags: _Tags; comments: CreateCommentsArray; listId: ListId; itemId: ItemId; @@ -44,10 +43,10 @@ interface CreateExceptionListItemOptions { tags: Tags; tieBreaker?: string; type: ExceptionListItemType; + osTypes: OsTypeArray; } export const createExceptionListItem = async ({ - _tags, comments, entries, itemId, @@ -55,6 +54,7 @@ export const createExceptionListItem = async ({ savedObjectsClient, namespaceType, name, + osTypes, description, meta, user, @@ -69,7 +69,6 @@ export const createExceptionListItem = async ({ user, }); const savedObject = await savedObjectsClient.create<ExceptionListSoSchema>(savedObjectType, { - _tags, comments: transformedComments, created_at: dateNow, created_by: user, @@ -81,6 +80,7 @@ export const createExceptionListItem = async ({ list_type: 'item', meta, name, + os_types: osTypes as OsTypeArray, tags, tie_breaker_id: tieBreaker ?? uuid.v4(), type, 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 747458175e3b8c..9747c58d1cd0fb 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 @@ -109,20 +109,19 @@ export class ExceptionListClient { * being there and existing before the item is inserted into the agnostic endpoint list. */ public createEndpointListItem = async ({ - _tags, comments, description, entries, itemId, meta, name, + osTypes, tags, type, }: CreateEndpointListItemOptions): Promise<ExceptionListItemSchema> => { const { savedObjectsClient, user } = this; await this.createEndpointList(); return createExceptionListItem({ - _tags, comments, description, entries, @@ -131,6 +130,7 @@ export class ExceptionListClient { meta, name, namespaceType: 'agnostic', + osTypes, savedObjectsClient, tags, type, @@ -145,7 +145,6 @@ export class ExceptionListClient { * return of null but at least the list exists again. */ public updateEndpointListItem = async ({ - _tags, _version, comments, description, @@ -154,13 +153,13 @@ export class ExceptionListClient { itemId, meta, name, + osTypes, tags, type, }: UpdateEndpointListItemOptions): Promise<ExceptionListItemSchema | null> => { const { savedObjectsClient, user } = this; await this.createEndpointList(); return updateExceptionListItem({ - _tags, _version, comments, description, @@ -170,6 +169,7 @@ export class ExceptionListClient { meta, name, namespaceType: 'agnostic', + osTypes, savedObjectsClient, tags, type, @@ -189,7 +189,6 @@ export class ExceptionListClient { }; public createExceptionList = async ({ - _tags, description, immutable, listId, @@ -202,7 +201,6 @@ export class ExceptionListClient { }: CreateExceptionListOptions): Promise<ExceptionListSchema> => { const { savedObjectsClient, user } = this; return createExceptionList({ - _tags, description, immutable, listId, @@ -218,7 +216,6 @@ export class ExceptionListClient { }; public updateExceptionList = async ({ - _tags, _version, id, description, @@ -226,13 +223,13 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, tags, type, version, }: UpdateExceptionListOptions): Promise<ExceptionListSchema | null> => { const { savedObjectsClient, user } = this; return updateExceptionList({ - _tags, _version, description, id, @@ -240,6 +237,7 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, savedObjectsClient, tags, type, @@ -263,7 +261,6 @@ export class ExceptionListClient { }; public createExceptionListItem = async ({ - _tags, comments, description, entries, @@ -272,12 +269,12 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, tags, type, }: CreateExceptionListItemOptions): Promise<ExceptionListItemSchema> => { const { savedObjectsClient, user } = this; return createExceptionListItem({ - _tags, comments, description, entries, @@ -286,6 +283,7 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, savedObjectsClient, tags, type, @@ -294,7 +292,6 @@ export class ExceptionListClient { }; public updateExceptionListItem = async ({ - _tags, _version, comments, description, @@ -304,12 +301,12 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, tags, type, }: UpdateExceptionListItemOptions): Promise<ExceptionListItemSchema | null> => { const { savedObjectsClient, user } = this; return updateExceptionListItem({ - _tags, _version, comments, description, @@ -319,6 +316,7 @@ export class ExceptionListClient { meta, name, namespaceType, + osTypes, savedObjectsClient, tags, type, 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 963716b55ea771..1fef2da5d975ed 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 @@ -30,6 +30,7 @@ import { Name, NameOrUndefined, NamespaceType, + OsTypeArray, PageOrUndefined, PerPageOrUndefined, SortFieldOrUndefined, @@ -39,8 +40,6 @@ import { UpdateCommentsArray, Version, VersionOrUndefined, - _Tags, - _TagsOrUndefined, _VersionOrUndefined, } from '../../../common/schemas'; @@ -56,7 +55,6 @@ export interface GetExceptionListOptions { } export interface CreateExceptionListOptions { - _tags: _Tags; listId: ListId; namespaceType: NamespaceType; name: Name; @@ -69,12 +67,12 @@ export interface CreateExceptionListOptions { } export interface UpdateExceptionListOptions { - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; id: IdOrUndefined; listId: ListIdOrUndefined; namespaceType: NamespaceType; name: NameOrUndefined; + osTypes: OsTypeArray; description: DescriptionOrUndefined; meta: MetaOrUndefined; tags: TagsOrUndefined; @@ -116,13 +114,13 @@ export interface GetEndpointListItemOptions { } export interface CreateExceptionListItemOptions { - _tags: _Tags; comments: CreateCommentsArray; entries: EntriesArray; itemId: ItemId; listId: ListId; namespaceType: NamespaceType; name: Name; + osTypes: OsTypeArray; description: Description; meta: MetaOrUndefined; tags: Tags; @@ -130,19 +128,18 @@ export interface CreateExceptionListItemOptions { } export interface CreateEndpointListItemOptions { - _tags: _Tags; comments: CreateCommentsArray; entries: EntriesArray; itemId: ItemId; name: Name; description: Description; meta: MetaOrUndefined; + osTypes: OsTypeArray; tags: Tags; type: ExceptionListItemType; } export interface UpdateExceptionListItemOptions { - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; comments: UpdateCommentsArray; entries: EntriesArray; @@ -150,6 +147,7 @@ export interface UpdateExceptionListItemOptions { itemId: ItemIdOrUndefined; namespaceType: NamespaceType; name: NameOrUndefined; + osTypes: OsTypeArray; description: DescriptionOrUndefined; meta: MetaOrUndefined; tags: TagsOrUndefined; @@ -157,13 +155,13 @@ export interface UpdateExceptionListItemOptions { } export interface UpdateEndpointListItemOptions { - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; comments: UpdateCommentsArray; entries: EntriesArray; id: IdOrUndefined; itemId: ItemIdOrUndefined; name: NameOrUndefined; + osTypes: OsTypeArray; description: DescriptionOrUndefined; meta: MetaOrUndefined; tags: TagsOrUndefined; diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts index c26ff1bca4484f..a9a666672d7bb1 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list.ts @@ -16,9 +16,9 @@ import { MetaOrUndefined, NameOrUndefined, NamespaceType, + OsTypeArray, TagsOrUndefined, VersionOrUndefined, - _TagsOrUndefined, _VersionOrUndefined, } from '../../../common/schemas'; @@ -27,12 +27,12 @@ import { getExceptionList } from './get_exception_list'; interface UpdateExceptionListOptions { id: IdOrUndefined; - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; name: NameOrUndefined; description: DescriptionOrUndefined; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; + osTypes: OsTypeArray; listId: ListIdOrUndefined; meta: MetaOrUndefined; user: string; @@ -43,7 +43,6 @@ interface UpdateExceptionListOptions { } export const updateExceptionList = async ({ - _tags, _version, id, savedObjectsClient, @@ -67,7 +66,6 @@ export const updateExceptionList = async ({ savedObjectType, exceptionList.id, { - _tags, description, meta, name, diff --git a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts index ccb74e87967057..9c3399b7509a5d 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/update_exception_list_item.ts @@ -17,9 +17,9 @@ import { MetaOrUndefined, NameOrUndefined, NamespaceType, + OsTypeArray, TagsOrUndefined, UpdateCommentsArrayOrUndefined, - _TagsOrUndefined, _VersionOrUndefined, } from '../../../common/schemas'; @@ -33,13 +33,13 @@ import { getExceptionListItem } from './get_exception_list_item'; interface UpdateExceptionListItemOptions { id: IdOrUndefined; comments: UpdateCommentsArrayOrUndefined; - _tags: _TagsOrUndefined; _version: _VersionOrUndefined; name: NameOrUndefined; description: DescriptionOrUndefined; entries: EntriesArray; savedObjectsClient: SavedObjectsClientContract; namespaceType: NamespaceType; + osTypes: OsTypeArray; itemId: ItemIdOrUndefined; meta: MetaOrUndefined; user: string; @@ -49,7 +49,6 @@ interface UpdateExceptionListItemOptions { } export const updateExceptionListItem = async ({ - _tags, _version, comments, entries, @@ -57,6 +56,7 @@ export const updateExceptionListItem = async ({ savedObjectsClient, namespaceType, name, + osTypes, description, itemId, meta, @@ -83,12 +83,12 @@ export const updateExceptionListItem = async ({ savedObjectType, exceptionListItem.id, { - _tags, comments: transformedComments, description, entries, meta, name, + os_types: osTypes, tags, type, updated_by: user, diff --git a/x-pack/plugins/lists/server/services/exception_lists/utils.ts b/x-pack/plugins/lists/server/services/exception_lists/utils.ts index 2989a09b0ce00a..6a7bd249bf62aa 100644 --- a/x-pack/plugins/lists/server/services/exception_lists/utils.ts +++ b/x-pack/plugins/lists/server/services/exception_lists/utils.ts @@ -71,7 +71,6 @@ export const transformSavedObjectToExceptionList = ({ version: _version, attributes: { /* eslint-disable @typescript-eslint/naming-convention */ - _tags, created_at, created_by, description, @@ -79,6 +78,7 @@ export const transformSavedObjectToExceptionList = ({ list_id, meta, name, + os_types, tags, tie_breaker_id, type, @@ -93,7 +93,6 @@ export const transformSavedObjectToExceptionList = ({ // TODO: Change this to do a decode and throw if the saved object is not as expected. // TODO: Do a throw if after the decode this is not the correct "list_type: list" return { - _tags, _version, created_at, created_by, @@ -104,6 +103,7 @@ export const transformSavedObjectToExceptionList = ({ meta, name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types, tags, tie_breaker_id, type: exceptionListType.is(type) ? type : 'detection', @@ -124,11 +124,11 @@ export const transformSavedObjectUpdateToExceptionList = ({ const { version: _version, attributes: { - _tags, description, immutable, meta, name, + os_types: osTypes, tags, type, updated_by: updatedBy, @@ -141,7 +141,6 @@ export const transformSavedObjectUpdateToExceptionList = ({ // TODO: Change this to do a decode and throw if the saved object is not as expected. // TODO: Do a throw if after the decode this is not the correct "list_type: list" return { - _tags: _tags ?? exceptionList._tags, _version, created_at: exceptionList.created_at, created_by: exceptionList.created_by, @@ -152,6 +151,7 @@ export const transformSavedObjectUpdateToExceptionList = ({ meta: meta ?? exceptionList.meta, name: name ?? exceptionList.name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types: osTypes ?? exceptionList.os_types, tags: tags ?? exceptionList.tags, tie_breaker_id: exceptionList.tie_breaker_id, type: exceptionListType.is(type) ? type : exceptionList.type, @@ -171,7 +171,6 @@ export const transformSavedObjectToExceptionListItem = ({ version: _version, attributes: { /* eslint-disable @typescript-eslint/naming-convention */ - _tags, comments, created_at, created_by, @@ -181,6 +180,7 @@ export const transformSavedObjectToExceptionListItem = ({ list_id, meta, name, + os_types, tags, tie_breaker_id, type, @@ -194,7 +194,6 @@ export const transformSavedObjectToExceptionListItem = ({ // TODO: Do a throw if after the decode this is not the correct "list_type: item" // TODO: Do a throw if item_id or entries is not defined. return { - _tags, _version, comments: comments ?? [], created_at, @@ -207,6 +206,7 @@ export const transformSavedObjectToExceptionListItem = ({ meta, name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types, tags, tie_breaker_id, type: exceptionListItemType.is(type) ? type : 'simple', @@ -226,12 +226,12 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ const { version: _version, attributes: { - _tags, comments, description, entries, meta, name, + os_types: osTypes, tags, type, updated_by: updatedBy, @@ -245,7 +245,6 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ // TODO: Update exception list and item types (perhaps separating out) so as to avoid // defaulting return { - _tags: _tags ?? exceptionListItem._tags, _version, comments: comments ?? exceptionListItem.comments, created_at: exceptionListItem.created_at, @@ -258,6 +257,7 @@ export const transformSavedObjectUpdateToExceptionListItem = ({ meta: meta ?? exceptionListItem.meta, name: name ?? exceptionListItem.name, namespace_type: getExceptionListType({ savedObjectType: savedObject.type }), + os_types: osTypes ?? exceptionListItem.os_types, tags: tags ?? exceptionListItem.tags, tie_breaker_id: exceptionListItem.tie_breaker_id, type: exceptionListItemType.is(type) ? type : exceptionListItem.type, diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.test.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.test.ts new file mode 100644 index 00000000000000..6e23f31e8a994a --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.test.ts @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; + +import { DefaultArray } from './default_array'; +import { pipe } from 'fp-ts/lib/pipeable'; +import { left } from 'fp-ts/lib/Either'; +import { foldLeftRight, getPaths } from '../../../test_utils'; + +const testSchema = t.keyof({ + valid: true, + also_valid: true, +}); +type TestSchema = t.TypeOf<typeof testSchema>; + +const defaultArraySchema = DefaultArray(testSchema); + +describe('default_array', () => { + test('it should validate an empty array', () => { + const payload: string[] = []; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of testSchema', () => { + const payload: TestSchema[] = ['valid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should validate an array of valid testSchema strings', () => { + const payload = ['valid', 'also_valid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual(payload); + }); + + test('it should not validate an array with a number', () => { + const payload = ['valid', 123]; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "123" supplied to "DefaultArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should not validate an array with an invalid string', () => { + const payload = ['valid', 'invalid']; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([ + 'Invalid value "invalid" supplied to "DefaultArray"', + ]); + expect(message.schema).toEqual({}); + }); + + test('it should return a default array entry', () => { + const payload = null; + const decoded = defaultArraySchema.decode(payload); + const message = pipe(decoded, foldLeftRight); + + expect(getPaths(left(message.errors))).toEqual([]); + expect(message.schema).toEqual([]); + }); +}); diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.ts new file mode 100644 index 00000000000000..8388eb315b8f4b --- /dev/null +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/default_array.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as t from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; + +/** + * Types the DefaultArray<C> as: + * - If undefined, then a default array will be set + * - If an array is sent in, then the array will be validated to ensure all elements are type C + */ +export const DefaultArray = <C extends t.Mixed>(codec: C) => { + const arrType = t.array(codec); + type ArrType = t.TypeOf<typeof arrType>; + return new t.Type<ArrType, ArrType | undefined, unknown>( + 'DefaultArray', + arrType.is, + (input, context): Either<t.Errors, ArrType> => + input == null ? t.success([]) : arrType.validate(input, context), + t.identity + ); +}; diff --git a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts index 28a66d2948a92e..e76dd3fca37403 100644 --- a/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts +++ b/x-pack/plugins/security_solution/common/detection_engine/schemas/types/index.ts @@ -5,6 +5,7 @@ */ export * from './default_actions_array'; +export * from './default_array'; export * from './default_boolean_false'; export * from './default_boolean_true'; export * from './default_empty_string'; diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts index ef1d9a99b0aebe..352c628f9fa238 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.test.ts @@ -76,7 +76,7 @@ describe('When invoking Trusted Apps Schema', () => { os: 'windows', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', type: 'match', operator: 'included', value: 'c:/programs files/Anti-Virus', @@ -204,7 +204,7 @@ describe('When invoking Trusted Apps Schema', () => { field: 'process.hash.*', value: 'A4370C0CF81686C0B696FA6261c9d3e0d810ae704ab8301839dffd5d5112f476', }, - { field: 'process.executable.text', value: '/tmp/dir1' }, + { field: 'process.executable.caseless', value: '/tmp/dir1' }, ].forEach((partialEntry) => { const bodyMsg3 = { ...getCreateTrustedAppItem(), diff --git a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts index 25456115b37133..b4e837c472915c 100644 --- a/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/schema/trusted_apps.ts @@ -35,7 +35,7 @@ export const PostTrustedAppCreateRequestSchema = { schema.object({ field: schema.oneOf([ schema.literal('process.hash.*'), - schema.literal('process.executable.text'), + schema.literal('process.executable.caseless'), ]), type: schema.literal('match'), operator: schema.literal('included'), diff --git a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts index 75e0347b100780..3568136dd0e7b9 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/trusted_apps.ts @@ -33,7 +33,7 @@ export interface PostTrustedAppCreateResponse { } export interface MacosLinuxConditionEntry { - field: 'process.hash.*' | 'process.executable.text'; + field: 'process.hash.*' | 'process.executable.caseless'; type: 'match'; operator: 'included'; value: string; diff --git a/x-pack/plugins/security_solution/common/shared_exports.ts b/x-pack/plugins/security_solution/common/shared_exports.ts index bd1086a3f21e9b..6269c3cee999c9 100644 --- a/x-pack/plugins/security_solution/common/shared_exports.ts +++ b/x-pack/plugins/security_solution/common/shared_exports.ts @@ -5,6 +5,7 @@ */ export { NonEmptyString } from './detection_engine/schemas/types/non_empty_string'; +export { DefaultArray } from './detection_engine/schemas/types/default_array'; export { DefaultUuid } from './detection_engine/schemas/types/default_uuid'; export { DefaultStringArray } from './detection_engine/schemas/types/default_string_array'; export { diff --git a/x-pack/plugins/security_solution/common/shared_imports.ts b/x-pack/plugins/security_solution/common/shared_imports.ts index 564254b6a75963..bfe77d2f9e6268 100644 --- a/x-pack/plugins/security_solution/common/shared_imports.ts +++ b/x-pack/plugins/security_solution/common/shared_imports.ts @@ -42,4 +42,6 @@ export { ExceptionListType, Type, ENDPOINT_LIST_ID, + osTypeArray, + OsTypeArray, } from '../../lists/common'; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx index ef2a5770eee8d2..037462839c72d3 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.test.tsx @@ -257,7 +257,7 @@ describe('When the add exception modal is opened', () => { indexPatterns: { ...stubIndexPattern, fields: [ - { name: 'file.path.text', type: 'string' }, + { name: 'file.path.caseless', type: 'string' }, { name: 'subject_name', type: 'string' }, { name: 'trusted', type: 'string' }, { name: 'file.hash.sha256', type: 'string' }, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx index dee1db64820676..ad5bc982434677 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/add_exception_modal/index.tsx @@ -30,6 +30,7 @@ import * as i18nCommon from '../../../translations'; import * as i18n from './translations'; import * as sharedI18n from '../translations'; import { Ecs } from '../../../../../common/ecs'; +import { osTypeArray, OsTypeArray } from '../../../../../common/shared_imports'; import { useAppToasts } from '../../../hooks/use_app_toasts'; import { useKibana } from '../../../lib/kibana'; import { ExceptionBuilderComponent } from '../builder'; @@ -211,12 +212,7 @@ export const AddExceptionModal = memo(function AddExceptionModal({ const initialExceptionItems = useMemo((): ExceptionsBuilderExceptionItem[] => { if (exceptionListType === 'endpoint' && alertData != null && ruleExceptionList) { - return defaultEndpointExceptionItems( - exceptionListType, - ruleExceptionList.list_id, - ruleName, - alertData - ); + return defaultEndpointExceptionItems(ruleExceptionList.list_id, ruleName, alertData); } else { return []; } @@ -265,11 +261,11 @@ export const AddExceptionModal = memo(function AddExceptionModal({ [setShouldBulkCloseAlert] ); - const retrieveAlertOsTypes = useCallback((): string[] => { - const osDefaults = ['windows', 'macos']; + const retrieveAlertOsTypes = useCallback((): OsTypeArray => { + const osDefaults: OsTypeArray = ['windows', 'macos']; if (alertData != null) { const osTypes = alertData.host && alertData.host.os && alertData.host.os.family; - if (osTypes != null && osTypes.length > 0) { + if (osTypeArray.is(osTypes) && osTypes != null && osTypes.length > 0) { return osTypes; } return osDefaults; @@ -316,13 +312,14 @@ export const AddExceptionModal = memo(function AddExceptionModal({ [fetchOrCreateListError, exceptionItemsToAdd] ); + const addExceptionMessage = + exceptionListType === 'endpoint' ? i18n.ADD_ENDPOINT_EXCEPTION : i18n.ADD_EXCEPTION; + return ( <EuiOverlayMask onClick={onCancel}> <Modal onClose={onCancel} data-test-subj="add-exception-modal"> <ModalHeader> - <EuiModalHeaderTitle> - {exceptionListType === 'endpoint' ? i18n.ADD_ENDPOINT_EXCEPTION : i18n.ADD_EXCEPTION} - </EuiModalHeaderTitle> + <EuiModalHeaderTitle>{addExceptionMessage}</EuiModalHeaderTitle> <ModalHeaderSubtitle className="eui-textTruncate" title={ruleName}> {ruleName} </ModalHeaderSubtitle> @@ -429,7 +426,7 @@ export const AddExceptionModal = memo(function AddExceptionModal({ isDisabled={isSubmitButtonDisabled} fill > - {i18n.ADD_EXCEPTION} + {addExceptionMessage} </EuiButton> </EuiModalFooter> )} diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx index 9bfd04cc19d724..2ee0fe88f73f7d 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/helpers.test.tsx @@ -90,9 +90,9 @@ const getMockNestedParentBuilderEntry = (): FormattedBuilderEntry => ({ const mockEndpointFields = [ { - name: 'file.path.text', + name: 'file.path.caseless', type: 'string', - esTypes: ['text'], + esTypes: ['keyword'], count: 0, scripted: false, searchable: true, @@ -303,8 +303,8 @@ describe('Exception builder helpers', () => { { aggregatable: false, count: 0, - esTypes: ['text'], - name: 'file.path.text', + esTypes: ['keyword'], + name: 'file.path.caseless', readFromDocValues: false, scripted: false, searchable: true, diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx index 165f3314c2f154..5904e0034a51c4 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/builder/index.tsx @@ -234,13 +234,12 @@ export const ExceptionBuilderComponent = ({ // empty `entries` array. Thought about appending an entry item to one, but that // would then be arbitrary, decided to just create a new exception list item const newException = getNewExceptionItem({ - listType, listId, namespaceType: listNamespaceType, ruleName, }); setUpdateExceptions([...exceptions, { ...newException }]); - }, [setUpdateExceptions, exceptions, listType, listId, listNamespaceType, ruleName]); + }, [setUpdateExceptions, exceptions, listId, listNamespaceType, ruleName]); // The builder can have existing exception items, or new exception items that have yet // to be created (and thus lack an id), this was creating some React bugs with relying diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx index 128686428598c3..08f7e3af90d0c4 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/edit_exception_modal/index.tsx @@ -40,7 +40,6 @@ import { AddExceptionComments } from '../add_exception_comments'; import { enrichExistingExceptionItemWithComments, enrichExceptionItemsWithOS, - getOperatingSystems, entryHasListType, entryHasNonEcsType, lowercaseHashValues, @@ -228,8 +227,7 @@ export const EditExceptionModal = memo(function EditExceptionModal({ }, ]; if (exceptionListType === 'endpoint') { - const osTypes = exceptionItem._tags ? getOperatingSystems(exceptionItem._tags) : []; - enriched = lowercaseHashValues(enrichExceptionItemsWithOS(enriched, osTypes)); + enriched = lowercaseHashValues(enrichExceptionItemsWithOS(enriched, exceptionItem.os_types)); } return enriched; }, [exceptionItemsToAdd, exceptionItem, comment, exceptionListType]); diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json b/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json index 037e340ee7fa2c..2ea200466445b9 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/exceptionable_fields.json @@ -6,33 +6,33 @@ "Target.process.Ext.code_signature.valid", "Target.process.Ext.services", "Target.process.Ext.user", - "Target.process.command_line.text", - "Target.process.executable.text", + "Target.process.command_line.caseless", + "Target.process.executable.caseless", "Target.process.hash.md5", "Target.process.hash.sha1", "Target.process.hash.sha256", "Target.process.hash.sha512", - "Target.process.name.text", + "Target.process.name.caseless", "Target.process.parent.Ext.code_signature.status", "Target.process.parent.Ext.code_signature.subject_name", "Target.process.parent.Ext.code_signature.trusted", "Target.process.parent.Ext.code_signature.valid", - "Target.process.parent.command_line.text", - "Target.process.parent.executable.text", + "Target.process.parent.command_line.caseless", + "Target.process.parent.executable.caseless", "Target.process.parent.hash.md5", "Target.process.parent.hash.sha1", "Target.process.parent.hash.sha256", "Target.process.parent.hash.sha512", - "Target.process.parent.name.text", + "Target.process.parent.name.caseless", "Target.process.parent.pgid", - "Target.process.parent.working_directory.text", + "Target.process.parent.working_directory.caseless", "Target.process.pe.company", "Target.process.pe.description", "Target.process.pe.file_version", "Target.process.pe.original_file_name", "Target.process.pe.product", "Target.process.pgid", - "Target.process.working_directory.text", + "Target.process.working_directory.caseless", "agent.id", "agent.type", "agent.version", @@ -66,14 +66,14 @@ "file.mode", "file.name", "file.owner", - "file.path.text", + "file.path.caseless", "file.pe.company", "file.pe.description", "file.pe.file_version", "file.pe.original_file_name", "file.pe.product", "file.size", - "file.target_path.text", + "file.target_path.caseless", "file.type", "file.uid", "group.Ext.real.id", @@ -84,9 +84,9 @@ "host.id", "host.os.Ext.variant", "host.os.family", - "host.os.full.text", + "host.os.full.caseless", "host.os.kernel", - "host.os.name.text", + "host.os.name.caseless", "host.os.platform", "host.os.version", "host.type", @@ -96,33 +96,33 @@ "process.Ext.code_signature.valid", "process.Ext.services", "process.Ext.user", - "process.command_line.text", - "process.executable.text", + "process.command_line.caseless", + "process.executable.caseless", "process.hash.md5", "process.hash.sha1", "process.hash.sha256", "process.hash.sha512", - "process.name.text", + "process.name.caseless", "process.parent.Ext.code_signature.status", "process.parent.Ext.code_signature.subject_name", "process.parent.Ext.code_signature.trusted", "process.parent.Ext.code_signature.valid", - "process.parent.command_line.text", - "process.parent.executable.text", + "process.parent.command_line.caseless", + "process.parent.executable.caseless", "process.parent.hash.md5", "process.parent.hash.sha1", "process.parent.hash.sha256", "process.parent.hash.sha512", - "process.parent.name.text", + "process.parent.name.caseless", "process.parent.pgid", - "process.parent.working_directory.text", + "process.parent.working_directory.caseless", "process.pe.company", "process.pe.description", "process.pe.file_version", "process.pe.original_file_name", "process.pe.product", "process.pgid", - "process.working_directory.text", + "process.working_directory.caseless", "rule.uuid", "user.domain", "user.email", diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx index 26fb460aee3821..c89bde6d04dd32 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.test.tsx @@ -10,8 +10,6 @@ import moment from 'moment-timezone'; import { getOperatorType, getExceptionOperatorSelect, - getOperatingSystems, - getTagsInclude, getFormattedComments, filterExceptionItems, getNewExceptionItem, @@ -52,6 +50,7 @@ import { CreateExceptionListItemSchema, ExceptionListItemSchema, EntriesArray, + OsTypeArray, } from '../../../../../lists/common/schemas'; import { IIndexPattern } from 'src/plugins/data/common'; @@ -186,76 +185,18 @@ describe('Exception helpers', () => { }); }); - describe('#getOperatingSystems', () => { - test('it returns null if no operating system tag specified', () => { - const result = getOperatingSystems(['some tag', 'some other tag']); - - expect(result).toEqual([]); - }); - - test('it returns null if operating system tag malformed', () => { - const result = getOperatingSystems(['some tag', 'jibberos:mac,windows', 'some other tag']); - - expect(result).toEqual([]); - }); - - test('it returns operating systems if space included in os tag', () => { - const result = getOperatingSystems(['some tag', 'os: macos', 'some other tag']); - expect(result).toEqual(['macos']); - }); - - test('it returns operating systems if multiple os tags specified', () => { - const result = getOperatingSystems(['some tag', 'os: macos', 'some other tag', 'os:windows']); - expect(result).toEqual(['macos', 'windows']); - }); - }); - describe('#formatOperatingSystems', () => { test('it returns null if no operating system tag specified', () => { - const result = formatOperatingSystems(getOperatingSystems(['some tag', 'some other tag'])); - - expect(result).toEqual(''); - }); - - test('it returns null if operating system tag malformed', () => { - const result = formatOperatingSystems( - getOperatingSystems(['some tag', 'jibberos:mac,windows', 'some other tag']) - ); - + const result = formatOperatingSystems(['some os', 'some other os']); expect(result).toEqual(''); }); - test('it returns formatted operating systems if space included in os tag', () => { - const result = formatOperatingSystems( - getOperatingSystems(['some tag', 'os: macos', 'some other tag']) - ); - - expect(result).toEqual('macOS'); - }); - - test('it returns formatted operating systems if multiple os tags specified', () => { - const result = formatOperatingSystems( - getOperatingSystems(['some tag', 'os: macos', 'some other tag', 'os:windows']) - ); - + test('it returns formatted operating systems if multiple specified', () => { + const result = formatOperatingSystems(['some tag', 'macos', 'some other tag', 'windows']); expect(result).toEqual('macOS, Windows'); }); }); - describe('#getTagsInclude', () => { - test('it returns a tuple of "false" and "null" if no matches found', () => { - const result = getTagsInclude({ tags: ['some', 'tags', 'here'], regex: /(no match)/ }); - - expect(result).toEqual([false, null]); - }); - - test('it returns a tuple of "true" and matching string if matches found', () => { - const result = getTagsInclude({ tags: ['some', 'tags', 'here'], regex: /(some)/ }); - - expect(result).toEqual([true, 'some']); - }); - }); - describe('#getFormattedComments', () => { test('it returns formatted comment object with username and timestamp', () => { const payload = getCommentsArrayMock(); @@ -384,7 +325,6 @@ describe('Exception helpers', () => { test('it removes `temporaryId` from items', () => { const { meta, ...rest } = getNewExceptionItem({ - listType: 'detection', listId: '123', namespaceType: 'single', ruleName: 'rule name', @@ -400,7 +340,6 @@ describe('Exception helpers', () => { const payload = getExceptionListItemSchemaMock(); const result = formatExceptionItemForUpdate(payload); const expected = { - _tags: ['endpoint', 'process', 'malware', 'os:linux'], comments: [], description: 'some description', entries: ENTRIES, @@ -409,6 +348,7 @@ describe('Exception helpers', () => { meta: {}, name: 'some name', namespace_type: 'single', + os_types: ['linux'], tags: ['user added string for a tag', 'malware'], type: 'simple', }; @@ -489,14 +429,14 @@ describe('Exception helpers', () => { }); describe('#enrichExceptionItemsWithOS', () => { - test('it should add an os tag to an exception item', () => { + test('it should add an os to an exception item', () => { const payload = [getExceptionListItemSchemaMock()]; - const osTypes = ['windows']; + const osTypes: OsTypeArray = ['windows']; const result = enrichExceptionItemsWithOS(payload, osTypes); const expected = [ { ...getExceptionListItemSchemaMock(), - _tags: [...getExceptionListItemSchemaMock()._tags, 'os:windows'], + os_types: ['windows'], }, ]; expect(result).toEqual(expected); @@ -504,36 +444,16 @@ describe('Exception helpers', () => { test('it should add multiple os tags to all exception items', () => { const payload = [getExceptionListItemSchemaMock(), getExceptionListItemSchemaMock()]; - const osTypes = ['windows', 'macos']; - const result = enrichExceptionItemsWithOS(payload, osTypes); - const expected = [ - { - ...getExceptionListItemSchemaMock(), - _tags: [...getExceptionListItemSchemaMock()._tags, 'os:windows', 'os:macos'], - }, - { - ...getExceptionListItemSchemaMock(), - _tags: [...getExceptionListItemSchemaMock()._tags, 'os:windows', 'os:macos'], - }, - ]; - expect(result).toEqual(expected); - }); - - test('it should add os tag to all exception items without duplication', () => { - const payload = [ - { ...getExceptionListItemSchemaMock(), _tags: ['os:linux', 'os:windows'] }, - { ...getExceptionListItemSchemaMock(), _tags: ['os:linux'] }, - ]; - const osTypes = ['windows']; + const osTypes: OsTypeArray = ['windows', 'macos']; const result = enrichExceptionItemsWithOS(payload, osTypes); const expected = [ { ...getExceptionListItemSchemaMock(), - _tags: ['os:linux', 'os:windows'], + os_types: ['windows', 'macos'], }, { ...getExceptionListItemSchemaMock(), - _tags: ['os:linux', 'os:windows'], + os_types: ['windows', 'macos'], }, ]; expect(result).toEqual(expected); @@ -715,7 +635,6 @@ describe('Exception helpers', () => { describe('getPrepopulatedItem', () => { test('it returns prepopulated items', () => { const prepopulatedItem = getPrepopulatedItem({ - listType: 'endpoint', listId: 'some_id', ruleName: 'my rule', codeSignature: { subjectName: '', trusted: '' }, @@ -733,7 +652,7 @@ describe('Exception helpers', () => { field: 'file.Ext.code_signature', type: 'nested', }, - { field: 'file.path.text', operator: 'included', type: 'match', value: '' }, + { field: 'file.path.caseless', operator: 'included', type: 'match', value: '' }, { field: 'file.hash.sha256', operator: 'included', type: 'match', value: '' }, { field: 'event.code', operator: 'included', type: 'match', value: '' }, ]); @@ -741,7 +660,6 @@ describe('Exception helpers', () => { test('it returns prepopulated items with values', () => { const prepopulatedItem = getPrepopulatedItem({ - listType: 'endpoint', listId: 'some_id', ruleName: 'my rule', codeSignature: { subjectName: 'someSubjectName', trusted: 'false' }, @@ -764,7 +682,12 @@ describe('Exception helpers', () => { field: 'file.Ext.code_signature', type: 'nested', }, - { field: 'file.path.text', operator: 'included', type: 'match', value: 'some-file-path' }, + { + field: 'file.path.caseless', + operator: 'included', + type: 'match', + value: 'some-file-path', + }, { field: 'file.hash.sha256', operator: 'included', type: 'match', value: 'some-hash' }, { field: 'event.code', operator: 'included', type: 'match', value: 'some-event-code' }, ]); @@ -847,7 +770,7 @@ describe('Exception helpers', () => { describe('defaultEndpointExceptionItems', () => { test('it should return pre-populated items', () => { - const defaultItems = defaultEndpointExceptionItems('endpoint', 'list_id', 'my_rule', { + const defaultItems = defaultEndpointExceptionItems('list_id', 'my_rule', { _id: '123', file: { Ext: { @@ -881,7 +804,7 @@ describe('Exception helpers', () => { type: 'nested', }, { - field: 'file.path.text', + field: 'file.path.caseless', operator: 'included', type: 'match', value: 'some file path', @@ -904,7 +827,7 @@ describe('Exception helpers', () => { type: 'nested', }, { - field: 'file.path.text', + field: 'file.path.caseless', operator: 'included', type: 'match', value: 'some file path', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx index d4acfa39f995d1..684f3390ae41ac 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/helpers.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { EuiText, EuiCommentProps, EuiAvatar } from '@elastic/eui'; -import { capitalize, union } from 'lodash'; +import { capitalize } from 'lodash'; import moment from 'moment'; import uuid from 'uuid'; @@ -33,8 +33,8 @@ import { createExceptionListItemSchema, exceptionListItemSchema, UpdateExceptionListItemSchema, - ExceptionListType, EntryNested, + OsTypeArray, } from '../../../shared_imports'; import { IIndexPattern } from '../../../../../../../src/plugins/data/common'; import { validate } from '../../../../common/validate'; @@ -98,20 +98,12 @@ export const getEntryValue = (item: BuilderEntry): string | string[] | undefined } }; -/** - * Retrieves the values of tags marked as os - * - * @param tags an ExceptionItem's tags - */ -export const getOperatingSystems = (tags: string[]): string[] => { - return tags.filter((tag) => tag.startsWith('os:')).map((os) => os.substring(3).trim()); -}; - /** * Formats os value array to a displayable string */ export const formatOperatingSystems = (osTypes: string[]): string => { return osTypes + .filter((os) => ['linux', 'macos', 'windows'].includes(os)) .map((os) => { if (os === 'macos') { return 'macOS'; @@ -121,21 +113,6 @@ export const formatOperatingSystems = (osTypes: string[]): string => { .join(', '); }; -/** - * Returns all tags that match a given regex - */ -export const getTagsInclude = ({ - tags, - regex, -}: { - tags: string[]; - regex: RegExp; -}): [boolean, string | null] => { - const matches: string[] | null = tags.join(';').match(regex); - const match = matches != null ? matches[1] : null; - return [matches != null, match]; -}; - /** * Formats ExceptionItem.comments into EuiCommentList format * @@ -158,18 +135,15 @@ export const getFormattedComments = (comments: CommentsArray): EuiCommentProps[] })); export const getNewExceptionItem = ({ - listType, listId, namespaceType, ruleName, }: { - listType: ExceptionListType; listId: string; namespaceType: NamespaceType; ruleName: string; }): CreateExceptionListItemBuilderSchema => { return { - _tags: [listType], comments: [], description: `${ruleName} - exception list item`, entries: [ @@ -326,14 +300,12 @@ export const enrichExistingExceptionItemWithComments = ( */ export const enrichExceptionItemsWithOS = ( exceptionItems: Array<ExceptionListItemSchema | CreateExceptionListItemSchema>, - osTypes: string[] + osTypes: OsTypeArray ): Array<ExceptionListItemSchema | CreateExceptionListItemSchema> => { - const osTags = osTypes.map((os) => `os:${os}`); return exceptionItems.map((item: ExceptionListItemSchema | CreateExceptionListItemSchema) => { - const newTags = item._tags ? union(item._tags, osTags) : [...osTags]; return { ...item, - _tags: newTags, + os_types: osTypes, }; }); }; @@ -419,7 +391,6 @@ export const getCodeSignatureValue = ( * Returns the default values from the alert data to autofill new endpoint exceptions */ export const getPrepopulatedItem = ({ - listType, listId, ruleName, codeSignature, @@ -428,7 +399,6 @@ export const getPrepopulatedItem = ({ eventCode, listNamespace = 'agnostic', }: { - listType: ExceptionListType; listId: string; listNamespace?: NamespaceType; ruleName: string; @@ -438,7 +408,7 @@ export const getPrepopulatedItem = ({ eventCode: string; }): ExceptionsBuilderExceptionItem => { return { - ...getNewExceptionItem({ listType, listId, namespaceType: listNamespace, ruleName }), + ...getNewExceptionItem({ listId, namespaceType: listNamespace, ruleName }), entries: [ { field: 'file.Ext.code_signature', @@ -459,7 +429,7 @@ export const getPrepopulatedItem = ({ ], }, { - field: 'file.path.text', + field: 'file.path.caseless', operator: 'included', type: 'match', value: filePath ?? '', @@ -514,7 +484,6 @@ export const entryHasNonEcsType = ( * Returns the default values from the alert data to autofill new endpoint exceptions */ export const defaultEndpointExceptionItems = ( - listType: ExceptionListType, listId: string, ruleName: string, alertEcsData: Ecs @@ -523,7 +492,6 @@ export const defaultEndpointExceptionItems = ( return getCodeSignatureValue(alertEcsData).map((codeSignature) => getPrepopulatedItem({ - listType, listId, ruleName, filePath: file && file.path ? file.path[0] : '', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx index 944631d4e9fb5f..38cf5722fa894e 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/use_fetch_or_create_rule_exception_list.tsx @@ -82,7 +82,6 @@ export const useFetchOrCreateRuleExceptionList = ({ type: exceptionListType, namespace_type: 'single', list_id: undefined, - _tags: undefined, tags: undefined, meta: undefined, }; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx index 39f34ae8a3cf30..a1fa0884b6b0cd 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/exception_item/index.stories.tsx @@ -43,7 +43,6 @@ storiesOf('Components/ExceptionItem', module) }) .add('with description', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.comments = []; payload.entries = [ { @@ -66,7 +65,6 @@ storiesOf('Components/ExceptionItem', module) }) .add('with comments', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = ''; payload.comments = getCommentsArrayMock(); payload.entries = [ @@ -90,7 +88,6 @@ storiesOf('Components/ExceptionItem', module) }) .add('with nested entries', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = ''; payload.comments = []; diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx index 5f6e54b0d3cffd..dbd4c805aa950b 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.test.tsx @@ -175,10 +175,13 @@ describe('Exception viewer helpers', () => { test('it returns formatted description list with a description if one specified', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = 'Im a description'; const result = getDescriptionListContent(payload); const expected: DescriptionListItem[] = [ + { + description: 'Linux', + title: 'OS', + }, { description: 'April 20th 2020 @ 15:25:31', title: 'Date created', @@ -198,10 +201,13 @@ describe('Exception viewer helpers', () => { test('it returns just user and date created if no other fields specified', () => { const payload = getExceptionListItemSchemaMock(); - payload._tags = []; payload.description = ''; const result = getDescriptionListContent(payload); const expected: DescriptionListItem[] = [ + { + description: 'Linux', + title: 'OS', + }, { description: 'April 20th 2020 @ 15:25:31', title: 'Date created', diff --git a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx index 86b0512410e6f4..edc3d20b03e5a3 100644 --- a/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx +++ b/x-pack/plugins/security_solution/public/common/components/exceptions/viewer/helpers.tsx @@ -6,12 +6,7 @@ import moment from 'moment'; import { entriesNested, ExceptionListItemSchema } from '../../../../lists_plugin_deps'; -import { - getEntryValue, - getExceptionOperatorSelect, - formatOperatingSystems, - getOperatingSystems, -} from '../helpers'; +import { getEntryValue, getExceptionOperatorSelect, formatOperatingSystems } from '../helpers'; import { FormattedEntry, BuilderEntry, DescriptionListItem } from '../types'; import * as i18n from '../translations'; @@ -80,7 +75,7 @@ export const getDescriptionListContent = ( const details = [ { title: i18n.OPERATING_SYSTEM, - value: formatOperatingSystems(getOperatingSystems(exceptionItem._tags ?? [])), + value: formatOperatingSystems(exceptionItem.os_types), }, { title: i18n.DATE_CREATED, diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx index 25d5d757759310..55d0622c3f9a94 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/logical_condition/components/condition_entry.tsx @@ -83,7 +83,7 @@ export const ConditionEntry = memo<ConditionEntryProps>( 'xpack.securitySolution.trustedapps.logicalConditionBuilder.entry.field.path', { defaultMessage: 'Path' } ), - value: 'process.executable.text', + value: 'process.executable.caseless', }, ]; }, []); diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx index 4b64030a702c55..1959e040d18600 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/trusted_app_card/index.stories.tsx @@ -30,7 +30,7 @@ storiesOf('TrustedApps|TrustedAppCard', module) trustedApp.created_at = '2020-09-17T14:52:33.899Z'; trustedApp.entries = [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: '/some/path/on/file/system', @@ -44,7 +44,7 @@ storiesOf('TrustedApps|TrustedAppCard', module) trustedApp.created_at = '2020-09-17T14:52:33.899Z'; trustedApp.entries = [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: '/some/path/on/file/system', diff --git a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts index 3bd27259ad80c9..03f0bf94a42646 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/trusted_apps/index.ts @@ -67,7 +67,7 @@ const generateTrustedAppEntry: (options?: GenerateTrustedAppEntryOptions) => obj return { list_id: ENDPOINT_TRUSTED_APPS_LIST_ID, item_id: `generator_endpoint_trusted_apps_${generateUUID()}`, - _tags: ['endpoint', `os:${os}`], + os_types: [os], tags: ['user added string for a tag', 'malware'], type: 'simple', description: 'This is a sample agnostic endpoint trusted app entry', diff --git a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts index a10ba9d6be38c2..c1b97f2adfeabe 100644 --- a/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/lib/artifacts/lists.test.ts @@ -62,7 +62,7 @@ describe('buildEventTypeSignal', () => { test('it should convert simple fields', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, { field: 'host.hostname', operator: 'included', type: 'match', value: 'estc' }, ]; @@ -71,10 +71,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_cased', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -108,10 +108,10 @@ describe('buildEventTypeSignal', () => { test('it should convert fields case sensitive', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, { - field: 'host.hostname.text', + field: 'host.hostname.caseless', operator: 'included', type: 'match_any', value: ['estc', 'kibana'], @@ -122,10 +122,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_caseless', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -159,12 +159,12 @@ describe('buildEventTypeSignal', () => { test('it should deduplicate exception entries', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, { - field: 'host.hostname.text', + field: 'host.hostname', operator: 'included', type: 'match_any', value: ['estc', 'kibana'], @@ -175,10 +175,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_caseless', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -189,7 +189,7 @@ describe('buildEventTypeSignal', () => { { field: 'host.hostname', operator: 'included', - type: 'exact_caseless_any', + type: 'exact_cased_any', value: ['estc', 'kibana'], }, ], @@ -264,7 +264,7 @@ describe('buildEventTypeSignal', () => { test('it should deduplicate exception items', async () => { const testEntries: EntriesArray = [ - { field: 'server.domain.text', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full.caseless', operator: 'included', type: 'match', value: 'windows' }, { field: 'server.ip', operator: 'included', type: 'match', value: '192.168.1.1' }, ]; @@ -272,10 +272,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_caseless', - value: 'DOMAIN', + value: 'windows', }, { field: 'server.ip', @@ -308,9 +308,9 @@ describe('buildEventTypeSignal', () => { test('it should ignore unsupported entries', async () => { // Lists and exists are not supported by the Endpoint const testEntries: EntriesArray = [ - { field: 'server.domain', operator: 'included', type: 'match', value: 'DOMAIN' }, + { field: 'host.os.full', operator: 'included', type: 'match', value: 'windows' }, { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'list', list: { @@ -325,10 +325,10 @@ describe('buildEventTypeSignal', () => { type: 'simple', entries: [ { - field: 'server.domain', + field: 'host.os.full', operator: 'included', type: 'exact_cased', - value: 'DOMAIN', + value: 'windows', }, ], }; 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 731b083f3293c3..d0fd38c4f1af19 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 @@ -88,7 +88,7 @@ export async function getFullEndpointExceptionList( const response = await eClient.findExceptionListItem({ listId, namespaceType: 'agnostic', - filter: `exception-list-agnostic.attributes._tags:\"os:${os}\"`, + filter: `exception-list-agnostic.attributes.os_types:\"${os}\"`, perPage: 100, page, sortField: 'created_at', @@ -141,16 +141,16 @@ export function translateToEndpointExceptions( function getMatcherFunction(field: string, matchAny?: boolean): TranslatedEntryMatcher { return matchAny - ? field.endsWith('.text') + ? field.endsWith('.caseless') ? 'exact_caseless_any' : 'exact_cased_any' - : field.endsWith('.text') + : field.endsWith('.caseless') ? 'exact_caseless' : 'exact_cased'; } function normalizeFieldName(field: string): string { - return field.endsWith('.text') ? field.substring(0, field.length - 5) : field; + return field.endsWith('.caseless') ? field.substring(0, field.lastIndexOf('.')) : field; } function translateItem( diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts index 9e9a35ea353185..0fc469fa62a80b 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/trusted_apps.test.ts @@ -133,7 +133,6 @@ describe('when invoking endpoint trusted apps route handlers', () => { const emptyResponse: FoundExceptionListItemSchema = { data: [ { - _tags: ['os:windows'], _version: undefined, comments: [], created_at: '2020-09-21T19:43:48.240Z', @@ -165,6 +164,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { meta: undefined, name: 'test', namespace_type: 'agnostic', + os_types: ['windows'], tags: [], tie_breaker_id: '1', type: 'simple', @@ -240,7 +240,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { os: 'windows', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', type: 'match', operator: 'included', value: 'c:/programs files/Anti-Virus', @@ -267,6 +267,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { return ({ ...getExceptionListItemSchemaMock(), ...newExceptionItem, + os_types: newExceptionItem.osTypes, } as unknown) as ExceptionListItemSchema; }); }); @@ -288,12 +289,11 @@ describe('when invoking endpoint trusted apps route handlers', () => { const request = createPostRequest(); await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0]).toEqual({ - _tags: ['os:windows'], comments: [], description: 'this one is ok', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', @@ -304,6 +304,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { meta: undefined, name: 'Some Anti-Virus App', namespaceType: 'agnostic', + osTypes: ['windows'], tags: [], type: 'simple', }); @@ -320,7 +321,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { description: 'this one is ok', entries: [ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', @@ -357,7 +358,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { it('should trim condition entry values', async () => { const newTrustedApp = createNewTrustedAppBody(); newTrustedApp.entries.push({ - field: 'process.executable.text', + field: 'process.executable.caseless', value: '\n some value \r\n ', operator: 'included', type: 'match', @@ -366,13 +367,13 @@ describe('when invoking endpoint trusted apps route handlers', () => { await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', }, { - field: 'process.executable.text', + field: 'process.executable.caseless', value: 'some value', operator: 'included', type: 'match', @@ -392,7 +393,7 @@ describe('when invoking endpoint trusted apps route handlers', () => { await routeHandler(context, request, response); expect(exceptionsListClient.createExceptionListItem.mock.calls[0][0].entries).toEqual([ { - field: 'process.executable.text', + field: 'process.executable.caseless', operator: 'included', type: 'match', value: 'c:/programs files/Anti-Virus', diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts index 2b8129ab950c66..322d9a65162c09 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/trusted_apps/utils.ts @@ -20,8 +20,8 @@ export const exceptionItemToTrustedAppItem = ( exceptionListItem: ExceptionListItemSchema ): TrustedApp => { // eslint-disable-next-line @typescript-eslint/naming-convention - const { entries, description, created_by, created_at, name, _tags, id } = exceptionListItem; - const os = osFromTagsList(_tags); + const { entries, description, created_by, created_at, name, os_types, id } = exceptionListItem; + const os = os_types.length ? os_types[0] : 'unknown'; return { entries: entries.map((entry) => { if (entry.field.startsWith('process.hash')) { @@ -41,19 +41,6 @@ export const exceptionItemToTrustedAppItem = ( } as TrustedApp; }; -/** - * Retrieves the OS entry from a list of tags (property returned with ExcptionListItem). - * For Trusted Apps each entry must have at MOST 1 OS. - * */ -const osFromTagsList = (tags: string[]): TrustedApp['os'] | 'unknown' => { - for (const tag of tags) { - if (tag.startsWith('os:')) { - return tag.substr(3) as TrustedApp['os']; - } - } - return 'unknown'; -}; - export const newTrustedAppItemToExceptionItem = ({ os, entries, @@ -61,7 +48,6 @@ export const newTrustedAppItemToExceptionItem = ({ description = '', }: NewTrustedApp): NewExceptionItem => { return { - _tags: tagsListFromOs(os), comments: [], description, // @ts-ignore @@ -83,15 +69,12 @@ export const newTrustedAppItemToExceptionItem = ({ meta: undefined, name: name.trim(), namespaceType: 'agnostic', + osTypes: [os], tags: [], type: 'simple', }; }; -const tagsListFromOs = (os: NewTrustedApp['os']): NewExceptionItem['_tags'] => { - return [`os:${os}`]; -}; - const hashType = (hash: string): 'md5' | 'sha256' | 'sha1' | undefined => { switch (hash.length) { case 32: From 511eb0f23bbc85d8131a1b1364a5f5e06b837858 Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Fri, 2 Oct 2020 13:24:39 -0700 Subject: [PATCH 109/128] [babel/register] remove from build (#79176) Co-authored-by: spalger <spalger@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- package.json | 7 ++-- packages/kbn-babel-preset/node_preset.js | 8 ---- packages/kbn-babel-preset/package.json | 1 - .../lib/babel_register_for_test_plugins.js | 39 +++++++++++++++++++ .../functional_tests/lib/run_kibana_server.js | 20 +++++++++- scripts/build_plugin_list_docs.js | 2 +- scripts/es.js | 2 +- scripts/generate_plugin.js | 2 +- scripts/plugin_helpers.js | 2 +- scripts/register_git_hook.js | 2 +- scripts/release_notes.js | 2 +- scripts/telemetry_check.js | 2 +- scripts/telemetry_extract.js | 2 +- src/cli/index.js | 3 +- .../integration_tests/invalid_config.test.ts | 22 +++++++---- src/dev/build/tasks/copy_source_task.ts | 2 + src/setup_node_env/babel_register/register.js | 24 +++--------- src/setup_node_env/index.js | 2 +- ..._dev_only_entry.js => no_transpilation.js} | 0 x-pack/package.json | 2 +- yarn.lock | 18 +++------ 21 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js rename src/setup_node_env/{prebuilt_dev_only_entry.js => no_transpilation.js} (100%) diff --git a/package.json b/package.json index 5089e6e1a140d8..cebfddbe34e945 100644 --- a/package.json +++ b/package.json @@ -115,8 +115,6 @@ ] }, "dependencies": { - "@babel/core": "^7.11.1", - "@babel/register": "^7.10.5", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "7.9.1", "@elastic/eui": "29.0.0", @@ -128,7 +126,6 @@ "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", "@kbn/apm-config-loader": "1.0.0", - "@kbn/babel-preset": "1.0.0", "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", @@ -214,6 +211,7 @@ "rxjs": "^6.5.5", "seedrandom": "^3.0.5", "semver": "^5.7.0", + "source-map-support": "^0.5.19", "style-it": "^2.1.3", "symbol-observable": "^1.2.0", "tar": "4.4.13", @@ -227,7 +225,9 @@ "yauzl": "^2.10.0" }, "devDependencies": { + "@babel/core": "^7.11.1", "@babel/parser": "^7.11.2", + "@babel/register": "^7.10.5", "@babel/types": "^7.11.0", "@elastic/apm-rum": "^5.6.1", "@elastic/charts": "23.0.0", @@ -238,6 +238,7 @@ "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^6.0.0", "@elastic/ui-ace": "0.2.3", + "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/es-archiver": "1.0.0", diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 45afe5d5ebc32b..86817ed253e7cd 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -49,13 +49,5 @@ module.exports = (_, options = {}) => { ], require('./common_preset'), ], - plugins: [ - [ - require.resolve('babel-plugin-transform-define'), - { - 'global.__BUILT_WITH_BABEL__': 'true', - }, - ], - ], }; }; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index bc4e0ec338f94e..79d2fd8687dae5 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -14,7 +14,6 @@ "@babel/preset-typescript": "^7.10.4", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-styled-components": "^1.10.7", - "babel-plugin-transform-define": "^1.3.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "react-is": "^16.8.0", "styled-components": "^5.1.0" diff --git a/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js new file mode 100644 index 00000000000000..44ff579411bd9b --- /dev/null +++ b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const Path = require('path'); + +const { REPO_ROOT } = require('@kbn/dev-utils'); + +// modifies all future calls to require() to automatically +// compile the required source with babel +require('@babel/register')({ + ignore: [/[\/\\](node_modules|target|dist)[\/\\]/], + only: [ + Path.resolve(REPO_ROOT, 'test'), + Path.resolve(REPO_ROOT, 'x-pack/test'), + Path.resolve(REPO_ROOT, 'examples'), + Path.resolve(REPO_ROOT, 'x-pack/examples'), + // TODO: should should probably remove this link back to the source + Path.resolve(REPO_ROOT, 'x-pack/plugins/task_manager/server/config.ts'), + ], + babelrc: false, + presets: [require.resolve('@kbn/babel-preset/node_preset')], + extensions: ['.js', '.ts', '.tsx'], +}); diff --git a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js index fb9f8f7a52408c..e7ec99467ecfd8 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js +++ b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js @@ -17,9 +17,26 @@ * under the License. */ -import { resolve } from 'path'; +import { resolve, relative } from 'path'; import { KIBANA_ROOT, KIBANA_EXEC, KIBANA_EXEC_PATH } from './paths'; +function extendNodeOptions(installDir) { + if (!installDir) { + return {}; + } + + const testOnlyRegisterPath = relative( + installDir, + require.resolve('./babel_register_for_test_plugins') + ); + + return { + NODE_OPTIONS: `--require=${testOnlyRegisterPath}${ + process.env.NODE_OPTIONS ? ` ${process.env.NODE_OPTIONS}` : '' + }`, + }; +} + export async function runKibanaServer({ procs, config, options }) { const { installDir } = options; @@ -29,6 +46,7 @@ export async function runKibanaServer({ procs, config, options }) { env: { FORCE_COLOR: 1, ...process.env, + ...extendNodeOptions(installDir), }, cwd: installDir || KIBANA_ROOT, wait: /http server running/, diff --git a/scripts/build_plugin_list_docs.js b/scripts/build_plugin_list_docs.js index 54821a1b10ee8d..6f184ca7b14c69 100644 --- a/scripts/build_plugin_list_docs.js +++ b/scripts/build_plugin_list_docs.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/dev-utils').runPluginListCli(); diff --git a/scripts/es.js b/scripts/es.js index 2d56496f2fdd23..53b01d8cb44140 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -17,7 +17,7 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); var resolve = require('path').resolve; var pkg = require('../package.json'); diff --git a/scripts/generate_plugin.js b/scripts/generate_plugin.js index f695eabb30f219..af3d31048ecfc6 100644 --- a/scripts/generate_plugin.js +++ b/scripts/generate_plugin.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/plugin-generator').runCli(); diff --git a/scripts/plugin_helpers.js b/scripts/plugin_helpers.js index a07ba7a9185f80..f28bf8fcfff903 100644 --- a/scripts/plugin_helpers.js +++ b/scripts/plugin_helpers.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/plugin-helpers').runCli(); diff --git a/scripts/register_git_hook.js b/scripts/register_git_hook.js index af3f54619bcecc..50dfeaf46109fc 100644 --- a/scripts/register_git_hook.js +++ b/scripts/register_git_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/dev-utils/target/precommit_hook/cli'); diff --git a/scripts/release_notes.js b/scripts/release_notes.js index f46ee5823d70d1..ee9275194ae94a 100644 --- a/scripts/release_notes.js +++ b/scripts/release_notes.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/release-notes').runReleaseNotesCli(); diff --git a/scripts/telemetry_check.js b/scripts/telemetry_check.js index 06b3ed46bdba6a..22a22b401cb15f 100644 --- a/scripts/telemetry_check.js +++ b/scripts/telemetry_check.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/telemetry-tools').runTelemetryCheck(); diff --git a/scripts/telemetry_extract.js b/scripts/telemetry_extract.js index 051bee26537b9b..e2fbb64c267196 100644 --- a/scripts/telemetry_extract.js +++ b/scripts/telemetry_extract.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/telemetry-tools').runTelemetryExtract(); diff --git a/src/cli/index.js b/src/cli/index.js index 45f88eaf82a5b8..e5480d21376249 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -18,5 +18,6 @@ */ require('../apm')(); -require('../setup_node_env'); +require('../setup_node_env/no_transpilation'); +require('../setup_node_env/babel_register/polyfill'); require('./cli'); diff --git a/src/cli/serve/integration_tests/invalid_config.test.ts b/src/cli/serve/integration_tests/invalid_config.test.ts index fd6fa1bf192fc3..a72142faa22fe6 100644 --- a/src/cli/serve/integration_tests/invalid_config.test.ts +++ b/src/cli/serve/integration_tests/invalid_config.test.ts @@ -18,10 +18,10 @@ */ import { spawnSync } from 'child_process'; -import { resolve } from 'path'; -const ROOT_DIR = resolve(__dirname, '../../../../'); -const INVALID_CONFIG_PATH = resolve(__dirname, '__fixtures__/invalid_config.yml'); +import { REPO_ROOT } from '@kbn/dev-utils'; + +const INVALID_CONFIG_PATH = require.resolve('./__fixtures__/invalid_config.yml'); interface LogEntry { message: string; @@ -35,11 +35,11 @@ describe('cli invalid config support', function () { function () { // Unused keys only throw once LegacyService starts, so disable migrations so that Core // will finish the start lifecycle without a running Elasticsearch instance. - const { error, status, stdout } = spawnSync( + const { error, status, stdout, stderr } = spawnSync( process.execPath, - ['src/cli', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], + ['scripts/kibana', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], { - cwd: ROOT_DIR, + cwd: REPO_ROOT, } ); @@ -57,13 +57,21 @@ describe('cli invalid config support', function () { })); expect(error).toBe(undefined); - expect(status).toBe(64); + + if (!fatalLogLine) { + throw new Error( + `cli did not log the expected fatal error message:\n\nstdout: \n${stdout}\n\nstderr:\n${stderr}` + ); + } + expect(fatalLogLine.message).toContain( 'Error: Unknown configuration key(s): "unknown.key", "other.unknown.key", "other.third", "some.flat.key", ' + '"some.array". Check for spelling errors and ensure that expected plugins are installed.' ); expect(fatalLogLine.tags).toEqual(['fatal', 'root']); expect(fatalLogLine.type).toEqual('log'); + + expect(status).toBe(64); }, 20 * 1000 ); diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 948e2357effb04..78e1395586a17f 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -37,6 +37,8 @@ export const CopySource: Task = { '!src/cli/repl/**', '!src/functional_test_runner/**', '!src/dev/**', + '!src/setup_node_env/babel_register/index.js', + '!src/setup_node_env/babel_register/register.js', '!**/public/**', 'typings/**', 'config/kibana.yml', diff --git a/src/setup_node_env/babel_register/register.js b/src/setup_node_env/babel_register/register.js index 6d573d89222449..3c0bd387c8e44d 100644 --- a/src/setup_node_env/babel_register/register.js +++ b/src/setup_node_env/babel_register/register.js @@ -46,26 +46,12 @@ var ignore = [ // ignore paths matching `/canvas/canvas_plugin/` /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, -]; -if (global.__BUILT_WITH_BABEL__) { - // when building the Kibana source we replace the statement - // `global.__BUILT_WITH_BABEL__` with the value `true` so that - // when @babel/register is required for the first time by users - // it will exclude kibana's `src` directory. - // - // We still need @babel/register for plugins though, we've been - // building their server code at require-time since version 4.2 - // TODO: the plugin install process could transpile plugin server code... - ignore.push(resolve(__dirname, '../../../src')); -} else { - ignore.push( - // ignore any path in the packages, unless it is in the package's - // root `src` directory, in any test or __tests__ directory, or it - // ends with .test.js, .test.ts, or .test.tsx - /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/ - ); -} + // ignore any path in the packages, unless it is in the package's + // root `src` directory, in any test or __tests__ directory, or it + // ends with .test.js, .test.ts, or .test.tsx + /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, +]; // modifies all future calls to require() to automatically // compile the required source with babel diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index d84249df7fd8f0..60f0982f50d209 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -17,5 +17,5 @@ * under the License. */ -require('./prebuilt_dev_only_entry'); +require('./no_transpilation'); require('./babel_register'); diff --git a/src/setup_node_env/prebuilt_dev_only_entry.js b/src/setup_node_env/no_transpilation.js similarity index 100% rename from src/setup_node_env/prebuilt_dev_only_entry.js rename to src/setup_node_env/no_transpilation.js diff --git a/x-pack/package.json b/x-pack/package.json index 5742200b55d9f4..4145d8d72cc636 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -32,6 +32,7 @@ "@cypress/webpack-preprocessor": "^5.4.1", "@elastic/apm-rum-react": "^1.2.5", "@elastic/maki": "6.3.0", + "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/expect": "1.0.0", @@ -280,7 +281,6 @@ "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.0", "@elastic/safer-lodash-set": "0.0.0", - "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 971a94bfe56c3b..806424b222ad3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7545,14 +7545,6 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= -babel-plugin-transform-define@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.1.tgz#b21b7bad3b84cf8e3f07cdc8c660b99cbbc01213" - integrity sha512-JXZ1xE9jIbKCGYZ4wbSMPSI5mdS4DRLi5+SkTHgZqWn5YIf/EucykkzUsPmzJlpkX8fsMVdLnA5vt/LvT97Zbg== - dependencies: - lodash "^4.17.11" - traverse "0.6.6" - babel-plugin-transform-inline-consecutive-adds@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz#323d47a3ea63a83a7ac3c811ae8e6941faf2b0d1" @@ -27110,10 +27102,10 @@ source-map-support@^0.3.2: dependencies: source-map "0.1.32" -source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== +source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -28936,7 +28928,7 @@ traverse-chain@~0.1.0: resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= -traverse@0.6.6, traverse@^0.6.6, traverse@~0.6.6: +traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= From 43cf97e1f2fb7880c47df6b43d2998854d2cdeae Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" <devin.hurley@elastic.co> Date: Fri, 2 Oct 2020 16:46:12 -0400 Subject: [PATCH 110/128] [Security Solution] [Detections] Only display actions options if user has "read" privileges (#78812) * adds new 'can_read_actions' property to privileges api * only display rule actions piece if user has 'read' privileges for actions * display dropdown with custom text telling user they do not have read privileges for actions * fixes type error * update tests * utilize application capabilities instead of making a server request * remove changes to route tests * don't show form unless user has read permissions for actions, display text saying user is missing required privileges * pr feedback: refactor logic for rendering form fields --- .../rules/step_rule_actions/index.test.tsx | 8 ++ .../rules/step_rule_actions/index.tsx | 79 ++++++++++++------- .../rules/step_rule_actions/translations.tsx | 8 ++ 3 files changed, 68 insertions(+), 27 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx index 165aa5a30b0f03..aea75b5b3b796a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.test.tsx @@ -14,6 +14,14 @@ jest.mock('../../../../common/lib/kibana', () => ({ services: { application: { getUrlForApp: jest.fn(), + capabilities: { + siem: { + crud: true, + }, + actions: { + read: true, + }, + }, }, triggers_actions_ui: { actionTypeRegistry: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx index e6f1c25bf9dacc..77168e62492c63 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/index.tsx @@ -11,6 +11,7 @@ import { EuiFlexItem, EuiButton, EuiSpacer, + EuiText, } from '@elastic/eui'; import { findIndex } from 'lodash/fp'; import React, { FC, memo, useCallback, useEffect, useMemo } from 'react'; @@ -141,37 +142,61 @@ const StepRuleActionsComponent: FC<StepRuleActionsProps> = ({ [isLoading, throttleOptions] ); - return isReadOnlyView ? ( - <StepContentWrapper addPadding={addPadding}> - <StepRuleDescription schema={schema} data={initialState} columns="single" /> - </StepContentWrapper> + if (isReadOnlyView) { + return ( + <StepContentWrapper addPadding={addPadding}> + <StepRuleDescription schema={schema} data={initialState} columns="single" /> + </StepContentWrapper> + ); + } + + const displayActionsOptions = + throttle !== stepActionsDefaultValue.throttle ? ( + <> + <EuiSpacer /> + <UseField + path="actions" + component={RuleActionsField} + componentProps={{ + messageVariables: actionMessageParams, + }} + /> + </> + ) : ( + <UseField path="actions" component={GhostFormField} /> + ); + + // only display the actions dropdown if the user has "read" privileges for actions + const displayActionsDropDown = application.capabilities.actions.show ? ( + <> + <UseField + path="throttle" + component={ThrottleSelectField} + componentProps={throttleFieldComponentProps} + /> + {displayActionsOptions} + <UseField path="kibanaSiemAppUrl" component={GhostFormField} /> + <UseField path="enabled" component={GhostFormField} /> + </> ) : ( + <> + <EuiText>{I18n.NO_ACTIONS_READ_PERMISSIONS}</EuiText> + <UseField + path="throttle" + componentProps={throttleFieldComponentProps} + component={GhostFormField} + /> + <UseField path="actions" component={GhostFormField} /> + <UseField path="kibanaSiemAppUrl" component={GhostFormField} /> + <UseField path="enabled" component={GhostFormField} /> + </> + ); + + return ( <> <StepContentWrapper addPadding={!isUpdateView}> <Form form={form} data-test-subj="stepRuleActions"> - <EuiForm> - <UseField - path="throttle" - component={ThrottleSelectField} - componentProps={throttleFieldComponentProps} - /> - {throttle !== stepActionsDefaultValue.throttle ? ( - <> - <EuiSpacer /> - <UseField - path="actions" - component={RuleActionsField} - componentProps={{ - messageVariables: actionMessageParams, - }} - /> - </> - ) : ( - <UseField path="actions" component={GhostFormField} /> - )} - <UseField path="kibanaSiemAppUrl" component={GhostFormField} /> - <UseField path="enabled" component={GhostFormField} /> - </EuiForm> + <EuiForm>{displayActionsDropDown}</EuiForm> </Form> </StepContentWrapper> diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx index a276cb17e95f59..b2677b761aabf7 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_rule_actions/translations.tsx @@ -28,6 +28,14 @@ export const NO_CONNECTOR_SELECTED = i18n.translate( } ); +export const NO_ACTIONS_READ_PERMISSIONS = i18n.translate( + 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.noReadActionsPrivileges', + { + defaultMessage: + 'Cannot create rule actions. You do not have "Read" permissions for the "Actions" plugin.', + } +); + export const INVALID_MUSTACHE_TEMPLATE = (paramKey: string) => i18n.translate( 'xpack.securitySolution.detectionEngine.createRule.stepRuleActions.invalidMustacheTemplateErrorMessage', From 0ce802522cb254342d3de1debc59f8f483c7f720 Mon Sep 17 00:00:00 2001 From: Justin Kambic <justin.kambic@elastic.co> Date: Fri, 2 Oct 2020 16:52:51 -0400 Subject: [PATCH 111/128] [Uptime] Synthetics UI (#77960) * Checkpoint * Various * Display synthetics steps. * Add loading state for snapshots. Add error and stack trace fields. * Lazy load screenshot images and cache screenshot GET endpoint. * Fix extra requests bug. * Improve screenshot empty state. * Switch to use of code block for stack and error. * Add onmouseenter and onmouseleave for image input/popover. * Add image overlay. * Support `skipped` state. * Add synthetics type for Ping. * Fix type references in reducer, api request, components. * Add required mapping logic to journey request function. * Modularize new components. * Delete obsolete code. * Delete unused code. * Test expanded row changes. * Add a test for ping list expand check. * Various fixes * Extract code accordion to new component * Subsume synthetics type into Ping type. * Add new journey viz for 0 steps case. * Use code block for console output. * Expand step count cap. * Improve formatting of console steps visualization. * Improve empty prompt. * Extract empty prompt to dedicated file. * Extract executed journey UI to dedicated file. * Extract console step list components to dedicated files. * Update empty journey prompt to accept only check_group. * Clean up script expanded row component. * Translate console output steps component. * Fix logic error. * Clean up console step component. * Translate empty journey component. * Translate status badge component. * Translate screenshot component. * Add experimental warning callout. * Re-introduce deleted code. * Simplify console output step list. * Support skipped step for executed journeys. * Simplify executed journey component. * Add translations for executed step. * Refresh outdated test snapshots. * Simplify journey reducer signature. * Repair types. * Fix broken i18n naming. * Add summary field to outdated ping test data. * Fix linting error. * Remove @ts-ignore comment. * Add tests for step screenshot display. * Add tests for status badge. * Rename test file. * Add tests for script expanded row. * Add tests for executed step. * Delete request and response fields from Ping's `synthetics` field. * Fix screenshot querying effect, add flag to journey step state. * Update screenshot api route to reply 404 when screenshot is null. * Simplify screenshot image fetching. * Delete obsolete code. * Rename BrowserExpandedRow component. * Remove all references to "suitejourney". * Add intentional var names. * Rename Console components to use "event" terminology instead of "step". * Employ better copy. * First names always bad names. * Rename CodeBlockAccordion component. * Add blob_mime field to Ping type. * Fix busted import path. * Update ping type for new position of errors field. * Repair broken types. * Fix summary querying * Type fixes. * Switch state object from list to KVP. * Checkpoint. * Fix screenshot display test. * Fix executed step test. * Refresh outdated test snapshots. * Repair broken types. * More typing fixes. * Fix console log and add a test. Co-authored-by: Andrew Cholakian <andrew@andrewvc.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../uptime/common/runtime_types/ping/ping.ts | 49 +++++ .../plugins/uptime/public/apps/uptime_app.tsx | 1 + .../ping_list/__tests__/expanded_row.test.tsx | 22 ++ .../ping_list/__tests__/ping_list.test.tsx | 12 +- .../monitor/ping_list/expanded_row.tsx | 20 ++ .../components/monitor/ping_list/index.tsx | 2 +- .../monitor/ping_list/ping_list.tsx | 85 +++++++- .../monitor/ping_list/ping_list_container.tsx | 51 ----- .../__tests__/browser_expanded_row.test.tsx | 181 ++++++++++++++++ .../__tests__/executed_step.test.tsx | 119 +++++++++++ .../__tests__/status_badge.test.tsx | 41 ++++ .../step_screenshot_display.test.tsx | 193 ++++++++++++++++++ .../synthetics/browser_expanded_row.tsx | 64 ++++++ .../synthetics/code_block_accordion.tsx | 35 ++++ .../monitor/synthetics/console_event.tsx | 37 ++++ .../synthetics/console_output_event_list.tsx | 41 ++++ .../monitor/synthetics/empty_journey.tsx | 52 +++++ .../monitor/synthetics/executed_journey.tsx | 80 ++++++++ .../monitor/synthetics/executed_step.tsx | 92 +++++++++ .../monitor/synthetics/status_badge.tsx | 52 +++++ .../synthetics/step_screenshot_display.tsx | 175 ++++++++++++++++ .../public/contexts/uptime_theme_context.tsx | 3 + .../uptime/public/state/actions/journey.ts | 24 +++ .../uptime/public/state/api/journey.ts | 22 ++ .../uptime/public/state/effects/index.ts | 2 + .../uptime/public/state/effects/journey.ts | 26 +++ .../uptime/public/state/reducers/index.ts | 2 + .../uptime/public/state/reducers/journey.ts | 98 +++++++++ .../state/selectors/__tests__/index.test.ts | 1 + .../uptime/public/state/selectors/index.ts | 2 + .../lib/requests/__tests__/get_pings.test.ts | 120 +++++++++++ .../lib/requests/get_journey_screenshot.ts | 50 +++++ .../server/lib/requests/get_journey_steps.ts | 61 ++++++ .../uptime/server/lib/requests/get_pings.ts | 44 +++- .../uptime/server/lib/requests/index.ts | 4 + .../plugins/uptime/server/rest_api/index.ts | 9 +- .../uptime/server/rest_api/pings/index.ts | 2 + .../rest_api/pings/journey_screenshots.ts | 40 ++++ .../uptime/server/rest_api/pings/journeys.ts | 34 +++ .../es_archives/uptime/pings/data.json.gz | Bin 765 -> 808 bytes 40 files changed, 1890 insertions(+), 58 deletions(-) delete mode 100644 x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx create mode 100644 x-pack/plugins/uptime/public/state/actions/journey.ts create mode 100644 x-pack/plugins/uptime/public/state/api/journey.ts create mode 100644 x-pack/plugins/uptime/public/state/effects/journey.ts create mode 100644 x-pack/plugins/uptime/public/state/reducers/journey.ts create mode 100644 x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts create mode 100644 x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts create mode 100644 x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts create mode 100644 x-pack/plugins/uptime/server/rest_api/pings/journeys.ts diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts index f954f8ba308493..775078a7e5df16 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/ping.ts @@ -180,6 +180,48 @@ export const PingType = t.intersection([ down: t.number, up: t.number, }), + synthetics: t.partial({ + index: t.number, + journey: t.type({ + id: t.string, + name: t.string, + }), + error: t.partial({ + message: t.string, + name: t.string, + stack: t.string, + }), + package_version: t.string, + step: t.type({ + index: t.number, + name: t.string, + }), + type: t.string, + // ui-related field + screenshotLoading: t.boolean, + // ui-related field + screenshotExists: t.boolean, + blob: t.string, + blob_mime: t.string, + payload: t.partial({ + duration: t.number, + index: t.number, + is_navigation_request: t.boolean, + message: t.string, + method: t.string, + name: t.string, + params: t.partial({ + homepage: t.string, + }), + source: t.string, + start: t.number, + status: t.string, + ts: t.number, + type: t.string, + url: t.string, + end: t.number, + }), + }), tags: t.array(t.string), tcp: t.partial({ rtt: t.partial({ @@ -202,6 +244,13 @@ export const PingType = t.intersection([ }), ]); +export const SyntheticsJourneyApiResponseType = t.type({ + checkGroup: t.string, + steps: t.array(PingType), +}); + +export type SyntheticsJourneyApiResponse = t.TypeOf<typeof SyntheticsJourneyApiResponseType>; + export type Ping = t.TypeOf<typeof PingType>; // Convenience function for tests etc that makes an empty ping diff --git a/x-pack/plugins/uptime/public/apps/uptime_app.tsx b/x-pack/plugins/uptime/public/apps/uptime_app.tsx index 4b58ba104314f4..a5b8bc859ad94f 100644 --- a/x-pack/plugins/uptime/public/apps/uptime_app.tsx +++ b/x-pack/plugins/uptime/public/apps/uptime_app.tsx @@ -40,6 +40,7 @@ export interface UptimeAppColors { range: string; mean: string; warning: string; + lightestShade: string; } export interface UptimeAppProps { diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx index 2c1434cfd64bd5..85d1362ed3766c 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/expanded_row.test.tsx @@ -17,6 +17,7 @@ describe('PingListExpandedRow', () => { docId: 'fdeio12', timestamp: '19290310', monitor: { + check_group: 'check_group_id', duration: { us: 12345, }, @@ -87,4 +88,25 @@ describe('PingListExpandedRow', () => { expect(docLinkComponent).toHaveLength(1); }); + + it('renders a synthetics expanded row for synth monitor', () => { + ping.monitor.type = 'browser'; + expect(shallowWithIntl(<PingListExpandedRowComponent ping={ping} />)).toMatchInlineSnapshot(` + <EuiFlexGroup + direction="column" + > + <EuiFlexItem> + <EuiCallOut + iconType="beaker" + title="Experimental feature" + /> + </EuiFlexItem> + <EuiFlexItem> + <BrowserExpandedRow + checkGroup="check_group_id" + /> + </EuiFlexItem> + </EuiFlexGroup> + `); + }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx index cb8413ba08a816..8de4b89b7880d2 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/__tests__/ping_list.test.tsx @@ -6,7 +6,7 @@ import React from 'react'; import { shallowWithIntl } from 'test_utils/enzyme_helpers'; -import { PingListComponent, toggleDetails } from '../ping_list'; +import { PingListComponent, rowShouldExpand, toggleDetails } from '../ping_list'; import { Ping, PingsResponse } from '../../../../../common/runtime_types'; import { ExpandedRowMap } from '../../../overview/monitor_list/types'; @@ -182,6 +182,7 @@ describe('PingList component', () => { to: 'now', }} getPings={jest.fn()} + pruneJourneysCallback={jest.fn()} lastRefresh={123} loading={false} locations={[]} @@ -273,5 +274,14 @@ describe('PingList component', () => { /> `); }); + + describe('rowShouldExpand', () => { + // TODO: expand for all cases + it('returns true for browser monitors', () => { + const ping = pings[0]; + ping.monitor.type = 'browser'; + expect(rowShouldExpand(ping)).toBe(true); + }); + }); }); }); diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx index 201803ff3a9513..6af38eca6b0e97 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/expanded_row.tsx @@ -3,6 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ + // @ts-ignore formatNumber import { formatNumber } from '@elastic/eui/lib/services/format'; import { @@ -19,6 +20,7 @@ import { i18n } from '@kbn/i18n'; import { Ping, HttpResponseBody } from '../../../../common/runtime_types'; import { DocLinkForBody } from './doc_link_body'; import { PingRedirects } from './ping_redirects'; +import { BrowserExpandedRow } from '../synthetics/browser_expanded_row'; interface Props { ping: Ping; @@ -53,6 +55,24 @@ const BodyExcerpt = ({ content }: { content: string }) => export const PingListExpandedRowComponent = ({ ping }: Props) => { const listItems = []; + if (ping.monitor.type === 'browser') { + return ( + <EuiFlexGroup direction="column"> + <EuiFlexItem> + <EuiCallOut + iconType="beaker" + title={i18n.translate('xpack.uptime.synthetics.experimentalCallout.title', { + defaultMessage: 'Experimental feature', + })} + /> + </EuiFlexItem> + <EuiFlexItem> + <BrowserExpandedRow checkGroup={ping.monitor.check_group} /> + </EuiFlexItem> + </EuiFlexGroup> + ); + } + // Show the error block if (ping.error) { listItems.push({ diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx index 7fc19bbc9622b2..da82d025f478b0 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/index.tsx @@ -5,4 +5,4 @@ */ export { PingListComponent } from './ping_list'; -export { PingList } from './ping_list_container'; +export { PingList } from './ping_list'; diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx index 09782c1b76edb9..590b2f787bac46 100644 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list.tsx @@ -21,14 +21,63 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import moment from 'moment'; -import React, { useState, useEffect } from 'react'; +import React, { useCallback, useContext, useState, useEffect } from 'react'; import styled from 'styled-components'; +import { useDispatch, useSelector } from 'react-redux'; import { Ping, GetPingsParams, DateRange } from '../../../../common/runtime_types'; import { convertMicrosecondsToMilliseconds as microsToMillis } from '../../../lib/helper'; import { LocationName } from './location_name'; import { Pagination } from '../../overview/monitor_list'; import { PingListExpandedRowComponent } from './expanded_row'; -import { PingListProps } from './ping_list_container'; +// import { PingListProps } from './ping_list_container'; +import { pruneJourneyState } from '../../../state/actions/journey'; +import { selectPingList } from '../../../state/selectors'; +import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts'; +import { getPings as getPingsAction } from '../../../state/actions'; + +export interface PingListProps { + monitorId: string; +} + +export const PingList = (props: PingListProps) => { + const { + error, + loading, + pingList: { locations, pings, total }, + } = useSelector(selectPingList); + + const { lastRefresh } = useContext(UptimeRefreshContext); + + const { dateRangeStart: drs, dateRangeEnd: dre } = useContext(UptimeSettingsContext); + + const dispatch = useDispatch(); + const getPingsCallback = useCallback( + (params: GetPingsParams) => dispatch(getPingsAction(params)), + [dispatch] + ); + const pruneJourneysCallback = useCallback( + (checkGroups: string[]) => dispatch(pruneJourneyState(checkGroups)), + [dispatch] + ); + + return ( + <PingListComponent + dateRange={{ + from: drs, + to: dre, + }} + error={error} + getPings={getPingsCallback} + pruneJourneysCallback={pruneJourneysCallback} + lastRefresh={lastRefresh} + loading={loading} + locations={locations} + pings={pings} + total={total} + {...props} + /> + ); +}; export const AllLocationOption = { 'data-test-subj': 'xpack.uptime.pingList.locationOptions.all', @@ -63,6 +112,7 @@ interface Props extends PingListProps { dateRange: DateRange; error?: Error; getPings: (props: GetPingsParams) => void; + pruneJourneysCallback: (checkGroups: string[]) => void; lastRefresh: number; loading: boolean; locations: string[]; @@ -96,6 +146,13 @@ const statusOptions = [ }, ]; +export function rowShouldExpand(item: Ping) { + const errorPresent = !!item.error; + const httpBodyPresent = item.http?.response?.body?.bytes ?? 0 > 0; + const isBrowserMonitor = item.monitor.type === 'browser'; + return errorPresent || httpBodyPresent || isBrowserMonitor; +} + export const PingListComponent = (props: Props) => { const [selectedLocation, setSelectedLocation] = useState<string>(''); const [status, setStatus] = useState<string>(''); @@ -105,6 +162,7 @@ export const PingListComponent = (props: Props) => { dateRange: { from, to }, error, getPings, + pruneJourneysCallback, lastRefresh, loading, locations, @@ -129,6 +187,27 @@ export const PingListComponent = (props: Props) => { const [expandedRows, setExpandedRows] = useState<Record<string, JSX.Element>>({}); + const expandedIdsToRemove = JSON.stringify( + Object.keys(expandedRows).filter((e) => !pings.some(({ docId }) => docId === e)) + ); + useEffect(() => { + const parsed = JSON.parse(expandedIdsToRemove); + if (parsed.length) { + parsed.forEach((docId: string) => { + delete expandedRows[docId]; + }); + setExpandedRows(expandedRows); + } + }, [expandedIdsToRemove, expandedRows]); + + const expandedCheckGroups = pings + .filter((p: Ping) => Object.keys(expandedRows).some((f) => p.docId === f)) + .map(({ monitor: { check_group: cg } }) => cg); + const expandedCheckGroupsStr = JSON.stringify(expandedCheckGroups); + useEffect(() => { + pruneJourneysCallback(JSON.parse(expandedCheckGroupsStr)); + }, [pruneJourneysCallback, expandedCheckGroupsStr]); + const locationOptions = !locations ? [AllLocationOption] : [AllLocationOption].concat( @@ -239,7 +318,7 @@ export const PingListComponent = (props: Props) => { <EuiButtonIcon data-test-subj="uptimePingListExpandBtn" onClick={() => toggleDetails(item, expandedRows, setExpandedRows)} - disabled={!item.error && !(item.http?.response?.body?.bytes ?? 0 > 0)} + disabled={!rowShouldExpand(item)} aria-label={ expandedRows[item.docId] ? i18n.translate('xpack.uptime.pingList.collapseRow', { diff --git a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx b/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx deleted file mode 100644 index 4d4bb175be9a85..00000000000000 --- a/x-pack/plugins/uptime/public/components/monitor/ping_list/ping_list_container.tsx +++ /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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { useSelector, useDispatch } from 'react-redux'; -import React, { useContext, useCallback } from 'react'; -import { selectPingList } from '../../../state/selectors'; -import { getPings } from '../../../state/actions'; -import { GetPingsParams } from '../../../../common/runtime_types'; -import { UptimeRefreshContext, UptimeSettingsContext } from '../../../contexts'; -import { PingListComponent } from './index'; - -export interface PingListProps { - monitorId: string; -} - -export const PingList = (props: PingListProps) => { - const { - error, - loading, - pingList: { locations, pings, total }, - } = useSelector(selectPingList); - - const { lastRefresh } = useContext(UptimeRefreshContext); - - const { dateRangeStart: drs, dateRangeEnd: dre } = useContext(UptimeSettingsContext); - - const dispatch = useDispatch(); - const getPingsCallback = useCallback((params: GetPingsParams) => dispatch(getPings(params)), [ - dispatch, - ]); - - return ( - <PingListComponent - dateRange={{ - from: drs, - to: dre, - }} - error={error} - getPings={getPingsCallback} - lastRefresh={lastRefresh} - loading={loading} - locations={locations} - pings={pings} - total={total} - {...props} - /> - ); -}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx new file mode 100644 index 00000000000000..191632d6ab713b --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/browser_expanded_row.test.tsx @@ -0,0 +1,181 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { BrowserExpandedRowComponent } from '../browser_expanded_row'; +import { Ping } from '../../../../../common/runtime_types'; + +describe('BrowserExpandedRowComponent', () => { + let defStep: Ping; + beforeEach(() => { + defStep = { + docId: 'doc-id', + timestamp: '123', + monitor: { + duration: { + us: 100, + }, + id: 'mon-id', + status: 'up', + type: 'browser', + }, + }; + }); + + it('returns empty step state when no journey', () => { + expect(shallowWithIntl(<BrowserExpandedRowComponent />)).toMatchInlineSnapshot( + `<EmptyStepState />` + ); + }); + + it('returns empty step state when journey has no steps', () => { + expect( + shallowWithIntl( + <BrowserExpandedRowComponent + journey={{ + checkGroup: 'check_group', + loading: false, + steps: [], + }} + /> + ) + ).toMatchInlineSnapshot(`<EmptyStepState />`); + }); + + it('displays loading spinner when loading', () => { + expect( + shallowWithIntl( + <BrowserExpandedRowComponent + journey={{ + checkGroup: 'check_group', + loading: true, + steps: [], + }} + /> + ) + ).toMatchInlineSnapshot(` + <div> + <EuiLoadingSpinner /> + </div> + `); + }); + + it('renders executed journey when step/end is present', () => { + expect( + shallowWithIntl( + <BrowserExpandedRowComponent + journey={{ + checkGroup: 'check_group', + loading: false, + steps: [ + { + ...defStep, + synthetics: { + type: 'step/end', + }, + }, + ], + }} + /> + ) + ).toMatchInlineSnapshot(` + <ExecutedJourney + journey={ + Object { + "checkGroup": "check_group", + "loading": false, + "steps": Array [ + Object { + "docId": "doc-id", + "monitor": Object { + "duration": Object { + "us": 100, + }, + "id": "mon-id", + "status": "up", + "type": "browser", + }, + "synthetics": Object { + "type": "step/end", + }, + "timestamp": "123", + }, + ], + } + } + /> + `); + }); + + it('renders console output step list when only console steps are present', () => { + expect( + shallowWithIntl( + <BrowserExpandedRowComponent + journey={{ + checkGroup: 'check_group', + loading: false, + steps: [ + { + ...defStep, + synthetics: { + type: 'stderr', + }, + }, + ], + }} + /> + ) + ).toMatchInlineSnapshot(` + <ConsoleOutputEventList + journey={ + Object { + "checkGroup": "check_group", + "loading": false, + "steps": Array [ + Object { + "docId": "doc-id", + "monitor": Object { + "duration": Object { + "us": 100, + }, + "id": "mon-id", + "status": "up", + "type": "browser", + }, + "synthetics": Object { + "type": "stderr", + }, + "timestamp": "123", + }, + ], + } + } + /> + `); + }); + + it('renders null when only unsupported steps are present', () => { + expect( + shallowWithIntl( + <BrowserExpandedRowComponent + journey={{ + checkGroup: 'check_group', + loading: false, + steps: [ + { + ...defStep, + synthetics: { + type: 'some other type', + }, + }, + ], + }} + /> + ) + ).toMatchInlineSnapshot(`""`); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx new file mode 100644 index 00000000000000..e3a8430cfa888a --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/executed_step.test.tsx @@ -0,0 +1,119 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { ExecutedStep } from '../executed_step'; +import { Ping } from '../../../../../common/runtime_types'; + +describe('ExecutedStep', () => { + let step: Ping; + + beforeEach(() => { + step = { + docId: 'docID', + monitor: { + duration: { + us: 123, + }, + id: 'id', + status: 'up', + type: 'browser', + }, + synthetics: { + step: { + index: 4, + name: 'STEP_NAME', + }, + }, + timestamp: 'timestamp', + }; + }); + + it('renders correct step heading', () => { + expect(mountWithIntl(<ExecutedStep index={3} step={step} />).find('EuiText')) + .toMatchInlineSnapshot(` + <EuiText> + <div + className="euiText euiText--medium" + > + <strong> + <FormattedMessage + defaultMessage="{stepNumber}. {stepName}" + id="xpack.uptime.synthetics.executedStep.stepName" + values={ + Object { + "stepName": "STEP_NAME", + "stepNumber": 4, + } + } + > + 4. STEP_NAME + </FormattedMessage> + </strong> + </div> + </EuiText> + `); + }); + + it('supplies status badge correct status', () => { + step.synthetics = { + payload: { status: 'THE_STATUS' }, + }; + expect(shallowWithIntl(<ExecutedStep index={3} step={step} />).find('StatusBadge')) + .toMatchInlineSnapshot(` + <StatusBadge + status="THE_STATUS" + /> + `); + }); + + it('renders accordions for step, error message, and error stack script', () => { + step.synthetics = { + error: { + message: 'There was an error executing the step.', + stack: 'some.stack.trace.string', + }, + payload: { + source: 'const someVar = "the var"', + }, + step: { + index: 3, + name: 'STEP_NAME', + }, + }; + + expect(shallowWithIntl(<ExecutedStep index={3} step={step} />).find('CodeBlockAccordion')) + .toMatchInlineSnapshot(` + Array [ + <CodeBlockAccordion + buttonContent="Step script" + id="STEP_NAME3" + language="javascript" + overflowHeight={360} + > + const someVar = "the var" + </CodeBlockAccordion>, + <CodeBlockAccordion + buttonContent="Error" + id="STEP_NAME_error" + language="html" + overflowHeight={360} + > + There was an error executing the step. + </CodeBlockAccordion>, + <CodeBlockAccordion + buttonContent="Stack trace" + id="STEP_NAME_stack" + language="html" + overflowHeight={360} + > + some.stack.trace.string + </CodeBlockAccordion>, + ] + `); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.tsx new file mode 100644 index 00000000000000..1171c24ad899c8 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/status_badge.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import { StatusBadge } from '../status_badge'; + +describe('StatusBadge', () => { + it('displays success message', () => { + expect(shallowWithIntl(<StatusBadge status="succeeded" />)).toMatchInlineSnapshot(` + <EuiBadge + color="#017d73" + > + Succeeded + </EuiBadge> + `); + }); + + it('displays failed message', () => { + expect(shallowWithIntl(<StatusBadge status="failed" />)).toMatchInlineSnapshot(` + <EuiBadge + color="#bd271e" + > + Failed + </EuiBadge> + `); + }); + + it('displays skipped message', () => { + expect(shallowWithIntl(<StatusBadge status="skipped" />)).toMatchInlineSnapshot(` + <EuiBadge + color="default" + > + Skipped + </EuiBadge> + `); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.test.tsx new file mode 100644 index 00000000000000..16db430dbd73a1 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/__tests__/step_screenshot_display.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowWithIntl, mountWithIntl } from 'test_utils/enzyme_helpers'; +import React from 'react'; +import * as reactUse from 'react-use'; +import { StepScreenshotDisplay } from '../step_screenshot_display'; + +describe('StepScreenshotDisplayProps', () => { + // @ts-ignore missing fields don't matter in this test, the component in question only relies on `isIntersecting` + jest.spyOn(reactUse, 'useIntersection').mockImplementation(() => ({ + isIntersecting: true, + })); + + it('displays screenshot thumbnail when present', () => { + const wrapper = mountWithIntl( + <StepScreenshotDisplay + checkGroup="check_group" + screenshotExists={true} + stepIndex={1} + stepName="STEP_NAME" + /> + ); + + wrapper.update(); + + expect(wrapper.find('img')).toMatchInlineSnapshot(`null`); + + expect(wrapper.find('EuiPopover')).toMatchInlineSnapshot(` + <EuiPopover + anchorPosition="rightCenter" + button={ + <input + alt="Screenshot for step with name \\"STEP_NAME\\"" + onClick={[Function]} + onMouseEnter={[Function]} + onMouseLeave={[Function]} + src="/api/uptime/journey/screenshot/check_group/1" + style={ + Object { + "height": 180, + "objectFit": "cover", + "objectPosition": "center top", + "width": 320, + } + } + type="image" + /> + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="m" + > + <EuiOutsideClickDetector + isDisabled={true} + onOutsideClick={[Function]} + > + <div + className="euiPopover euiPopover--anchorRightCenter" + onKeyDown={[Function]} + onMouseDown={[Function]} + onMouseUp={[Function]} + onTouchEnd={[Function]} + onTouchStart={[Function]} + > + <div + className="euiPopover__anchor" + > + <input + alt="Screenshot for step with name \\"STEP_NAME\\"" + onClick={[Function]} + onMouseEnter={[Function]} + onMouseLeave={[Function]} + src="/api/uptime/journey/screenshot/check_group/1" + style={ + Object { + "height": 180, + "objectFit": "cover", + "objectPosition": "center top", + "width": 320, + } + } + type="image" + /> + </div> + </div> + </EuiOutsideClickDetector> + </EuiPopover> + `); + }); + + it('uses alternative text when step name not available', () => { + const wrapper = mountWithIntl( + <StepScreenshotDisplay checkGroup="check_group" screenshotExists={true} stepIndex={1} /> + ); + + wrapper.update(); + + expect(wrapper.find('EuiPopover')).toMatchInlineSnapshot(` + <EuiPopover + anchorPosition="rightCenter" + button={ + <input + alt="Screenshot" + onClick={[Function]} + onMouseEnter={[Function]} + onMouseLeave={[Function]} + src="/api/uptime/journey/screenshot/check_group/1" + style={ + Object { + "height": 180, + "objectFit": "cover", + "objectPosition": "center top", + "width": 320, + } + } + type="image" + /> + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="m" + > + <EuiOutsideClickDetector + isDisabled={true} + onOutsideClick={[Function]} + > + <div + className="euiPopover euiPopover--anchorRightCenter" + onKeyDown={[Function]} + onMouseDown={[Function]} + onMouseUp={[Function]} + onTouchEnd={[Function]} + onTouchStart={[Function]} + > + <div + className="euiPopover__anchor" + > + <input + alt="Screenshot" + onClick={[Function]} + onMouseEnter={[Function]} + onMouseLeave={[Function]} + src="/api/uptime/journey/screenshot/check_group/1" + style={ + Object { + "height": 180, + "objectFit": "cover", + "objectPosition": "center top", + "width": 320, + } + } + type="image" + /> + </div> + </div> + </EuiOutsideClickDetector> + </EuiPopover> + `); + }); + + it('displays No Image message when screenshot does not exist', () => { + expect( + shallowWithIntl( + <StepScreenshotDisplay + checkGroup="check_group" + stepIndex={1} + stepName="STEP_NAME" + screenshotExists={false} + /> + ).find('EuiText') + ).toMatchInlineSnapshot(` + <EuiText> + <strong> + <FormattedMessage + defaultMessage="No image available" + id="xpack.uptime.synthetics.screenshot.noImageMessage" + values={Object {}} + /> + </strong> + </EuiText> + `); + }); +}); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx new file mode 100644 index 00000000000000..2546c5fb9a5d84 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/browser_expanded_row.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiLoadingSpinner } from '@elastic/eui'; +import React, { useEffect, FC } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Ping } from '../../../../common/runtime_types'; +import { getJourneySteps } from '../../../state/actions/journey'; +import { JourneyState } from '../../../state/reducers/journey'; +import { journeySelector } from '../../../state/selectors'; +import { EmptyStepState } from './empty_journey'; +import { ExecutedJourney } from './executed_journey'; +import { ConsoleOutputEventList } from './console_output_event_list'; + +interface BrowserExpandedRowProps { + checkGroup?: string; +} + +export const BrowserExpandedRow: React.FC<BrowserExpandedRowProps> = ({ checkGroup }) => { + const dispatch = useDispatch(); + useEffect(() => { + if (checkGroup) { + dispatch(getJourneySteps({ checkGroup })); + } + }, [dispatch, checkGroup]); + + const journeys = useSelector(journeySelector); + const journey = journeys[checkGroup ?? '']; + + return <BrowserExpandedRowComponent checkGroup={checkGroup} journey={journey} />; +}; + +type ComponentProps = BrowserExpandedRowProps & { + journey?: JourneyState; +}; + +const stepEnd = (step: Ping) => step.synthetics?.type === 'step/end'; +const stepConsole = (step: Ping) => + ['stderr', 'cmd/status'].indexOf(step.synthetics?.type ?? '') !== -1; + +export const BrowserExpandedRowComponent: FC<ComponentProps> = ({ checkGroup, journey }) => { + if (!!journey && journey.loading) { + return ( + <div> + <EuiLoadingSpinner /> + </div> + ); + } + + if (!journey || journey.steps.length === 0) { + return <EmptyStepState checkGroup={checkGroup} />; + } + + if (journey.steps.some(stepEnd)) return <ExecutedJourney journey={journey} />; + + if (journey.steps.some(stepConsole)) return <ConsoleOutputEventList journey={journey} />; + + // TODO: should not happen, this means that the journey has no step/end and no console logs, but some other steps; filmstrip, screenshot, etc. + // we should probably create an error prompt letting the user know this step is not supported yet + return null; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.tsx new file mode 100644 index 00000000000000..9f6559f0c27092 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/code_block_accordion.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiAccordion, EuiCodeBlock } from '@elastic/eui'; +import React, { FC } from 'react'; + +interface Props { + buttonContent: string; + id?: string; + language: 'html' | 'javascript'; + overflowHeight: number; +} + +/** + * Utility for showing `EuiAccordions` with code blocks which we use frequently in synthetics to display + * stack traces, long error messages, and synthetics journey code. + */ +export const CodeBlockAccordion: FC<Props> = ({ + buttonContent, + children, + id, + language, + overflowHeight, +}) => { + return children && id ? ( + <EuiAccordion id={id} buttonContent={buttonContent}> + <EuiCodeBlock isCopyable={true} overflowHeight={overflowHeight} language={language}> + {children} + </EuiCodeBlock> + </EuiAccordion> + ) : null; +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx new file mode 100644 index 00000000000000..0f2930db96b547 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_event.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import React, { useContext, FC } from 'react'; +import { Ping } from '../../../../common/runtime_types'; +import { UptimeThemeContext } from '../../../contexts'; + +interface Props { + event: Ping; +} + +export const ConsoleEvent: FC<Props> = ({ event }) => { + const { + colors: { danger }, + } = useContext(UptimeThemeContext); + + let typeColor: string | undefined; + if (event.synthetics?.type === 'stderr') { + typeColor = danger; + } else { + typeColor = undefined; + } + + return ( + <EuiFlexGroup> + <EuiFlexItem grow={false}>{event.timestamp}</EuiFlexItem> + <EuiFlexItem grow={false} style={{ color: typeColor }}> + {event.synthetics?.type} + </EuiFlexItem> + <EuiFlexItem>{event.synthetics?.payload?.message}</EuiFlexItem> + </EuiFlexGroup> + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.tsx new file mode 100644 index 00000000000000..9159c61532f155 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/console_output_event_list.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; +import { JourneyState } from '../../../state/reducers/journey'; +import { ConsoleEvent } from './console_event'; + +interface Props { + journey: JourneyState; +} + +export const ConsoleOutputEventList: FC<Props> = ({ journey }) => ( + <div> + <EuiTitle> + <h4> + <FormattedMessage + id="xpack.uptime.synthetics.consoleStepList.title" + defaultMessage="No steps ran" + /> + </h4> + </EuiTitle> + <EuiSpacer /> + <p> + <FormattedMessage + id="xpack.uptime.synthetics.consoleStepList.message" + defaultMessage="This journey failed to run, recorded console output is shown below:" + /> + </p> + <EuiSpacer /> + <EuiCodeBlock> + {journey.steps.map((consoleEvent) => ( + <ConsoleEvent event={consoleEvent} /> + ))} + </EuiCodeBlock> + </div> +); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx new file mode 100644 index 00000000000000..b6fead2bbbe09d --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/empty_journey.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; + +interface EmptyStepStateProps { + checkGroup?: string; +} + +export const EmptyStepState: FC<EmptyStepStateProps> = ({ checkGroup }) => ( + <EuiEmptyPrompt + iconType="cross" + title={ + <h2> + <FormattedMessage + id="xpack.uptime.synthetics.emptyJourney.title" + defaultMessage="There are no steps for this journey" + /> + </h2> + } + body={ + <> + <p> + <FormattedMessage + id="xpack.uptime.synthetics.emptyJourney.message.heading" + defaultMessage="This journey did not contain any steps." + /> + </p> + {!!checkGroup && ( + <p> + <FormattedMessage + id="xpack.uptime.synthetics.emptyJourney.message.checkGroupField" + defaultMessage="The journey's check group is {codeBlock}." + values={{ codeBlock: <code>{checkGroup}</code> }} + /> + </p> + )} + <p> + <FormattedMessage + id="xpack.uptime.synthetics.emptyJourney.message.footer" + defaultMessage="There is no further information to display." + /> + </p> + </> + } + /> +); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx new file mode 100644 index 00000000000000..2c37f95428d0e6 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_journey.tsx @@ -0,0 +1,80 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; +import { Ping } from '../../../../common/runtime_types'; +import { JourneyState } from '../../../state/reducers/journey'; +import { ExecutedStep } from './executed_step'; + +interface StepStatusCount { + failed: number; + skipped: number; + succeeded: number; +} + +function statusMessage(count: StepStatusCount) { + const total = count.succeeded + count.failed + count.skipped; + if (count.failed + count.skipped === total) { + return i18n.translate('xpack.uptime.synthetics.journey.allFailedMessage', { + defaultMessage: '{total} Steps - all failed or skipped', + values: { total }, + }); + } else if (count.succeeded === total) { + return i18n.translate('xpack.uptime.synthetics.journey.allSucceededMessage', { + defaultMessage: '{total} Steps - all succeeded', + values: { total }, + }); + } + return i18n.translate('xpack.uptime.synthetics.journey.partialSuccessMessage', { + defaultMessage: '{total} Steps - {succeeded} succeeded', + values: { succeeded: count.succeeded, total }, + }); +} + +function reduceStepStatus(prev: StepStatusCount, cur: Ping): StepStatusCount { + if (cur.synthetics?.payload?.status === 'succeeded') { + prev.succeeded += 1; + return prev; + } else if (cur.synthetics?.payload?.status === 'skipped') { + prev.skipped += 1; + return prev; + } + prev.failed += 1; + return prev; +} + +interface ExecutedJourneyProps { + journey: JourneyState; +} + +export const ExecutedJourney: FC<ExecutedJourneyProps> = ({ journey }) => ( + <div> + <EuiText> + <h3> + <FormattedMessage + id="xpack.uptime.synthetics.executedJourney.heading" + defaultMessage="Summary information" + /> + </h3> + <p> + {statusMessage( + journey.steps.reduce(reduceStepStatus, { failed: 0, skipped: 0, succeeded: 0 }) + )} + </p> + </EuiText> + <EuiSpacer /> + <EuiFlexGroup direction="column"> + {journey.steps + .filter((step) => step.synthetics?.type === 'step/end') + .map((step, index) => ( + <ExecutedStep key={index} index={index} step={step} /> + ))} + </EuiFlexGroup> + </div> +); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx new file mode 100644 index 00000000000000..3c26ba12eea651 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/executed_step.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiFlexItem, EuiFlexGroup, EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { CodeBlockAccordion } from './code_block_accordion'; +import { StepScreenshotDisplay } from './step_screenshot_display'; +import { StatusBadge } from './status_badge'; +import { Ping } from '../../../../common/runtime_types'; + +const CODE_BLOCK_OVERFLOW_HEIGHT = 360; + +interface ExecutedStepProps { + step: Ping; + index: number; +} + +export const ExecutedStep: FC<ExecutedStepProps> = ({ step, index }) => ( + <> + <div style={{ padding: '8px' }}> + <div> + <EuiText> + <strong> + <FormattedMessage + id="xpack.uptime.synthetics.executedStep.stepName" + defaultMessage="{stepNumber}. {stepName}" + values={{ + stepNumber: index + 1, + stepName: step.synthetics?.step?.name, + }} + /> + </strong> + </EuiText> + </div> + <EuiSpacer size="s" /> + <div> + <StatusBadge status={step.synthetics?.payload?.status} /> + </div> + <EuiSpacer /> + <div> + <EuiFlexGroup> + <EuiFlexItem grow={false}> + <StepScreenshotDisplay + checkGroup={step.monitor.check_group} + screenshotExists={step.synthetics?.screenshotExists} + stepIndex={step.synthetics?.step?.index} + stepName={step.synthetics?.step?.name} + /> + </EuiFlexItem> + <EuiFlexItem> + <CodeBlockAccordion + id={step.synthetics?.step?.name + String(index)} + buttonContent={i18n.translate('xpack.uptime.synthetics.executedStep.scriptHeading', { + defaultMessage: 'Step script', + })} + overflowHeight={CODE_BLOCK_OVERFLOW_HEIGHT} + language="javascript" + > + {step.synthetics?.payload?.source} + </CodeBlockAccordion> + <CodeBlockAccordion + id={`${step.synthetics?.step?.name}_error`} + buttonContent={i18n.translate('xpack.uptime.synthetics.executedStep.errorHeading', { + defaultMessage: 'Error', + })} + language="html" + overflowHeight={CODE_BLOCK_OVERFLOW_HEIGHT} + > + {step.synthetics?.error?.message} + </CodeBlockAccordion> + <CodeBlockAccordion + id={`${step.synthetics?.step?.name}_stack`} + buttonContent={i18n.translate('xpack.uptime.synthetics.executedStep.stackTrace', { + defaultMessage: 'Stack trace', + })} + language="html" + overflowHeight={CODE_BLOCK_OVERFLOW_HEIGHT} + > + {step.synthetics?.error?.stack} + </CodeBlockAccordion> + </EuiFlexItem> + </EuiFlexGroup> + </div> + </div> + <EuiSpacer /> + </> +); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx new file mode 100644 index 00000000000000..f8fbeccaabf425 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/status_badge.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiBadge } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React, { useContext, FC } from 'react'; +import { UptimeAppColors } from '../../../apps/uptime_app'; +import { UptimeThemeContext } from '../../../contexts'; + +interface StatusBadgeProps { + status?: string; +} + +export function colorFromStatus(color: UptimeAppColors, status?: string) { + switch (status) { + case 'succeeded': + return color.success; + case 'failed': + return color.danger; + default: + return 'default'; + } +} + +export function textFromStatus(status?: string) { + switch (status) { + case 'succeeded': + return i18n.translate('xpack.uptime.synthetics.statusBadge.succeededMessage', { + defaultMessage: 'Succeeded', + }); + case 'failed': + return i18n.translate('xpack.uptime.synthetics.statusBadge.failedMessage', { + defaultMessage: 'Failed', + }); + case 'skipped': + return i18n.translate('xpack.uptime.synthetics.statusBadge.skippedMessage', { + defaultMessage: 'Skipped', + }); + default: + return null; + } +} + +export const StatusBadge: FC<StatusBadgeProps> = ({ status }) => { + const theme = useContext(UptimeThemeContext); + return ( + <EuiBadge color={colorFromStatus(theme.colors, status)}>{textFromStatus(status)}</EuiBadge> + ); +}; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx new file mode 100644 index 00000000000000..2e8ad4bd0c9a8d --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_screenshot_display.tsx @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiOverlayMask, + EuiPopover, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import React, { useContext, useEffect, useRef, useState, FC } from 'react'; +import { useIntersection } from 'react-use'; +import { UptimeThemeContext } from '../../../contexts'; + +interface StepScreenshotDisplayProps { + screenshotExists?: boolean; + checkGroup?: string; + stepIndex?: number; + stepName?: string; +} + +const THUMBNAIL_WIDTH = 320; +const THUMBNAIL_HEIGHT = 180; +const POPOVER_IMG_WIDTH = 640; +const POPOVER_IMG_HEIGHT = 360; + +export const StepScreenshotDisplay: FC<StepScreenshotDisplayProps> = ({ + checkGroup, + screenshotExists, + stepIndex, + stepName, +}) => { + const containerRef = useRef(null); + const { + colors: { lightestShade: pageBackground }, + } = useContext(UptimeThemeContext); + + const [isImagePopoverOpen, setIsImagePopoverOpen] = useState<boolean>(false); + const [isOverlayOpen, setIsOverlayOpen] = useState<boolean>(false); + + const intersection = useIntersection(containerRef, { + root: null, + rootMargin: '0px', + threshold: 1, + }); + + const [hasIntersected, setHasIntersected] = useState<boolean>(false); + const isIntersecting = intersection?.isIntersecting; + useEffect(() => { + if (hasIntersected === false && isIntersecting === true) { + setHasIntersected(true); + } + }, [hasIntersected, isIntersecting, setHasIntersected]); + + let content: JSX.Element | null = null; + const imgSrc = `/api/uptime/journey/screenshot/${checkGroup}/${stepIndex}`; + if (hasIntersected && screenshotExists) { + content = ( + <> + {isOverlayOpen && ( + <EuiOverlayMask onClick={() => setIsOverlayOpen(false)}> + <input + type="image" + src={imgSrc} + alt={ + stepName + ? i18n.translate( + 'xpack.uptime.synthetics.screenshotDisplay.fullScreenshotAltText', + { + defaultMessage: 'Full screenshot for step with name "{stepName}"', + values: { + stepName, + }, + } + ) + : i18n.translate( + 'xpack.uptime.synthetics.screenshotDisplay.fullScreenshotAltTextWithoutName', + { + defaultMessage: 'Full screenshot', + } + ) + } + style={{ objectFit: 'contain' }} + onClick={() => setIsOverlayOpen(false)} + /> + </EuiOverlayMask> + )} + <EuiPopover + anchorPosition="rightCenter" + button={ + <input + type="image" + style={{ + width: THUMBNAIL_WIDTH, + height: THUMBNAIL_HEIGHT, + objectFit: 'cover', + objectPosition: 'center top', + }} + src={imgSrc} + alt={ + stepName + ? i18n.translate('xpack.uptime.synthetics.screenshotDisplay.altText', { + defaultMessage: 'Screenshot for step with name "{stepName}"', + values: { + stepName, + }, + }) + : i18n.translate('xpack.uptime.synthetics.screenshotDisplay.altTextWithoutName', { + defaultMessage: 'Screenshot', + }) + } + onClick={() => setIsOverlayOpen(true)} + onMouseEnter={() => setIsImagePopoverOpen(true)} + onMouseLeave={() => setIsImagePopoverOpen(false)} + /> + } + closePopover={() => setIsImagePopoverOpen(false)} + isOpen={isImagePopoverOpen} + > + <img + alt={ + stepName + ? i18n.translate('xpack.uptime.synthetics.screenshotDisplay.thumbnailAltText', { + defaultMessage: 'Thumbnail screenshot for step with name "{stepName}"', + values: { + stepName, + }, + }) + : i18n.translate( + 'xpack.uptime.synthetics.screenshotDisplay.thumbnailAltTextWithoutName', + { + defaultMessage: 'Thumbnail screenshot', + } + ) + } + src={imgSrc} + style={{ width: POPOVER_IMG_WIDTH, height: POPOVER_IMG_HEIGHT, objectFit: 'contain' }} + /> + </EuiPopover> + </> + ); + } else if (screenshotExists === false) { + content = ( + <EuiFlexGroup alignItems="center" direction="column" style={{ paddingTop: '32px' }}> + <EuiFlexItem grow={false}> + <EuiIcon color="subdued" size="xxl" type="image" /> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiText> + <strong> + <FormattedMessage + id="xpack.uptime.synthetics.screenshot.noImageMessage" + defaultMessage="No image available" + /> + </strong> + </EuiText> + </EuiFlexItem> + </EuiFlexGroup> + ); + } + return ( + <div + ref={containerRef} + style={{ backgroundColor: pageBackground, height: THUMBNAIL_HEIGHT, width: THUMBNAIL_WIDTH }} + > + {content} + </div> + ); +}; diff --git a/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx b/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx index 51e8bcaed986ff..f0a702b9c0b751 100644 --- a/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx +++ b/x-pack/plugins/uptime/public/contexts/uptime_theme_context.tsx @@ -31,6 +31,7 @@ const defaultContext: UptimeThemeContextValues = { success: euiLightVars.euiColorSuccess, warning: euiLightVars.euiColorWarning, gray: euiLightVars.euiColorLightShade, + lightestShade: euiLightVars.euiColorLightestShade, }, chartTheme: { baseTheme: LIGHT_THEME, @@ -54,6 +55,7 @@ export const UptimeThemeContextProvider: React.FC<ThemeContextProps> = ({ darkMo range: euiDarkVars.euiFocusBackgroundColor, success: euiDarkVars.euiColorSuccess, warning: euiDarkVars.euiColorWarning, + lightestShade: euiDarkVars.euiColorLightestShade, }; } else { colors = { @@ -63,6 +65,7 @@ export const UptimeThemeContextProvider: React.FC<ThemeContextProps> = ({ darkMo range: euiLightVars.euiFocusBackgroundColor, success: euiLightVars.euiColorSuccess, warning: euiLightVars.euiColorWarning, + lightestShade: euiLightVars.euiColorLightestShade, }; } const value = useMemo(() => { diff --git a/x-pack/plugins/uptime/public/state/actions/journey.ts b/x-pack/plugins/uptime/public/state/actions/journey.ts new file mode 100644 index 00000000000000..0d35559d97fc38 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/actions/journey.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { createAction } from 'redux-actions'; +import { SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; + +export interface FetchJourneyStepsParams { + checkGroup: string; +} + +export interface GetJourneyFailPayload { + checkGroup: string; + error: Error; +} + +export const getJourneySteps = createAction<FetchJourneyStepsParams>('GET_JOURNEY_STEPS'); +export const getJourneyStepsSuccess = createAction<SyntheticsJourneyApiResponse>( + 'GET_JOURNEY_STEPS_SUCCESS' +); +export const getJourneyStepsFail = createAction<GetJourneyFailPayload>('GET_JOURNEY_STEPS_FAIL'); +export const pruneJourneyState = createAction<string[]>('PRUNE_JOURNEY_STATE'); diff --git a/x-pack/plugins/uptime/public/state/api/journey.ts b/x-pack/plugins/uptime/public/state/api/journey.ts new file mode 100644 index 00000000000000..e9ea9d8744bc88 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/api/journey.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { apiService } from './utils'; +import { FetchJourneyStepsParams } from '../actions/journey'; +import { + SyntheticsJourneyApiResponse, + SyntheticsJourneyApiResponseType, +} from '../../../common/runtime_types'; + +export async function fetchJourneySteps( + params: FetchJourneyStepsParams +): Promise<SyntheticsJourneyApiResponse> { + return (await apiService.get( + `/api/uptime/journey/${params.checkGroup}`, + undefined, + SyntheticsJourneyApiResponseType + )) as SyntheticsJourneyApiResponse; +} diff --git a/x-pack/plugins/uptime/public/state/effects/index.ts b/x-pack/plugins/uptime/public/state/effects/index.ts index a2a5dd58dcf4df..4951f2102c8a74 100644 --- a/x-pack/plugins/uptime/public/state/effects/index.ts +++ b/x-pack/plugins/uptime/public/state/effects/index.ts @@ -18,6 +18,7 @@ import { fetchMLJobEffect } from './ml_anomaly'; import { fetchIndexStatusEffect } from './index_status'; import { fetchCertificatesEffect } from '../certificates/certificates'; import { fetchAlertsEffect } from '../alerts/alerts'; +import { fetchJourneyStepsEffect } from './journey'; export function* rootEffect() { yield fork(fetchMonitorDetailsEffect); @@ -35,4 +36,5 @@ export function* rootEffect() { yield fork(fetchIndexStatusEffect); yield fork(fetchCertificatesEffect); yield fork(fetchAlertsEffect); + yield fork(fetchJourneyStepsEffect); } diff --git a/x-pack/plugins/uptime/public/state/effects/journey.ts b/x-pack/plugins/uptime/public/state/effects/journey.ts new file mode 100644 index 00000000000000..2ba125535dea46 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/effects/journey.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { Action } from 'redux-actions'; +import { call, put, takeLatest } from 'redux-saga/effects'; +import { + getJourneySteps, + getJourneyStepsSuccess, + getJourneyStepsFail, + FetchJourneyStepsParams, +} from '../actions/journey'; +import { fetchJourneySteps } from '../api/journey'; + +export function* fetchJourneyStepsEffect() { + yield takeLatest(getJourneySteps, function* (action: Action<FetchJourneyStepsParams>) { + try { + const response = yield call(fetchJourneySteps, action.payload); + yield put(getJourneyStepsSuccess(response)); + } catch (e) { + yield put(getJourneyStepsFail({ checkGroup: action.payload.checkGroup, error: e })); + } + }); +} diff --git a/x-pack/plugins/uptime/public/state/reducers/index.ts b/x-pack/plugins/uptime/public/state/reducers/index.ts index a1aa8b9fbaf5b9..c0bab124d5f9de 100644 --- a/x-pack/plugins/uptime/public/state/reducers/index.ts +++ b/x-pack/plugins/uptime/public/state/reducers/index.ts @@ -21,6 +21,7 @@ import { mlJobsReducer } from './ml_anomaly'; import { certificatesReducer } from '../certificates/certificates'; import { selectedFiltersReducer } from './selected_filters'; import { alertsReducer } from '../alerts/alerts'; +import { journeyReducer } from './journey'; export const rootReducer = combineReducers({ monitor: monitorReducer, @@ -39,4 +40,5 @@ export const rootReducer = combineReducers({ certificates: certificatesReducer, selectedFilters: selectedFiltersReducer, alerts: alertsReducer, + journeys: journeyReducer, }); diff --git a/x-pack/plugins/uptime/public/state/reducers/journey.ts b/x-pack/plugins/uptime/public/state/reducers/journey.ts new file mode 100644 index 00000000000000..e1c3dc808f1bf6 --- /dev/null +++ b/x-pack/plugins/uptime/public/state/reducers/journey.ts @@ -0,0 +1,98 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { handleActions, Action } from 'redux-actions'; +import { Ping, SyntheticsJourneyApiResponse } from '../../../common/runtime_types'; +import { pruneJourneyState } from '../actions/journey'; +import { + FetchJourneyStepsParams, + GetJourneyFailPayload, + getJourneySteps, + getJourneyStepsFail, + getJourneyStepsSuccess, +} from '../actions/journey'; + +export interface JourneyState { + checkGroup: string; + steps: Ping[]; + loading: boolean; + error?: Error; +} + +interface JourneyKVP { + [checkGroup: string]: JourneyState; +} + +const initialState: JourneyKVP = {}; + +type Payload = FetchJourneyStepsParams & + SyntheticsJourneyApiResponse & + GetJourneyFailPayload & + string[]; + +export const journeyReducer = handleActions<JourneyKVP, Payload>( + { + [String(getJourneySteps)]: ( + state: JourneyKVP, + { payload: { checkGroup } }: Action<FetchJourneyStepsParams> + ) => ({ + ...state, + // add an empty entry while fetching the check group, + // or update the previously-loaded entry to a new loading state + [checkGroup]: state[checkGroup] + ? { + ...state[checkGroup], + loading: true, + } + : { + checkGroup, + steps: [], + loading: true, + }, + }), + + [String(getJourneyStepsSuccess)]: ( + state: JourneyKVP, + { payload: { checkGroup, steps } }: Action<SyntheticsJourneyApiResponse> + ) => ({ + ...state, + [checkGroup]: { + loading: false, + checkGroup, + steps, + }, + }), + + [String(getJourneyStepsFail)]: ( + state: JourneyKVP, + { payload: { checkGroup, error } }: Action<GetJourneyFailPayload> + ) => ({ + ...state, + [checkGroup]: state[checkGroup] + ? { + ...state[checkGroup], + loading: false, + error, + } + : { + checkGroup, + loading: false, + steps: [], + error, + }, + }), + + [String(pruneJourneyState)]: (state: JourneyKVP, action: Action<string[]>) => + action.payload.reduce( + (prev, cur) => ({ + ...prev, + [cur]: state[cur], + }), + {} + ), + }, + initialState +); diff --git a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts index 1a8929990ab55d..a59e0be5cdf3f8 100644 --- a/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -117,6 +117,7 @@ describe('state selectors', () => { newAlert: { data: null, loading: false }, anomalyAlertDeletion: { data: null, loading: false }, }, + journeys: {}, }; it('selects base path from state', () => { diff --git a/x-pack/plugins/uptime/public/state/selectors/index.ts b/x-pack/plugins/uptime/public/state/selectors/index.ts index 6b3b46cc151375..6bfe67468aae58 100644 --- a/x-pack/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/plugins/uptime/public/state/selectors/index.ts @@ -94,3 +94,5 @@ export const searchTextSelector = ({ ui: { searchText } }: AppState) => searchTe export const selectedFiltersSelector = ({ selectedFilters }: AppState) => selectedFilters; export const monitorIdSelector = ({ ui: { monitorId } }: AppState) => monitorId; + +export const journeySelector = ({ journeys }: AppState) => journeys; diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts index a52bf86499396c..cb84cc2eb05b6e 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_pings.test.ts @@ -144,6 +144,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 12, @@ -198,6 +222,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 12, @@ -252,6 +300,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 25, @@ -311,6 +383,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 25, @@ -370,6 +466,30 @@ describe('getAll', () => { }, }, ], + "must_not": Array [ + Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "monitor.type": "browser", + }, + }, + Object { + "bool": Object { + "must_not": Array [ + Object { + "exists": Object { + "field": "summary", + }, + }, + ], + }, + }, + ], + }, + }, + ], }, }, "size": 25, diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts new file mode 100644 index 00000000000000..f726ef47915b89 --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_screenshot.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { UMElasticsearchQueryFn } from '../adapters/framework'; + +interface GetJourneyScreenshotParams { + checkGroup: string; + stepIndex: number; +} + +export const getJourneyScreenshot: UMElasticsearchQueryFn< + GetJourneyScreenshotParams, + any +> = async ({ callES, dynamicSettings, checkGroup, stepIndex }) => { + const params: any = { + index: dynamicSettings.heartbeatIndices, + body: { + query: { + bool: { + filter: [ + { + term: { + 'monitor.check_group': checkGroup, + }, + }, + { + term: { + 'synthetics.type': 'step/screenshot', + }, + }, + { + term: { + 'synthetics.step.index': stepIndex, + }, + }, + ], + }, + }, + _source: ['synthetics.blob'], + }, + }; + const result = await callES('search', params); + if (!Array.isArray(result?.hits?.hits) || result.hits.hits.length < 1) { + return null; + } + return result.hits.hits.map(({ _source }: any) => _source?.synthetics?.blob ?? null)[0]; +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts new file mode 100644 index 00000000000000..e4392480f5b72a --- /dev/null +++ b/x-pack/plugins/uptime/server/lib/requests/get_journey_steps.ts @@ -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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { UMElasticsearchQueryFn } from '../adapters/framework'; +import { Ping } from '../../../common/runtime_types'; + +interface GetJourneyStepsParams { + checkGroup: string; +} + +export const getJourneySteps: UMElasticsearchQueryFn<GetJourneyStepsParams, Ping> = async ({ + callES, + dynamicSettings, + checkGroup, +}) => { + const params: any = { + index: dynamicSettings.heartbeatIndices, + body: { + query: { + bool: { + filter: [ + { + terms: { + 'synthetics.type': ['step/end', 'stderr', 'cmd/status', 'step/screenshot'], + }, + }, + { + term: { + 'monitor.check_group': checkGroup, + }, + }, + ], + }, + }, + _source: { + excludes: ['synthetics.blob'], + }, + }, + size: 500, + }; + const result = await callES('search', params); + const screenshotIndexes: number[] = result.hits.hits + .filter((h: any) => h?._source?.synthetics?.type === 'step/screenshot') + .map((h: any) => h?._source?.synthetics?.step?.index); + return result.hits.hits + .filter((h: any) => h?._source?.synthetics?.type !== 'step/screenshot') + .map( + ({ _id, _source, _source: { synthetics } }: any): Ping => ({ + ..._source, + timestamp: _source['@timestamp'], + docId: _id, + synthetics: { + ...synthetics, + screenshotExists: screenshotIndexes.some((i) => i === synthetics?.step?.index), + }, + }) + ); +}; diff --git a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts index a6a0e3c3d6542e..03ec2d7343c9ab 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_pings.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_pings.ts @@ -14,6 +14,43 @@ import { const DEFAULT_PAGE_SIZE = 25; +/** + * This branch of filtering is used for monitors of type `browser`. This monitor + * type represents an unbounded set of steps, with each `check_group` representing + * a distinct journey. The document containing the `summary` field is indexed last, and + * contains the data necessary for querying a journey. + * + * Because of this, when querying for "pings", it is important that we treat `browser` summary + * checks as the "ping" we want. Without this filtering, we will receive >= N pings for a journey + * of N steps, because an individual step may also contain multiple documents. + */ +const REMOVE_NON_SUMMARY_BROWSER_CHECKS = { + must_not: [ + { + bool: { + filter: [ + { + term: { + 'monitor.type': 'browser', + }, + }, + { + bool: { + must_not: [ + { + exists: { + field: 'summary', + }, + }, + ], + }, + }, + ], + }, + }, + ], +}; + export const getPings: UMElasticsearchQueryFn<GetPingsParams, PingsResponse> = async ({ callES, dynamicSettings, @@ -39,7 +76,12 @@ export const getPings: UMElasticsearchQueryFn<GetPingsParams, PingsResponse> = a if (location) { postFilterClause = { post_filter: { term: { 'observer.geo.name': location } } }; } - const queryContext = { bool: { filter } }; + const queryContext = { + bool: { + filter, + ...REMOVE_NON_SUMMARY_BROWSER_CHECKS, + }, + }; const params: any = { index: dynamicSettings.heartbeatIndices, body: { diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts index 8fa4561268e8f7..1806495d14cc47 100644 --- a/x-pack/plugins/uptime/server/lib/requests/index.ts +++ b/x-pack/plugins/uptime/server/lib/requests/index.ts @@ -18,6 +18,8 @@ import { getPings } from './get_pings'; import { getPingHistogram } from './get_ping_histogram'; import { getSnapshotCount } from './get_snapshot_counts'; import { getIndexStatus } from './get_index_status'; +import { getJourneySteps } from './get_journey_steps'; +import { getJourneyScreenshot } from './get_journey_screenshot'; export const requests = { getCerts, @@ -34,6 +36,8 @@ export const requests = { getPingHistogram, getSnapshotCount, getIndexStatus, + getJourneySteps, + getJourneyScreenshot, }; export type UptimeRequests = typeof requests; diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 2b598be284e1cb..de44b2565a2f8b 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -6,7 +6,12 @@ import { createGetCertsRoute } from './certs/certs'; import { createGetOverviewFilters } from './overview_filters'; -import { createGetPingHistogramRoute, createGetPingsRoute } from './pings'; +import { + createGetPingHistogramRoute, + createGetPingsRoute, + createJourneyRoute, + createJourneyScreenshotRoute, +} from './pings'; import { createGetDynamicSettingsRoute, createPostDynamicSettingsRoute } from './dynamic_settings'; import { createLogPageViewRoute } from './telemetry'; import { createGetSnapshotCount } from './snapshot'; @@ -40,4 +45,6 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createLogPageViewRoute, createGetPingHistogramRoute, createGetMonitorDurationRoute, + createJourneyRoute, + createJourneyScreenshotRoute, ]; diff --git a/x-pack/plugins/uptime/server/rest_api/pings/index.ts b/x-pack/plugins/uptime/server/rest_api/pings/index.ts index a10ab435e4b0a3..88fc0a84c46211 100644 --- a/x-pack/plugins/uptime/server/rest_api/pings/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/pings/index.ts @@ -6,3 +6,5 @@ export { createGetPingsRoute } from './get_pings'; export { createGetPingHistogramRoute } from './get_ping_histogram'; +export { createJourneyRoute } from './journeys'; +export { createJourneyScreenshotRoute } from './journey_screenshots'; diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.ts new file mode 100644 index 00000000000000..1fc52dd24f9d07 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/pings/journey_screenshots.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; + +export const createJourneyScreenshotRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: '/api/uptime/journey/screenshot/{checkGroup}/{stepIndex}', + validate: { + params: schema.object({ + checkGroup: schema.string(), + stepIndex: schema.number(), + }), + }, + handler: async ({ callES, dynamicSettings }, _context, request, response) => { + const { checkGroup, stepIndex } = request.params; + const result = await libs.requests.getJourneyScreenshot({ + callES, + dynamicSettings, + checkGroup, + stepIndex, + }); + + if (result === null) { + return response.notFound(); + } + return response.ok({ + body: Buffer.from(result, 'base64'), + headers: { + 'content-type': 'image/png', + 'cache-control': 'max-age=600', + }, + }); + }, +}); diff --git a/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts new file mode 100644 index 00000000000000..b6e06850ad3b60 --- /dev/null +++ b/x-pack/plugins/uptime/server/rest_api/pings/journeys.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { UMServerLibs } from '../../lib/lib'; +import { UMRestApiRouteFactory } from '../types'; + +export const createJourneyRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ + method: 'GET', + path: '/api/uptime/journey/{checkGroup}', + validate: { + params: schema.object({ + checkGroup: schema.string(), + }), + }, + handler: async ({ callES, dynamicSettings }, _context, request, response) => { + const { checkGroup } = request.params; + const result = await libs.requests.getJourneySteps({ + callES, + dynamicSettings, + checkGroup, + }); + + return response.ok({ + body: { + checkGroup, + steps: result, + }, + }); + }, +}); diff --git a/x-pack/test/functional/es_archives/uptime/pings/data.json.gz b/x-pack/test/functional/es_archives/uptime/pings/data.json.gz index 83441218aad7340284d2fd04ae2ab71e496a9f5c..a8f7b84b7d0a86cbdb16cb0638de7d7802dd1695 100644 GIT binary patch literal 808 zcmV+@1K0c?iwFokUUOdn17u-zVJ>QOZ*BnHR?BYcFc97I6;akD*pBmD_5MRw-9&C; z8mmdnwH;_x@$c7mc{q?jAE=8fp$g_b<1=&mKJz?Zy{)L9dw#`B|IP^3r0I?1UH{NO zSy$0lV^C8f)DtCYo(3QQ)>!K$??rjE*OGh1S}GN3SH=win9d=Xhu{IGc?9z)4w4K# z?)<gAiRt)jNzJ9|NNej_avs!Nf4@8GFX`4)NX9#(j)O?oxl?Tq`Aqa-J|fDRDO##t z*toB0T*UaZ8Mz!pGadD;sf;;H|AZNIjJ0w}TGqVTkrT0D-B7z~h+1;dPU)TYAP6A# z56bO%qe`uYjS4DPyp!~_VEtOEmqp1dqe1{2qdJWWrA{47-YMFhinXFQLxI2%ZU=qr zD=9RrDk>%dBoKn>7>X;>Or?&K5T}!_8sw}Z>U0oJUI-_hH$sD@5cF?P<y7lY=DLrL zvlorsEB#6`Uo1A8P4LWA-9H5-Zx`pdZF$EOACAhedO_3&AO;WfF@!9MvuHH=ZVCKh zRu|T++E;f!RibNcmmOv(2tk0NFn|z^Es({eBbQ}Oj~Y2pSx_2PKXBP!EF0=<N*Y+a zSq~rfGu>XPe+2usHWWi=z-QISzHNzk^Swbix*sioPWXy%IwJ#T5mb4y!z(UKDHw-W zSF_V6Z33Se4&gXL&nsw#2hK04P;ALcy+1|ARbiK@AWLQUSYLbu<{+Mf@ByM60R0I7 z0FUlC$3r|(efsEuH9#~6>4Ob07GtF6nsk+{$;;GUqb$P7<+f-0zuVb%HhceVio`k2 zaufz>nthET_o+{T?HCmYqpuMr72@>l=;|eTwt)pLDO=OZ``A~{kcfu?_UP+M<BxUE zhET>9_;7@8P0{}~JL}Qjh@G(`{0=)qgyUo~#zl4x0s1yM>$EBQ5jo>z@K41xcGk?- z@RBqJl{rKq&K8o2H7n_2$5FT7YMkQuB2^!1xWLlaOFid2l$c``z^~KwI9ppTyA5BD mw>(|;6idS>J0j*cS=v6qPk0(9(KuAc`05|O=9Re74FCYwL4cnC literal 765 zcmV<Z0s{RXiwFP!000026WvwIZlf?1-RCPJ){SEu^IUcQp{s6?3r?{HFk_pfRh54q z+sy+KNSZd9#Yhnf_kAzt-t#e=&prL#GB2Hbg~+`JD_GO2vyu<<VqSSuus3T^GAi{8 zqk0ho2%s}IdL=rUAMK6gQS+K9P3zXWVTj@df(su$p*Rgt8iXK9@bk{!*xQ&re<LY{ z(haR$T`M6#Da`+eqyCa^&7|U@vFZ>6rp}#ed&p;_5A#u(mt3=4chbdui^D7=U(G1g zAe!x{<5gkJQT)qKP|w&XSG4BUyBj%`8{Q1HZAJA;$a+fOYY#zyh<8wK&l}ac9X2YN zYDJ^iX~E`e=}u+2D69%Yc#PU>RBC<dn2ScU=2UDHogE5-J>hoH$G(!xz^Y(!B0z)? zoQ|P|rqxvHF!D(}>1sgE3#!?4UW6u{H`0J5lk88&)Ku$1(sv&nXD2JSSLQ3Jbh+GY zHsF=(vU>r!sF&xstwqDN7>>#-I!W~>AO;`ia|lTkCc$X(-4b}itbJJXVqe|kREd3S zx9ljvz=r?_K0pYMEs(=xPp<Nk9W`p8a-g)TZs4-NST5Arlnk&$wPxoB<x00#>L0<r zs||-JxUhmcKiR-mNE?U{|LS5j<{(X=D<M7_$JcoU)$lxdIg^^Ncy7?2H9}`@6*9Wg zZVS!5#&7|{1@xZ~P9Zcu03m$t4`V#U3p0m{9d14h7BGHt0mfpC^jy-WP$m66wUsyt zNOZaF+5WkmZD+HOe@$>CB}s~X5XZ@F1h-Fp3d^2Rb}+h)?wFLPN5fRF=&K7XS<d*H z74y$+n_CK)6G-Oltu^@L&8ww@vGqHg(0dc?f6c9Cw0GiG@<VP#n1s>yxYdXF+tg~( z#`s6nN+Ng*w;JB6L{2M<$`oOrBumBQn&)h}W0-p|Es05Zk!TM!T;SO2rJgeuj_k1l v=ytLl=h{MSnme)Wc+1mePjM^?68sI0bp!kf%aSPgI@|mOfb?6LO$`75A}V&H From a6e9d16310eb43463a066a964bf65da281c0ca1a Mon Sep 17 00:00:00 2001 From: Brian Seeders <seeders@gmail.com> Date: Fri, 2 Oct 2020 17:28:47 -0400 Subject: [PATCH 112/128] skip flaky suite (#75241) --- .../apis/fleet/agents/complete_flow.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts index a59b3ff0890f77..d925d77b5b93fd 100644 --- a/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts +++ b/x-pack/test/ingest_manager_api_integration/apis/fleet/agents/complete_flow.ts @@ -19,7 +19,8 @@ export default function (providerContext: FtrProviderContext) { const supertestWithoutAuth = getSupertestWithoutAuth(providerContext); const esClient = getService('es'); - describe('fleet_agent_flow', () => { + // Failing: See https://github.com/elastic/kibana/issues/75241 + describe.skip('fleet_agent_flow', () => { skipIfNoDockerRegistry(providerContext); before(async () => { await esArchiver.load('empty_kibana'); From f7a18e64547161a4b6d46a40fa9435092ec64c1e Mon Sep 17 00:00:00 2001 From: spalger <spalger@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:49:01 -0700 Subject: [PATCH 113/128] Revert "[babel/register] remove from build (#79176)" This reverts commit 511eb0f23bbc85d8131a1b1364a5f5e06b837858. --- package.json | 7 ++-- packages/kbn-babel-preset/node_preset.js | 8 ++++ packages/kbn-babel-preset/package.json | 1 + .../lib/babel_register_for_test_plugins.js | 39 ------------------- .../functional_tests/lib/run_kibana_server.js | 20 +--------- scripts/build_plugin_list_docs.js | 2 +- scripts/es.js | 2 +- scripts/generate_plugin.js | 2 +- scripts/plugin_helpers.js | 2 +- scripts/register_git_hook.js | 2 +- scripts/release_notes.js | 2 +- scripts/telemetry_check.js | 2 +- scripts/telemetry_extract.js | 2 +- src/cli/index.js | 3 +- .../integration_tests/invalid_config.test.ts | 22 ++++------- src/dev/build/tasks/copy_source_task.ts | 2 - src/setup_node_env/babel_register/register.js | 24 +++++++++--- src/setup_node_env/index.js | 2 +- ...pilation.js => prebuilt_dev_only_entry.js} | 0 x-pack/package.json | 2 +- yarn.lock | 18 ++++++--- 21 files changed, 63 insertions(+), 101 deletions(-) delete mode 100644 packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js rename src/setup_node_env/{no_transpilation.js => prebuilt_dev_only_entry.js} (100%) diff --git a/package.json b/package.json index cebfddbe34e945..5089e6e1a140d8 100644 --- a/package.json +++ b/package.json @@ -115,6 +115,8 @@ ] }, "dependencies": { + "@babel/core": "^7.11.1", + "@babel/register": "^7.10.5", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "7.9.1", "@elastic/eui": "29.0.0", @@ -126,6 +128,7 @@ "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", "@kbn/apm-config-loader": "1.0.0", + "@kbn/babel-preset": "1.0.0", "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", @@ -211,7 +214,6 @@ "rxjs": "^6.5.5", "seedrandom": "^3.0.5", "semver": "^5.7.0", - "source-map-support": "^0.5.19", "style-it": "^2.1.3", "symbol-observable": "^1.2.0", "tar": "4.4.13", @@ -225,9 +227,7 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@babel/core": "^7.11.1", "@babel/parser": "^7.11.2", - "@babel/register": "^7.10.5", "@babel/types": "^7.11.0", "@elastic/apm-rum": "^5.6.1", "@elastic/charts": "23.0.0", @@ -238,7 +238,6 @@ "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^6.0.0", "@elastic/ui-ace": "0.2.3", - "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/es-archiver": "1.0.0", diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 86817ed253e7cd..45afe5d5ebc32b 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -49,5 +49,13 @@ module.exports = (_, options = {}) => { ], require('./common_preset'), ], + plugins: [ + [ + require.resolve('babel-plugin-transform-define'), + { + 'global.__BUILT_WITH_BABEL__': 'true', + }, + ], + ], }; }; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index 79d2fd8687dae5..bc4e0ec338f94e 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -14,6 +14,7 @@ "@babel/preset-typescript": "^7.10.4", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-styled-components": "^1.10.7", + "babel-plugin-transform-define": "^1.3.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "react-is": "^16.8.0", "styled-components": "^5.1.0" diff --git a/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js deleted file mode 100644 index 44ff579411bd9b..00000000000000 --- a/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -const Path = require('path'); - -const { REPO_ROOT } = require('@kbn/dev-utils'); - -// modifies all future calls to require() to automatically -// compile the required source with babel -require('@babel/register')({ - ignore: [/[\/\\](node_modules|target|dist)[\/\\]/], - only: [ - Path.resolve(REPO_ROOT, 'test'), - Path.resolve(REPO_ROOT, 'x-pack/test'), - Path.resolve(REPO_ROOT, 'examples'), - Path.resolve(REPO_ROOT, 'x-pack/examples'), - // TODO: should should probably remove this link back to the source - Path.resolve(REPO_ROOT, 'x-pack/plugins/task_manager/server/config.ts'), - ], - babelrc: false, - presets: [require.resolve('@kbn/babel-preset/node_preset')], - extensions: ['.js', '.ts', '.tsx'], -}); diff --git a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js index e7ec99467ecfd8..fb9f8f7a52408c 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js +++ b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js @@ -17,26 +17,9 @@ * under the License. */ -import { resolve, relative } from 'path'; +import { resolve } from 'path'; import { KIBANA_ROOT, KIBANA_EXEC, KIBANA_EXEC_PATH } from './paths'; -function extendNodeOptions(installDir) { - if (!installDir) { - return {}; - } - - const testOnlyRegisterPath = relative( - installDir, - require.resolve('./babel_register_for_test_plugins') - ); - - return { - NODE_OPTIONS: `--require=${testOnlyRegisterPath}${ - process.env.NODE_OPTIONS ? ` ${process.env.NODE_OPTIONS}` : '' - }`, - }; -} - export async function runKibanaServer({ procs, config, options }) { const { installDir } = options; @@ -46,7 +29,6 @@ export async function runKibanaServer({ procs, config, options }) { env: { FORCE_COLOR: 1, ...process.env, - ...extendNodeOptions(installDir), }, cwd: installDir || KIBANA_ROOT, wait: /http server running/, diff --git a/scripts/build_plugin_list_docs.js b/scripts/build_plugin_list_docs.js index 6f184ca7b14c69..54821a1b10ee8d 100644 --- a/scripts/build_plugin_list_docs.js +++ b/scripts/build_plugin_list_docs.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/dev-utils').runPluginListCli(); diff --git a/scripts/es.js b/scripts/es.js index 53b01d8cb44140..2d56496f2fdd23 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -17,7 +17,7 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); var resolve = require('path').resolve; var pkg = require('../package.json'); diff --git a/scripts/generate_plugin.js b/scripts/generate_plugin.js index af3d31048ecfc6..f695eabb30f219 100644 --- a/scripts/generate_plugin.js +++ b/scripts/generate_plugin.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/plugin-generator').runCli(); diff --git a/scripts/plugin_helpers.js b/scripts/plugin_helpers.js index f28bf8fcfff903..a07ba7a9185f80 100644 --- a/scripts/plugin_helpers.js +++ b/scripts/plugin_helpers.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/plugin-helpers').runCli(); diff --git a/scripts/register_git_hook.js b/scripts/register_git_hook.js index 50dfeaf46109fc..af3f54619bcecc 100644 --- a/scripts/register_git_hook.js +++ b/scripts/register_git_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/dev-utils/target/precommit_hook/cli'); diff --git a/scripts/release_notes.js b/scripts/release_notes.js index ee9275194ae94a..f46ee5823d70d1 100644 --- a/scripts/release_notes.js +++ b/scripts/release_notes.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/release-notes').runReleaseNotesCli(); diff --git a/scripts/telemetry_check.js b/scripts/telemetry_check.js index 22a22b401cb15f..06b3ed46bdba6a 100644 --- a/scripts/telemetry_check.js +++ b/scripts/telemetry_check.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/telemetry-tools').runTelemetryCheck(); diff --git a/scripts/telemetry_extract.js b/scripts/telemetry_extract.js index e2fbb64c267196..051bee26537b9b 100644 --- a/scripts/telemetry_extract.js +++ b/scripts/telemetry_extract.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/no_transpilation'); +require('../src/setup_node_env/prebuilt_dev_only_entry'); require('@kbn/telemetry-tools').runTelemetryExtract(); diff --git a/src/cli/index.js b/src/cli/index.js index e5480d21376249..45f88eaf82a5b8 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -18,6 +18,5 @@ */ require('../apm')(); -require('../setup_node_env/no_transpilation'); -require('../setup_node_env/babel_register/polyfill'); +require('../setup_node_env'); require('./cli'); diff --git a/src/cli/serve/integration_tests/invalid_config.test.ts b/src/cli/serve/integration_tests/invalid_config.test.ts index a72142faa22fe6..fd6fa1bf192fc3 100644 --- a/src/cli/serve/integration_tests/invalid_config.test.ts +++ b/src/cli/serve/integration_tests/invalid_config.test.ts @@ -18,10 +18,10 @@ */ import { spawnSync } from 'child_process'; +import { resolve } from 'path'; -import { REPO_ROOT } from '@kbn/dev-utils'; - -const INVALID_CONFIG_PATH = require.resolve('./__fixtures__/invalid_config.yml'); +const ROOT_DIR = resolve(__dirname, '../../../../'); +const INVALID_CONFIG_PATH = resolve(__dirname, '__fixtures__/invalid_config.yml'); interface LogEntry { message: string; @@ -35,11 +35,11 @@ describe('cli invalid config support', function () { function () { // Unused keys only throw once LegacyService starts, so disable migrations so that Core // will finish the start lifecycle without a running Elasticsearch instance. - const { error, status, stdout, stderr } = spawnSync( + const { error, status, stdout } = spawnSync( process.execPath, - ['scripts/kibana', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], + ['src/cli', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], { - cwd: REPO_ROOT, + cwd: ROOT_DIR, } ); @@ -57,21 +57,13 @@ describe('cli invalid config support', function () { })); expect(error).toBe(undefined); - - if (!fatalLogLine) { - throw new Error( - `cli did not log the expected fatal error message:\n\nstdout: \n${stdout}\n\nstderr:\n${stderr}` - ); - } - + expect(status).toBe(64); expect(fatalLogLine.message).toContain( 'Error: Unknown configuration key(s): "unknown.key", "other.unknown.key", "other.third", "some.flat.key", ' + '"some.array". Check for spelling errors and ensure that expected plugins are installed.' ); expect(fatalLogLine.tags).toEqual(['fatal', 'root']); expect(fatalLogLine.type).toEqual('log'); - - expect(status).toBe(64); }, 20 * 1000 ); diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 78e1395586a17f..948e2357effb04 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -37,8 +37,6 @@ export const CopySource: Task = { '!src/cli/repl/**', '!src/functional_test_runner/**', '!src/dev/**', - '!src/setup_node_env/babel_register/index.js', - '!src/setup_node_env/babel_register/register.js', '!**/public/**', 'typings/**', 'config/kibana.yml', diff --git a/src/setup_node_env/babel_register/register.js b/src/setup_node_env/babel_register/register.js index 3c0bd387c8e44d..6d573d89222449 100644 --- a/src/setup_node_env/babel_register/register.js +++ b/src/setup_node_env/babel_register/register.js @@ -46,13 +46,27 @@ var ignore = [ // ignore paths matching `/canvas/canvas_plugin/` /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, - - // ignore any path in the packages, unless it is in the package's - // root `src` directory, in any test or __tests__ directory, or it - // ends with .test.js, .test.ts, or .test.tsx - /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, ]; +if (global.__BUILT_WITH_BABEL__) { + // when building the Kibana source we replace the statement + // `global.__BUILT_WITH_BABEL__` with the value `true` so that + // when @babel/register is required for the first time by users + // it will exclude kibana's `src` directory. + // + // We still need @babel/register for plugins though, we've been + // building their server code at require-time since version 4.2 + // TODO: the plugin install process could transpile plugin server code... + ignore.push(resolve(__dirname, '../../../src')); +} else { + ignore.push( + // ignore any path in the packages, unless it is in the package's + // root `src` directory, in any test or __tests__ directory, or it + // ends with .test.js, .test.ts, or .test.tsx + /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/ + ); +} + // modifies all future calls to require() to automatically // compile the required source with babel require('@babel/register')({ diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index 60f0982f50d209..d84249df7fd8f0 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -17,5 +17,5 @@ * under the License. */ -require('./no_transpilation'); +require('./prebuilt_dev_only_entry'); require('./babel_register'); diff --git a/src/setup_node_env/no_transpilation.js b/src/setup_node_env/prebuilt_dev_only_entry.js similarity index 100% rename from src/setup_node_env/no_transpilation.js rename to src/setup_node_env/prebuilt_dev_only_entry.js diff --git a/x-pack/package.json b/x-pack/package.json index 4145d8d72cc636..5742200b55d9f4 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -32,7 +32,6 @@ "@cypress/webpack-preprocessor": "^5.4.1", "@elastic/apm-rum-react": "^1.2.5", "@elastic/maki": "6.3.0", - "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/expect": "1.0.0", @@ -281,6 +280,7 @@ "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.0", "@elastic/safer-lodash-set": "0.0.0", + "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 806424b222ad3f..971a94bfe56c3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7545,6 +7545,14 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= +babel-plugin-transform-define@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.1.tgz#b21b7bad3b84cf8e3f07cdc8c660b99cbbc01213" + integrity sha512-JXZ1xE9jIbKCGYZ4wbSMPSI5mdS4DRLi5+SkTHgZqWn5YIf/EucykkzUsPmzJlpkX8fsMVdLnA5vt/LvT97Zbg== + dependencies: + lodash "^4.17.11" + traverse "0.6.6" + babel-plugin-transform-inline-consecutive-adds@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz#323d47a3ea63a83a7ac3c811ae8e6941faf2b0d1" @@ -27102,10 +27110,10 @@ source-map-support@^0.3.2: dependencies: source-map "0.1.32" -source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== +source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -28928,7 +28936,7 @@ traverse-chain@~0.1.0: resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= -traverse@^0.6.6, traverse@~0.6.6: +traverse@0.6.6, traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= From ed10d9f8ddd127ad6078747acc793ce36a2dcba2 Mon Sep 17 00:00:00 2001 From: Lee Drengenberg <lee.drengenberg@elastic.co> Date: Fri, 2 Oct 2020 17:07:14 -0500 Subject: [PATCH 114/128] =?UTF-8?q?define=20integrationTestRoot=20in=20con?= =?UTF-8?q?fig=20file=20and=20use=20to=20define=20screensho=E2=80=A6=20(#7?= =?UTF-8?q?9247)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apps/metricbeat/_metricbeat_dashboard.js | 3 +-- .../config.stack_functional_integration_base.js | 14 +++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js index 42f707fb778547..0ce09e8f59d2d1 100644 --- a/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js +++ b/x-pack/test/stack_functional_integration/apps/metricbeat/_metricbeat_dashboard.js @@ -5,7 +5,6 @@ */ import expect from '@kbn/expect'; -import { REPO_ROOT } from '@kbn/dev-utils'; export default function ({ getService, getPageObjects, updateBaselines }) { const screenshot = getService('screenshots'); @@ -15,7 +14,7 @@ export default function ({ getService, getPageObjects, updateBaselines }) { describe('check metricbeat Dashboard', function () { before(async function () { - await esArchiver.load(`${REPO_ROOT}/../integration-test/test/es_archives/metricbeat`); + await esArchiver.load('metricbeat'); // this navigateToActualURL takes the place of navigating to the dashboard landing page, // filtering on the dashboard name, selecting it, setting the timepicker, and going to full screen diff --git a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js index a838b129242a15..bc5b28641c28c2 100644 --- a/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js +++ b/x-pack/test/stack_functional_integration/configs/config.stack_functional_integration_base.js @@ -16,10 +16,14 @@ const log = new ToolingLog({ level: 'info', writeTo: process.stdout, }); +log.info(`REPO_ROOT = ${REPO_ROOT}`); log.info(`WORKSPACE in config file ${process.env.WORKSPACE}`); -const stateFilePath = process.env.WORKSPACE - ? `${process.env.WORKSPACE}/qa/envvars.sh` - : `${REPO_ROOT}/../integration-test/qa/envvars.sh`; + +const INTEGRATION_TEST_ROOT = process.env.WORKSPACE || resolve(REPO_ROOT, '../integration-test'); +log.info(`INTEGRATION_TEST_ROOT = ${INTEGRATION_TEST_ROOT}`); + +const stateFilePath = resolve(INTEGRATION_TEST_ROOT, 'qa/envvars.sh'); +log.info(`stateFilePath = ${stateFilePath}`); const prepend = (testFile) => require.resolve(`${testsFolder}/${testFile}`); @@ -46,11 +50,11 @@ export default async ({ readConfigFile }) => { security: { disableTestUser: true }, // choose where screenshots should be saved screenshots: { - directory: resolve(`${REPO_ROOT}/../integration-test`, 'test/screenshots'), + directory: resolve(INTEGRATION_TEST_ROOT, 'test/screenshots'), }, // choose where esArchiver should load archives from esArchiver: { - directory: resolve(`${REPO_ROOT}/../integration-test`, 'test/es_archives'), + directory: resolve(INTEGRATION_TEST_ROOT, 'test/es_archives'), }, }; return settings; From bd80d3c7479ce36b7a0d7bbdc3e2a07d63435e43 Mon Sep 17 00:00:00 2001 From: Davis Plumlee <56367316+dplumlee@users.noreply.github.com> Date: Fri, 2 Oct 2020 16:18:55 -0600 Subject: [PATCH 115/128] [Security Solution] Changes rules table tag display (#77102) --- .../detection_engine/rules/all/columns.tsx | 22 ++-- .../rules/all/helpers.test.tsx | 13 ++- .../detection_engine/rules/all/helpers.ts | 4 + .../detection_engine/rules/all/index.test.tsx | 50 +++++---- .../tags_filter_popover.tsx | 60 ++++++---- .../rules/all/tag_display.test.tsx | 47 ++++++++ .../rules/all/tag_display.tsx | 104 ++++++++++++++++++ .../detection_engine/rules/translations.ts | 7 ++ 8 files changed, 248 insertions(+), 59 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.tsx create mode 100644 x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx index 3666e16dcbaf8f..c0786f5234157f 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/columns.tsx @@ -7,7 +7,6 @@ /* eslint-disable react/display-name */ import { - EuiBadge, EuiBasicTableColumn, EuiTableActionsColumnType, EuiText, @@ -24,7 +23,6 @@ import { getEmptyTagValue } from '../../../../../common/components/empty_value'; import { FormattedDate } from '../../../../../common/components/formatted_date'; import { getRuleDetailsUrl } from '../../../../../common/components/link_to/redirect_to_detection_engine'; import { ActionToaster } from '../../../../../common/components/toasters'; -import { TruncatableText } from '../../../../../common/components/truncatable_text'; import { getStatusColor } from '../../../../components/rules/rule_status/helpers'; import { RuleSwitch } from '../../../../components/rules/rule_switch'; import { SeverityBadge } from '../../../../components/rules/severity_badge'; @@ -39,6 +37,7 @@ import { Action } from './reducer'; import { LocalizedDateTooltip } from '../../../../../common/components/localized_date_tooltip'; import * as detectionI18n from '../../translations'; import { LinkAnchor } from '../../../../../common/components/links'; +import { TagsDisplay } from './tag_display'; export const getActions = ( dispatch: React.Dispatch<Action>, @@ -207,22 +206,19 @@ export const getColumns = ({ ); }, truncateText: true, - width: '10%', + width: '8%', }, { field: 'tags', name: i18n.COLUMN_TAGS, - render: (value: Rule['tags']) => ( - <TruncatableText data-test-subj="tags"> - {value.map((tag, i) => ( - <EuiBadge color="hollow" key={`${tag}-${i}`}> - {tag} - </EuiBadge> - ))} - </TruncatableText> - ), + render: (value: Rule['tags']) => { + if (value.length > 0) { + return <TagsDisplay tags={value} />; + } + return getEmptyTagValue(); + }, truncateText: true, - width: '14%', + width: '20%', }, { align: 'center', diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx index 062d7967bf3018..e06aea2fb2785e 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.test.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { bucketRulesResponse, showRulesTable } from './helpers'; +import { bucketRulesResponse, caseInsensitiveSort, showRulesTable } from './helpers'; import { mockRule, mockRuleError } from './__mocks__/mock'; import uuid from 'uuid'; import { Rule, RuleError } from '../../../../containers/detection_engine/rules'; @@ -86,4 +86,15 @@ describe('AllRulesTable Helpers', () => { expect(result).toBeTruthy(); }); }); + + describe('caseInsensitiveSort', () => { + describe('when an array of differently cased tags is passed', () => { + const unsortedTags = ['atest', 'Ctest', 'Btest', 'ctest', 'btest', 'Atest']; + const result = caseInsensitiveSort(unsortedTags); + it('returns an alphabetically sorted array with no regard for casing', () => { + const expected = ['atest', 'Atest', 'Btest', 'btest', 'Ctest', 'ctest']; + expect(result).toEqual(expected); + }); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts index 0ebeb84d57468c..c54c9ed9b8ef9d 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/helpers.ts @@ -33,3 +33,7 @@ export const showRulesTable = ({ }) => (rulesCustomInstalled != null && rulesCustomInstalled > 0) || (rulesInstalled != null && rulesInstalled > 0); + +export const caseInsensitiveSort = (tags: string[]): string[] => { + return tags.sort((a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase())); // Case insensitive +}; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx index 13c6985a30c2b3..f8ed57aa7537e0 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/index.test.tsx @@ -5,7 +5,8 @@ */ import React from 'react'; -import { shallow, mount } from 'enzyme'; +import { shallow, mount, ReactWrapper } from 'enzyme'; +import { act } from 'react-dom/test-utils'; import '../../../../../common/mock/match_media'; import '../../../../../common/mock/formatted_relative'; @@ -179,27 +180,34 @@ describe('AllRules', () => { expect(wrapper.find('[title="All rules"]')).toHaveLength(1); }); - it('renders rules tab', async () => { - const wrapper = mount( - <TestProviders> - <AllRules - createPrePackagedRules={jest.fn()} - hasNoPermissions={false} - loading={false} - loadingCreatePrePackagedRules={false} - refetchPrePackagedRulesStatus={jest.fn()} - rulesCustomInstalled={1} - rulesInstalled={0} - rulesNotInstalled={0} - rulesNotUpdated={0} - setRefreshRulesData={jest.fn()} - /> - </TestProviders> - ); + describe('rules tab', () => { + let wrapper: ReactWrapper; + beforeEach(() => { + wrapper = mount( + <TestProviders> + <AllRules + createPrePackagedRules={jest.fn()} + hasNoPermissions={false} + loading={false} + loadingCreatePrePackagedRules={false} + refetchPrePackagedRulesStatus={jest.fn()} + rulesCustomInstalled={1} + rulesInstalled={0} + rulesNotInstalled={0} + rulesNotUpdated={0} + setRefreshRulesData={jest.fn()} + /> + </TestProviders> + ); + }); - await waitFor(() => { - expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); - expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); + it('renders correctly', async () => { + await act(async () => { + await waitFor(() => { + expect(wrapper.exists('[data-test-subj="monitoring-table"]')).toBeFalsy(); + expect(wrapper.exists('[data-test-subj="rules-table"]')).toBeTruthy(); + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx index 4fe0bc8f835dff..9748dde91d18be 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_table_filters/tags_filter_popover.tsx @@ -27,6 +27,7 @@ import { import styled from 'styled-components'; import * as i18n from '../../translations'; import { toggleSelectedGroup } from '../../../../../../common/components/ml_popover/jobs_table/filters/toggle_selected_group'; +import { caseInsensitiveSort } from '../helpers'; interface TagsFilterPopoverProps { selectedTags: string[]; @@ -36,9 +37,19 @@ interface TagsFilterPopoverProps { isLoading: boolean; // TO DO reimplement? } +const PopoverContentWrapper = styled.div` + width: 275px; +`; + const ScrollableDiv = styled.div` max-height: 250px; - overflow: auto; + overflow-y: auto; +`; + +const TagOverflowContainer = styled.span` + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; `; /** @@ -52,9 +63,7 @@ const TagsFilterPopoverComponent = ({ selectedTags, onSelectedTagsChanged, }: TagsFilterPopoverProps) => { - const sortedTags = useMemo(() => { - return tags.sort((a: string, b: string) => a.toLowerCase().localeCompare(b.toLowerCase())); // Case insensitive - }, [tags]); + const sortedTags = useMemo(() => caseInsensitiveSort(tags), [tags]); const [isTagPopoverOpen, setIsTagPopoverOpen] = useState(false); const [searchInput, setSearchInput] = useState(''); const [filterTags, setFilterTags] = useState(sortedTags); @@ -65,8 +74,9 @@ const TagsFilterPopoverComponent = ({ checked={selectedTags.includes(tag) ? 'on' : undefined} key={`${index}-${tag}`} onClick={() => toggleSelectedGroup(tag, selectedTags, onSelectedTagsChanged)} + title={tag} > - {`${tag}`} + <TagOverflowContainer>{tag}</TagOverflowContainer> </EuiFilterSelectItem> )); }, [onSelectedTagsChanged, selectedTags, filterTags]); @@ -101,25 +111,27 @@ const TagsFilterPopoverComponent = ({ panelPaddingSize="none" repositionOnScroll > - <EuiPopoverTitle> - <EuiFieldSearch - placeholder="Search tags" - value={searchInput} - onChange={onSearchInputChange} - isClearable - aria-label="Rules tag search" - /> - </EuiPopoverTitle> - <ScrollableDiv>{tagsComponent}</ScrollableDiv> - {filterTags.length === 0 && ( - <EuiFlexGroup gutterSize="m" justifyContent="spaceAround"> - <EuiFlexItem grow={true}> - <EuiPanel> - <EuiText>{i18n.NO_TAGS_AVAILABLE}</EuiText> - </EuiPanel> - </EuiFlexItem> - </EuiFlexGroup> - )} + <PopoverContentWrapper> + <EuiPopoverTitle> + <EuiFieldSearch + placeholder="Search tags" + value={searchInput} + onChange={onSearchInputChange} + isClearable + aria-label="Rules tag search" + /> + </EuiPopoverTitle> + <ScrollableDiv>{tagsComponent}</ScrollableDiv> + {filterTags.length === 0 && ( + <EuiFlexGroup gutterSize="m" justifyContent="spaceAround"> + <EuiFlexItem grow={true}> + <EuiPanel> + <EuiText>{i18n.NO_TAGS_AVAILABLE}</EuiText> + </EuiPanel> + </EuiFlexItem> + </EuiFlexGroup> + )} + </PopoverContentWrapper> </EuiPopover> ); }; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.tsx new file mode 100644 index 00000000000000..3e60bb2d3168b0 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { mount, ReactWrapper } from 'enzyme'; + +import { TagsDisplay } from './tag_display'; +import { TestProviders } from '../../../../../common/mock'; +import { waitFor } from '@testing-library/react'; + +const mockTags = ['Elastic', 'Endpoint', 'Data Protection', 'ML', 'Continuous Monitoring']; + +describe('When tag display loads', () => { + let wrapper: ReactWrapper; + beforeEach(() => { + wrapper = mount( + <TestProviders> + <TagsDisplay tags={mockTags} /> + </TestProviders> + ); + }); + it('visibly renders 3 initial tags', () => { + for (let i = 0; i < 3; i++) { + expect(wrapper.exists(`[data-test-subj="rules-table-column-tags-${i}"]`)).toBeTruthy(); + } + }); + describe("when the 'see all' button is clicked", () => { + beforeEach(() => { + const seeAllButton = wrapper.find('[data-test-subj="tags-display-popover-button"] button'); + seeAllButton.simulate('click'); + }); + it('renders all the tags in the popover', async () => { + await waitFor(() => { + wrapper.update(); + expect(wrapper.exists('[data-test-subj="tags-display-popover"]')).toBeTruthy(); + for (let i = 0; i < mockTags.length; i++) { + expect( + wrapper.exists(`[data-test-subj="rules-table-column-popover-tags-${i}"]`) + ).toBeTruthy(); + } + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx new file mode 100644 index 00000000000000..9503f5b8841330 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/tag_display.tsx @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useMemo, useState } from 'react'; +import { EuiPopover, EuiBadgeGroup, EuiBadge, EuiButtonEmpty } from '@elastic/eui'; +import styled from 'styled-components'; +import * as i18n from '../translations'; +import { caseInsensitiveSort } from './helpers'; + +interface TagsDisplayProps { + tags: string[]; +} + +const TagWrapper = styled(EuiBadgeGroup)` + width: 100%; +`; + +const TagPopoverWrapper = styled(EuiBadgeGroup)` + max-height: 200px; + max-width: 600px; + overflow: auto; +`; + +const TagPopoverButton = styled(EuiButtonEmpty)` + font-size: ${({ theme }) => theme.eui.euiFontSizeXS} + font-weight: 500; + height: 20px; +`; + +/** + * @param tags to display for filtering + */ +const TagsDisplayComponent = ({ tags }: TagsDisplayProps) => { + const [isTagPopoverOpen, setIsTagPopoverOpen] = useState(false); + const sortedTags = useMemo(() => caseInsensitiveSort(tags), [tags]); + + return ( + <> + {sortedTags.length <= 3 ? ( + <TagWrapper data-test-subj="tags"> + {sortedTags.map((tag: string, i: number) => ( + <EuiBadge + color="hollow" + key={`${tag}-${i}`} + data-test-subj={`rules-table-column-tags-${i}`} + > + {tag} + </EuiBadge> + ))} + </TagWrapper> + ) : ( + <TagWrapper data-test-subj="tags"> + {sortedTags.slice(0, 3).map((tag: string, i: number) => ( + <EuiBadge + color="hollow" + key={`${tag}-${i}`} + data-test-subj={`rules-table-column-tags-${i}`} + > + {tag} + </EuiBadge> + ))} + + <EuiPopover + ownFocus + display="block" + data-test-subj="tags-display-popover" + button={ + <TagPopoverButton + size="xs" + data-test-subj={'tags-display-popover-button'} + onClick={() => setIsTagPopoverOpen(!isTagPopoverOpen)} + > + {i18n.COLUMN_SEE_ALL_POPOVER} + </TagPopoverButton> + } + isOpen={isTagPopoverOpen} + closePopover={() => setIsTagPopoverOpen(!isTagPopoverOpen)} + repositionOnScroll + > + <TagPopoverWrapper> + {sortedTags.map((tag: string, i: number) => ( + <EuiBadge + color="hollow" + key={`${tag}-${i}`} + data-test-subj={`rules-table-column-popover-tags-${i}`} + title={tag} + > + {tag} + </EuiBadge> + ))} + </TagPopoverWrapper> + </EuiPopover> + </TagWrapper> + )} + </> + ); +}; + +export const TagsDisplay = React.memo(TagsDisplayComponent); + +TagsDisplay.displayName = 'TagsDisplay'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index 09503fcf1ef0ff..f2f3265ead1ba9 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -315,6 +315,13 @@ export const COLUMN_TAGS = i18n.translate( } ); +export const COLUMN_SEE_ALL_POPOVER = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.allRules.columns.tagsPopoverTitle', + { + defaultMessage: 'See all', + } +); + export const COLUMN_ACTIVATE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.allRules.columns.activateTitle', { From dc0bccf8f881951e47fdec419d6edccf998c220f Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Fri, 2 Oct 2020 16:30:16 -0700 Subject: [PATCH 116/128] [babel/register] remove from build (take 2) (#79379) Co-authored-by: spalger <spalger@users.noreply.github.com> --- package.json | 7 ++-- packages/kbn-babel-preset/node_preset.js | 8 ---- packages/kbn-babel-preset/package.json | 1 - .../lib/babel_register_for_test_plugins.js | 39 +++++++++++++++++++ .../functional_tests/lib/run_kibana_server.js | 20 +++++++++- scripts/build_plugin_list_docs.js | 2 +- scripts/es.js | 2 +- scripts/generate_plugin.js | 2 +- scripts/kibana.js | 4 +- scripts/plugin_helpers.js | 2 +- scripts/register_git_hook.js | 2 +- scripts/release_notes.js | 2 +- scripts/telemetry_check.js | 2 +- scripts/telemetry_extract.js | 2 +- src/cli/cluster/worker.ts | 2 +- src/cli/{index.js => dev.js} | 2 +- src/cli/dist.js | 23 +++++++++++ .../integration_tests/invalid_config.test.ts | 22 +++++++---- src/dev/build/tasks/bin/scripts/kibana | 2 +- src/dev/build/tasks/copy_source_task.ts | 3 ++ src/setup_node_env/babel_register/register.js | 24 +++--------- src/setup_node_env/index.js | 2 +- ..._dev_only_entry.js => no_transpilation.js} | 0 x-pack/package.json | 2 +- yarn.lock | 18 +++------ 25 files changed, 127 insertions(+), 68 deletions(-) create mode 100644 packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js rename src/cli/{index.js => dev.js} (91%) create mode 100644 src/cli/dist.js rename src/setup_node_env/{prebuilt_dev_only_entry.js => no_transpilation.js} (100%) diff --git a/package.json b/package.json index 5089e6e1a140d8..cebfddbe34e945 100644 --- a/package.json +++ b/package.json @@ -115,8 +115,6 @@ ] }, "dependencies": { - "@babel/core": "^7.11.1", - "@babel/register": "^7.10.5", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "7.9.1", "@elastic/eui": "29.0.0", @@ -128,7 +126,6 @@ "@hapi/wreck": "^15.0.2", "@kbn/analytics": "1.0.0", "@kbn/apm-config-loader": "1.0.0", - "@kbn/babel-preset": "1.0.0", "@kbn/config": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", @@ -214,6 +211,7 @@ "rxjs": "^6.5.5", "seedrandom": "^3.0.5", "semver": "^5.7.0", + "source-map-support": "^0.5.19", "style-it": "^2.1.3", "symbol-observable": "^1.2.0", "tar": "4.4.13", @@ -227,7 +225,9 @@ "yauzl": "^2.10.0" }, "devDependencies": { + "@babel/core": "^7.11.1", "@babel/parser": "^7.11.2", + "@babel/register": "^7.10.5", "@babel/types": "^7.11.0", "@elastic/apm-rum": "^5.6.1", "@elastic/charts": "23.0.0", @@ -238,6 +238,7 @@ "@elastic/github-checks-reporter": "0.0.20b3", "@elastic/makelogs": "^6.0.0", "@elastic/ui-ace": "0.2.3", + "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/es-archiver": "1.0.0", diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 45afe5d5ebc32b..86817ed253e7cd 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -49,13 +49,5 @@ module.exports = (_, options = {}) => { ], require('./common_preset'), ], - plugins: [ - [ - require.resolve('babel-plugin-transform-define'), - { - 'global.__BUILT_WITH_BABEL__': 'true', - }, - ], - ], }; }; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index bc4e0ec338f94e..79d2fd8687dae5 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -14,7 +14,6 @@ "@babel/preset-typescript": "^7.10.4", "babel-plugin-add-module-exports": "^1.0.2", "babel-plugin-styled-components": "^1.10.7", - "babel-plugin-transform-define": "^1.3.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "react-is": "^16.8.0", "styled-components": "^5.1.0" diff --git a/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js new file mode 100644 index 00000000000000..44ff579411bd9b --- /dev/null +++ b/packages/kbn-test/src/functional_tests/lib/babel_register_for_test_plugins.js @@ -0,0 +1,39 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const Path = require('path'); + +const { REPO_ROOT } = require('@kbn/dev-utils'); + +// modifies all future calls to require() to automatically +// compile the required source with babel +require('@babel/register')({ + ignore: [/[\/\\](node_modules|target|dist)[\/\\]/], + only: [ + Path.resolve(REPO_ROOT, 'test'), + Path.resolve(REPO_ROOT, 'x-pack/test'), + Path.resolve(REPO_ROOT, 'examples'), + Path.resolve(REPO_ROOT, 'x-pack/examples'), + // TODO: should should probably remove this link back to the source + Path.resolve(REPO_ROOT, 'x-pack/plugins/task_manager/server/config.ts'), + ], + babelrc: false, + presets: [require.resolve('@kbn/babel-preset/node_preset')], + extensions: ['.js', '.ts', '.tsx'], +}); diff --git a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js index fb9f8f7a52408c..e7ec99467ecfd8 100644 --- a/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js +++ b/packages/kbn-test/src/functional_tests/lib/run_kibana_server.js @@ -17,9 +17,26 @@ * under the License. */ -import { resolve } from 'path'; +import { resolve, relative } from 'path'; import { KIBANA_ROOT, KIBANA_EXEC, KIBANA_EXEC_PATH } from './paths'; +function extendNodeOptions(installDir) { + if (!installDir) { + return {}; + } + + const testOnlyRegisterPath = relative( + installDir, + require.resolve('./babel_register_for_test_plugins') + ); + + return { + NODE_OPTIONS: `--require=${testOnlyRegisterPath}${ + process.env.NODE_OPTIONS ? ` ${process.env.NODE_OPTIONS}` : '' + }`, + }; +} + export async function runKibanaServer({ procs, config, options }) { const { installDir } = options; @@ -29,6 +46,7 @@ export async function runKibanaServer({ procs, config, options }) { env: { FORCE_COLOR: 1, ...process.env, + ...extendNodeOptions(installDir), }, cwd: installDir || KIBANA_ROOT, wait: /http server running/, diff --git a/scripts/build_plugin_list_docs.js b/scripts/build_plugin_list_docs.js index 54821a1b10ee8d..6f184ca7b14c69 100644 --- a/scripts/build_plugin_list_docs.js +++ b/scripts/build_plugin_list_docs.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/dev-utils').runPluginListCli(); diff --git a/scripts/es.js b/scripts/es.js index 2d56496f2fdd23..53b01d8cb44140 100644 --- a/scripts/es.js +++ b/scripts/es.js @@ -17,7 +17,7 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); var resolve = require('path').resolve; var pkg = require('../package.json'); diff --git a/scripts/generate_plugin.js b/scripts/generate_plugin.js index f695eabb30f219..af3d31048ecfc6 100644 --- a/scripts/generate_plugin.js +++ b/scripts/generate_plugin.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/plugin-generator').runCli(); diff --git a/scripts/kibana.js b/scripts/kibana.js index f5a63e6c07dd60..2767e555f27367 100644 --- a/scripts/kibana.js +++ b/scripts/kibana.js @@ -17,6 +17,4 @@ * under the License. */ -require('../src/apm')(process.env.ELASTIC_APM_PROXY_SERVICE_NAME || 'kibana-proxy'); -require('../src/setup_node_env'); -require('../src/cli/cli'); +require('../src/cli/dev'); diff --git a/scripts/plugin_helpers.js b/scripts/plugin_helpers.js index a07ba7a9185f80..f28bf8fcfff903 100644 --- a/scripts/plugin_helpers.js +++ b/scripts/plugin_helpers.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/plugin-helpers').runCli(); diff --git a/scripts/register_git_hook.js b/scripts/register_git_hook.js index af3f54619bcecc..50dfeaf46109fc 100644 --- a/scripts/register_git_hook.js +++ b/scripts/register_git_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/dev-utils/target/precommit_hook/cli'); diff --git a/scripts/release_notes.js b/scripts/release_notes.js index f46ee5823d70d1..ee9275194ae94a 100644 --- a/scripts/release_notes.js +++ b/scripts/release_notes.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/release-notes').runReleaseNotesCli(); diff --git a/scripts/telemetry_check.js b/scripts/telemetry_check.js index 06b3ed46bdba6a..22a22b401cb15f 100644 --- a/scripts/telemetry_check.js +++ b/scripts/telemetry_check.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/telemetry-tools').runTelemetryCheck(); diff --git a/scripts/telemetry_extract.js b/scripts/telemetry_extract.js index 051bee26537b9b..e2fbb64c267196 100644 --- a/scripts/telemetry_extract.js +++ b/scripts/telemetry_extract.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/prebuilt_dev_only_entry'); +require('../src/setup_node_env/no_transpilation'); require('@kbn/telemetry-tools').runTelemetryExtract(); diff --git a/src/cli/cluster/worker.ts b/src/cli/cluster/worker.ts index c8a8a067d30bf6..f6205b41ac5a59 100644 --- a/src/cli/cluster/worker.ts +++ b/src/cli/cluster/worker.ts @@ -24,7 +24,7 @@ import { EventEmitter } from 'events'; import { BinderFor } from './binder_for'; import { fromRoot } from '../../core/server/utils'; -const cliPath = fromRoot('src/cli'); +const cliPath = fromRoot('src/cli/dev'); const baseArgs = _.difference(process.argv.slice(2), ['--no-watch']); const baseArgv = [process.execPath, cliPath].concat(baseArgs); diff --git a/src/cli/index.js b/src/cli/dev.js similarity index 91% rename from src/cli/index.js rename to src/cli/dev.js index 45f88eaf82a5b8..9d0cb35c3d7309 100644 --- a/src/cli/index.js +++ b/src/cli/dev.js @@ -17,6 +17,6 @@ * under the License. */ -require('../apm')(); +require('../apm')(process.env.ELASTIC_APM_PROXY_SERVICE_NAME || 'kibana-proxy'); require('../setup_node_env'); require('./cli'); diff --git a/src/cli/dist.js b/src/cli/dist.js new file mode 100644 index 00000000000000..e5480d21376249 --- /dev/null +++ b/src/cli/dist.js @@ -0,0 +1,23 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../apm')(); +require('../setup_node_env/no_transpilation'); +require('../setup_node_env/babel_register/polyfill'); +require('./cli'); diff --git a/src/cli/serve/integration_tests/invalid_config.test.ts b/src/cli/serve/integration_tests/invalid_config.test.ts index fd6fa1bf192fc3..a72142faa22fe6 100644 --- a/src/cli/serve/integration_tests/invalid_config.test.ts +++ b/src/cli/serve/integration_tests/invalid_config.test.ts @@ -18,10 +18,10 @@ */ import { spawnSync } from 'child_process'; -import { resolve } from 'path'; -const ROOT_DIR = resolve(__dirname, '../../../../'); -const INVALID_CONFIG_PATH = resolve(__dirname, '__fixtures__/invalid_config.yml'); +import { REPO_ROOT } from '@kbn/dev-utils'; + +const INVALID_CONFIG_PATH = require.resolve('./__fixtures__/invalid_config.yml'); interface LogEntry { message: string; @@ -35,11 +35,11 @@ describe('cli invalid config support', function () { function () { // Unused keys only throw once LegacyService starts, so disable migrations so that Core // will finish the start lifecycle without a running Elasticsearch instance. - const { error, status, stdout } = spawnSync( + const { error, status, stdout, stderr } = spawnSync( process.execPath, - ['src/cli', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], + ['scripts/kibana', '--config', INVALID_CONFIG_PATH, '--migrations.skip=true'], { - cwd: ROOT_DIR, + cwd: REPO_ROOT, } ); @@ -57,13 +57,21 @@ describe('cli invalid config support', function () { })); expect(error).toBe(undefined); - expect(status).toBe(64); + + if (!fatalLogLine) { + throw new Error( + `cli did not log the expected fatal error message:\n\nstdout: \n${stdout}\n\nstderr:\n${stderr}` + ); + } + expect(fatalLogLine.message).toContain( 'Error: Unknown configuration key(s): "unknown.key", "other.unknown.key", "other.third", "some.flat.key", ' + '"some.array". Check for spelling errors and ensure that expected plugins are installed.' ); expect(fatalLogLine.tags).toEqual(['fatal', 'root']); expect(fatalLogLine.type).toEqual('log'); + + expect(status).toBe(64); }, 20 * 1000 ); diff --git a/src/dev/build/tasks/bin/scripts/kibana b/src/dev/build/tasks/bin/scripts/kibana index 3283e17008e7c9..c606436c7b83f3 100755 --- a/src/dev/build/tasks/bin/scripts/kibana +++ b/src/dev/build/tasks/bin/scripts/kibana @@ -26,4 +26,4 @@ if [ -f "${CONFIG_DIR}/node.options" ]; then KBN_NODE_OPTS="$(grep -v ^# < ${CONFIG_DIR}/node.options | xargs)" fi -NODE_OPTIONS="--no-warnings --max-http-header-size=65536 $KBN_NODE_OPTS $NODE_OPTIONS" NODE_ENV=production exec "${NODE}" "${DIR}/src/cli" ${@} +NODE_OPTIONS="--no-warnings --max-http-header-size=65536 $KBN_NODE_OPTS $NODE_OPTIONS" NODE_ENV=production exec "${NODE}" "${DIR}/src/cli/dist" ${@} diff --git a/src/dev/build/tasks/copy_source_task.ts b/src/dev/build/tasks/copy_source_task.ts index 948e2357effb04..a5039717760aef 100644 --- a/src/dev/build/tasks/copy_source_task.ts +++ b/src/dev/build/tasks/copy_source_task.ts @@ -35,8 +35,11 @@ export const CopySource: Task = { '!src/fixtures/**', '!src/cli/cluster/**', '!src/cli/repl/**', + '!src/cli/dev.js', '!src/functional_test_runner/**', '!src/dev/**', + '!src/setup_node_env/babel_register/index.js', + '!src/setup_node_env/babel_register/register.js', '!**/public/**', 'typings/**', 'config/kibana.yml', diff --git a/src/setup_node_env/babel_register/register.js b/src/setup_node_env/babel_register/register.js index 6d573d89222449..3c0bd387c8e44d 100644 --- a/src/setup_node_env/babel_register/register.js +++ b/src/setup_node_env/babel_register/register.js @@ -46,26 +46,12 @@ var ignore = [ // ignore paths matching `/canvas/canvas_plugin/` /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, -]; -if (global.__BUILT_WITH_BABEL__) { - // when building the Kibana source we replace the statement - // `global.__BUILT_WITH_BABEL__` with the value `true` so that - // when @babel/register is required for the first time by users - // it will exclude kibana's `src` directory. - // - // We still need @babel/register for plugins though, we've been - // building their server code at require-time since version 4.2 - // TODO: the plugin install process could transpile plugin server code... - ignore.push(resolve(__dirname, '../../../src')); -} else { - ignore.push( - // ignore any path in the packages, unless it is in the package's - // root `src` directory, in any test or __tests__ directory, or it - // ends with .test.js, .test.ts, or .test.tsx - /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/ - ); -} + // ignore any path in the packages, unless it is in the package's + // root `src` directory, in any test or __tests__ directory, or it + // ends with .test.js, .test.ts, or .test.tsx + /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, +]; // modifies all future calls to require() to automatically // compile the required source with babel diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index d84249df7fd8f0..60f0982f50d209 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -17,5 +17,5 @@ * under the License. */ -require('./prebuilt_dev_only_entry'); +require('./no_transpilation'); require('./babel_register'); diff --git a/src/setup_node_env/prebuilt_dev_only_entry.js b/src/setup_node_env/no_transpilation.js similarity index 100% rename from src/setup_node_env/prebuilt_dev_only_entry.js rename to src/setup_node_env/no_transpilation.js diff --git a/x-pack/package.json b/x-pack/package.json index 5742200b55d9f4..4145d8d72cc636 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -32,6 +32,7 @@ "@cypress/webpack-preprocessor": "^5.4.1", "@elastic/apm-rum-react": "^1.2.5", "@elastic/maki": "6.3.0", + "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/es": "1.0.0", "@kbn/expect": "1.0.0", @@ -280,7 +281,6 @@ "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.0", "@elastic/safer-lodash-set": "0.0.0", - "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", "@kbn/interpreter": "1.0.0", diff --git a/yarn.lock b/yarn.lock index 971a94bfe56c3b..806424b222ad3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7545,14 +7545,6 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= -babel-plugin-transform-define@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.1.tgz#b21b7bad3b84cf8e3f07cdc8c660b99cbbc01213" - integrity sha512-JXZ1xE9jIbKCGYZ4wbSMPSI5mdS4DRLi5+SkTHgZqWn5YIf/EucykkzUsPmzJlpkX8fsMVdLnA5vt/LvT97Zbg== - dependencies: - lodash "^4.17.11" - traverse "0.6.6" - babel-plugin-transform-inline-consecutive-adds@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz#323d47a3ea63a83a7ac3c811ae8e6941faf2b0d1" @@ -27110,10 +27102,10 @@ source-map-support@^0.3.2: dependencies: source-map "0.1.32" -source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== +source-map-support@^0.5.1, source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@^0.5.9, source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -28936,7 +28928,7 @@ traverse-chain@~0.1.0: resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= -traverse@0.6.6, traverse@^0.6.6, traverse@~0.6.6: +traverse@^0.6.6, traverse@~0.6.6: version "0.6.6" resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.6.6.tgz#cbdf560fd7b9af632502fed40f918c157ea97137" integrity sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc= From 9ee9bc7f2000540f266cb45d9c87fc2a3f745899 Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Fri, 2 Oct 2020 16:41:05 -0700 Subject: [PATCH 117/128] [core/server/plugins] don't run discovery in dev server parent process (take 2) (#79358) Co-authored-by: spalger <spalger@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/plugins/plugins_service.test.ts | 81 +++++++++++++------ src/core/server/plugins/plugins_service.ts | 18 +++-- src/core/server/server.test.ts | 17 ++++ src/core/server/server.ts | 11 ++- 4 files changed, 93 insertions(+), 34 deletions(-) diff --git a/src/core/server/plugins/plugins_service.test.ts b/src/core/server/plugins/plugins_service.test.ts index d36fd2251176a2..64a382e539fb0f 100644 --- a/src/core/server/plugins/plugins_service.test.ts +++ b/src/core/server/plugins/plugins_service.test.ts @@ -102,35 +102,42 @@ const createPlugin = ( }); }; -describe('PluginsService', () => { - beforeEach(async () => { - mockPackage.raw = { - branch: 'feature-v1', - version: 'v1', - build: { - distributable: true, - number: 100, - sha: 'feature-v1-build-sha', - }, - }; +async function testSetup(options: { isDevClusterMaster?: boolean } = {}) { + mockPackage.raw = { + branch: 'feature-v1', + version: 'v1', + build: { + distributable: true, + number: 100, + sha: 'feature-v1-build-sha', + }, + }; - coreId = Symbol('core'); - env = Env.createDefault(REPO_ROOT, getEnvOptions()); + coreId = Symbol('core'); + env = Env.createDefault(REPO_ROOT, { + ...getEnvOptions(), + isDevClusterMaster: options.isDevClusterMaster ?? false, + }); - config$ = new BehaviorSubject<Record<string, any>>({ plugins: { initialize: true } }); - const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); - configService = new ConfigService(rawConfigService, env, logger); - await configService.setSchema(config.path, config.schema); - pluginsService = new PluginsService({ coreId, env, logger, configService }); + config$ = new BehaviorSubject<Record<string, any>>({ plugins: { initialize: true } }); + const rawConfigService = rawConfigServiceMock.create({ rawConfig$: config$ }); + configService = new ConfigService(rawConfigService, env, logger); + await configService.setSchema(config.path, config.schema); + pluginsService = new PluginsService({ coreId, env, logger, configService }); - [mockPluginSystem] = MockPluginsSystem.mock.instances as any; - mockPluginSystem.uiPlugins.mockReturnValue(new Map()); + [mockPluginSystem] = MockPluginsSystem.mock.instances as any; + mockPluginSystem.uiPlugins.mockReturnValue(new Map()); - environmentSetup = environmentServiceMock.createSetupContract(); - }); + environmentSetup = environmentServiceMock.createSetupContract(); +} - afterEach(() => { - jest.clearAllMocks(); +afterEach(() => { + jest.clearAllMocks(); +}); + +describe('PluginsService', () => { + beforeEach(async () => { + await testSetup(); }); describe('#discover()', () => { @@ -613,3 +620,29 @@ describe('PluginsService', () => { }); }); }); + +describe('PluginService when isDevClusterMaster is true', () => { + beforeEach(async () => { + await testSetup({ + isDevClusterMaster: true, + }); + }); + + describe('#discover()', () => { + it('does not try to run discovery', async () => { + await expect(pluginsService.discover({ environment: environmentSetup })).resolves + .toMatchInlineSnapshot(` + Object { + "pluginTree": undefined, + "uiPlugins": Object { + "browserConfigs": Map {}, + "internal": Map {}, + "public": Map {}, + }, + } + `); + + expect(mockDiscover).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/core/server/plugins/plugins_service.ts b/src/core/server/plugins/plugins_service.ts index e8fe42ee491ca5..a1062bde7765f0 100644 --- a/src/core/server/plugins/plugins_service.ts +++ b/src/core/server/plugins/plugins_service.ts @@ -18,7 +18,7 @@ */ import Path from 'path'; -import { Observable } from 'rxjs'; +import { Observable, EMPTY } from 'rxjs'; import { filter, first, map, mergeMap, tap, toArray } from 'rxjs/operators'; import { pick } from '@kbn/std'; @@ -86,9 +86,11 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS private readonly config$: Observable<PluginsConfig>; private readonly pluginConfigDescriptors = new Map<PluginName, PluginConfigDescriptor>(); private readonly uiPluginInternalInfo = new Map<PluginName, InternalPluginInfo>(); + private readonly discoveryDisabled: boolean; constructor(private readonly coreContext: CoreContext) { this.log = coreContext.logger.get('plugins-service'); + this.discoveryDisabled = coreContext.env.isDevClusterMaster; this.pluginsSystem = new PluginsSystem(coreContext); this.configService = coreContext.configService; this.config$ = coreContext.configService @@ -97,13 +99,17 @@ export class PluginsService implements CoreService<PluginsServiceSetup, PluginsS } public async discover({ environment }: PluginsServiceDiscoverDeps) { - this.log.debug('Discovering plugins'); - const config = await this.config$.pipe(first()).toPromise(); - const { error$, plugin$ } = discover(config, this.coreContext, { - uuid: environment.instanceUuid, - }); + const { error$, plugin$ } = this.discoveryDisabled + ? { + error$: EMPTY, + plugin$: EMPTY, + } + : discover(config, this.coreContext, { + uuid: environment.instanceUuid, + }); + await this.handleDiscoveryErrors(error$); await this.handleDiscoveredPlugins(plugin$); diff --git a/src/core/server/server.test.ts b/src/core/server/server.test.ts index 3258840d09df22..51defb7d0392e0 100644 --- a/src/core/server/server.test.ts +++ b/src/core/server/server.test.ts @@ -215,3 +215,20 @@ test(`doesn't setup core services if legacy config validation fails`, async () = expect(mockStatusService.setup).not.toHaveBeenCalled(); expect(mockLoggingService.setup).not.toHaveBeenCalled(); }); + +test(`doesn't validate config if env.isDevClusterMaster is true`, async () => { + const devParentEnv = Env.createDefault(REPO_ROOT, { + ...getEnvOptions(), + isDevClusterMaster: true, + }); + + const server = new Server(rawConfigService, devParentEnv, logger); + await server.setup(); + + expect(mockEnsureValidConfiguration).not.toHaveBeenCalled(); + expect(mockContextService.setup).toHaveBeenCalled(); + expect(mockAuditTrailService.setup).toHaveBeenCalled(); + expect(mockHttpService.setup).toHaveBeenCalled(); + expect(mockElasticsearchService.setup).toHaveBeenCalled(); + expect(mockSavedObjectsService.setup).toHaveBeenCalled(); +}); diff --git a/src/core/server/server.ts b/src/core/server/server.ts index ece10db41962d4..600f45e0b50daa 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -117,10 +117,13 @@ export class Server { }); const legacyConfigSetup = await this.legacy.setupLegacyConfig(); - // Immediately terminate in case of invalid configuration - // This needs to be done after plugin discovery - await this.configService.validate(); - await ensureValidConfiguration(this.configService, legacyConfigSetup); + // rely on dev server to validate config, don't validate in the parent process + if (!this.env.isDevClusterMaster) { + // Immediately terminate in case of invalid configuration + // This needs to be done after plugin discovery + await this.configService.validate(); + await ensureValidConfiguration(this.configService, legacyConfigSetup); + } const contextServiceSetup = this.context.setup({ // We inject a fake "legacy plugin" with dependencies on every plugin so that legacy plugins: From 92a629fc1f3ed49c5839ba12df36a1c2e1e8d135 Mon Sep 17 00:00:00 2001 From: Nicolas Chaulet <n.chaulet@gmail.com> Date: Fri, 2 Oct 2020 20:11:37 -0400 Subject: [PATCH 118/128] =?UTF-8?q?[Ingest=20Manager]=20Rename=20Fleet=20s?= =?UTF-8?q?etup=20and=20requirement,=20Fleet=20=3D>=20Central=E2=80=A6=20(?= =?UTF-8?q?#79291)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_enrollment_flyout/managed_instructions.tsx | 2 +- .../sections/fleet/setup_page/index.tsx | 12 ++++++------ x-pack/plugins/translations/translations/ja-JP.json | 1 - x-pack/plugins/translations/translations/zh-CN.json | 1 - 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx index c840b487a39704..e620424f88f4e3 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/managed_instructions.tsx @@ -81,7 +81,7 @@ export const ManagedInstructions = React.memo<Props>(({ agentPolicies }) => { <EuiLink href={getHref('fleet')}> <FormattedMessage id="xpack.ingestManager.agentEnrollment.setUpAgentsLink" - defaultMessage="set up Agents" + defaultMessage="set up central management for Elastic Agents" /> </EuiLink> ), diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx index fbd74f8b03e72d..7f0a23caa5fa22 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/setup_page/index.tsx @@ -73,7 +73,7 @@ export const SetupPage: React.FunctionComponent<{ ) { return ( <WithoutHeaderLayout> - <EuiPageBody restrictWidth={548}> + <EuiPageBody restrictWidth={648}> <EuiPageContent verticalPosition="center" horizontalPosition="center" @@ -87,7 +87,7 @@ export const SetupPage: React.FunctionComponent<{ <h2> <FormattedMessage id="xpack.ingestManager.setupPage.enableTitle" - defaultMessage="Enable Fleet" + defaultMessage="Enable central management for Elastic Agents" /> </h2> </EuiTitle> @@ -95,15 +95,15 @@ export const SetupPage: React.FunctionComponent<{ <EuiText color="subdued"> <FormattedMessage id="xpack.ingestManager.setupPage.enableText" - defaultMessage="Fleet requires an Elastic user who can create API keys and write to logs-* and metrics-*." + defaultMessage="Central management requires an Elastic user who can create API keys and write to logs-* and metrics-*." /> </EuiText> <EuiSpacer size="l" /> <EuiForm> <EuiButton onClick={onSubmit} fill isLoading={isFormLoading} type="submit"> <FormattedMessage - id="xpack.ingestManager.setupPage.enableFleet" - defaultMessage="Create user and enable Fleet" + id="xpack.ingestManager.setupPage.enableCentralManagement" + defaultMessage="Create user and enable central management" /> </EuiButton> </EuiForm> @@ -127,7 +127,7 @@ export const SetupPage: React.FunctionComponent<{ > <FormattedMessage id="xpack.ingestManager.setupPage.missingRequirementsCalloutDescription" - defaultMessage="In order to use Fleet, you must enable the following Elasticsearch and Kibana security features." + defaultMessage="To use central management for Elastic Agents, enable the following Elasticsearch and Kibana security features." /> </EuiCallOut> <EuiSpacer size="m" /> diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f144cbd3873f76..420df7618b934b 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -9246,7 +9246,6 @@ "xpack.ingestManager.setupPage.elasticsearchApiKeyFlagText": "{apiKeyLink}.{apiKeyFlag}を{true}に設定します。", "xpack.ingestManager.setupPage.elasticsearchSecurityFlagText": "{esSecurityLink}.{securityFlag}を{true}に設定します。", "xpack.ingestManager.setupPage.elasticsearchSecurityLink": "Elasticsearchセキュリティ", - "xpack.ingestManager.setupPage.enableFleet": "ユーザーを作成してフリートを有効にます", "xpack.ingestManager.setupPage.enableText": "フリートを使用するには、Elasticユーザーを作成する必要があります。このユーザーは、APIキーを作成して、logs-*およびmetrics-*に書き込むことができます。", "xpack.ingestManager.setupPage.enableTitle": "フリートを有効にする", "xpack.ingestManager.setupPage.encryptionKeyFlagText": "{encryptionKeyLink}.{keyFlag}を32文字以上の英数字に設定します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index c4a599fa35e7fa..a17be0f992b65b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -9252,7 +9252,6 @@ "xpack.ingestManager.setupPage.elasticsearchApiKeyFlagText": "{apiKeyLink}。将 {apiKeyFlag} 设置为 {true}。", "xpack.ingestManager.setupPage.elasticsearchSecurityFlagText": "{esSecurityLink}。将 {securityFlag} 设置为 {true}。", "xpack.ingestManager.setupPage.elasticsearchSecurityLink": "Elasticsearch 安全", - "xpack.ingestManager.setupPage.enableFleet": "创建用户并启用 Fleet", "xpack.ingestManager.setupPage.enableText": "要使用 Fleet,必须创建 Elastic 用户。此用户可以创建 API 密钥并写入到 logs-* and metrics-*。", "xpack.ingestManager.setupPage.enableTitle": "启用 Fleet", "xpack.ingestManager.setupPage.encryptionKeyFlagText": "{encryptionKeyLink}。将 {keyFlag} 设置为至少 32 个字符的字母数字值。", From ddf2d82e4446701024a0db9ba278174289874ff0 Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Fri, 2 Oct 2020 18:36:25 -0700 Subject: [PATCH 119/128] [kbn/optimizer] implement more efficient auto transpilation for node (#79052) Co-authored-by: spalger <spalger@users.noreply.github.com> --- NOTICE.txt | 24 +++ package.json | 7 +- packages/kbn-apm-config-loader/package.json | 2 +- packages/kbn-config/package.json | 2 +- packages/kbn-dev-utils/package.json | 2 +- packages/kbn-dev-utils/src/index.ts | 2 +- packages/kbn-i18n/package.json | 2 +- packages/kbn-interpreter/package.json | 2 +- packages/kbn-optimizer/package.json | 8 + packages/kbn-optimizer/src/index.ts | 1 + packages/kbn-optimizer/src/node/cache.ts | 171 ++++++++++++++++ .../kbn-optimizer/src/node/index.ts | 6 +- .../src/node/node_auto_tranpilation.ts | 187 ++++++++++++++++++ .../kbn-optimizer/src/node/polyfill.ts | 11 +- packages/kbn-pm/package.json | 2 +- packages/kbn-ui-framework/package.json | 4 +- packages/kbn-ui-shared-deps/package.json | 2 +- packages/kbn-utils/src/repo_root.ts | 50 +++-- scripts/precommit_hook.js | 2 +- src/cli/dist.js | 2 +- .../tasks/create_empty_dirs_and_files_task.ts | 3 +- .../service_templates/sysv/etc/default/kibana | 2 - src/dev/ci_setup/setup_env.sh | 9 - src/dev/jest/setup/babel_polyfill.js | 2 +- src/setup_node_env/babel_register/register.js | 63 ------ src/setup_node_env/index.js | 2 +- x-pack/package.json | 5 +- .../apm/scripts/aggregate-latency-metrics.js | 12 +- .../create-functional-tests-archive.js | 9 +- x-pack/plugins/apm/scripts/jest.js | 9 +- .../apm/scripts/setup-kibana-security.js | 9 +- .../apm/scripts/upload-telemetry-data.js | 12 +- yarn.lock | 128 ++++++++++-- 33 files changed, 563 insertions(+), 191 deletions(-) create mode 100644 packages/kbn-optimizer/src/node/cache.ts rename src/setup_node_env/babel_register/index.js => packages/kbn-optimizer/src/node/index.ts (81%) create mode 100644 packages/kbn-optimizer/src/node/node_auto_tranpilation.ts rename src/setup_node_env/babel_register/polyfill.js => packages/kbn-optimizer/src/node/polyfill.ts (61%) delete mode 100644 src/setup_node_env/babel_register/register.js diff --git a/NOTICE.txt b/NOTICE.txt index d689abf4c4e05c..0504b7f7d6db2a 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -208,6 +208,30 @@ 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. +--- +MIT License + +Copyright (c) 2014-present Sebastian McKenzie and other 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. + --- This product bundles bootstrap@3.3.6 which is available under a "MIT" license. diff --git a/package.json b/package.json index cebfddbe34e945..30d614aa43f7bc 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,6 @@ "chokidar": "^3.4.2", "color": "1.0.3", "commander": "^3.0.2", - "core-js": "^3.6.4", "cypress-promise": "^1.1.0", "deep-freeze-strict": "^1.1.1", "del": "^5.1.0", @@ -178,7 +177,7 @@ "inert": "^5.1.0", "inline-style": "^2.0.0", "joi": "^13.5.2", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "json-stable-stringify": "^1.0.1", "json-stringify-safe": "5.0.1", "lodash": "^4.17.20", @@ -225,7 +224,7 @@ "yauzl": "^2.10.0" }, "devDependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@babel/parser": "^7.11.2", "@babel/register": "^7.10.5", "@babel/types": "^7.11.0", @@ -262,7 +261,7 @@ "@types/angular": "^1.6.56", "@types/angular-mocks": "^1.7.0", "@types/archiver": "^3.1.0", - "@types/babel__core": "^7.1.2", + "@types/babel__core": "^7.1.10", "@types/bluebird": "^3.1.1", "@types/boom": "^7.2.0", "@types/chance": "^1.0.0", diff --git a/packages/kbn-apm-config-loader/package.json b/packages/kbn-apm-config-loader/package.json index c570fdc0218b98..6865e9ec9bf667 100644 --- a/packages/kbn-apm-config-loader/package.json +++ b/packages/kbn-apm-config-loader/package.json @@ -13,7 +13,7 @@ "dependencies": { "@elastic/safer-lodash-set": "0.0.0", "@kbn/utils": "1.0.0", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "lodash": "^4.17.20" }, "devDependencies": { diff --git a/packages/kbn-config/package.json b/packages/kbn-config/package.json index 062520f47f0f93..6d2d56b929eadf 100644 --- a/packages/kbn-config/package.json +++ b/packages/kbn-config/package.json @@ -16,7 +16,7 @@ "@kbn/logging": "1.0.0", "@kbn/std": "1.0.0", "@kbn/utility-types": "1.0.0", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "load-json-file": "^6.2.0", "lodash": "^4.17.20", "moment": "^2.24.0", diff --git a/packages/kbn-dev-utils/package.json b/packages/kbn-dev-utils/package.json index a85f5924f0ea20..a51734168cf765 100644 --- a/packages/kbn-dev-utils/package.json +++ b/packages/kbn-dev-utils/package.json @@ -10,7 +10,7 @@ "kbn:watch": "yarn build --watch" }, "dependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@kbn/utils": "1.0.0", "axios": "^0.19.2", "chalk": "^4.1.0", diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 8217999b01128a..6a845825f0fd44 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { REPO_ROOT } from '@kbn/utils'; +export * from '@kbn/utils'; export { withProcRunner, ProcRunner } from './proc_runner'; export * from './tooling_log'; export * from './serializers'; diff --git a/packages/kbn-i18n/package.json b/packages/kbn-i18n/package.json index 9e0ec50fb838e3..f23faecbeaa67f 100644 --- a/packages/kbn-i18n/package.json +++ b/packages/kbn-i18n/package.json @@ -13,7 +13,7 @@ }, "devDependencies": { "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@types/intl-relativeformat": "^2.1.0", diff --git a/packages/kbn-interpreter/package.json b/packages/kbn-interpreter/package.json index 430dac6cb2e00b..4d415e96389df4 100644 --- a/packages/kbn-interpreter/package.json +++ b/packages/kbn-interpreter/package.json @@ -16,7 +16,7 @@ }, "devDependencies": { "@babel/cli": "^7.10.5", - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@babel/plugin-transform-modules-commonjs": "^7.10.4", "@babel/plugin-transform-runtime": "^7.11.0", "@kbn/babel-preset": "1.0.0", diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index 5871c81f48aea1..52f9349aec696b 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@babel/cli": "^7.10.5", + "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", @@ -19,21 +20,26 @@ "clean-webpack-plugin": "^3.0.0", "compression-webpack-plugin": "^4.0.0", "cpy": "^8.0.0", + "core-js": "^3.6.5", "css-loader": "^3.4.2", "del": "^5.1.0", "execa": "^4.0.2", "file-loader": "^4.2.0", "istanbul-instrumenter-loader": "^3.0.1", "jest-diff": "^26.4.2", + "js-yaml": "^3.14.0", "json-stable-stringify": "^1.0.1", + "lmdb-store": "^0.6.10", "loader-utils": "^1.2.3", "node-sass": "^4.13.1", "normalize-path": "^3.0.0", + "pirates": "^4.0.1", "postcss": "^7.0.32", "postcss-loader": "^3.0.0", "raw-loader": "^3.1.0", "rxjs": "^6.5.5", "sass-loader": "^8.0.2", + "source-map-support": "^0.5.19", "style-loader": "^1.1.3", "terser-webpack-plugin": "^2.1.2", "tinymath": "1.2.1", @@ -44,8 +50,10 @@ "webpack-merge": "^4.2.2" }, "devDependencies": { + "@types/babel__core": "^7.1.10", "@types/compression-webpack-plugin": "^2.0.2", "@types/loader-utils": "^1.1.3", + "@types/source-map-support": "^0.5.3", "@types/watchpack": "^1.1.5", "@types/webpack": "^4.41.3" } diff --git a/packages/kbn-optimizer/src/index.ts b/packages/kbn-optimizer/src/index.ts index 39cf2120baf0a1..549e4b13a4ac01 100644 --- a/packages/kbn-optimizer/src/index.ts +++ b/packages/kbn-optimizer/src/index.ts @@ -21,3 +21,4 @@ export { OptimizerConfig } from './optimizer'; export * from './run_optimizer'; export * from './log_optimizer_state'; export * from './report_optimizer_stats'; +export * from './node'; diff --git a/packages/kbn-optimizer/src/node/cache.ts b/packages/kbn-optimizer/src/node/cache.ts new file mode 100644 index 00000000000000..7fbf009e38a7df --- /dev/null +++ b/packages/kbn-optimizer/src/node/cache.ts @@ -0,0 +1,171 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Path from 'path'; + +// @ts-expect-error no types available +import * as LmdbStore from 'lmdb-store'; +import { REPO_ROOT, UPSTREAM_BRANCH } from '@kbn/dev-utils'; + +const CACHE_DIR = Path.resolve(REPO_ROOT, 'data/node_auto_transpilation_cache', UPSTREAM_BRANCH); +const reportError = () => { + // right now I'm not sure we need to worry about errors, the cache isn't actually + // necessary, and if the cache is broken it should just rebuild on the next restart + // of the process. We don't know how often errors occur though and what types of + // things might fail on different machines so we probably want some way to signal + // to users that something is wrong +}; + +const GLOBAL_ATIME = `${Date.now()}`; +const MINUTE = 1000 * 60; +const HOUR = MINUTE * 60; +const DAY = HOUR * 24; + +interface Lmdb<T> { + get(key: string): T | undefined; + put(key: string, value: T, version?: number, ifVersion?: number): Promise<boolean>; + remove(key: string, ifVersion?: number): Promise<boolean>; + openDB<T>(options: { name: string; encoding: 'msgpack' | 'string' | 'json' | 'binary' }): Lmdb<T>; + getRange(options?: { + start?: T; + end?: T; + reverse?: boolean; + limit?: number; + versions?: boolean; + }): Iterable<{ key: string; value: T }>; +} + +export class Cache { + private readonly codes: Lmdb<string>; + private readonly atimes: Lmdb<string>; + private readonly mtimes: Lmdb<string>; + private readonly sourceMaps: Lmdb<any>; + private readonly prefix: string; + + constructor(config: { prefix: string }) { + this.prefix = config.prefix; + + this.codes = LmdbStore.open({ + name: 'codes', + path: CACHE_DIR, + }); + + this.atimes = this.codes.openDB({ + name: 'atimes', + encoding: 'string', + }); + + this.mtimes = this.codes.openDB({ + name: 'mtimes', + encoding: 'string', + }); + + this.sourceMaps = this.codes.openDB({ + name: 'sourceMaps', + encoding: 'msgpack', + }); + + // after the process has been running for 30 minutes prune the + // keys which haven't been used in 30 days. We use `unref()` to + // make sure this timer doesn't hold other processes open + // unexpectedly + setTimeout(() => { + this.pruneOldKeys(); + }, 30 * MINUTE).unref(); + } + + getMtime(path: string) { + return this.mtimes.get(this.getKey(path)); + } + + getCode(path: string) { + const key = this.getKey(path); + + // when we use a file from the cache set the "atime" of that cache entry + // so that we know which cache items we use and which haven't been + // touched in a long time (currently 30 days) + this.atimes.put(key, GLOBAL_ATIME).catch(reportError); + + return this.codes.get(key); + } + + getSourceMap(path: string) { + return this.sourceMaps.get(this.getKey(path)); + } + + update(path: string, file: { mtime: string; code: string; map: any }) { + const key = this.getKey(path); + + Promise.all([ + this.atimes.put(key, GLOBAL_ATIME), + this.mtimes.put(key, file.mtime), + this.codes.put(key, file.code), + this.sourceMaps.put(key, file.map), + ]).catch(reportError); + } + + private getKey(path: string) { + return `${this.prefix}${path}`; + } + + private async pruneOldKeys() { + try { + const ATIME_LIMIT = Date.now() - 30 * DAY; + const BATCH_SIZE = 1000; + + const validKeys: string[] = []; + const invalidKeys: string[] = []; + + for (const { key, value } of this.atimes.getRange()) { + const atime = parseInt(value, 10); + if (atime < ATIME_LIMIT) { + invalidKeys.push(key); + } else { + validKeys.push(key); + } + + if (validKeys.length + invalidKeys.length >= BATCH_SIZE) { + const promises = new Set(); + + if (invalidKeys.length) { + for (const k of invalidKeys) { + // all these promises are the same currently, so Set() will + // optimise this to a single promise, but I wouldn't be shocked + // if a future version starts returning independent promises so + // this is just for some future-proofing + promises.add(this.atimes.remove(k)); + promises.add(this.mtimes.remove(k)); + promises.add(this.codes.remove(k)); + promises.add(this.sourceMaps.remove(k)); + } + } else { + // delay a smidge to allow other things to happen before the next batch of checks + promises.add(new Promise((resolve) => setTimeout(resolve, 1))); + } + + invalidKeys.length = 0; + validKeys.length = 0; + await Promise.all(Array.from(promises)); + } + } + } catch { + // ignore errors, the cache is totally disposable and will rebuild if there is some sort of corruption + } + } +} diff --git a/src/setup_node_env/babel_register/index.js b/packages/kbn-optimizer/src/node/index.ts similarity index 81% rename from src/setup_node_env/babel_register/index.js rename to packages/kbn-optimizer/src/node/index.ts index 1574be8937a29e..64dd75ce573e67 100644 --- a/src/setup_node_env/babel_register/index.js +++ b/packages/kbn-optimizer/src/node/index.ts @@ -17,8 +17,4 @@ * under the License. */ -// register and polyfill need to happen in this -// order and in separate files. Checkout each file -// for a much more detailed explanation -require('./register'); -require('./polyfill'); +export * from './node_auto_tranpilation'; diff --git a/packages/kbn-optimizer/src/node/node_auto_tranpilation.ts b/packages/kbn-optimizer/src/node/node_auto_tranpilation.ts new file mode 100644 index 00000000000000..ff6ab1c68da532 --- /dev/null +++ b/packages/kbn-optimizer/src/node/node_auto_tranpilation.ts @@ -0,0 +1,187 @@ +/* eslint-disable @kbn/eslint/require-license-header */ + +/** + * This module is based on @babel/register @ 9808d25, modified to use + * a more efficient caching implementation which writes to disk as + * the cache is built rather than keeping the whole cache in memory + * and then dumping it to disk when the process exits. + */ + +/** + * @notice + * MIT License + * + * Copyright (c) 2014-present Sebastian McKenzie and other 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. + */ + +import Fs from 'fs'; +import Path from 'path'; +import Crypto from 'crypto'; + +import * as babel from '@babel/core'; +import { addHook } from 'pirates'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import sourceMapSupport from 'source-map-support'; + +import { Cache } from './cache'; + +const cwd = process.cwd(); + +const IGNORE_PATTERNS = [ + /[\/\\]kbn-pm[\/\\]dist[\/\\]/, + + // ignore paths matching `/node_modules/{a}/{b}`, unless `a` + // is `x-pack` and `b` is not `node_modules` + /[\/\\]node_modules[\/\\](?!x-pack[\/\\](?!node_modules)([^\/\\]+))([^\/\\]+[\/\\][^\/\\]+)/, + + // ignore paths matching `/canvas/canvas_plugin/` + /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, + + // ignore any path in the packages, unless it is in the package's + // root `src` directory, in any test or __tests__ directory, or it + // ends with .test.js, .test.ts, or .test.tsx + /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, +]; + +function getBabelOptions(path: string) { + return babel.loadOptions({ + cwd, + sourceRoot: Path.dirname(path) + Path.sep, + filename: path, + babelrc: false, + presets: [require.resolve('@kbn/babel-preset/node_preset')], + sourceMaps: 'both', + ast: false, + })!; +} + +/** + * @babel/register uses a JSON encoded copy of the config + babel.version + * as the cache key for files, so we do something similar but we don't need + * a unique cache key for every file as our config isn't different for + * different files (by design). Instead we determine a unique prefix and + * automatically prepend all paths with the prefix to create cache keys + */ +function determineCachePrefix() { + const json = JSON.stringify({ + babelVersion: babel.version, + // get a config for a fake js, ts, and tsx file to make sure we + // capture conditional config portions based on the file extension + js: getBabelOptions(Path.resolve(REPO_ROOT, 'foo.js')), + ts: getBabelOptions(Path.resolve(REPO_ROOT, 'foo.ts')), + tsx: getBabelOptions(Path.resolve(REPO_ROOT, 'foo.tsx')), + }); + + const checksum = Crypto.createHash('sha256').update(json).digest('hex'); + return `${checksum}:`; +} + +function compile(cache: Cache, source: string, path: string) { + try { + const mtime = `${Fs.statSync(path).mtimeMs}`; + if (cache.getMtime(path) === mtime) { + const code = cache.getCode(path); + if (code) { + // code *should* always be defined, but if it isn't for some reason rebuild it + return code; + } + } + + const options = getBabelOptions(path); + const result = babel.transform(source, options); + + if (!result || !result.code || !result.map) { + throw new Error(`babel failed to transpile [${path}]`); + } + + cache.update(path, { + mtime, + map: result.map, + code: result.code, + }); + + return result.code; + } catch (error) { + throw error; + } +} + +let installed = false; + +export function registerNodeAutoTranspilation() { + if (installed) { + return; + } + installed = true; + + const cache = new Cache({ + prefix: determineCachePrefix(), + }); + + sourceMapSupport.install({ + handleUncaughtExceptions: false, + environment: 'node', + // @ts-expect-error bad source-map-support types + retrieveSourceMap(path: string) { + const map = cache.getSourceMap(path); + + if (map) { + return { + url: null, + map, + }; + } else { + return null; + } + }, + }); + + let compiling = false; + + addHook( + (code, path) => { + if (compiling) { + return code; + } + + if (IGNORE_PATTERNS.some((re) => re.test(path))) { + return code; + } + + try { + compiling = true; + return compile(cache, code, path); + } finally { + compiling = false; + } + }, + { + exts: ['.js', '.ts', '.tsx'], + ignoreNodeModules: false, + } + ); + + // require the polyfills after setting up the require hook so that @babel/preset-env + // will spot the import in the polyfill file and replace it with the necessary polyfills + // for the current node.js version + require('./polyfill'); +} diff --git a/src/setup_node_env/babel_register/polyfill.js b/packages/kbn-optimizer/src/node/polyfill.ts similarity index 61% rename from src/setup_node_env/babel_register/polyfill.js rename to packages/kbn-optimizer/src/node/polyfill.ts index b6928f6cb8caf8..5d445ef89f71eb 100644 --- a/src/setup_node_env/babel_register/polyfill.js +++ b/packages/kbn-optimizer/src/node/polyfill.ts @@ -17,13 +17,4 @@ * under the License. */ -// `@babel/preset-env` looks for and rewrites the following import -// statement into a list of import statements based on the polyfills -// necessary for our target environment (the current version of node) -// but since it does that during compilation, `import 'core-js/stable'` -// must be in a file that is loaded with `require()` AFTER `@babel/register` -// is configured. -// -// This is why we have this single statement in it's own file and require -// it from ./index.js -require('core-js/stable'); +import 'core-js/stable'; diff --git a/packages/kbn-pm/package.json b/packages/kbn-pm/package.json index f4e9ee82499004..8ffd86b84bf76b 100644 --- a/packages/kbn-pm/package.json +++ b/packages/kbn-pm/package.json @@ -10,7 +10,7 @@ "prettier": "prettier --write './src/**/*.ts'" }, "devDependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/plugin-proposal-object-rest-spread": "^7.11.0", "@babel/preset-env": "^7.11.0", diff --git a/packages/kbn-ui-framework/package.json b/packages/kbn-ui-framework/package.json index 676985fa247402..639d4e17d0e71d 100644 --- a/packages/kbn-ui-framework/package.json +++ b/packages/kbn-ui-framework/package.json @@ -30,7 +30,7 @@ "enzyme-adapter-react-16": "^1.9.1" }, "devDependencies": { - "@babel/core": "^7.11.1", + "@babel/core": "^7.11.6", "@elastic/eui": "29.0.0", "@kbn/babel-preset": "1.0.0", "@kbn/optimizer": "1.0.0", @@ -38,7 +38,7 @@ "brace": "0.11.1", "chalk": "^4.1.0", "chokidar": "^3.4.2", - "core-js": "^3.6.4", + "core-js": "^3.6.5", "css-loader": "^3.4.2", "expose-loader": "^0.7.5", "file-loader": "^4.2.0", diff --git a/packages/kbn-ui-shared-deps/package.json b/packages/kbn-ui-shared-deps/package.json index e5f1a06e5bffa9..d2a590d29947b4 100644 --- a/packages/kbn-ui-shared-deps/package.json +++ b/packages/kbn-ui-shared-deps/package.json @@ -17,7 +17,7 @@ "abortcontroller-polyfill": "^1.4.0", "angular": "^1.8.0", "compression-webpack-plugin": "^4.0.0", - "core-js": "^3.6.4", + "core-js": "^3.6.5", "custom-event-polyfill": "^0.3.0", "jquery": "^3.5.0", "lodash": "^4.17.20", diff --git a/packages/kbn-utils/src/repo_root.ts b/packages/kbn-utils/src/repo_root.ts index b33b28d8d6e2fc..ed3516a7304f9d 100644 --- a/packages/kbn-utils/src/repo_root.ts +++ b/packages/kbn-utils/src/repo_root.ts @@ -22,38 +22,50 @@ import Fs from 'fs'; import loadJsonFile from 'load-json-file'; -const isKibanaDir = (dir: string) => { +const readKibanaPkgJson = (dir: string) => { try { const path = Path.resolve(dir, 'package.json'); const json = loadJsonFile.sync(path); if (json && typeof json === 'object' && 'name' in json && json.name === 'kibana') { - return true; + return json; } } catch (error) { if (error && error.code === 'ENOENT') { - return false; + return; } throw error; } }; -// search for the kibana directory, since this file is moved around it might -// not be where we think but should always be a relatively close parent -// of this directory -const startDir = Fs.realpathSync(__dirname); -const { root: rootDir } = Path.parse(startDir); -let cursor = startDir; -while (true) { - if (isKibanaDir(cursor)) { - break; - } +const findKibanaPackageJson = () => { + // search for the kibana directory, since this file is moved around it might + // not be where we think but should always be a relatively close parent + // of this directory + const startDir = Fs.realpathSync(__dirname); + const { root: rootDir } = Path.parse(startDir); + let cursor = startDir; + while (true) { + const kibanaPkgJson = readKibanaPkgJson(cursor); + if (kibanaPkgJson) { + return { + kibanaDir: cursor, + kibanaPkgJson: kibanaPkgJson as { + name: string; + branch: string; + }, + }; + } - const parent = Path.dirname(cursor); - if (parent === rootDir) { - throw new Error(`unable to find kibana directory from ${startDir}`); + const parent = Path.dirname(cursor); + if (parent === rootDir) { + throw new Error(`unable to find kibana directory from ${startDir}`); + } + cursor = parent; } - cursor = parent; -} +}; + +const { kibanaDir, kibanaPkgJson } = findKibanaPackageJson(); -export const REPO_ROOT = cursor; +export const REPO_ROOT = kibanaDir; +export const UPSTREAM_BRANCH = kibanaPkgJson.branch; diff --git a/scripts/precommit_hook.js b/scripts/precommit_hook.js index 7b9647cb7a9116..7749fab6d371fa 100644 --- a/scripts/precommit_hook.js +++ b/scripts/precommit_hook.js @@ -17,5 +17,5 @@ * under the License. */ -require('../src/setup_node_env/babel_register'); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('../src/dev/run_precommit_hook'); diff --git a/src/cli/dist.js b/src/cli/dist.js index e5480d21376249..2e26eaf52e8365 100644 --- a/src/cli/dist.js +++ b/src/cli/dist.js @@ -19,5 +19,5 @@ require('../apm')(); require('../setup_node_env/no_transpilation'); -require('../setup_node_env/babel_register/polyfill'); +require('core-js/stable'); require('./cli'); diff --git a/src/dev/build/tasks/create_empty_dirs_and_files_task.ts b/src/dev/build/tasks/create_empty_dirs_and_files_task.ts index a72c6a45983384..48745572def78c 100644 --- a/src/dev/build/tasks/create_empty_dirs_and_files_task.ts +++ b/src/dev/build/tasks/create_empty_dirs_and_files_task.ts @@ -17,7 +17,7 @@ * under the License. */ -import { mkdirp, write, Task } from '../lib'; +import { mkdirp, Task } from '../lib'; export const CreateEmptyDirsAndFiles: Task = { description: 'Creating some empty directories and files to prevent file-permission issues', @@ -26,7 +26,6 @@ export const CreateEmptyDirsAndFiles: Task = { await Promise.all([ mkdirp(build.resolvePath('plugins')), mkdirp(build.resolvePath('data/optimize')), - write(build.resolvePath('data/optimize/.babel_register_cache.json'), '{}'), ]); }, }; diff --git a/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana b/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana index ee019d348ed97f..9c9f58ded350bc 100644 --- a/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana +++ b/src/dev/build/tasks/os_packages/service_templates/sysv/etc/default/kibana @@ -10,6 +10,4 @@ nice="" # The default behavior is to simply log a message "program stop failed; still running" KILL_ON_STOP_TIMEOUT=0 -BABEL_CACHE_PATH="/var/lib/kibana/optimize/.babel_register_cache.json" - KBN_PATH_CONF="/etc/kibana" diff --git a/src/dev/ci_setup/setup_env.sh b/src/dev/ci_setup/setup_env.sh index 72ec73ad810e6d..8ec80ac295c73c 100644 --- a/src/dev/ci_setup/setup_env.sh +++ b/src/dev/ci_setup/setup_env.sh @@ -24,15 +24,6 @@ export NODE_OPTIONS="$NODE_OPTIONS --max-old-space-size=4096" ### export FORCE_COLOR=1 -### -### The @babel/register cache collects the build output from each file in -### a map, in memory, and then when the process exits it writes that to the -### babel cache file as a JSON encoded object. Stringifying that object -### causes OOMs on CI regularly enough that we need to find another solution, -### and until we do we need to disable the cache -### -export BABEL_DISABLE_CACHE=true - ### ### check that we seem to be in a kibana project ### diff --git a/src/dev/jest/setup/babel_polyfill.js b/src/dev/jest/setup/babel_polyfill.js index 58325c1a67f94f..085c8e2a25eefc 100644 --- a/src/dev/jest/setup/babel_polyfill.js +++ b/src/dev/jest/setup/babel_polyfill.js @@ -20,4 +20,4 @@ // Note: In theory importing the polyfill should not be needed, as Babel should // include the necessary polyfills when using `@babel/preset-env`, but for some // reason it did not work. See https://github.com/elastic/kibana/issues/14506 -import '../../../setup_node_env/babel_register/polyfill'; +import '@kbn/optimizer/src/node/polyfill'; diff --git a/src/setup_node_env/babel_register/register.js b/src/setup_node_env/babel_register/register.js deleted file mode 100644 index 3c0bd387c8e44d..00000000000000 --- a/src/setup_node_env/babel_register/register.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -var resolve = require('path').resolve; - -// this must happen before `require('@babel/register')` and can't be changed -// once the module has been loaded -if (!process.env.BABEL_CACHE_PATH) { - process.env.BABEL_CACHE_PATH = resolve( - __dirname, - '../../../data/optimize/.babel_register_cache.json' - ); -} - -// paths that @babel/register should ignore -var ignore = [ - /[\/\\]bower_components[\/\\]/, - /[\/\\]kbn-pm[\/\\]dist[\/\\]/, - - // TODO: remove this and just transpile plugins at build time, but - // has tricky edge cases that will probably require better eslint - // restrictions to make sure that code destined for the server/browser - // follows respects the limitations of each environment. - // - // https://github.com/elastic/kibana/issues/14800#issuecomment-366130268 - - // ignore paths matching `/node_modules/{a}/{b}`, unless `a` - // is `x-pack` and `b` is not `node_modules` - /[\/\\]node_modules[\/\\](?!x-pack[\/\\](?!node_modules)([^\/\\]+))([^\/\\]+[\/\\][^\/\\]+)/, - - // ignore paths matching `/canvas/canvas_plugin/` - /[\/\\]canvas[\/\\]canvas_plugin[\/\\]/, - - // ignore any path in the packages, unless it is in the package's - // root `src` directory, in any test or __tests__ directory, or it - // ends with .test.js, .test.ts, or .test.tsx - /[\/\\]packages[\/\\](eslint-|kbn-)[^\/\\]+[\/\\](?!src[\/\\].*|(.+[\/\\])?(test|__tests__)[\/\\].+|.+\.test\.(js|ts|tsx)$)(.+$)/, -]; - -// modifies all future calls to require() to automatically -// compile the required source with babel -require('@babel/register')({ - ignore, - babelrc: false, - presets: [require.resolve('@kbn/babel-preset/node_preset')], - extensions: ['.js', '.ts', '.tsx'], -}); diff --git a/src/setup_node_env/index.js b/src/setup_node_env/index.js index 60f0982f50d209..55539c56a364e4 100644 --- a/src/setup_node_env/index.js +++ b/src/setup_node_env/index.js @@ -18,4 +18,4 @@ */ require('./no_transpilation'); -require('./babel_register'); +require('@kbn/optimizer').registerNodeAutoTranspilation(); diff --git a/x-pack/package.json b/x-pack/package.json index 4145d8d72cc636..ffe1a088558885 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -271,8 +271,7 @@ "yargs": "^15.4.1" }, "dependencies": { - "@babel/core": "^7.11.1", - "@babel/register": "^7.10.5", + "@babel/core": "^7.11.6", "@babel/runtime": "^7.11.2", "@elastic/datemath": "5.0.3", "@elastic/ems-client": "7.10.0", @@ -334,7 +333,7 @@ "isbinaryfile": "4.0.2", "joi": "^13.5.2", "jquery": "^3.5.0", - "js-yaml": "3.13.1", + "js-yaml": "^3.14.0", "json-stable-stringify": "^1.0.1", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.20", diff --git a/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js b/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js index 287f267343b115..15414c8166520e 100644 --- a/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js +++ b/x-pack/plugins/apm/scripts/aggregate-latency-metrics.js @@ -4,17 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.ts'], - plugins: [ - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - ], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); const { aggregateLatencyMetrics, diff --git a/x-pack/plugins/apm/scripts/create-functional-tests-archive.js b/x-pack/plugins/apm/scripts/create-functional-tests-archive.js index 6b3473dc2ac0a9..ce265acf39c8bc 100644 --- a/x-pack/plugins/apm/scripts/create-functional-tests-archive.js +++ b/x-pack/plugins/apm/scripts/create-functional-tests-archive.js @@ -6,13 +6,6 @@ // compile typescript on the fly // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.js', '.ts'], - plugins: ['@babel/plugin-proposal-optional-chaining'], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('./create-functional-tests-archive/index.ts'); diff --git a/x-pack/plugins/apm/scripts/jest.js b/x-pack/plugins/apm/scripts/jest.js index 5c29dd91269378..3a73dbf6e3464e 100644 --- a/x-pack/plugins/apm/scripts/jest.js +++ b/x-pack/plugins/apm/scripts/jest.js @@ -4,14 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.js'], - plugins: [], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); // eslint-disable-next-line import/no-extraneous-dependencies const { run } = require('jest'); diff --git a/x-pack/plugins/apm/scripts/setup-kibana-security.js b/x-pack/plugins/apm/scripts/setup-kibana-security.js index 414d7208a78648..86b8c30988ca5a 100644 --- a/x-pack/plugins/apm/scripts/setup-kibana-security.js +++ b/x-pack/plugins/apm/scripts/setup-kibana-security.js @@ -17,13 +17,6 @@ // compile typescript on the fly // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.ts'], - plugins: ['@babel/plugin-proposal-optional-chaining'], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('./kibana-security/setup-custom-kibana-user-role.ts'); diff --git a/x-pack/plugins/apm/scripts/upload-telemetry-data.js b/x-pack/plugins/apm/scripts/upload-telemetry-data.js index 03cd5095ec02d7..76bf9c5aebe642 100644 --- a/x-pack/plugins/apm/scripts/upload-telemetry-data.js +++ b/x-pack/plugins/apm/scripts/upload-telemetry-data.js @@ -6,16 +6,6 @@ // compile typescript on the fly // eslint-disable-next-line import/no-extraneous-dependencies -require('@babel/register')({ - extensions: ['.ts'], - plugins: [ - '@babel/plugin-proposal-optional-chaining', - '@babel/plugin-proposal-nullish-coalescing-operator', - ], - presets: [ - '@babel/typescript', - ['@babel/preset-env', { targets: { node: 'current' } }], - ], -}); +require('@kbn/optimizer').registerNodeAutoTranspilation(); require('./upload-telemetry-data/index.ts'); diff --git a/yarn.lock b/yarn.lock index 806424b222ad3f..c4af49d12c8cb6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,7 +63,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.7.5", "@babel/core@^7.9.0": +"@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.11.1" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== @@ -85,6 +85,28 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" + integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.6" + "@babel/helper-module-transforms" "^7.11.0" + "@babel/helpers" "^7.10.4" + "@babel/parser" "^7.11.5" + "@babel/template" "^7.10.4" + "@babel/traverse" "^7.11.5" + "@babel/types" "^7.11.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@^7.10.5", "@babel/generator@^7.11.0", "@babel/generator@^7.9.6": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" @@ -94,6 +116,15 @@ jsesc "^2.5.1" source-map "^0.5.0" +"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": + version "7.11.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" + integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== + dependencies: + "@babel/types" "^7.11.5" + jsesc "^2.5.1" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz#5bf0d495a3f757ac3bda48b5bf3b3ba309c72ba3" @@ -325,6 +356,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.3.tgz#9e1eae46738bcd08e23e867bab43e7b95299a8f9" integrity sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA== +"@babel/parser@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" + integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== + "@babel/plugin-proposal-async-generator-functions@^7.10.4": version "7.10.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz#3491cabf2f7c179ab820606cec27fed15e0e8558" @@ -1105,6 +1141,21 @@ globals "^11.1.0" lodash "^4.17.19" +"@babel/traverse@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" + integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.11.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.11.5" + "@babel/types" "^7.11.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + "@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.5": version "7.11.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" @@ -1114,6 +1165,15 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@babel/types@^7.11.5": + version "7.11.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" + integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + "@base2/pretty-print-object@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.0.tgz#860ce718b0b73f4009e153541faff2cb6b85d047" @@ -3734,7 +3794,7 @@ resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.4.tgz#bfd5b0d0d1ba13e351dff65b6e52783b816826c8" integrity sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw== -"@types/babel__core@^7.0.0": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.10": version "7.1.10" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw== @@ -3745,17 +3805,6 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" - integrity sha512-cfCCrFmiGY/yq0NuKNxIQvZFy9kY/1immpSpTngOnyIbD4+eJOG5mxphhHDv3CHL9GltO4GcKr54kGBg3RNdbg== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - "@types/babel__core@^7.1.7": version "7.1.7" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" @@ -5053,6 +5102,13 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== +"@types/source-map-support@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@types/source-map-support/-/source-map-support-0.5.3.tgz#acb6b3e499c20692552d16934c16162c84594e16" + integrity sha512-fvjMjVH8Rmokw2dWh1dkj90iX5R8FPjeZzjNH+6eFXReh0QnHFf1YBl3B0CF0RohIAA3SDRJsGeeUWKl6d7HqA== + dependencies: + source-map "^0.6.0" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -10021,7 +10077,7 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3, core-js@^2.6.5, core-js@^2.6.9: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.1, core-js@^3.0.4, core-js@^3.6.4: +core-js@^3.0.1, core-js@^3.0.4: version "3.6.4" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== @@ -18700,6 +18756,14 @@ js-yaml@3.13.1, js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.4.6, js-yaml@^3.5.1 argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -19496,6 +19560,17 @@ livereload-js@^2.3.0: resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== +lmdb-store@^0.6.10: + version "0.6.10" + resolved "https://registry.yarnpkg.com/lmdb-store/-/lmdb-store-0.6.10.tgz#db8efde6e052aabd17ebc63c8a913e1f31694129" + integrity sha512-ZLvp3qbBQ5VlBmaWa4EUAPyYEZ8qdUHsW69HmxkDi84pFQ37WMxYhFaF/7PQkdtxS/vyiKkZigd9TFgHjek1Nw== + dependencies: + fs-extra "^9.0.1" + msgpackr "^0.5.0" + nan "^2.14.1" + node-gyp-build "^4.2.3" + weak-lru-cache "^0.2.0" + load-bmfont@^1.3.1, load-bmfont@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b" @@ -21151,6 +21226,21 @@ ms@2.1.1, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== +msgpackr-extract@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-0.3.4.tgz#8ee5e73d1135340e564c498e8c593134365eb060" + integrity sha512-d3+qwTJzgqqsq2L2sQuH0SoO4StvpUhMqMAKy6tMimn7XdBaRtDlquFzRJsp0iMGt2hnU4UOqD8Tz9mb0KglTA== + dependencies: + nan "^2.14.1" + node-gyp-build "^4.2.3" + +msgpackr@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-0.5.1.tgz#7eecbf342645b7718dd2e3386894368d06732b3f" + integrity sha512-nK2uJl67Q5KU3MWkYBUlYynqKS1UUzJ5M1h6TQejuJtJzD3hW2Suv2T1pf01E9lUEr93xaLokf/xC+jwBShMPQ== + optionalDependencies: + msgpackr-extract "^0.3.4" + multicast-dns-service-types@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" @@ -21489,6 +21579,11 @@ node-forge@^0.10.0, node-forge@^0.7.6: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== +node-gyp-build@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" + integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== + node-gyp@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" @@ -30798,6 +30893,11 @@ wcwidth@^1.0.1: dependencies: defaults "^1.0.3" +weak-lru-cache@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-0.2.0.tgz#447379ccff6dfda1b7a9566c9ef168260be859d1" + integrity sha512-M1l5CzKvM7maa7tCbtL0NW6sOnp8gqup853+9Aq7GL0XNWKNnFOkeE3v3Z5X2IeMzedPwQyPbi4RlFvD6rxs7A== + web-namespaces@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" From 8a8066ffe8f258f5f3ba0f3780dcfc859b8e4852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Kopyci=C5=84ski?= <patryk.kopycinski@elastic.co> Date: Sat, 3 Oct 2020 11:53:03 +0200 Subject: [PATCH 120/128] Cleanup yarn.lock from duplicates (#66617) --- .../basic_optimization.test.ts.snap | 4 +- packages/kbn-pm/dist/index.js | 40130 ++++++++-------- .../header/__snapshots__/header.test.tsx.snap | 272 +- yarn.lock | 2999 +- 4 files changed, 21533 insertions(+), 21872 deletions(-) diff --git a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap index 79442c35df2658..038beca703720c 100644 --- a/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap +++ b/packages/kbn-optimizer/src/integration_tests/__snapshots__/basic_optimization.test.ts.snap @@ -98,7 +98,7 @@ OptimizerConfig { } `; -exports[`prepares assets for distribution: bar bundle 1`] = `"(function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__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};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";return __webpack_require__(__webpack_require__.s=3)})([function(module,exports,__webpack_require__){\\"use strict\\";var isOldIE=function isOldIE(){var memo;return function memorize(){if(typeof memo===\\"undefined\\"){memo=Boolean(window&&document&&document.all&&!window.atob)}return memo}}();var getTarget=function getTarget(){var memo={};return function memorize(target){if(typeof memo[target]===\\"undefined\\"){var styleTarget=document.querySelector(target);if(window.HTMLIFrameElement&&styleTarget instanceof window.HTMLIFrameElement){try{styleTarget=styleTarget.contentDocument.head}catch(e){styleTarget=null}}memo[target]=styleTarget}return memo[target]}}();var stylesInDom=[];function getIndexByIdentifier(identifier){var result=-1;for(var i=0;i<stylesInDom.length;i++){if(stylesInDom[i].identifier===identifier){result=i;break}}return result}function modulesToDom(list,options){var idCountMap={};var identifiers=[];for(var i=0;i<list.length;i++){var item=list[i];var id=options.base?item[0]+options.base:item[0];var count=idCountMap[id]||0;var identifier=\\"\\".concat(id,\\" \\").concat(count);idCountMap[id]=count+1;var index=getIndexByIdentifier(identifier);var obj={css:item[1],media:item[2],sourceMap:item[3]};if(index!==-1){stylesInDom[index].references++;stylesInDom[index].updater(obj)}else{stylesInDom.push({identifier:identifier,updater:addStyle(obj,options),references:1})}identifiers.push(identifier)}return identifiers}function insertStyleElement(options){var style=document.createElement(\\"style\\");var attributes=options.attributes||{};if(typeof attributes.nonce===\\"undefined\\"){var nonce=true?__webpack_require__.nc:undefined;if(nonce){attributes.nonce=nonce}}Object.keys(attributes).forEach((function(key){style.setAttribute(key,attributes[key])}));if(typeof options.insert===\\"function\\"){options.insert(style)}else{var target=getTarget(options.insert||\\"head\\");if(!target){throw new Error(\\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\\")}target.appendChild(style)}return style}function removeStyleElement(style){if(style.parentNode===null){return false}style.parentNode.removeChild(style)}var replaceText=function replaceText(){var textStore=[];return function replace(index,replacement){textStore[index]=replacement;return textStore.filter(Boolean).join(\\"\\\\n\\")}}();function applyToSingletonTag(style,index,remove,obj){var css=remove?\\"\\":obj.media?\\"@media \\".concat(obj.media,\\" {\\").concat(obj.css,\\"}\\"):obj.css;if(style.styleSheet){style.styleSheet.cssText=replaceText(index,css)}else{var cssNode=document.createTextNode(css);var childNodes=style.childNodes;if(childNodes[index]){style.removeChild(childNodes[index])}if(childNodes.length){style.insertBefore(cssNode,childNodes[index])}else{style.appendChild(cssNode)}}}function applyToTag(style,options,obj){var css=obj.css;var media=obj.media;var sourceMap=obj.sourceMap;if(media){style.setAttribute(\\"media\\",media)}else{style.removeAttribute(\\"media\\")}if(sourceMap&&btoa){css+=\\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))),\\" */\\")}if(style.styleSheet){style.styleSheet.cssText=css}else{while(style.firstChild){style.removeChild(style.firstChild)}style.appendChild(document.createTextNode(css))}}var singleton=null;var singletonCounter=0;function addStyle(obj,options){var style;var update;var remove;if(options.singleton){var styleIndex=singletonCounter++;style=singleton||(singleton=insertStyleElement(options));update=applyToSingletonTag.bind(null,style,styleIndex,false);remove=applyToSingletonTag.bind(null,style,styleIndex,true)}else{style=insertStyleElement(options);update=applyToTag.bind(null,style,options);remove=function remove(){removeStyleElement(style)}}update(obj);return function updateStyle(newObj){if(newObj){if(newObj.css===obj.css&&newObj.media===obj.media&&newObj.sourceMap===obj.sourceMap){return}update(obj=newObj)}else{remove()}}}module.exports=function(list,options){options=options||{};if(!options.singleton&&typeof options.singleton!==\\"boolean\\"){options.singleton=isOldIE()}list=list||[];var lastIdentifiers=modulesToDom(list,options);return function update(newList){newList=newList||[];if(Object.prototype.toString.call(newList)!==\\"[object Array]\\"){return}for(var i=0;i<lastIdentifiers.length;i++){var identifier=lastIdentifiers[i];var index=getIndexByIdentifier(identifier);stylesInDom[index].references--}var newLastIdentifiers=modulesToDom(newList,options);for(var _i=0;_i<lastIdentifiers.length;_i++){var _identifier=lastIdentifiers[_i];var _index=getIndexByIdentifier(_identifier);if(stylesInDom[_index].references===0){stylesInDom[_index].updater();stylesInDom.splice(_index,1)}}lastIdentifiers=newLastIdentifiers}}},function(module,exports,__webpack_require__){\\"use strict\\";module.exports=function(useSourceMap){var list=[];list.toString=function toString(){return this.map((function(item){var content=cssWithMappingToString(item,useSourceMap);if(item[2]){return\\"@media \\".concat(item[2],\\" {\\").concat(content,\\"}\\")}return content})).join(\\"\\")};list.i=function(modules,mediaQuery,dedupe){if(typeof modules===\\"string\\"){modules=[[null,modules,\\"\\"]]}var alreadyImportedModules={};if(dedupe){for(var i=0;i<this.length;i++){var id=this[i][0];if(id!=null){alreadyImportedModules[id]=true}}}for(var _i=0;_i<modules.length;_i++){var item=[].concat(modules[_i]);if(dedupe&&alreadyImportedModules[item[0]]){continue}if(mediaQuery){if(!item[2]){item[2]=mediaQuery}else{item[2]=\\"\\".concat(mediaQuery,\\" and \\").concat(item[2])}}list.push(item)}};return list};function cssWithMappingToString(item,useSourceMap){var content=item[1]||\\"\\";var cssMapping=item[3];if(!cssMapping){return content}if(useSourceMap&&typeof btoa===\\"function\\"){var sourceMapping=toComment(cssMapping);var sourceURLs=cssMapping.sources.map((function(source){return\\"/*# sourceURL=\\".concat(cssMapping.sourceRoot||\\"\\").concat(source,\\" */\\")}));return[content].concat(sourceURLs).concat([sourceMapping]).join(\\"\\\\n\\")}return[content].join(\\"\\\\n\\")}function toComment(sourceMap){var base64=btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));var data=\\"sourceMappingURL=data:application/json;charset=utf-8;base64,\\".concat(base64);return\\"/*# \\".concat(data,\\" */\\")}},function(module,__webpack_exports__,__webpack_require__){__webpack_require__.r(__webpack_exports__);var ns=__kbnBundles__.get(\\"plugin/foo/public\\");Object.defineProperties(__webpack_exports__,Object.getOwnPropertyDescriptors(ns))},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_bar_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(4);var _node_modules_val_loader_dist_cjs_js_key_bar_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_bar_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__kbnBundles__.define(\\"plugin/bar/public\\",__webpack_require__,23)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__kbnPublicPath__[\\"bar\\"]},function(module,exports,__webpack_require__){switch(window.__kbnThemeTag__){case\\"v7dark\\":return __webpack_require__(6);case\\"v7light\\":return __webpack_require__(8);case\\"v8dark\\":return __webpack_require__(10);case\\"v8light\\":return __webpack_require__(12)}},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(7);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 10; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(9);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 11; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(11);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 12; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(13);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 13; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){switch(window.__kbnThemeTag__){case\\"v7dark\\":return __webpack_require__(15);case\\"v7light\\":return __webpack_require__(17);case\\"v8dark\\":return __webpack_require__(19);case\\"v8light\\":return __webpack_require__(21)}},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(16);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(18);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(20);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(22);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);var exported=content.locals?content.locals:{};module.exports=exported},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var styles=__webpack_require__(5);var public_0=__webpack_require__(14);var public_=__webpack_require__(2);function barLibFn(){return\\"bar\\"}__webpack_require__.d(__webpack_exports__,\\"barLibFn\\",(function(){return barLibFn}));__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return public_[\\"fooLibFn\\"]}))}]);"`; +exports[`prepares assets for distribution: bar bundle 1`] = `"(function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__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};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";return __webpack_require__(__webpack_require__.s=3)})([function(module,exports,__webpack_require__){\\"use strict\\";var isOldIE=function isOldIE(){var memo;return function memorize(){if(typeof memo===\\"undefined\\"){memo=Boolean(window&&document&&document.all&&!window.atob)}return memo}}();var getTarget=function getTarget(){var memo={};return function memorize(target){if(typeof memo[target]===\\"undefined\\"){var styleTarget=document.querySelector(target);if(window.HTMLIFrameElement&&styleTarget instanceof window.HTMLIFrameElement){try{styleTarget=styleTarget.contentDocument.head}catch(e){styleTarget=null}}memo[target]=styleTarget}return memo[target]}}();var stylesInDom=[];function getIndexByIdentifier(identifier){var result=-1;for(var i=0;i<stylesInDom.length;i++){if(stylesInDom[i].identifier===identifier){result=i;break}}return result}function modulesToDom(list,options){var idCountMap={};var identifiers=[];for(var i=0;i<list.length;i++){var item=list[i];var id=options.base?item[0]+options.base:item[0];var count=idCountMap[id]||0;var identifier=\\"\\".concat(id,\\" \\").concat(count);idCountMap[id]=count+1;var index=getIndexByIdentifier(identifier);var obj={css:item[1],media:item[2],sourceMap:item[3]};if(index!==-1){stylesInDom[index].references++;stylesInDom[index].updater(obj)}else{stylesInDom.push({identifier:identifier,updater:addStyle(obj,options),references:1})}identifiers.push(identifier)}return identifiers}function insertStyleElement(options){var style=document.createElement(\\"style\\");var attributes=options.attributes||{};if(typeof attributes.nonce===\\"undefined\\"){var nonce=true?__webpack_require__.nc:undefined;if(nonce){attributes.nonce=nonce}}Object.keys(attributes).forEach((function(key){style.setAttribute(key,attributes[key])}));if(typeof options.insert===\\"function\\"){options.insert(style)}else{var target=getTarget(options.insert||\\"head\\");if(!target){throw new Error(\\"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.\\")}target.appendChild(style)}return style}function removeStyleElement(style){if(style.parentNode===null){return false}style.parentNode.removeChild(style)}var replaceText=function replaceText(){var textStore=[];return function replace(index,replacement){textStore[index]=replacement;return textStore.filter(Boolean).join(\\"\\\\n\\")}}();function applyToSingletonTag(style,index,remove,obj){var css=remove?\\"\\":obj.media?\\"@media \\".concat(obj.media,\\" {\\").concat(obj.css,\\"}\\"):obj.css;if(style.styleSheet){style.styleSheet.cssText=replaceText(index,css)}else{var cssNode=document.createTextNode(css);var childNodes=style.childNodes;if(childNodes[index]){style.removeChild(childNodes[index])}if(childNodes.length){style.insertBefore(cssNode,childNodes[index])}else{style.appendChild(cssNode)}}}function applyToTag(style,options,obj){var css=obj.css;var media=obj.media;var sourceMap=obj.sourceMap;if(media){style.setAttribute(\\"media\\",media)}else{style.removeAttribute(\\"media\\")}if(sourceMap&&btoa){css+=\\"\\\\n/*# sourceMappingURL=data:application/json;base64,\\".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))),\\" */\\")}if(style.styleSheet){style.styleSheet.cssText=css}else{while(style.firstChild){style.removeChild(style.firstChild)}style.appendChild(document.createTextNode(css))}}var singleton=null;var singletonCounter=0;function addStyle(obj,options){var style;var update;var remove;if(options.singleton){var styleIndex=singletonCounter++;style=singleton||(singleton=insertStyleElement(options));update=applyToSingletonTag.bind(null,style,styleIndex,false);remove=applyToSingletonTag.bind(null,style,styleIndex,true)}else{style=insertStyleElement(options);update=applyToTag.bind(null,style,options);remove=function remove(){removeStyleElement(style)}}update(obj);return function updateStyle(newObj){if(newObj){if(newObj.css===obj.css&&newObj.media===obj.media&&newObj.sourceMap===obj.sourceMap){return}update(obj=newObj)}else{remove()}}}module.exports=function(list,options){options=options||{};if(!options.singleton&&typeof options.singleton!==\\"boolean\\"){options.singleton=isOldIE()}list=list||[];var lastIdentifiers=modulesToDom(list,options);return function update(newList){newList=newList||[];if(Object.prototype.toString.call(newList)!==\\"[object Array]\\"){return}for(var i=0;i<lastIdentifiers.length;i++){var identifier=lastIdentifiers[i];var index=getIndexByIdentifier(identifier);stylesInDom[index].references--}var newLastIdentifiers=modulesToDom(newList,options);for(var _i=0;_i<lastIdentifiers.length;_i++){var _identifier=lastIdentifiers[_i];var _index=getIndexByIdentifier(_identifier);if(stylesInDom[_index].references===0){stylesInDom[_index].updater();stylesInDom.splice(_index,1)}}lastIdentifiers=newLastIdentifiers}}},function(module,exports,__webpack_require__){\\"use strict\\";module.exports=function(useSourceMap){var list=[];list.toString=function toString(){return this.map((function(item){var content=cssWithMappingToString(item,useSourceMap);if(item[2]){return\\"@media \\".concat(item[2],\\" {\\").concat(content,\\"}\\")}return content})).join(\\"\\")};list.i=function(modules,mediaQuery,dedupe){if(typeof modules===\\"string\\"){modules=[[null,modules,\\"\\"]]}var alreadyImportedModules={};if(dedupe){for(var i=0;i<this.length;i++){var id=this[i][0];if(id!=null){alreadyImportedModules[id]=true}}}for(var _i=0;_i<modules.length;_i++){var item=[].concat(modules[_i]);if(dedupe&&alreadyImportedModules[item[0]]){continue}if(mediaQuery){if(!item[2]){item[2]=mediaQuery}else{item[2]=\\"\\".concat(mediaQuery,\\" and \\").concat(item[2])}}list.push(item)}};return list};function cssWithMappingToString(item,useSourceMap){var content=item[1]||\\"\\";var cssMapping=item[3];if(!cssMapping){return content}if(useSourceMap&&typeof btoa===\\"function\\"){var sourceMapping=toComment(cssMapping);var sourceURLs=cssMapping.sources.map((function(source){return\\"/*# sourceURL=\\".concat(cssMapping.sourceRoot||\\"\\").concat(source,\\" */\\")}));return[content].concat(sourceURLs).concat([sourceMapping]).join(\\"\\\\n\\")}return[content].join(\\"\\\\n\\")}function toComment(sourceMap){var base64=btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));var data=\\"sourceMappingURL=data:application/json;charset=utf-8;base64,\\".concat(base64);return\\"/*# \\".concat(data,\\" */\\")}},function(module,__webpack_exports__,__webpack_require__){__webpack_require__.r(__webpack_exports__);var ns=__kbnBundles__.get(\\"plugin/foo/public\\");Object.defineProperties(__webpack_exports__,Object.getOwnPropertyDescriptors(ns))},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_bar_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(4);var _node_modules_val_loader_dist_cjs_js_key_bar_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_bar_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__kbnBundles__.define(\\"plugin/bar/public\\",__webpack_require__,23)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__kbnPublicPath__[\\"bar\\"]},function(module,exports,__webpack_require__){switch(window.__kbnThemeTag__){case\\"v7dark\\":return __webpack_require__(6);case\\"v7light\\":return __webpack_require__(8);case\\"v8dark\\":return __webpack_require__(10);case\\"v8light\\":return __webpack_require__(12)}},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(7);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 10; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(9);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 11; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(11);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 12; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(13);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"p {\\\\n background-color: rebeccapurple; }\\\\n\\\\nbody {\\\\n width: 13; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){switch(window.__kbnThemeTag__){case\\"v7dark\\":return __webpack_require__(15);case\\"v7light\\":return __webpack_require__(17);case\\"v8dark\\":return __webpack_require__(19);case\\"v8light\\":return __webpack_require__(21)}},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(16);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(18);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(20);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,exports,__webpack_require__){var api=__webpack_require__(0);var content=__webpack_require__(22);content=content.__esModule?content.default:content;if(typeof content===\\"string\\"){content=[[module.i,content,\\"\\"]]}var options={};options.insert=\\"head\\";options.singleton=false;var update=api(content,options);module.exports=content.locals||{}},function(module,exports,__webpack_require__){var ___CSS_LOADER_API_IMPORT___=__webpack_require__(1);exports=___CSS_LOADER_API_IMPORT___(false);exports.push([module.i,\\"body {\\\\n color: green; }\\\\n\\",\\"\\"]);module.exports=exports},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"barLibFn\\",(function(){return barLibFn}));__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return public_[\\"fooLibFn\\"]}));var styles=__webpack_require__(5);var public_0=__webpack_require__(14);var public_=__webpack_require__(2);function barLibFn(){return\\"bar\\"}}]);"`; exports[`prepares assets for distribution: baz bundle 1`] = ` "/*! Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one or more contributor license agreements. @@ -107,4 +107,4 @@ exports[`prepares assets for distribution: baz bundle 1`] = ` exports[`prepares assets for distribution: foo async bundle 1`] = `"(window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[]).push([[1],{3:function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"foo\\",(function(){return foo}));function foo(){}}}]);"`; -exports[`prepares assets for distribution: foo bundle 1`] = `"(function(modules){function webpackJsonpCallback(data){var chunkIds=data[0];var moreModules=data[1];var moduleId,chunkId,i=0,resolves=[];for(;i<chunkIds.length;i++){chunkId=chunkIds[i];if(Object.prototype.hasOwnProperty.call(installedChunks,chunkId)&&installedChunks[chunkId]){resolves.push(installedChunks[chunkId][0])}installedChunks[chunkId]=0}for(moduleId in moreModules){if(Object.prototype.hasOwnProperty.call(moreModules,moduleId)){modules[moduleId]=moreModules[moduleId]}}if(parentJsonpFunction)parentJsonpFunction(data);while(resolves.length){resolves.shift()()}}var installedModules={};var installedChunks={0:0};function jsonpScriptSrc(chunkId){return __webpack_require__.p+\\"foo.chunk.\\"+chunkId+\\".js\\"}function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.e=function requireEnsure(chunkId){var promises=[];var installedChunkData=installedChunks[chunkId];if(installedChunkData!==0){if(installedChunkData){promises.push(installedChunkData[2])}else{var promise=new Promise((function(resolve,reject){installedChunkData=installedChunks[chunkId]=[resolve,reject]}));promises.push(installedChunkData[2]=promise);var script=document.createElement(\\"script\\");var onScriptComplete;script.charset=\\"utf-8\\";script.timeout=120;if(__webpack_require__.nc){script.setAttribute(\\"nonce\\",__webpack_require__.nc)}script.src=jsonpScriptSrc(chunkId);var error=new Error;onScriptComplete=function(event){script.onerror=script.onload=null;clearTimeout(timeout);var chunk=installedChunks[chunkId];if(chunk!==0){if(chunk){var errorType=event&&(event.type===\\"load\\"?\\"missing\\":event.type);var realSrc=event&&event.target&&event.target.src;error.message=\\"Loading chunk \\"+chunkId+\\" failed.\\\\n(\\"+errorType+\\": \\"+realSrc+\\")\\";error.name=\\"ChunkLoadError\\";error.type=errorType;error.request=realSrc;chunk[1](error)}installedChunks[chunkId]=undefined}};var timeout=setTimeout((function(){onScriptComplete({type:\\"timeout\\",target:script})}),12e4);script.onerror=script.onload=onScriptComplete;document.head.appendChild(script)}}return Promise.all(promises)};__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__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};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";__webpack_require__.oe=function(err){console.error(err);throw err};var jsonpArray=window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[];var oldJsonpFunction=jsonpArray.push.bind(jsonpArray);jsonpArray.push=webpackJsonpCallback;jsonpArray=jsonpArray.slice();for(var i=0;i<jsonpArray.length;i++)webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction=oldJsonpFunction;return __webpack_require__(__webpack_require__.s=0)})([function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__kbnBundles__.define(\\"plugin/foo/public\\",__webpack_require__,2)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__kbnPublicPath__[\\"foo\\"]},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);function fooLibFn(){return\\"foo\\"}var ext=\\"TRUE\\";__webpack_require__.d(__webpack_exports__,\\"getFoo\\",(function(){return getFoo}));__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return fooLibFn}));__webpack_require__.d(__webpack_exports__,\\"ext\\",(function(){return ext}));function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{Promise.resolve(value).then(_next,_throw)}}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,\\"next\\",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,\\"throw\\",err)}_next(undefined)}))}}function getFoo(){return _getFoo.apply(this,arguments)}function _getFoo(){_getFoo=_asyncToGenerator(regeneratorRuntime.mark((function _callee(){return regeneratorRuntime.wrap((function _callee$(_context){while(1){switch(_context.prev=_context.next){case 0:_context.next=2;return __webpack_require__.e(1).then(__webpack_require__.bind(null,3));case 2:return _context.abrupt(\\"return\\",_context.sent);case 3:case\\"end\\":return _context.stop()}}}),_callee)})));return _getFoo.apply(this,arguments)}}]);"`; +exports[`prepares assets for distribution: foo bundle 1`] = `"(function(modules){function webpackJsonpCallback(data){var chunkIds=data[0];var moreModules=data[1];var moduleId,chunkId,i=0,resolves=[];for(;i<chunkIds.length;i++){chunkId=chunkIds[i];if(Object.prototype.hasOwnProperty.call(installedChunks,chunkId)&&installedChunks[chunkId]){resolves.push(installedChunks[chunkId][0])}installedChunks[chunkId]=0}for(moduleId in moreModules){if(Object.prototype.hasOwnProperty.call(moreModules,moduleId)){modules[moduleId]=moreModules[moduleId]}}if(parentJsonpFunction)parentJsonpFunction(data);while(resolves.length){resolves.shift()()}}var installedModules={};var installedChunks={0:0};function jsonpScriptSrc(chunkId){return __webpack_require__.p+\\"foo.chunk.\\"+chunkId+\\".js\\"}function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.e=function requireEnsure(chunkId){var promises=[];var installedChunkData=installedChunks[chunkId];if(installedChunkData!==0){if(installedChunkData){promises.push(installedChunkData[2])}else{var promise=new Promise((function(resolve,reject){installedChunkData=installedChunks[chunkId]=[resolve,reject]}));promises.push(installedChunkData[2]=promise);var script=document.createElement(\\"script\\");var onScriptComplete;script.charset=\\"utf-8\\";script.timeout=120;if(__webpack_require__.nc){script.setAttribute(\\"nonce\\",__webpack_require__.nc)}script.src=jsonpScriptSrc(chunkId);var error=new Error;onScriptComplete=function(event){script.onerror=script.onload=null;clearTimeout(timeout);var chunk=installedChunks[chunkId];if(chunk!==0){if(chunk){var errorType=event&&(event.type===\\"load\\"?\\"missing\\":event.type);var realSrc=event&&event.target&&event.target.src;error.message=\\"Loading chunk \\"+chunkId+\\" failed.\\\\n(\\"+errorType+\\": \\"+realSrc+\\")\\";error.name=\\"ChunkLoadError\\";error.type=errorType;error.request=realSrc;chunk[1](error)}installedChunks[chunkId]=undefined}};var timeout=setTimeout((function(){onScriptComplete({type:\\"timeout\\",target:script})}),12e4);script.onerror=script.onload=onScriptComplete;document.head.appendChild(script)}}return Promise.all(promises)};__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{enumerable:true,get:getter})}};__webpack_require__.r=function(exports){if(typeof Symbol!==\\"undefined\\"&&Symbol.toStringTag){Object.defineProperty(exports,Symbol.toStringTag,{value:\\"Module\\"})}Object.defineProperty(exports,\\"__esModule\\",{value:true})};__webpack_require__.t=function(value,mode){if(mode&1)value=__webpack_require__(value);if(mode&8)return value;if(mode&4&&typeof value===\\"object\\"&&value&&value.__esModule)return value;var ns=Object.create(null);__webpack_require__.r(ns);Object.defineProperty(ns,\\"default\\",{enumerable:true,value:value});if(mode&2&&typeof value!=\\"string\\")for(var key in value)__webpack_require__.d(ns,key,function(key){return value[key]}.bind(null,key));return ns};__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};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p=\\"\\";__webpack_require__.oe=function(err){console.error(err);throw err};var jsonpArray=window[\\"foo_bundle_jsonpfunction\\"]=window[\\"foo_bundle_jsonpfunction\\"]||[];var oldJsonpFunction=jsonpArray.push.bind(jsonpArray);jsonpArray.push=webpackJsonpCallback;jsonpArray=jsonpArray.slice();for(var i=0;i<jsonpArray.length;i++)webpackJsonpCallback(jsonpArray[i]);var parentJsonpFunction=oldJsonpFunction;return __webpack_require__(__webpack_require__.s=0)})([function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1);var _node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_node_modules_val_loader_dist_cjs_js_key_foo_kbn_ui_shared_deps_public_path_module_creator_js__WEBPACK_IMPORTED_MODULE_0__);__kbnBundles__.define(\\"plugin/foo/public\\",__webpack_require__,2)},function(module,exports,__webpack_require__){__webpack_require__.p=window.__kbnPublicPath__[\\"foo\\"]},function(module,__webpack_exports__,__webpack_require__){\\"use strict\\";__webpack_require__.r(__webpack_exports__);__webpack_require__.d(__webpack_exports__,\\"fooLibFn\\",(function(){return fooLibFn}));__webpack_require__.d(__webpack_exports__,\\"ext\\",(function(){return ext}));__webpack_require__.d(__webpack_exports__,\\"getFoo\\",(function(){return getFoo}));function fooLibFn(){return\\"foo\\"}var ext=\\"TRUE\\";function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{Promise.resolve(value).then(_next,_throw)}}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,\\"next\\",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,\\"throw\\",err)}_next(undefined)}))}}function getFoo(){return _getFoo.apply(this,arguments)}function _getFoo(){_getFoo=_asyncToGenerator(regeneratorRuntime.mark((function _callee(){return regeneratorRuntime.wrap((function _callee$(_context){while(1){switch(_context.prev=_context.next){case 0:_context.next=2;return __webpack_require__.e(1).then(__webpack_require__.bind(null,3));case 2:return _context.abrupt(\\"return\\",_context.sent);case 3:case\\"end\\":return _context.stop()}}}),_callee)})));return _getFoo.apply(this,arguments)}}]);"`; diff --git a/packages/kbn-pm/dist/index.js b/packages/kbn-pm/dist/index.js index 2882a72b3ac9d8..48dba22505232a 100644 --- a/packages/kbn-pm/dist/index.js +++ b/packages/kbn-pm/dist/index.js @@ -94,19 +94,19 @@ __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__(500); +/* harmony import */ var _production__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(507); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _production__WEBPACK_IMPORTED_MODULE_1__["buildProductionProjects"]; }); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(146); /* 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__(163); +/* harmony import */ var _utils_project__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(164); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Project", function() { return _utils_project__WEBPACK_IMPORTED_MODULE_3__["Project"]; }); -/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(270); +/* harmony import */ var _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(271); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return _utils_workspaces__WEBPACK_IMPORTED_MODULE_4__["copyWorkspacePackages"]; }); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(271); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(272); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getProjectPaths", function() { return _config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"]; }); /* @@ -149,9 +149,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); /* 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__(127); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(493); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(143); +/* harmony import */ var _commands__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(128); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(500); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(144); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -565,12 +565,12 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.ToolingLogCollectingWriter = exports.parseLogLevel = exports.pickLevelFromFlags = exports.ToolingLogTextWriter = exports.ToolingLog = void 0; var tooling_log_1 = __webpack_require__(6); Object.defineProperty(exports, "ToolingLog", { enumerable: true, get: function () { return tooling_log_1.ToolingLog; } }); -var tooling_log_text_writer_1 = __webpack_require__(110); +var tooling_log_text_writer_1 = __webpack_require__(111); Object.defineProperty(exports, "ToolingLogTextWriter", { enumerable: true, get: function () { return tooling_log_text_writer_1.ToolingLogTextWriter; } }); -var log_levels_1 = __webpack_require__(125); +var log_levels_1 = __webpack_require__(126); Object.defineProperty(exports, "pickLevelFromFlags", { enumerable: true, get: function () { return log_levels_1.pickLevelFromFlags; } }); Object.defineProperty(exports, "parseLogLevel", { enumerable: true, get: function () { return log_levels_1.parseLogLevel; } }); -var tooling_log_collecting_writer_1 = __webpack_require__(126); +var tooling_log_collecting_writer_1 = __webpack_require__(127); Object.defineProperty(exports, "ToolingLogCollectingWriter", { enumerable: true, get: function () { return tooling_log_collecting_writer_1.ToolingLogCollectingWriter; } }); @@ -602,7 +602,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.ToolingLog = void 0; const tslib_1 = __webpack_require__(7); const Rx = tslib_1.__importStar(__webpack_require__(8)); -const tooling_log_text_writer_1 = __webpack_require__(110); +const tooling_log_text_writer_1 = __webpack_require__(111); class ToolingLog { constructor(writerConfig) { this.identWidth = 0; @@ -711,7 +711,7 @@ PERFORMANCE OF THIS SOFTWARE. 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]; }; + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; @@ -805,8 +805,8 @@ var __createBinding = Object.create ? (function(o, m, k, k2) { o[k2] = m[k]; }); -function __exportStar(m, exports) { - for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); +function __exportStar(m, o) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p); } function __values(o) { @@ -896,7 +896,7 @@ var __setModuleDefault = Object.create ? (function(o, v) { function __importStar(mod) { if (mod && mod.__esModule) return mod; var result = {}; - if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; } @@ -952,16 +952,24 @@ __webpack_require__.r(__webpack_exports__); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "AsyncSubject", function() { return _internal_AsyncSubject__WEBPACK_IMPORTED_MODULE_7__["AsyncSubject"]; }); /* harmony import */ var _internal_scheduler_asap__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(51); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "asapScheduler", function() { return _internal_scheduler_asap__WEBPACK_IMPORTED_MODULE_8__["asap"]; }); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "asap", function() { return _internal_scheduler_asap__WEBPACK_IMPORTED_MODULE_8__["asap"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "asapScheduler", function() { return _internal_scheduler_asap__WEBPACK_IMPORTED_MODULE_8__["asapScheduler"]; }); /* harmony import */ var _internal_scheduler_async__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(55); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "asyncScheduler", function() { return _internal_scheduler_async__WEBPACK_IMPORTED_MODULE_9__["async"]; }); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "async", function() { return _internal_scheduler_async__WEBPACK_IMPORTED_MODULE_9__["async"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "asyncScheduler", function() { return _internal_scheduler_async__WEBPACK_IMPORTED_MODULE_9__["asyncScheduler"]; }); /* harmony import */ var _internal_scheduler_queue__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(34); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "queueScheduler", function() { return _internal_scheduler_queue__WEBPACK_IMPORTED_MODULE_10__["queue"]; }); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "queue", function() { return _internal_scheduler_queue__WEBPACK_IMPORTED_MODULE_10__["queue"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "queueScheduler", function() { return _internal_scheduler_queue__WEBPACK_IMPORTED_MODULE_10__["queueScheduler"]; }); /* harmony import */ var _internal_scheduler_animationFrame__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(56); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "animationFrameScheduler", function() { return _internal_scheduler_animationFrame__WEBPACK_IMPORTED_MODULE_11__["animationFrame"]; }); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "animationFrame", function() { return _internal_scheduler_animationFrame__WEBPACK_IMPORTED_MODULE_11__["animationFrame"]; }); + +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "animationFrameScheduler", function() { return _internal_scheduler_animationFrame__WEBPACK_IMPORTED_MODULE_11__["animationFrameScheduler"]; }); /* harmony import */ var _internal_scheduler_VirtualTimeScheduler__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(59); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "VirtualTimeScheduler", function() { return _internal_scheduler_VirtualTimeScheduler__WEBPACK_IMPORTED_MODULE_12__["VirtualTimeScheduler"]; }); @@ -1021,67 +1029,67 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _internal_observable_concat__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(79); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_observable_concat__WEBPACK_IMPORTED_MODULE_29__["concat"]; }); -/* harmony import */ var _internal_observable_defer__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(90); +/* harmony import */ var _internal_observable_defer__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(91); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "defer", function() { return _internal_observable_defer__WEBPACK_IMPORTED_MODULE_30__["defer"]; }); /* harmony import */ var _internal_observable_empty__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(43); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "empty", function() { return _internal_observable_empty__WEBPACK_IMPORTED_MODULE_31__["empty"]; }); -/* harmony import */ var _internal_observable_forkJoin__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(91); +/* harmony import */ var _internal_observable_forkJoin__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(92); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "forkJoin", function() { return _internal_observable_forkJoin__WEBPACK_IMPORTED_MODULE_32__["forkJoin"]; }); /* harmony import */ var _internal_observable_from__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(83); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "from", function() { return _internal_observable_from__WEBPACK_IMPORTED_MODULE_33__["from"]; }); -/* harmony import */ var _internal_observable_fromEvent__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(92); +/* harmony import */ var _internal_observable_fromEvent__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(93); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "fromEvent", function() { return _internal_observable_fromEvent__WEBPACK_IMPORTED_MODULE_34__["fromEvent"]; }); -/* harmony import */ var _internal_observable_fromEventPattern__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(93); +/* harmony import */ var _internal_observable_fromEventPattern__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(94); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "fromEventPattern", function() { return _internal_observable_fromEventPattern__WEBPACK_IMPORTED_MODULE_35__["fromEventPattern"]; }); -/* harmony import */ var _internal_observable_generate__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(94); +/* harmony import */ var _internal_observable_generate__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(95); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "generate", function() { return _internal_observable_generate__WEBPACK_IMPORTED_MODULE_36__["generate"]; }); -/* harmony import */ var _internal_observable_iif__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(95); +/* harmony import */ var _internal_observable_iif__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(96); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "iif", function() { return _internal_observable_iif__WEBPACK_IMPORTED_MODULE_37__["iif"]; }); -/* harmony import */ var _internal_observable_interval__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(96); +/* harmony import */ var _internal_observable_interval__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(97); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "interval", function() { return _internal_observable_interval__WEBPACK_IMPORTED_MODULE_38__["interval"]; }); -/* harmony import */ var _internal_observable_merge__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(98); +/* harmony import */ var _internal_observable_merge__WEBPACK_IMPORTED_MODULE_39__ = __webpack_require__(99); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_observable_merge__WEBPACK_IMPORTED_MODULE_39__["merge"]; }); -/* harmony import */ var _internal_observable_never__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(99); +/* harmony import */ var _internal_observable_never__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(100); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "never", function() { return _internal_observable_never__WEBPACK_IMPORTED_MODULE_40__["never"]; }); /* harmony import */ var _internal_observable_of__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(44); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "of", function() { return _internal_observable_of__WEBPACK_IMPORTED_MODULE_41__["of"]; }); -/* harmony import */ var _internal_observable_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(100); +/* harmony import */ var _internal_observable_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(101); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_observable_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_42__["onErrorResumeNext"]; }); -/* harmony import */ var _internal_observable_pairs__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(101); +/* harmony import */ var _internal_observable_pairs__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(102); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairs", function() { return _internal_observable_pairs__WEBPACK_IMPORTED_MODULE_43__["pairs"]; }); -/* harmony import */ var _internal_observable_partition__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(102); +/* harmony import */ var _internal_observable_partition__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(103); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_observable_partition__WEBPACK_IMPORTED_MODULE_44__["partition"]; }); -/* harmony import */ var _internal_observable_race__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(105); +/* harmony import */ var _internal_observable_race__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(106); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_observable_race__WEBPACK_IMPORTED_MODULE_45__["race"]; }); -/* harmony import */ var _internal_observable_range__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(106); +/* harmony import */ var _internal_observable_range__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(107); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "range", function() { return _internal_observable_range__WEBPACK_IMPORTED_MODULE_46__["range"]; }); /* harmony import */ var _internal_observable_throwError__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(49); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwError", function() { return _internal_observable_throwError__WEBPACK_IMPORTED_MODULE_47__["throwError"]; }); -/* harmony import */ var _internal_observable_timer__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(107); +/* harmony import */ var _internal_observable_timer__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(108); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timer", function() { return _internal_observable_timer__WEBPACK_IMPORTED_MODULE_48__["timer"]; }); -/* harmony import */ var _internal_observable_using__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(108); +/* harmony import */ var _internal_observable_using__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(109); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "using", function() { return _internal_observable_using__WEBPACK_IMPORTED_MODULE_49__["using"]; }); -/* harmony import */ var _internal_observable_zip__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(109); +/* harmony import */ var _internal_observable_zip__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(110); /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "zip", function() { return _internal_observable_zip__WEBPACK_IMPORTED_MODULE_50__["zip"]; }); /* harmony import */ var _internal_scheduled_scheduled__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(84); @@ -1916,6 +1924,7 @@ var Subscription = /*@__PURE__*/ (function () { this._parentOrParents = null; this._subscriptions = null; if (unsubscribe) { + this._ctorUnsubscribe = true; this._unsubscribe = unsubscribe; } } @@ -1924,7 +1933,7 @@ var Subscription = /*@__PURE__*/ (function () { if (this.closed) { return; } - var _a = this, _parentOrParents = _a._parentOrParents, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; + var _a = this, _parentOrParents = _a._parentOrParents, _ctorUnsubscribe = _a._ctorUnsubscribe, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; this.closed = true; this._parentOrParents = null; this._subscriptions = null; @@ -1938,6 +1947,9 @@ var Subscription = /*@__PURE__*/ (function () { } } if (Object(_util_isFunction__WEBPACK_IMPORTED_MODULE_2__["isFunction"])(_unsubscribe)) { + if (_ctorUnsubscribe) { + this._unsubscribe = undefined; + } try { _unsubscribe.call(this); } @@ -3059,13 +3071,15 @@ var ReplayEvent = /*@__PURE__*/ (function () { "use strict"; __webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "queueScheduler", function() { return queueScheduler; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "queue", function() { return queue; }); /* harmony import */ var _QueueAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(35); /* harmony import */ var _QueueScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(38); /** PURE_IMPORTS_START _QueueAction,_QueueScheduler PURE_IMPORTS_END */ -var queue = /*@__PURE__*/ new _QueueScheduler__WEBPACK_IMPORTED_MODULE_1__["QueueScheduler"](_QueueAction__WEBPACK_IMPORTED_MODULE_0__["QueueAction"]); +var queueScheduler = /*@__PURE__*/ new _QueueScheduler__WEBPACK_IMPORTED_MODULE_1__["QueueScheduler"](_QueueAction__WEBPACK_IMPORTED_MODULE_0__["QueueAction"]); +var queue = queueScheduler; //# sourceMappingURL=queue.js.map @@ -3781,13 +3795,15 @@ var AsyncSubject = /*@__PURE__*/ (function (_super) { "use strict"; __webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "asapScheduler", function() { return asapScheduler; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "asap", function() { return asap; }); /* harmony import */ var _AsapAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(52); /* harmony import */ var _AsapScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(54); /** PURE_IMPORTS_START _AsapAction,_AsapScheduler PURE_IMPORTS_END */ -var asap = /*@__PURE__*/ new _AsapScheduler__WEBPACK_IMPORTED_MODULE_1__["AsapScheduler"](_AsapAction__WEBPACK_IMPORTED_MODULE_0__["AsapAction"]); +var asapScheduler = /*@__PURE__*/ new _AsapScheduler__WEBPACK_IMPORTED_MODULE_1__["AsapScheduler"](_AsapAction__WEBPACK_IMPORTED_MODULE_0__["AsapAction"]); +var asap = asapScheduler; //# sourceMappingURL=asap.js.map @@ -3930,13 +3946,15 @@ var AsapScheduler = /*@__PURE__*/ (function (_super) { "use strict"; __webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "asyncScheduler", function() { return asyncScheduler; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "async", function() { return async; }); /* harmony import */ var _AsyncAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(36); /* harmony import */ var _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(39); /** PURE_IMPORTS_START _AsyncAction,_AsyncScheduler PURE_IMPORTS_END */ -var async = /*@__PURE__*/ new _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__["AsyncScheduler"](_AsyncAction__WEBPACK_IMPORTED_MODULE_0__["AsyncAction"]); +var asyncScheduler = /*@__PURE__*/ new _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__["AsyncScheduler"](_AsyncAction__WEBPACK_IMPORTED_MODULE_0__["AsyncAction"]); +var async = asyncScheduler; //# sourceMappingURL=async.js.map @@ -3946,13 +3964,15 @@ var async = /*@__PURE__*/ new _AsyncScheduler__WEBPACK_IMPORTED_MODULE_1__["Asyn "use strict"; __webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "animationFrameScheduler", function() { return animationFrameScheduler; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "animationFrame", function() { return animationFrame; }); /* harmony import */ var _AnimationFrameAction__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(57); /* harmony import */ var _AnimationFrameScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(58); /** PURE_IMPORTS_START _AnimationFrameAction,_AnimationFrameScheduler PURE_IMPORTS_END */ -var animationFrame = /*@__PURE__*/ new _AnimationFrameScheduler__WEBPACK_IMPORTED_MODULE_1__["AnimationFrameScheduler"](_AnimationFrameAction__WEBPACK_IMPORTED_MODULE_0__["AnimationFrameAction"]); +var animationFrameScheduler = /*@__PURE__*/ new _AnimationFrameScheduler__WEBPACK_IMPORTED_MODULE_1__["AnimationFrameScheduler"](_AnimationFrameAction__WEBPACK_IMPORTED_MODULE_0__["AnimationFrameAction"]); +var animationFrame = animationFrameScheduler; //# sourceMappingURL=animationFrame.js.map @@ -4599,8 +4619,8 @@ function combineLatest() { for (var _i = 0; _i < arguments.length; _i++) { observables[_i] = arguments[_i]; } - var resultSelector = null; - var scheduler = null; + var resultSelector = undefined; + var scheduler = undefined; if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_1__["isScheduler"])(observables[observables.length - 1])) { scheduler = observables.pop(); } @@ -4647,7 +4667,7 @@ var CombineLatestSubscriber = /*@__PURE__*/ (function (_super) { this.toRespond = len; for (var i = 0; i < len; i++) { var observable = observables[i]; - this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, observable, observable, i)); + this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, observable, undefined, i)); } } }; @@ -4656,7 +4676,7 @@ var CombineLatestSubscriber = /*@__PURE__*/ (function (_super) { this.destination.complete(); } }; - CombineLatestSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + CombineLatestSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { var values = this.values; var oldVal = values[outerIndex]; var toRespond = !this.toRespond @@ -4877,7 +4897,14 @@ var subscribeToIterable = function (iterable) { return function (subscriber) { var iterator = iterable[_symbol_iterator__WEBPACK_IMPORTED_MODULE_0__["iterator"]](); do { - var item = iterator.next(); + var item = void 0; + try { + item = iterator.next(); + } + catch (err) { + subscriber.error(err); + return subscriber; + } if (item.done) { subscriber.complete(); break; @@ -5039,15 +5066,12 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeMap", function() { return mergeMap; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeMapOperator", function() { return MergeMapOperator; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MergeMapSubscriber", function() { return MergeMapSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return flatMap; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(70); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(71); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(66); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(83); -/** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_InnerSubscriber,_map,_observable_from PURE_IMPORTS_END */ - - +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(66); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(83); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ @@ -5057,7 +5081,7 @@ function mergeMap(project, resultSelector, concurrent) { concurrent = Number.POSITIVE_INFINITY; } if (typeof resultSelector === 'function') { - return function (source) { return source.pipe(mergeMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_5__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_4__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); }, concurrent)); }; + return function (source) { return source.pipe(mergeMap(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)); }; } else if (typeof resultSelector === 'number') { concurrent = resultSelector; @@ -5112,13 +5136,13 @@ var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { return; } this.active++; - this._innerSub(result, value, index); + this._innerSub(result); }; - MergeMapSubscriber.prototype._innerSub = function (ish, value, index) { - var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__["InnerSubscriber"](this, value, index); + MergeMapSubscriber.prototype._innerSub = function (ish) { + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); var destination = this.destination; destination.add(innerSubscriber); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__["subscribeToResult"])(this, ish, undefined, undefined, innerSubscriber); + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(ish, innerSubscriber); if (innerSubscription !== innerSubscriber) { destination.add(innerSubscription); } @@ -5130,12 +5154,11 @@ var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { } this.unsubscribe(); }; - MergeMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + MergeMapSubscriber.prototype.notifyNext = function (innerValue) { this.destination.next(innerValue); }; - MergeMapSubscriber.prototype.notifyComplete = function (innerSub) { + MergeMapSubscriber.prototype.notifyComplete = function () { var buffer = this.buffer; - this.remove(innerSub); this.active--; if (buffer.length > 0) { this._next(buffer.shift()); @@ -5145,8 +5168,9 @@ var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { } }; return MergeMapSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); +var flatMap = mergeMap; //# sourceMappingURL=mergeMap.js.map @@ -5378,6 +5402,116 @@ function isIterable(input) { /* 90 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleInnerSubscriber", function() { return SimpleInnerSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ComplexInnerSubscriber", function() { return ComplexInnerSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SimpleOuterSubscriber", function() { return SimpleOuterSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ComplexOuterSubscriber", function() { return ComplexOuterSubscriber; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "innerSubscribe", function() { return innerSubscribe; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9); +/* harmony import */ var _util_subscribeTo__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(72); +/** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_util_subscribeTo PURE_IMPORTS_END */ + + + + +var SimpleInnerSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SimpleInnerSubscriber, _super); + function SimpleInnerSubscriber(parent) { + var _this = _super.call(this) || this; + _this.parent = parent; + return _this; + } + SimpleInnerSubscriber.prototype._next = function (value) { + this.parent.notifyNext(value); + }; + SimpleInnerSubscriber.prototype._error = function (error) { + this.parent.notifyError(error); + this.unsubscribe(); + }; + SimpleInnerSubscriber.prototype._complete = function () { + this.parent.notifyComplete(); + this.unsubscribe(); + }; + return SimpleInnerSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); + +var ComplexInnerSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ComplexInnerSubscriber, _super); + function ComplexInnerSubscriber(parent, outerValue, outerIndex) { + var _this = _super.call(this) || this; + _this.parent = parent; + _this.outerValue = outerValue; + _this.outerIndex = outerIndex; + return _this; + } + ComplexInnerSubscriber.prototype._next = function (value) { + this.parent.notifyNext(this.outerValue, value, this.outerIndex, this); + }; + ComplexInnerSubscriber.prototype._error = function (error) { + this.parent.notifyError(error); + this.unsubscribe(); + }; + ComplexInnerSubscriber.prototype._complete = function () { + this.parent.notifyComplete(this); + this.unsubscribe(); + }; + return ComplexInnerSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); + +var SimpleOuterSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SimpleOuterSubscriber, _super); + function SimpleOuterSubscriber() { + return _super !== null && _super.apply(this, arguments) || this; + } + SimpleOuterSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); + }; + SimpleOuterSubscriber.prototype.notifyError = function (err) { + this.destination.error(err); + }; + SimpleOuterSubscriber.prototype.notifyComplete = function () { + this.destination.complete(); + }; + return SimpleOuterSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); + +var ComplexOuterSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ComplexOuterSubscriber, _super); + function ComplexOuterSubscriber() { + return _super !== null && _super.apply(this, arguments) || this; + } + ComplexOuterSubscriber.prototype.notifyNext = function (_outerValue, innerValue, _outerIndex, _innerSub) { + this.destination.next(innerValue); + }; + ComplexOuterSubscriber.prototype.notifyError = function (error) { + this.destination.error(error); + }; + ComplexOuterSubscriber.prototype.notifyComplete = function (_innerSub) { + this.destination.complete(); + }; + return ComplexOuterSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); + +function innerSubscribe(result, innerSubscriber) { + if (innerSubscriber.closed) { + return undefined; + } + if (result instanceof _Observable__WEBPACK_IMPORTED_MODULE_2__["Observable"]) { + return result.subscribe(innerSubscriber); + } + return Object(_util_subscribeTo__WEBPACK_IMPORTED_MODULE_3__["subscribeTo"])(result)(innerSubscriber); +} +//# sourceMappingURL=innerSubscribe.js.map + + +/***/ }), +/* 91 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "defer", function() { return defer; }); @@ -5406,7 +5540,7 @@ function defer(observableFactory) { /***/ }), -/* 91 */ +/* 92 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5489,7 +5623,7 @@ function forkJoinInternal(sources, keys) { /***/ }), -/* 92 */ +/* 93 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5565,7 +5699,7 @@ function isEventTarget(sourceObj) { /***/ }), -/* 93 */ +/* 94 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5610,7 +5744,7 @@ function fromEventPattern(addHandler, removeHandler, resultSelector) { /***/ }), -/* 94 */ +/* 95 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5747,13 +5881,13 @@ function dispatch(state) { /***/ }), -/* 95 */ +/* 96 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "iif", function() { return iif; }); -/* harmony import */ var _defer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(90); +/* harmony import */ var _defer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(91); /* harmony import */ var _empty__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(43); /** PURE_IMPORTS_START _defer,_empty PURE_IMPORTS_END */ @@ -5771,7 +5905,7 @@ function iif(condition, trueResult, falseResult) { /***/ }), -/* 96 */ +/* 97 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5779,7 +5913,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "interval", function() { return interval; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(97); +/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(98); /** PURE_IMPORTS_START _Observable,_scheduler_async,_util_isNumeric PURE_IMPORTS_END */ @@ -5811,7 +5945,7 @@ function dispatch(state) { /***/ }), -/* 97 */ +/* 98 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5827,7 +5961,7 @@ function isNumeric(val) { /***/ }), -/* 98 */ +/* 99 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5868,7 +6002,7 @@ function merge() { /***/ }), -/* 99 */ +/* 100 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5888,7 +6022,7 @@ function never() { /***/ }), -/* 100 */ +/* 101 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5928,7 +6062,7 @@ function onErrorResumeNext() { /***/ }), -/* 101 */ +/* 102 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -5979,15 +6113,15 @@ function dispatch(state) { /***/ }), -/* 102 */ +/* 103 */ /***/ (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__(103); +/* harmony import */ var _util_not__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(104); /* harmony import */ var _util_subscribeTo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(72); -/* harmony import */ var _operators_filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(104); +/* harmony import */ var _operators_filter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(105); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9); /** PURE_IMPORTS_START _util_not,_util_subscribeTo,_operators_filter,_Observable PURE_IMPORTS_END */ @@ -6004,7 +6138,7 @@ function partition(source, predicate, thisArg) { /***/ }), -/* 103 */ +/* 104 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -6023,7 +6157,7 @@ function not(pred, thisArg) { /***/ }), -/* 104 */ +/* 105 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -6077,7 +6211,7 @@ var FilterSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 105 */ +/* 106 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -6141,7 +6275,7 @@ var RaceSubscriber = /*@__PURE__*/ (function (_super) { else { for (var i = 0; i < len && !this.hasFirst; i++) { var observable = observables[i]; - var subscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, observable, observable, i); + var subscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(this, observable, undefined, i); if (this.subscriptions) { this.subscriptions.push(subscription); } @@ -6150,7 +6284,7 @@ var RaceSubscriber = /*@__PURE__*/ (function (_super) { this.observables = null; } }; - RaceSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + RaceSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { if (!this.hasFirst) { this.hasFirst = true; for (var i = 0; i < this.subscriptions.length; i++) { @@ -6171,7 +6305,7 @@ var RaceSubscriber = /*@__PURE__*/ (function (_super) { /***/ }), -/* 106 */ +/* 107 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -6230,7 +6364,7 @@ function dispatch(state) { /***/ }), -/* 107 */ +/* 108 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -6238,7 +6372,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timer", function() { return timer; }); /* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(9); /* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(97); +/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(98); /* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(45); /** PURE_IMPORTS_START _Observable,_scheduler_async,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ @@ -6284,7 +6418,7 @@ function dispatch(state) { /***/ }), -/* 108 */ +/* 109 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -6329,7 +6463,7 @@ function using(resourceFactory, observableFactory) { /***/ }), -/* 109 */ +/* 110 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -6341,11 +6475,9 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _fromArray__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(46); /* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(70); -/* harmony import */ var _internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(75); -/** PURE_IMPORTS_START tslib,_fromArray,_util_isArray,_Subscriber,_OuterSubscriber,_util_subscribeToResult,_.._internal_symbol_iterator PURE_IMPORTS_END */ - +/* harmony import */ var _internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(75); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_fromArray,_util_isArray,_Subscriber,_.._internal_symbol_iterator,_innerSubscribe PURE_IMPORTS_END */ @@ -6380,10 +6512,10 @@ var ZipSubscriber = /*@__PURE__*/ (function (_super) { values = Object.create(null); } var _this = _super.call(this, destination) || this; + _this.resultSelector = resultSelector; _this.iterators = []; _this.active = 0; - _this.resultSelector = (typeof resultSelector === 'function') ? resultSelector : null; - _this.values = values; + _this.resultSelector = (typeof resultSelector === 'function') ? resultSelector : undefined; return _this; } ZipSubscriber.prototype._next = function (value) { @@ -6391,8 +6523,8 @@ var ZipSubscriber = /*@__PURE__*/ (function (_super) { if (Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(value)) { iterators.push(new StaticArrayIterator(value)); } - else if (typeof value[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_6__["iterator"]] === 'function') { - iterators.push(new StaticIterator(value[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_6__["iterator"]]())); + else if (typeof value[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_4__["iterator"]] === 'function') { + iterators.push(new StaticIterator(value[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_4__["iterator"]]())); } else { iterators.push(new ZipBufferIterator(this.destination, this, value)); @@ -6411,7 +6543,7 @@ var ZipSubscriber = /*@__PURE__*/ (function (_super) { var iterator = iterators[i]; if (iterator.stillUnsubscribed) { var destination = this.destination; - destination.add(iterator.subscribe(iterator, i)); + destination.add(iterator.subscribe()); } else { this.active--; @@ -6487,7 +6619,7 @@ var StaticIterator = /*@__PURE__*/ (function () { }; StaticIterator.prototype.hasCompleted = function () { var nextResult = this.nextResult; - return nextResult && nextResult.done; + return Boolean(nextResult && nextResult.done); }; return StaticIterator; }()); @@ -6498,7 +6630,7 @@ var StaticArrayIterator = /*@__PURE__*/ (function () { this.length = 0; this.length = array.length; } - StaticArrayIterator.prototype[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_6__["iterator"]] = function () { + StaticArrayIterator.prototype[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_4__["iterator"]] = function () { return this; }; StaticArrayIterator.prototype.next = function (value) { @@ -6525,7 +6657,7 @@ var ZipBufferIterator = /*@__PURE__*/ (function (_super) { _this.isComplete = false; return _this; } - ZipBufferIterator.prototype[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_6__["iterator"]] = function () { + ZipBufferIterator.prototype[_internal_symbol_iterator__WEBPACK_IMPORTED_MODULE_4__["iterator"]] = function () { return this; }; ZipBufferIterator.prototype.next = function () { @@ -6552,20 +6684,20 @@ var ZipBufferIterator = /*@__PURE__*/ (function (_super) { this.destination.complete(); } }; - ZipBufferIterator.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + ZipBufferIterator.prototype.notifyNext = function (innerValue) { this.buffer.push(innerValue); this.parent.checkIterators(); }; - ZipBufferIterator.prototype.subscribe = function (value, index) { - return Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__["subscribeToResult"])(this, this.observable, this, index); + ZipBufferIterator.prototype.subscribe = function () { + return Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_5__["innerSubscribe"])(this.observable, new _innerSubscribe__WEBPACK_IMPORTED_MODULE_5__["SimpleInnerSubscriber"](this)); }; return ZipBufferIterator; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_4__["OuterSubscriber"])); +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_5__["SimpleOuterSubscriber"])); //# sourceMappingURL=zip.js.map /***/ }), -/* 110 */ +/* 111 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -6591,9 +6723,9 @@ var ZipBufferIterator = /*@__PURE__*/ (function (_super) { Object.defineProperty(exports, "__esModule", { value: true }); exports.ToolingLogTextWriter = void 0; const tslib_1 = __webpack_require__(7); -const util_1 = __webpack_require__(111); -const chalk_1 = tslib_1.__importDefault(__webpack_require__(112)); -const log_levels_1 = __webpack_require__(125); +const util_1 = __webpack_require__(112); +const chalk_1 = tslib_1.__importDefault(__webpack_require__(113)); +const log_levels_1 = __webpack_require__(126); const { magentaBright, yellow, red, blue, green, dim } = chalk_1.default; const PREFIX_INDENT = ' '.repeat(6); const MSG_PREFIXES = { @@ -6660,23 +6792,23 @@ exports.ToolingLogTextWriter = ToolingLogTextWriter; /***/ }), -/* 111 */ +/* 112 */ /***/ (function(module, exports) { module.exports = require("util"); /***/ }), -/* 112 */ +/* 113 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiStyles = __webpack_require__(113); -const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(119); +const ansiStyles = __webpack_require__(114); +const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(120); const { stringReplaceAll, stringEncaseCRLFWithFirstIndex -} = __webpack_require__(123); +} = __webpack_require__(124); const {isArray} = Array; @@ -6885,7 +7017,7 @@ const chalkTag = (chalk, ...strings) => { } if (template === undefined) { - template = __webpack_require__(124); + template = __webpack_require__(125); } return template(chalk, parts.join('')); @@ -6902,7 +7034,7 @@ module.exports = chalk; /***/ }), -/* 113 */ +/* 114 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -6948,7 +7080,7 @@ const setLazyProperty = (object, property, get) => { let colorConvert; const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { if (colorConvert === undefined) { - colorConvert = __webpack_require__(115); + colorConvert = __webpack_require__(116); } const offset = isBackground ? 10 : 0; @@ -7070,10 +7202,10 @@ Object.defineProperty(module, 'exports', { get: assembleStyles }); -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 114 */ +/* 115 */ /***/ (function(module, exports) { module.exports = function(module) { @@ -7101,11 +7233,11 @@ module.exports = function(module) { /***/ }), -/* 115 */ +/* 116 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(116); -const route = __webpack_require__(118); +const conversions = __webpack_require__(117); +const route = __webpack_require__(119); const convert = {}; @@ -7188,12 +7320,12 @@ module.exports = convert; /***/ }), -/* 116 */ +/* 117 */ /***/ (function(module, exports, __webpack_require__) { /* MIT license */ /* eslint-disable no-mixed-operators */ -const cssKeywords = __webpack_require__(117); +const cssKeywords = __webpack_require__(118); // NOTE: conversions should only return primitive values (i.e. arrays, or // values that give correct `typeof` results). @@ -8033,7 +8165,7 @@ convert.rgb.gray = function (rgb) { /***/ }), -/* 117 */ +/* 118 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8192,10 +8324,10 @@ module.exports = { /***/ }), -/* 118 */ +/* 119 */ /***/ (function(module, exports, __webpack_require__) { -const conversions = __webpack_require__(116); +const conversions = __webpack_require__(117); /* This function routes a model to all other models. @@ -8295,14 +8427,14 @@ module.exports = function (fromModel) { /***/ }), -/* 119 */ +/* 120 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const os = __webpack_require__(120); -const tty = __webpack_require__(121); -const hasFlag = __webpack_require__(122); +const os = __webpack_require__(121); +const tty = __webpack_require__(122); +const hasFlag = __webpack_require__(123); const {env} = process; @@ -8441,19 +8573,19 @@ module.exports = { /***/ }), -/* 120 */ +/* 121 */ /***/ (function(module, exports) { module.exports = require("os"); /***/ }), -/* 121 */ +/* 122 */ /***/ (function(module, exports) { module.exports = require("tty"); /***/ }), -/* 122 */ +/* 123 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8468,7 +8600,7 @@ module.exports = (flag, argv = process.argv) => { /***/ }), -/* 123 */ +/* 124 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8514,7 +8646,7 @@ module.exports = { /***/ }), -/* 124 */ +/* 125 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8655,7 +8787,7 @@ module.exports = (chalk, temporary) => { /***/ }), -/* 125 */ +/* 126 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8712,7 +8844,7 @@ exports.parseLogLevel = parseLogLevel; /***/ }), -/* 126 */ +/* 127 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -8737,7 +8869,7 @@ exports.parseLogLevel = parseLogLevel; */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ToolingLogCollectingWriter = void 0; -const tooling_log_text_writer_1 = __webpack_require__(110); +const tooling_log_text_writer_1 = __webpack_require__(111); class ToolingLogCollectingWriter extends tooling_log_text_writer_1.ToolingLogTextWriter { constructor(level = 'verbose') { super({ @@ -8756,16 +8888,16 @@ exports.ToolingLogCollectingWriter = ToolingLogCollectingWriter; /***/ }), -/* 127 */ +/* 128 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __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__(128); -/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(279); -/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(392); -/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(393); +/* harmony import */ var _bootstrap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(129); +/* harmony import */ var _clean__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(280); +/* harmony import */ var _run__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(399); +/* harmony import */ var _watch__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(400); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8796,20 +8928,20 @@ const commands = { }; /***/ }), -/* 128 */ +/* 129 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BootstrapCommand", function() { return BootstrapCommand; }); -/* harmony import */ var _utils_link_project_executables__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(129); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); -/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(272); -/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(277); -/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(274); -/* harmony import */ var _utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(278); +/* harmony import */ var _utils_link_project_executables__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(130); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); +/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(146); +/* harmony import */ var _utils_project_checksums__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(273); +/* harmony import */ var _utils_bootstrap_cache_file__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(278); +/* harmony import */ var _utils_yarn_lock__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(275); +/* harmony import */ var _utils_validate_yarn_lock__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(279); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8916,7 +9048,7 @@ const BootstrapCommand = { }; /***/ }), -/* 129 */ +/* 130 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -8924,8 +9056,8 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "linkProjectExecutables", function() { return linkProjectExecutables; }); /* 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__(130); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(143); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(131); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -8987,7 +9119,7 @@ async function linkProjectExecutables(projectsByName, projectGraph) { } /***/ }), -/* 130 */ +/* 131 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -9002,15 +9134,15 @@ __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isDirectory", function() { return isDirectory; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isFile", function() { return isFile; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "createSymlink", function() { return createSymlink; }); -/* harmony import */ var cmd_shim__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(131); +/* harmony import */ var cmd_shim__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(132); /* harmony import */ var cmd_shim__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(cmd_shim__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(133); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(134); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(142); +/* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(143); /* harmony import */ var ncp__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(ncp__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 util__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(111); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(112); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_4__); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -9123,7 +9255,7 @@ async function forceCreate(src, dest, type) { } /***/ }), -/* 131 */ +/* 132 */ /***/ (function(module, exports, __webpack_require__) { // On windows, create a .cmd file. @@ -9139,11 +9271,11 @@ async function forceCreate(src, dest, type) { module.exports = cmdShim cmdShim.ifExists = cmdShimIfExists -var fs = __webpack_require__(132) +var fs = __webpack_require__(133) -var mkdir = __webpack_require__(140) +var mkdir = __webpack_require__(141) , path = __webpack_require__(4) - , toBatchSyntax = __webpack_require__(141) + , toBatchSyntax = __webpack_require__(142) , shebangExpr = /^#\!\s*(?:\/usr\/bin\/env)?\s*([^ \t]+=[^ \t]+\s+)*\s*([^ \t]+)(.*)$/ function cmdShimIfExists (from, to, cb) { @@ -9376,15 +9508,15 @@ function times(n, ok, cb) { /***/ }), -/* 132 */ +/* 133 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(133) -var polyfills = __webpack_require__(134) -var legacy = __webpack_require__(136) -var clone = __webpack_require__(138) +var fs = __webpack_require__(134) +var polyfills = __webpack_require__(135) +var legacy = __webpack_require__(137) +var clone = __webpack_require__(139) -var util = __webpack_require__(111) +var util = __webpack_require__(112) /* istanbul ignore next - node 0.x polyfill */ var gracefulQueue @@ -9465,7 +9597,7 @@ if (!fs[gracefulQueue]) { if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || '')) { process.on('exit', function() { debug(fs[gracefulQueue]) - __webpack_require__(139).equal(fs[gracefulQueue].length, 0) + __webpack_require__(140).equal(fs[gracefulQueue].length, 0) }) } } @@ -9736,16 +9868,16 @@ function retry () { /***/ }), -/* 133 */ +/* 134 */ /***/ (function(module, exports) { module.exports = require("fs"); /***/ }), -/* 134 */ +/* 135 */ /***/ (function(module, exports, __webpack_require__) { -var constants = __webpack_require__(135) +var constants = __webpack_require__(136) var origCwd = process.cwd var cwd = null @@ -10090,16 +10222,16 @@ function patch (fs) { /***/ }), -/* 135 */ +/* 136 */ /***/ (function(module, exports) { module.exports = require("constants"); /***/ }), -/* 136 */ +/* 137 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(137).Stream +var Stream = __webpack_require__(138).Stream module.exports = legacy @@ -10220,13 +10352,13 @@ function legacy (fs) { /***/ }), -/* 137 */ +/* 138 */ /***/ (function(module, exports) { module.exports = require("stream"); /***/ }), -/* 138 */ +/* 139 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -10252,17 +10384,17 @@ function clone (obj) { /***/ }), -/* 139 */ +/* 140 */ /***/ (function(module, exports) { module.exports = require("assert"); /***/ }), -/* 140 */ +/* 141 */ /***/ (function(module, exports, __webpack_require__) { var path = __webpack_require__(4); -var fs = __webpack_require__(133); +var fs = __webpack_require__(134); var _0777 = parseInt('0777', 8); module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP; @@ -10363,7 +10495,7 @@ mkdirP.sync = function sync (p, opts, made) { /***/ }), -/* 141 */ +/* 142 */ /***/ (function(module, exports) { exports.replaceDollarWithPercentPair = replaceDollarWithPercentPair @@ -10421,10 +10553,10 @@ function replaceDollarWithPercentPair(value) { /***/ }), -/* 142 */ +/* 143 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(133), +var fs = __webpack_require__(134), path = __webpack_require__(4); module.exports = ncp; @@ -10688,7 +10820,7 @@ function ncp (source, dest, options, callback) { /***/ }), -/* 143 */ +/* 144 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -10748,7 +10880,7 @@ const log = new Log(); /***/ }), -/* 144 */ +/* 145 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -10814,7 +10946,7 @@ async function parallelize(items, fn, concurrency = 4) { } /***/ }), -/* 145 */ +/* 146 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -10823,15 +10955,15 @@ __webpack_require__.r(__webpack_exports__); /* 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__(146); +/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(147); /* 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__(111); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(112); /* 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__(162); -/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(163); -/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(270); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(163); +/* harmony import */ var _project__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(164); +/* harmony import */ var _workspaces__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(271); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -11030,7 +11162,7 @@ function includeTransitiveProjects(subsetOfProjects, allProjects, { } /***/ }), -/* 146 */ +/* 147 */ /***/ (function(module, exports, __webpack_require__) { // Approach: @@ -11075,27 +11207,27 @@ function includeTransitiveProjects(subsetOfProjects, allProjects, { module.exports = glob -var fs = __webpack_require__(133) -var rp = __webpack_require__(147) -var minimatch = __webpack_require__(149) +var fs = __webpack_require__(134) +var rp = __webpack_require__(148) +var minimatch = __webpack_require__(150) var Minimatch = minimatch.Minimatch -var inherits = __webpack_require__(153) -var EE = __webpack_require__(155).EventEmitter +var inherits = __webpack_require__(154) +var EE = __webpack_require__(156).EventEmitter var path = __webpack_require__(4) -var assert = __webpack_require__(139) -var isAbsolute = __webpack_require__(156) -var globSync = __webpack_require__(157) -var common = __webpack_require__(158) +var assert = __webpack_require__(140) +var isAbsolute = __webpack_require__(157) +var globSync = __webpack_require__(158) +var common = __webpack_require__(159) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts var ownProp = common.ownProp -var inflight = __webpack_require__(159) -var util = __webpack_require__(111) +var inflight = __webpack_require__(160) +var util = __webpack_require__(112) var childrenIgnored = common.childrenIgnored var isIgnored = common.isIgnored -var once = __webpack_require__(161) +var once = __webpack_require__(162) function glob (pattern, options, cb) { if (typeof options === 'function') cb = options, options = {} @@ -11826,7 +11958,7 @@ Glob.prototype._stat2 = function (f, abs, er, stat, cb) { /***/ }), -/* 147 */ +/* 148 */ /***/ (function(module, exports, __webpack_require__) { module.exports = realpath @@ -11836,13 +11968,13 @@ realpath.realpathSync = realpathSync realpath.monkeypatch = monkeypatch realpath.unmonkeypatch = unmonkeypatch -var fs = __webpack_require__(133) +var fs = __webpack_require__(134) var origRealpath = fs.realpath var origRealpathSync = fs.realpathSync var version = process.version var ok = /^v[0-5]\./.test(version) -var old = __webpack_require__(148) +var old = __webpack_require__(149) function newError (er) { return er && er.syscall === 'realpath' && ( @@ -11898,7 +12030,7 @@ function unmonkeypatch () { /***/ }), -/* 148 */ +/* 149 */ /***/ (function(module, exports, __webpack_require__) { // Copyright Joyent, Inc. and other Node contributors. @@ -11924,7 +12056,7 @@ function unmonkeypatch () { var pathModule = __webpack_require__(4); var isWindows = process.platform === 'win32'; -var fs = __webpack_require__(133); +var fs = __webpack_require__(134); // JavaScript implementation of realpath, ported from node pre-v6 @@ -12207,7 +12339,7 @@ exports.realpath = function realpath(p, cache, cb) { /***/ }), -/* 149 */ +/* 150 */ /***/ (function(module, exports, __webpack_require__) { module.exports = minimatch @@ -12219,7 +12351,7 @@ try { } catch (er) {} var GLOBSTAR = minimatch.GLOBSTAR = Minimatch.GLOBSTAR = {} -var expand = __webpack_require__(150) +var expand = __webpack_require__(151) var plTypes = { '!': { open: '(?:(?!(?:', close: '))[^/]*?)'}, @@ -13136,11 +13268,11 @@ function regExpEscape (s) { /***/ }), -/* 150 */ +/* 151 */ /***/ (function(module, exports, __webpack_require__) { -var concatMap = __webpack_require__(151); -var balanced = __webpack_require__(152); +var concatMap = __webpack_require__(152); +var balanced = __webpack_require__(153); module.exports = expandTop; @@ -13343,7 +13475,7 @@ function expand(str, isTop) { /***/ }), -/* 151 */ +/* 152 */ /***/ (function(module, exports) { module.exports = function (xs, fn) { @@ -13362,7 +13494,7 @@ var isArray = Array.isArray || function (xs) { /***/ }), -/* 152 */ +/* 153 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -13428,22 +13560,22 @@ function range(a, b, str) { /***/ }), -/* 153 */ +/* 154 */ /***/ (function(module, exports, __webpack_require__) { try { - var util = __webpack_require__(111); + var util = __webpack_require__(112); /* istanbul ignore next */ if (typeof util.inherits !== 'function') throw ''; module.exports = util.inherits; } catch (e) { /* istanbul ignore next */ - module.exports = __webpack_require__(154); + module.exports = __webpack_require__(155); } /***/ }), -/* 154 */ +/* 155 */ /***/ (function(module, exports) { if (typeof Object.create === 'function') { @@ -13476,13 +13608,13 @@ if (typeof Object.create === 'function') { /***/ }), -/* 155 */ +/* 156 */ /***/ (function(module, exports) { module.exports = require("events"); /***/ }), -/* 156 */ +/* 157 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -13509,22 +13641,22 @@ module.exports.win32 = win32; /***/ }), -/* 157 */ +/* 158 */ /***/ (function(module, exports, __webpack_require__) { module.exports = globSync globSync.GlobSync = GlobSync -var fs = __webpack_require__(133) -var rp = __webpack_require__(147) -var minimatch = __webpack_require__(149) +var fs = __webpack_require__(134) +var rp = __webpack_require__(148) +var minimatch = __webpack_require__(150) var Minimatch = minimatch.Minimatch -var Glob = __webpack_require__(146).Glob -var util = __webpack_require__(111) +var Glob = __webpack_require__(147).Glob +var util = __webpack_require__(112) var path = __webpack_require__(4) -var assert = __webpack_require__(139) -var isAbsolute = __webpack_require__(156) -var common = __webpack_require__(158) +var assert = __webpack_require__(140) +var isAbsolute = __webpack_require__(157) +var common = __webpack_require__(159) var alphasort = common.alphasort var alphasorti = common.alphasorti var setopts = common.setopts @@ -14001,7 +14133,7 @@ GlobSync.prototype._makeAbs = function (f) { /***/ }), -/* 158 */ +/* 159 */ /***/ (function(module, exports, __webpack_require__) { exports.alphasort = alphasort @@ -14019,8 +14151,8 @@ function ownProp (obj, field) { } var path = __webpack_require__(4) -var minimatch = __webpack_require__(149) -var isAbsolute = __webpack_require__(156) +var minimatch = __webpack_require__(150) +var isAbsolute = __webpack_require__(157) var Minimatch = minimatch.Minimatch function alphasorti (a, b) { @@ -14247,12 +14379,12 @@ function childrenIgnored (self, path) { /***/ }), -/* 159 */ +/* 160 */ /***/ (function(module, exports, __webpack_require__) { -var wrappy = __webpack_require__(160) +var wrappy = __webpack_require__(161) var reqs = Object.create(null) -var once = __webpack_require__(161) +var once = __webpack_require__(162) module.exports = wrappy(inflight) @@ -14307,7 +14439,7 @@ function slice (args) { /***/ }), -/* 160 */ +/* 161 */ /***/ (function(module, exports) { // Returns a wrapper function that returns a wrapped callback @@ -14346,10 +14478,10 @@ function wrappy (fn, cb) { /***/ }), -/* 161 */ +/* 162 */ /***/ (function(module, exports, __webpack_require__) { -var wrappy = __webpack_require__(160) +var wrappy = __webpack_require__(161) module.exports = wrappy(once) module.exports.strict = wrappy(onceStrict) @@ -14394,7 +14526,7 @@ function onceStrict (fn) { /***/ }), -/* 162 */ +/* 163 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -14427,22 +14559,22 @@ class CliError extends Error { } /***/ }), -/* 163 */ +/* 164 */ /***/ (function(module, __webpack_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__(133); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(134); /* 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__(111); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(112); /* 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__(162); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(143); -/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(164); -/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(225); +/* harmony import */ var _errors__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(163); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(144); +/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(165); +/* harmony import */ var _scripts__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(226); 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; } @@ -14679,7 +14811,7 @@ function normalizePath(path) { } /***/ }), -/* 164 */ +/* 165 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -14687,9 +14819,9 @@ __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__, "isLinkDependency", function() { return isLinkDependency; }); -/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(165); +/* harmony import */ var read_pkg__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(166); /* 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__(213); +/* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(214); /* harmony import */ var write_pkg__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(write_pkg__WEBPACK_IMPORTED_MODULE_1__); /* * Licensed to Elasticsearch B.V. under one or more contributor @@ -14723,15 +14855,15 @@ function writePackageJson(path, json) { const isLinkDependency = depVersion => depVersion.startsWith('link:'); /***/ }), -/* 165 */ +/* 166 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {promisify} = __webpack_require__(111); -const fs = __webpack_require__(133); +const {promisify} = __webpack_require__(112); +const fs = __webpack_require__(134); const path = __webpack_require__(4); -const parseJson = __webpack_require__(166); +const parseJson = __webpack_require__(167); const readFileAsync = promisify(fs.readFile); @@ -14746,7 +14878,7 @@ module.exports = async options => { const json = parseJson(await readFileAsync(filePath, 'utf8')); if (options.normalize) { - __webpack_require__(187)(json); + __webpack_require__(188)(json); } return json; @@ -14763,7 +14895,7 @@ module.exports.sync = options => { const json = parseJson(fs.readFileSync(filePath, 'utf8')); if (options.normalize) { - __webpack_require__(187)(json); + __webpack_require__(188)(json); } return json; @@ -14771,15 +14903,15 @@ module.exports.sync = options => { /***/ }), -/* 166 */ +/* 167 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const errorEx = __webpack_require__(167); -const fallback = __webpack_require__(169); -const {default: LinesAndColumns} = __webpack_require__(170); -const {codeFrameColumns} = __webpack_require__(171); +const errorEx = __webpack_require__(168); +const fallback = __webpack_require__(170); +const {default: LinesAndColumns} = __webpack_require__(171); +const {codeFrameColumns} = __webpack_require__(172); const JSONError = errorEx('JSONError', { fileName: errorEx.append('in %s'), @@ -14828,14 +14960,14 @@ module.exports = (string, reviver, filename) => { /***/ }), -/* 167 */ +/* 168 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var util = __webpack_require__(111); -var isArrayish = __webpack_require__(168); +var util = __webpack_require__(112); +var isArrayish = __webpack_require__(169); var errorEx = function errorEx(name, properties) { if (!name || name.constructor !== String) { @@ -14968,7 +15100,7 @@ module.exports = errorEx; /***/ }), -/* 168 */ +/* 169 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -14985,7 +15117,7 @@ module.exports = function isArrayish(obj) { /***/ }), -/* 169 */ +/* 170 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -14997,6 +15129,12 @@ function parseJson (txt, reviver, context) { 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] @@ -15024,7 +15162,7 @@ function parseJson (txt, reviver, context) { /***/ }), -/* 170 */ +/* 171 */ /***/ (function(__webpack_module__, __webpack_exports__, __webpack_require__) { "use strict"; @@ -15088,7 +15226,7 @@ var LinesAndColumns = (function () { /***/ }), -/* 171 */ +/* 172 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -15100,7 +15238,7 @@ Object.defineProperty(exports, "__esModule", { exports.codeFrameColumns = codeFrameColumns; exports.default = _default; -var _highlight = _interopRequireWildcard(__webpack_require__(172)); +var _highlight = _interopRequireWildcard(__webpack_require__(173)); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } @@ -15261,7 +15399,7 @@ function _default(rawLines, lineNumber, colNumber, opts = {}) { } /***/ }), -/* 172 */ +/* 173 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -15274,11 +15412,11 @@ exports.shouldHighlight = shouldHighlight; exports.getChalk = getChalk; exports.default = highlight; -var _jsTokens = _interopRequireWildcard(__webpack_require__(173)); +var _jsTokens = _interopRequireWildcard(__webpack_require__(174)); -var _helperValidatorIdentifier = __webpack_require__(174); +var _helperValidatorIdentifier = __webpack_require__(175); -var _chalk = _interopRequireDefault(__webpack_require__(177)); +var _chalk = _interopRequireDefault(__webpack_require__(178)); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } @@ -15374,7 +15512,7 @@ function highlight(code, options = {}) { } /***/ }), -/* 173 */ +/* 174 */ /***/ (function(module, exports) { // Copyright 2014, 2015, 2016, 2017, 2018 Simon Lydell @@ -15403,7 +15541,7 @@ exports.matchToToken = function(match) { /***/ }), -/* 174 */ +/* 175 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -15461,12 +15599,12 @@ Object.defineProperty(exports, "isKeyword", { } }); -var _identifier = __webpack_require__(175); +var _identifier = __webpack_require__(176); -var _keyword = __webpack_require__(176); +var _keyword = __webpack_require__(177); /***/ }), -/* 175 */ +/* 176 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -15549,7 +15687,7 @@ function isIdentifierName(name) { } /***/ }), -/* 176 */ +/* 177 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -15593,16 +15731,16 @@ function isKeyword(word) { } /***/ }), -/* 177 */ +/* 178 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const escapeStringRegexp = __webpack_require__(178); -const ansiStyles = __webpack_require__(179); -const stdoutColor = __webpack_require__(184).stdout; +const escapeStringRegexp = __webpack_require__(179); +const ansiStyles = __webpack_require__(180); +const stdoutColor = __webpack_require__(185).stdout; -const template = __webpack_require__(186); +const template = __webpack_require__(187); const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); @@ -15828,7 +15966,7 @@ module.exports.default = module.exports; // For TypeScript /***/ }), -/* 178 */ +/* 179 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -15846,12 +15984,12 @@ module.exports = function (str) { /***/ }), -/* 179 */ +/* 180 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(180); +const colorConvert = __webpack_require__(181); const wrapAnsi16 = (fn, offset) => function () { const code = fn.apply(colorConvert, arguments); @@ -16016,14 +16154,14 @@ Object.defineProperty(module, 'exports', { get: assembleStyles }); -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 180 */ +/* 181 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(181); -var route = __webpack_require__(183); +var conversions = __webpack_require__(182); +var route = __webpack_require__(184); var convert = {}; @@ -16103,11 +16241,11 @@ module.exports = convert; /***/ }), -/* 181 */ +/* 182 */ /***/ (function(module, exports, __webpack_require__) { /* MIT license */ -var cssKeywords = __webpack_require__(182); +var cssKeywords = __webpack_require__(183); // NOTE: conversions should only return primitive values (i.e. arrays, or // values that give correct `typeof` results). @@ -16977,7 +17115,7 @@ convert.rgb.gray = function (rgb) { /***/ }), -/* 182 */ +/* 183 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -17136,10 +17274,10 @@ module.exports = { /***/ }), -/* 183 */ +/* 184 */ /***/ (function(module, exports, __webpack_require__) { -var conversions = __webpack_require__(181); +var conversions = __webpack_require__(182); /* this function routes a model to all other models. @@ -17239,13 +17377,13 @@ module.exports = function (fromModel) { /***/ }), -/* 184 */ +/* 185 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const os = __webpack_require__(120); -const hasFlag = __webpack_require__(185); +const os = __webpack_require__(121); +const hasFlag = __webpack_require__(186); const env = process.env; @@ -17377,7 +17515,7 @@ module.exports = { /***/ }), -/* 185 */ +/* 186 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -17392,7 +17530,7 @@ module.exports = (flag, argv) => { /***/ }), -/* 186 */ +/* 187 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -17527,15 +17665,15 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 187 */ +/* 188 */ /***/ (function(module, exports, __webpack_require__) { module.exports = normalize -var fixer = __webpack_require__(188) +var fixer = __webpack_require__(189) normalize.fixer = fixer -var makeWarning = __webpack_require__(211) +var makeWarning = __webpack_require__(212) var fieldsToFix = ['name','version','description','repository','modules','scripts' ,'files','bin','man','bugs','keywords','readme','homepage','license'] @@ -17572,17 +17710,17 @@ function ucFirst (string) { /***/ }), -/* 188 */ +/* 189 */ /***/ (function(module, exports, __webpack_require__) { -var semver = __webpack_require__(189) -var validateLicense = __webpack_require__(190); -var hostedGitInfo = __webpack_require__(195) -var isBuiltinModule = __webpack_require__(199).isCore +var semver = __webpack_require__(190) +var validateLicense = __webpack_require__(191); +var hostedGitInfo = __webpack_require__(196) +var isBuiltinModule = __webpack_require__(200).isCore var depTypes = ["dependencies","devDependencies","optionalDependencies"] -var extractDescription = __webpack_require__(209) -var url = __webpack_require__(196) -var typos = __webpack_require__(210) +var extractDescription = __webpack_require__(210) +var url = __webpack_require__(197) +var typos = __webpack_require__(211) var fixer = module.exports = { // default warning function @@ -17996,7 +18134,7 @@ function bugsTypos(bugs, warn) { /***/ }), -/* 189 */ +/* 190 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -19485,11 +19623,11 @@ function coerce (version) { /***/ }), -/* 190 */ +/* 191 */ /***/ (function(module, exports, __webpack_require__) { -var parse = __webpack_require__(191); -var correct = __webpack_require__(193); +var parse = __webpack_require__(192); +var correct = __webpack_require__(194); var genericWarning = ( 'license should be ' + @@ -19575,10 +19713,10 @@ module.exports = function(argument) { /***/ }), -/* 191 */ +/* 192 */ /***/ (function(module, exports, __webpack_require__) { -var parser = __webpack_require__(192).parser +var parser = __webpack_require__(193).parser module.exports = function (argument) { return parser.parse(argument) @@ -19586,7 +19724,7 @@ module.exports = function (argument) { /***/ }), -/* 192 */ +/* 193 */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(module) {/* parser generated by jison 0.4.17 */ @@ -20939,7 +21077,7 @@ exports.main = function commonjsMain(args) { console.log('Usage: '+args[0]+' FILE'); process.exit(1); } - var source = __webpack_require__(133).readFileSync(__webpack_require__(4).normalize(args[1]), "utf8"); + var source = __webpack_require__(134).readFileSync(__webpack_require__(4).normalize(args[1]), "utf8"); return exports.parser.parse(source); }; if ( true && __webpack_require__.c[__webpack_require__.s] === module) { @@ -20947,13 +21085,13 @@ if ( true && __webpack_require__.c[__webpack_require__.s] === module) { } } -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), -/* 193 */ +/* 194 */ /***/ (function(module, exports, __webpack_require__) { -var licenseIDs = __webpack_require__(194); +var licenseIDs = __webpack_require__(195); function valid(string) { return licenseIDs.indexOf(string) > -1; @@ -21193,20 +21331,20 @@ module.exports = function(identifier) { /***/ }), -/* 194 */ +/* 195 */ /***/ (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\"]"); /***/ }), -/* 195 */ +/* 196 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var url = __webpack_require__(196) -var gitHosts = __webpack_require__(197) -var GitHost = module.exports = __webpack_require__(198) +var url = __webpack_require__(197) +var gitHosts = __webpack_require__(198) +var GitHost = module.exports = __webpack_require__(199) var protocolToRepresentationMap = { 'git+ssh': 'sshurl', @@ -21327,13 +21465,13 @@ function parseGitUrl (giturl) { /***/ }), -/* 196 */ +/* 197 */ /***/ (function(module, exports) { module.exports = require("url"); /***/ }), -/* 197 */ +/* 198 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -21408,13 +21546,13 @@ Object.keys(gitHosts).forEach(function (name) { /***/ }), -/* 198 */ +/* 199 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var gitHosts = __webpack_require__(197) -var extend = Object.assign || __webpack_require__(111)._extend +var gitHosts = __webpack_require__(198) +var extend = Object.assign || __webpack_require__(112)._extend var GitHost = module.exports = function (type, user, auth, project, committish, defaultRepresentation, opts) { var gitHostInfo = this @@ -21529,27 +21667,27 @@ GitHost.prototype.toString = function (opts) { /***/ }), -/* 199 */ +/* 200 */ /***/ (function(module, exports, __webpack_require__) { -var async = __webpack_require__(200); -async.core = __webpack_require__(206); -async.isCore = __webpack_require__(205); -async.sync = __webpack_require__(208); +var async = __webpack_require__(201); +async.core = __webpack_require__(207); +async.isCore = __webpack_require__(206); +async.sync = __webpack_require__(209); module.exports = async; /***/ }), -/* 200 */ +/* 201 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(133); +var fs = __webpack_require__(134); var path = __webpack_require__(4); -var caller = __webpack_require__(201); -var nodeModulesPaths = __webpack_require__(202); -var normalizeOptions = __webpack_require__(204); -var isCore = __webpack_require__(205); +var caller = __webpack_require__(202); +var nodeModulesPaths = __webpack_require__(203); +var normalizeOptions = __webpack_require__(205); +var isCore = __webpack_require__(206); var realpathFS = fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; @@ -21845,7 +21983,7 @@ module.exports = function resolve(x, options, callback) { /***/ }), -/* 201 */ +/* 202 */ /***/ (function(module, exports) { module.exports = function () { @@ -21859,11 +21997,11 @@ module.exports = function () { /***/ }), -/* 202 */ +/* 203 */ /***/ (function(module, exports, __webpack_require__) { var path = __webpack_require__(4); -var parse = path.parse || __webpack_require__(203); +var parse = path.parse || __webpack_require__(204); var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) { var prefix = '/'; @@ -21907,7 +22045,7 @@ module.exports = function nodeModulesPaths(start, opts, request) { /***/ }), -/* 203 */ +/* 204 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -22007,7 +22145,7 @@ module.exports.win32 = win32.parse; /***/ }), -/* 204 */ +/* 205 */ /***/ (function(module, exports) { module.exports = function (x, opts) { @@ -22023,10 +22161,10 @@ module.exports = function (x, opts) { /***/ }), -/* 205 */ +/* 206 */ /***/ (function(module, exports, __webpack_require__) { -var core = __webpack_require__(206); +var core = __webpack_require__(207); module.exports = function isCore(x) { return Object.prototype.hasOwnProperty.call(core, x); @@ -22034,7 +22172,7 @@ module.exports = function isCore(x) { /***/ }), -/* 206 */ +/* 207 */ /***/ (function(module, exports, __webpack_require__) { var current = (process.versions && process.versions.node && process.versions.node.split('.')) || []; @@ -22081,7 +22219,7 @@ function versionIncluded(specifierValue) { return matchesRange(specifierValue); } -var data = __webpack_require__(207); +var data = __webpack_require__(208); var core = {}; for (var mod in data) { // eslint-disable-line no-restricted-syntax @@ -22093,21 +22231,21 @@ module.exports = core; /***/ }), -/* 207 */ +/* 208 */ /***/ (function(module) { module.exports = JSON.parse("{\"assert\":true,\"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,\"dns\":true,\"domain\":true,\"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,\"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,\"string_decoder\":true,\"sys\":true,\"timers\":true,\"_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,\"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}"); /***/ }), -/* 208 */ +/* 209 */ /***/ (function(module, exports, __webpack_require__) { -var isCore = __webpack_require__(205); -var fs = __webpack_require__(133); +var isCore = __webpack_require__(206); +var fs = __webpack_require__(134); var path = __webpack_require__(4); -var caller = __webpack_require__(201); -var nodeModulesPaths = __webpack_require__(202); -var normalizeOptions = __webpack_require__(204); +var caller = __webpack_require__(202); +var nodeModulesPaths = __webpack_require__(203); +var normalizeOptions = __webpack_require__(205); var realpathFS = fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; @@ -22296,7 +22434,7 @@ module.exports = function resolveSync(x, options) { /***/ }), -/* 209 */ +/* 210 */ /***/ (function(module, exports) { module.exports = extractDescription @@ -22316,17 +22454,17 @@ function extractDescription (d) { /***/ }), -/* 210 */ +/* 211 */ /***/ (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\"}}"); /***/ }), -/* 211 */ +/* 212 */ /***/ (function(module, exports, __webpack_require__) { -var util = __webpack_require__(111) -var messages = __webpack_require__(212) +var util = __webpack_require__(112) +var messages = __webpack_require__(213) module.exports = function() { var args = Array.prototype.slice.call(arguments, 0) @@ -22351,20 +22489,20 @@ function makeTypoWarning (providedName, probableName, field) { /***/ }), -/* 212 */ +/* 213 */ /***/ (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.\"}"); /***/ }), -/* 213 */ +/* 214 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const writeJsonFile = __webpack_require__(214); -const sortKeys = __webpack_require__(220); +const writeJsonFile = __webpack_require__(215); +const sortKeys = __webpack_require__(221); const dependencyKeys = new Set([ 'dependencies', @@ -22429,18 +22567,18 @@ module.exports.sync = (filePath, data, options) => { /***/ }), -/* 214 */ +/* 215 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const fs = __webpack_require__(132); -const writeFileAtomic = __webpack_require__(215); -const sortKeys = __webpack_require__(220); -const makeDir = __webpack_require__(222); -const pify = __webpack_require__(223); -const detectIndent = __webpack_require__(224); +const fs = __webpack_require__(133); +const writeFileAtomic = __webpack_require__(216); +const sortKeys = __webpack_require__(221); +const makeDir = __webpack_require__(223); +const pify = __webpack_require__(224); +const detectIndent = __webpack_require__(225); const init = (fn, filePath, data, options) => { if (!filePath) { @@ -22512,7 +22650,7 @@ module.exports.sync = (filePath, data, options) => { /***/ }), -/* 215 */ +/* 216 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -22522,9 +22660,9 @@ module.exports.sync = writeFileSync module.exports._getTmpname = getTmpname // for testing module.exports._cleanupOnExit = cleanupOnExit -var fs = __webpack_require__(132) -var MurmurHash3 = __webpack_require__(216) -var onExit = __webpack_require__(217) +var fs = __webpack_require__(133) +var MurmurHash3 = __webpack_require__(217) +var onExit = __webpack_require__(218) var path = __webpack_require__(4) var activeFiles = {} @@ -22532,7 +22670,7 @@ var activeFiles = {} /* istanbul ignore next */ var threadId = (function getId () { try { - var workerThreads = __webpack_require__(219) + var workerThreads = __webpack_require__(220) /// if we are in main thread, this is set to `0` return workerThreads.threadId @@ -22757,7 +22895,7 @@ function writeFileSync (filename, data, options) { /***/ }), -/* 216 */ +/* 217 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -22899,16 +23037,16 @@ function writeFileSync (filename, data, options) { /***/ }), -/* 217 */ +/* 218 */ /***/ (function(module, exports, __webpack_require__) { // Note: since nyc uses this module to output coverage, any lines // that are in the direct sync flow of nyc's outputCoverage are // ignored, since we can never get coverage for them. -var assert = __webpack_require__(139) -var signals = __webpack_require__(218) +var assert = __webpack_require__(140) +var signals = __webpack_require__(219) -var EE = __webpack_require__(155) +var EE = __webpack_require__(156) /* istanbul ignore if */ if (typeof EE !== 'function') { EE = EE.EventEmitter @@ -23062,7 +23200,7 @@ function processEmit (ev, arg) { /***/ }), -/* 218 */ +/* 219 */ /***/ (function(module, exports) { // This is not the set of all possible signals. @@ -23121,18 +23259,18 @@ if (process.platform === 'linux') { /***/ }), -/* 219 */ +/* 220 */ /***/ (function(module, exports) { module.exports = require(undefined); /***/ }), -/* 220 */ +/* 221 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isPlainObj = __webpack_require__(221); +const isPlainObj = __webpack_require__(222); module.exports = (obj, opts) => { if (!isPlainObj(obj)) { @@ -23189,7 +23327,7 @@ module.exports = (obj, opts) => { /***/ }), -/* 221 */ +/* 222 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -23203,15 +23341,15 @@ module.exports = function (x) { /***/ }), -/* 222 */ +/* 223 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); +const fs = __webpack_require__(134); const path = __webpack_require__(4); -const pify = __webpack_require__(223); -const semver = __webpack_require__(189); +const pify = __webpack_require__(224); +const semver = __webpack_require__(190); const defaults = { mode: 0o777 & (~process.umask()), @@ -23349,7 +23487,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 223 */ +/* 224 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -23424,7 +23562,7 @@ module.exports = (input, options) => { /***/ }), -/* 224 */ +/* 225 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -23553,7 +23691,7 @@ module.exports = str => { /***/ }), -/* 225 */ +/* 226 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -23562,7 +23700,7 @@ __webpack_require__.r(__webpack_exports__); /* 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 export (binding) */ __webpack_require__.d(__webpack_exports__, "yarnWorkspacesInfo", function() { return yarnWorkspacesInfo; }); -/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(226); +/* harmony import */ var _child_process__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(227); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -23639,22 +23777,22 @@ async function yarnWorkspacesInfo(directory) { } /***/ }), -/* 226 */ +/* 227 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "spawn", function() { return spawn; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "spawnStreaming", function() { return spawnStreaming; }); -/* harmony import */ var stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(137); +/* harmony import */ var stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(138); /* harmony import */ var stream__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(stream__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(112); +/* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(113); /* harmony import */ var chalk__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(chalk__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(227); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(228); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(262); +/* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(263); /* harmony import */ var strong_log_transformer__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(strong_log_transformer__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(143); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(144); 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; } @@ -23738,23 +23876,23 @@ function spawnStreaming(command, args, opts, { } /***/ }), -/* 227 */ +/* 228 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const childProcess = __webpack_require__(228); -const crossSpawn = __webpack_require__(229); -const stripFinalNewline = __webpack_require__(242); -const npmRunPath = __webpack_require__(243); -const onetime = __webpack_require__(244); -const makeError = __webpack_require__(246); -const normalizeStdio = __webpack_require__(251); -const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(252); -const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(253); -const {mergePromise, getSpawnedPromise} = __webpack_require__(260); -const {joinCommand, parseCommand} = __webpack_require__(261); +const childProcess = __webpack_require__(229); +const crossSpawn = __webpack_require__(230); +const stripFinalNewline = __webpack_require__(243); +const npmRunPath = __webpack_require__(244); +const onetime = __webpack_require__(245); +const makeError = __webpack_require__(247); +const normalizeStdio = __webpack_require__(252); +const {spawnedKill, spawnedCancel, setupTimeout, setExitHandler} = __webpack_require__(253); +const {handleInput, getSpawnedResult, makeAllStream, validateInputSync} = __webpack_require__(254); +const {mergePromise, getSpawnedPromise} = __webpack_require__(261); +const {joinCommand, parseCommand} = __webpack_require__(262); const DEFAULT_MAX_BUFFER = 1000 * 1000 * 100; @@ -23768,7 +23906,7 @@ const getEnv = ({env: envOption, extendEnv, preferLocal, localDir, execPath}) => return env; }; -const handleArgs = (file, args, options = {}) => { +const handleArguments = (file, args, options = {}) => { const parsed = crossSpawn._parse(file, args, options); file = parsed.command; args = parsed.args; @@ -23816,7 +23954,7 @@ const handleOutput = (options, value, error) => { }; const execa = (file, args, options) => { - const parsed = handleArgs(file, args, options); + const parsed = handleArguments(file, args, options); const command = joinCommand(file, args); let spawned; @@ -23903,7 +24041,7 @@ const execa = (file, args, options) => { module.exports = execa; module.exports.sync = (file, args, options) => { - const parsed = handleArgs(file, args, options); + const parsed = handleArguments(file, args, options); const command = joinCommand(file, args); validateInputSync(parsed.options); @@ -24001,21 +24139,21 @@ module.exports.node = (scriptPath, args, options = {}) => { /***/ }), -/* 228 */ +/* 229 */ /***/ (function(module, exports) { module.exports = require("child_process"); /***/ }), -/* 229 */ +/* 230 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const cp = __webpack_require__(228); -const parse = __webpack_require__(230); -const enoent = __webpack_require__(241); +const cp = __webpack_require__(229); +const parse = __webpack_require__(231); +const enoent = __webpack_require__(242); function spawn(command, args, options) { // Parse the arguments @@ -24053,16 +24191,16 @@ module.exports._enoent = enoent; /***/ }), -/* 230 */ +/* 231 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const resolveCommand = __webpack_require__(231); -const escape = __webpack_require__(237); -const readShebang = __webpack_require__(238); +const resolveCommand = __webpack_require__(232); +const escape = __webpack_require__(238); +const readShebang = __webpack_require__(239); const isWin = process.platform === 'win32'; const isExecutableRegExp = /\.(?:com|exe)$/i; @@ -24151,15 +24289,15 @@ module.exports = parse; /***/ }), -/* 231 */ +/* 232 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const which = __webpack_require__(232); -const pathKey = __webpack_require__(236)(); +const which = __webpack_require__(233); +const pathKey = __webpack_require__(237)(); function resolveCommandAttempt(parsed, withoutPathExt) { const cwd = process.cwd(); @@ -24209,7 +24347,7 @@ module.exports = resolveCommand; /***/ }), -/* 232 */ +/* 233 */ /***/ (function(module, exports, __webpack_require__) { const isWindows = process.platform === 'win32' || @@ -24218,7 +24356,7 @@ const isWindows = process.platform === 'win32' || const path = __webpack_require__(4) const COLON = isWindows ? ';' : ':' -const isexe = __webpack_require__(233) +const isexe = __webpack_require__(234) const getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: 'ENOENT' }) @@ -24340,15 +24478,15 @@ which.sync = whichSync /***/ }), -/* 233 */ +/* 234 */ /***/ (function(module, exports, __webpack_require__) { -var fs = __webpack_require__(133) +var fs = __webpack_require__(134) var core if (process.platform === 'win32' || global.TESTING_WINDOWS) { - core = __webpack_require__(234) -} else { core = __webpack_require__(235) +} else { + core = __webpack_require__(236) } module.exports = isexe @@ -24403,13 +24541,13 @@ function sync (path, options) { /***/ }), -/* 234 */ +/* 235 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe isexe.sync = sync -var fs = __webpack_require__(133) +var fs = __webpack_require__(134) function checkPathExt (path, options) { var pathext = options.pathExt !== undefined ? @@ -24451,13 +24589,13 @@ function sync (path, options) { /***/ }), -/* 235 */ +/* 236 */ /***/ (function(module, exports, __webpack_require__) { module.exports = isexe isexe.sync = sync -var fs = __webpack_require__(133) +var fs = __webpack_require__(134) function isexe (path, options, cb) { fs.stat(path, function (er, stat) { @@ -24498,7 +24636,7 @@ function checkMode (stat, options) { /***/ }), -/* 236 */ +/* 237 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24521,7 +24659,7 @@ module.exports.default = pathKey; /***/ }), -/* 237 */ +/* 238 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24573,14 +24711,14 @@ module.exports.argument = escapeArgument; /***/ }), -/* 238 */ +/* 239 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); -const shebangCommand = __webpack_require__(239); +const fs = __webpack_require__(134); +const shebangCommand = __webpack_require__(240); function readShebang(command) { // Read the first 150 bytes from the file @@ -24603,12 +24741,12 @@ module.exports = readShebang; /***/ }), -/* 239 */ +/* 240 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const shebangRegex = __webpack_require__(240); +const shebangRegex = __webpack_require__(241); module.exports = (string = '') => { const match = string.match(shebangRegex); @@ -24629,7 +24767,7 @@ module.exports = (string = '') => { /***/ }), -/* 240 */ +/* 241 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24638,7 +24776,7 @@ module.exports = /^#!(.*)/; /***/ }), -/* 241 */ +/* 242 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24704,7 +24842,7 @@ module.exports = { /***/ }), -/* 242 */ +/* 243 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24727,13 +24865,13 @@ module.exports = input => { /***/ }), -/* 243 */ +/* 244 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathKey = __webpack_require__(236); +const pathKey = __webpack_require__(237); const npmRunPath = options => { options = { @@ -24781,12 +24919,12 @@ module.exports.env = options => { /***/ }), -/* 244 */ +/* 245 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const mimicFn = __webpack_require__(245); +const mimicFn = __webpack_require__(246); const calledFunctions = new WeakMap(); @@ -24838,7 +24976,7 @@ module.exports.callCount = fn => { /***/ }), -/* 245 */ +/* 246 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -24858,12 +24996,12 @@ module.exports.default = mimicFn; /***/ }), -/* 246 */ +/* 247 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {signalsByName} = __webpack_require__(247); +const {signalsByName} = __webpack_require__(248); const getErrorPrefix = ({timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled}) => { if (timedOut) { @@ -24951,14 +25089,14 @@ module.exports = makeError; /***/ }), -/* 247 */ +/* 248 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports,"__esModule",{value:true});exports.signalsByNumber=exports.signalsByName=void 0;var _os=__webpack_require__(120); +Object.defineProperty(exports,"__esModule",{value:true});exports.signalsByNumber=exports.signalsByName=void 0;var _os=__webpack_require__(121); -var _signals=__webpack_require__(248); -var _realtime=__webpack_require__(250); +var _signals=__webpack_require__(249); +var _realtime=__webpack_require__(251); @@ -25028,14 +25166,14 @@ const signalsByNumber=getSignalsByNumber();exports.signalsByNumber=signalsByNumb //# sourceMappingURL=main.js.map /***/ }), -/* 248 */ +/* 249 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -Object.defineProperty(exports,"__esModule",{value:true});exports.getSignals=void 0;var _os=__webpack_require__(120); +Object.defineProperty(exports,"__esModule",{value:true});exports.getSignals=void 0;var _os=__webpack_require__(121); -var _core=__webpack_require__(249); -var _realtime=__webpack_require__(250); +var _core=__webpack_require__(250); +var _realtime=__webpack_require__(251); @@ -25069,7 +25207,7 @@ return{name,number,description,supported,action,forced,standard}; //# sourceMappingURL=signals.js.map /***/ }), -/* 249 */ +/* 250 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25348,7 +25486,7 @@ standard:"other"}];exports.SIGNALS=SIGNALS; //# sourceMappingURL=core.js.map /***/ }), -/* 250 */ +/* 251 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25373,7 +25511,7 @@ const SIGRTMAX=64;exports.SIGRTMAX=SIGRTMAX; //# sourceMappingURL=realtime.js.map /***/ }), -/* 251 */ +/* 252 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25432,13 +25570,13 @@ module.exports.node = opts => { /***/ }), -/* 252 */ +/* 253 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const os = __webpack_require__(120); -const onExit = __webpack_require__(217); +const os = __webpack_require__(121); +const onExit = __webpack_require__(218); const DEFAULT_FORCE_KILL_TIMEOUT = 1000 * 5; @@ -25482,7 +25620,7 @@ const getForceKillAfterTimeout = ({forceKillAfterTimeout = true}) => { return DEFAULT_FORCE_KILL_TIMEOUT; } - if (!Number.isInteger(forceKillAfterTimeout) || forceKillAfterTimeout < 0) { + if (!Number.isFinite(forceKillAfterTimeout) || forceKillAfterTimeout < 0) { throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`); } @@ -25509,7 +25647,7 @@ const setupTimeout = (spawned, {timeout, killSignal = 'SIGTERM'}, spawnedPromise return spawnedPromise; } - if (!Number.isInteger(timeout) || timeout < 0) { + if (!Number.isFinite(timeout) || timeout < 0) { throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`); } @@ -25551,14 +25689,14 @@ module.exports = { /***/ }), -/* 253 */ +/* 254 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const isStream = __webpack_require__(254); -const getStream = __webpack_require__(255); -const mergeStream = __webpack_require__(259); +const isStream = __webpack_require__(255); +const getStream = __webpack_require__(256); +const mergeStream = __webpack_require__(260); // `input` option const handleInput = (spawned, input) => { @@ -25655,7 +25793,7 @@ module.exports = { /***/ }), -/* 254 */ +/* 255 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -25691,13 +25829,13 @@ module.exports = isStream; /***/ }), -/* 255 */ +/* 256 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pump = __webpack_require__(256); -const bufferStream = __webpack_require__(258); +const pump = __webpack_require__(257); +const bufferStream = __webpack_require__(259); class MaxBufferError extends Error { constructor() { @@ -25756,12 +25894,12 @@ module.exports.MaxBufferError = MaxBufferError; /***/ }), -/* 256 */ +/* 257 */ /***/ (function(module, exports, __webpack_require__) { -var once = __webpack_require__(161) -var eos = __webpack_require__(257) -var fs = __webpack_require__(133) // we only need fs to get the ReadStream and WriteStream prototypes +var once = __webpack_require__(162) +var eos = __webpack_require__(258) +var fs = __webpack_require__(134) // we only need fs to get the ReadStream and WriteStream prototypes var noop = function () {} var ancient = /^v?\.0/.test(process.version) @@ -25844,10 +25982,10 @@ module.exports = pump /***/ }), -/* 257 */ +/* 258 */ /***/ (function(module, exports, __webpack_require__) { -var once = __webpack_require__(161); +var once = __webpack_require__(162); var noop = function() {}; @@ -25944,12 +26082,12 @@ module.exports = eos; /***/ }), -/* 258 */ +/* 259 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {PassThrough: PassThroughStream} = __webpack_require__(137); +const {PassThrough: PassThroughStream} = __webpack_require__(138); module.exports = options => { options = {...options}; @@ -26003,13 +26141,13 @@ module.exports = options => { /***/ }), -/* 259 */ +/* 260 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const { PassThrough } = __webpack_require__(137); +const { PassThrough } = __webpack_require__(138); module.exports = function (/*streams...*/) { var sources = [] @@ -26051,7 +26189,7 @@ module.exports = function (/*streams...*/) { /***/ }), -/* 260 */ +/* 261 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26104,7 +26242,7 @@ module.exports = { /***/ }), -/* 261 */ +/* 262 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26149,7 +26287,7 @@ module.exports = { /***/ }), -/* 262 */ +/* 263 */ /***/ (function(module, exports, __webpack_require__) { // Copyright IBM Corp. 2014,2018. All Rights Reserved. @@ -26157,12 +26295,12 @@ module.exports = { // This file is licensed under the Apache License 2.0. // License text available at https://opensource.org/licenses/Apache-2.0 -module.exports = __webpack_require__(263); -module.exports.cli = __webpack_require__(267); +module.exports = __webpack_require__(264); +module.exports.cli = __webpack_require__(268); /***/ }), -/* 263 */ +/* 264 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26173,13 +26311,13 @@ module.exports.cli = __webpack_require__(267); -var stream = __webpack_require__(137); -var util = __webpack_require__(111); -var fs = __webpack_require__(133); +var stream = __webpack_require__(138); +var util = __webpack_require__(112); +var fs = __webpack_require__(134); -var through = __webpack_require__(264); -var duplexer = __webpack_require__(265); -var StringDecoder = __webpack_require__(266).StringDecoder; +var through = __webpack_require__(265); +var duplexer = __webpack_require__(266); +var StringDecoder = __webpack_require__(267).StringDecoder; module.exports = Logger; @@ -26368,10 +26506,10 @@ function lineMerger(host) { /***/ }), -/* 264 */ +/* 265 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(137) +var Stream = __webpack_require__(138) // through // @@ -26482,10 +26620,10 @@ function through (write, end, opts) { /***/ }), -/* 265 */ +/* 266 */ /***/ (function(module, exports, __webpack_require__) { -var Stream = __webpack_require__(137) +var Stream = __webpack_require__(138) var writeMethods = ["write", "end", "destroy"] var readMethods = ["resume", "pause"] var readEvents = ["data", "close"] @@ -26575,13 +26713,13 @@ function duplex(writer, reader) { /***/ }), -/* 266 */ +/* 267 */ /***/ (function(module, exports) { module.exports = require("string_decoder"); /***/ }), -/* 267 */ +/* 268 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -26592,11 +26730,11 @@ module.exports = require("string_decoder"); -var minimist = __webpack_require__(268); +var minimist = __webpack_require__(269); var path = __webpack_require__(4); -var Logger = __webpack_require__(263); -var pkg = __webpack_require__(269); +var Logger = __webpack_require__(264); +var pkg = __webpack_require__(270); module.exports = cli; @@ -26650,7 +26788,7 @@ function usage($0, p) { /***/ }), -/* 268 */ +/* 269 */ /***/ (function(module, exports) { module.exports = function (args, opts) { @@ -26901,29 +27039,29 @@ function isNumber (x) { /***/ }), -/* 269 */ +/* 270 */ /***/ (function(module) { module.exports = JSON.parse("{\"name\":\"strong-log-transformer\",\"version\":\"2.1.0\",\"description\":\"Stream transformer that prefixes lines with timestamps and other things.\",\"author\":\"Ryan Graham <ryan@strongloop.com>\",\"license\":\"Apache-2.0\",\"repository\":{\"type\":\"git\",\"url\":\"git://github.com/strongloop/strong-log-transformer\"},\"keywords\":[\"logging\",\"streams\"],\"bugs\":{\"url\":\"https://github.com/strongloop/strong-log-transformer/issues\"},\"homepage\":\"https://github.com/strongloop/strong-log-transformer\",\"directories\":{\"test\":\"test\"},\"bin\":{\"sl-log-transformer\":\"bin/sl-log-transformer.js\"},\"main\":\"index.js\",\"scripts\":{\"test\":\"tap --100 test/test-*\"},\"dependencies\":{\"duplexer\":\"^0.1.1\",\"minimist\":\"^1.2.0\",\"through\":\"^2.3.4\"},\"devDependencies\":{\"tap\":\"^12.0.1\"},\"engines\":{\"node\":\">=4\"}}"); /***/ }), -/* 270 */ +/* 271 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "workspacePackagePaths", function() { return workspacePackagePaths; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "copyWorkspacePackages", function() { return copyWorkspacePackages; }); -/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(146); +/* harmony import */ var glob__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(147); /* 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__(111); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(112); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(271); -/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(130); -/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(164); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(145); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(272); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(131); +/* harmony import */ var _package_json__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(165); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(146); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -27015,7 +27153,7 @@ function packagesFromGlobPattern({ } /***/ }), -/* 271 */ +/* 272 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; @@ -27085,21 +27223,21 @@ function getProjectPaths({ } /***/ }), -/* 272 */ +/* 273 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getAllChecksums", function() { return getAllChecksums; }); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(134); /* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_0__); -/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(273); +/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(274); /* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(111); +/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(112); /* harmony import */ var util__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(227); +/* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(228); /* harmony import */ var execa__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(execa__WEBPACK_IMPORTED_MODULE_3__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(274); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(275); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -27298,22 +27436,22 @@ async function getAllChecksums(kbn, log, yarnLock) { } /***/ }), -/* 273 */ +/* 274 */ /***/ (function(module, exports) { module.exports = require("crypto"); /***/ }), -/* 274 */ +/* 275 */ /***/ (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__(275); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* 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__(130); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(131); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -27424,7 +27562,7 @@ function resolveDepsForProject({ } /***/ }), -/* 275 */ +/* 276 */ /***/ (function(module, exports, __webpack_require__) { module.exports = @@ -27550,13 +27688,13 @@ exports.default = function (fn) { /* 2 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(111); +module.exports = __webpack_require__(112); /***/ }), /* 3 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(133); +module.exports = __webpack_require__(134); /***/ }), /* 4 */ @@ -28983,7 +29121,7 @@ module.exports = invariant; /* 9 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(273); +module.exports = __webpack_require__(274); /***/ }), /* 10 */, @@ -29409,7 +29547,7 @@ exports.default = Lockfile; /* 17 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(137); +module.exports = __webpack_require__(138); /***/ }), /* 18 */, @@ -29461,7 +29599,7 @@ function nullify(obj = {}) { /* 22 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(139); +module.exports = __webpack_require__(140); /***/ }), /* 23 */ @@ -29648,7 +29786,7 @@ module.exports = {}; /* 36 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(120); +module.exports = __webpack_require__(121); /***/ }), /* 37 */, @@ -29933,7 +30071,7 @@ exports.f = __webpack_require__(33) ? Object.defineProperty : function definePro /* 54 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(155); +module.exports = __webpack_require__(156); /***/ }), /* 55 */ @@ -31307,7 +31445,7 @@ function onceStrict (fn) { /* 63 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(276); +module.exports = __webpack_require__(277); /***/ }), /* 64 */, @@ -32245,7 +32383,7 @@ module.exports.win32 = win32; /* 79 */ /***/ (function(module, exports) { -module.exports = __webpack_require__(121); +module.exports = __webpack_require__(122); /***/ }), /* 80 */, @@ -37702,19 +37840,19 @@ module.exports = process && support(supportLevel); /******/ ]); /***/ }), -/* 276 */ +/* 277 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), -/* 277 */ +/* 278 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BootstrapCacheFile", function() { return BootstrapCacheFile; }); -/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(133); +/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(134); /* 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__); @@ -37805,18 +37943,18 @@ class BootstrapCacheFile { } /***/ }), -/* 278 */ +/* 279 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "validateYarnLock", function() { return validateYarnLock; }); -/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(275); +/* harmony import */ var _yarnpkg_lockfile__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(276); /* 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 _fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(130); -/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(143); +/* harmony import */ var _fs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(131); +/* harmony import */ var _log__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(144); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -37964,20 +38102,20 @@ async function validateYarnLock(kbn, yarnLock) { } /***/ }), -/* 279 */ +/* 280 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CleanCommand", function() { return CleanCommand; }); -/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(280); +/* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(281); /* harmony import */ var del__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(del__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(367); /* harmony import */ var ora__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(ora__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 _utils_fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(130); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(143); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(131); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(144); /* * Licensed to Elasticsearch B.V. under one or more contributor * license agreements. See the NOTICE file distributed with @@ -38072,17 +38210,17 @@ const CleanCommand = { }; /***/ }), -/* 280 */ +/* 281 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {promisify} = __webpack_require__(111); +const {promisify} = __webpack_require__(112); const path = __webpack_require__(4); -const globby = __webpack_require__(281); -const isGlob = __webpack_require__(359); -const slash = __webpack_require__(357); -const gracefulFs = __webpack_require__(132); +const globby = __webpack_require__(282); +const isGlob = __webpack_require__(294); +const slash = __webpack_require__(358); +const gracefulFs = __webpack_require__(133); const isPathCwd = __webpack_require__(360); const isPathInside = __webpack_require__(361); const rimraf = __webpack_require__(362); @@ -38200,19 +38338,19 @@ module.exports.sync = (patterns, {force, dryRun, cwd = process.cwd(), ...options /***/ }), -/* 281 */ +/* 282 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); -const arrayUnion = __webpack_require__(282); -const merge2 = __webpack_require__(283); -const glob = __webpack_require__(146); -const fastGlob = __webpack_require__(284); -const dirGlob = __webpack_require__(353); -const gitignore = __webpack_require__(355); -const {FilterStream, UniqueStream} = __webpack_require__(358); +const fs = __webpack_require__(134); +const arrayUnion = __webpack_require__(283); +const merge2 = __webpack_require__(284); +const glob = __webpack_require__(147); +const fastGlob = __webpack_require__(285); +const dirGlob = __webpack_require__(354); +const gitignore = __webpack_require__(356); +const {FilterStream, UniqueStream} = __webpack_require__(359); const DEFAULT_FILTER = () => false; @@ -38385,7 +38523,7 @@ module.exports.gitignore = gitignore; /***/ }), -/* 282 */ +/* 283 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38397,7 +38535,7 @@ module.exports = (...arguments_) => { /***/ }), -/* 283 */ +/* 284 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38409,7 +38547,7 @@ module.exports = (...arguments_) => { * Copyright (c) 2014-2020 Teambition * Licensed under the MIT license. */ -const Stream = __webpack_require__(137) +const Stream = __webpack_require__(138) const PassThrough = Stream.PassThrough const slice = Array.prototype.slice @@ -38548,17 +38686,17 @@ function pauseStreams (streams, options) { /***/ }), -/* 284 */ +/* 285 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const taskManager = __webpack_require__(285); -const async_1 = __webpack_require__(314); -const stream_1 = __webpack_require__(349); -const sync_1 = __webpack_require__(350); -const settings_1 = __webpack_require__(352); -const utils = __webpack_require__(286); +const taskManager = __webpack_require__(286); +const async_1 = __webpack_require__(315); +const stream_1 = __webpack_require__(350); +const sync_1 = __webpack_require__(351); +const settings_1 = __webpack_require__(353); +const utils = __webpack_require__(287); async function FastGlob(source, options) { assertPatternsInput(source); const works = getWorks(source, async_1.default, options); @@ -38622,13 +38760,13 @@ module.exports = FastGlob; /***/ }), -/* 285 */ +/* 286 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(286); +const utils = __webpack_require__(287); function generate(patterns, settings) { const positivePatterns = getPositivePatterns(patterns); const negativePatterns = getNegativePatternsAsPositive(patterns, settings.ignore); @@ -38693,30 +38831,30 @@ exports.convertPatternGroupToTask = convertPatternGroupToTask; /***/ }), -/* 286 */ +/* 287 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const array = __webpack_require__(287); +const array = __webpack_require__(288); exports.array = array; -const errno = __webpack_require__(288); +const errno = __webpack_require__(289); exports.errno = errno; -const fs = __webpack_require__(289); +const fs = __webpack_require__(290); exports.fs = fs; -const path = __webpack_require__(290); +const path = __webpack_require__(291); exports.path = path; -const pattern = __webpack_require__(291); +const pattern = __webpack_require__(292); exports.pattern = pattern; -const stream = __webpack_require__(312); +const stream = __webpack_require__(313); exports.stream = stream; -const string = __webpack_require__(313); +const string = __webpack_require__(314); exports.string = string; /***/ }), -/* 287 */ +/* 288 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38744,7 +38882,7 @@ exports.splitWhen = splitWhen; /***/ }), -/* 288 */ +/* 289 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38757,7 +38895,7 @@ exports.isEnoentCodeError = isEnoentCodeError; /***/ }), -/* 289 */ +/* 290 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38782,7 +38920,7 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 290 */ +/* 291 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -38821,16 +38959,16 @@ exports.removeLeadingDotSegment = removeLeadingDotSegment; /***/ }), -/* 291 */ +/* 292 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const globParent = __webpack_require__(292); -const micromatch = __webpack_require__(295); -const picomatch = __webpack_require__(306); +const globParent = __webpack_require__(293); +const micromatch = __webpack_require__(296); +const picomatch = __webpack_require__(307); const GLOBSTAR = '**'; const ESCAPE_SYMBOL = '\\'; const COMMON_GLOB_SYMBOLS_RE = /[*?]|^!/; @@ -38940,15 +39078,15 @@ exports.matchAny = matchAny; /***/ }), -/* 292 */ +/* 293 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isGlob = __webpack_require__(293); +var isGlob = __webpack_require__(294); var pathPosixDirname = __webpack_require__(4).posix.dirname; -var isWin32 = __webpack_require__(120).platform() === 'win32'; +var isWin32 = __webpack_require__(121).platform() === 'win32'; var slash = '/'; var backslash = /\\/g; @@ -38988,7 +39126,7 @@ module.exports = function globParent(str, opts) { /***/ }), -/* 293 */ +/* 294 */ /***/ (function(module, exports, __webpack_require__) { /*! @@ -38998,7 +39136,7 @@ module.exports = function globParent(str, opts) { * Released under the MIT License. */ -var isExtglob = __webpack_require__(294); +var isExtglob = __webpack_require__(295); var chars = { '{': '}', '(': ')', '[': ']'}; var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; @@ -39042,7 +39180,7 @@ module.exports = function isGlob(str, options) { /***/ }), -/* 294 */ +/* 295 */ /***/ (function(module, exports) { /*! @@ -39068,16 +39206,16 @@ module.exports = function isExtglob(str) { /***/ }), -/* 295 */ +/* 296 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const util = __webpack_require__(111); -const braces = __webpack_require__(296); -const picomatch = __webpack_require__(306); -const utils = __webpack_require__(309); +const util = __webpack_require__(112); +const braces = __webpack_require__(297); +const picomatch = __webpack_require__(307); +const utils = __webpack_require__(310); const isEmptyString = val => typeof val === 'string' && (val === '' || val === './'); /** @@ -39542,16 +39680,16 @@ module.exports = micromatch; /***/ }), -/* 296 */ +/* 297 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(297); -const compile = __webpack_require__(299); -const expand = __webpack_require__(303); -const parse = __webpack_require__(304); +const stringify = __webpack_require__(298); +const compile = __webpack_require__(300); +const expand = __webpack_require__(304); +const parse = __webpack_require__(305); /** * Expand the given pattern or create a regex-compatible string. @@ -39719,13 +39857,13 @@ module.exports = braces; /***/ }), -/* 297 */ +/* 298 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(298); +const utils = __webpack_require__(299); module.exports = (ast, options = {}) => { let stringify = (node, parent = {}) => { @@ -39758,7 +39896,7 @@ module.exports = (ast, options = {}) => { /***/ }), -/* 298 */ +/* 299 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39877,14 +40015,14 @@ exports.flatten = (...args) => { /***/ }), -/* 299 */ +/* 300 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(300); -const utils = __webpack_require__(298); +const fill = __webpack_require__(301); +const utils = __webpack_require__(299); const compile = (ast, options = {}) => { let walk = (node, parent = {}) => { @@ -39941,7 +40079,7 @@ module.exports = compile; /***/ }), -/* 300 */ +/* 301 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -39954,8 +40092,8 @@ module.exports = compile; -const util = __webpack_require__(111); -const toRegexRange = __webpack_require__(301); +const util = __webpack_require__(112); +const toRegexRange = __webpack_require__(302); const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); @@ -40197,7 +40335,7 @@ module.exports = fill; /***/ }), -/* 301 */ +/* 302 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40210,7 +40348,7 @@ module.exports = fill; -const isNumber = __webpack_require__(302); +const isNumber = __webpack_require__(303); const toRegexRange = (min, max, options) => { if (isNumber(min) === false) { @@ -40492,7 +40630,7 @@ module.exports = toRegexRange; /***/ }), -/* 302 */ +/* 303 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -40517,15 +40655,15 @@ module.exports = function(num) { /***/ }), -/* 303 */ +/* 304 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fill = __webpack_require__(300); -const stringify = __webpack_require__(297); -const utils = __webpack_require__(298); +const fill = __webpack_require__(301); +const stringify = __webpack_require__(298); +const utils = __webpack_require__(299); const append = (queue = '', stash = '', enclose = false) => { let result = []; @@ -40637,13 +40775,13 @@ module.exports = expand; /***/ }), -/* 304 */ +/* 305 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const stringify = __webpack_require__(297); +const stringify = __webpack_require__(298); /** * Constants @@ -40665,7 +40803,7 @@ const { CHAR_SINGLE_QUOTE, /* ' */ CHAR_NO_BREAK_SPACE, CHAR_ZERO_WIDTH_NOBREAK_SPACE -} = __webpack_require__(305); +} = __webpack_require__(306); /** * parse @@ -40977,7 +41115,7 @@ module.exports = parse; /***/ }), -/* 305 */ +/* 306 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41041,27 +41179,27 @@ module.exports = { /***/ }), -/* 306 */ +/* 307 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -module.exports = __webpack_require__(307); +module.exports = __webpack_require__(308); /***/ }), -/* 307 */ +/* 308 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const scan = __webpack_require__(308); -const parse = __webpack_require__(311); -const utils = __webpack_require__(309); -const constants = __webpack_require__(310); +const scan = __webpack_require__(309); +const parse = __webpack_require__(312); +const utils = __webpack_require__(310); +const constants = __webpack_require__(311); const isObject = val => val && typeof val === 'object' && !Array.isArray(val); /** @@ -41397,13 +41535,13 @@ module.exports = picomatch; /***/ }), -/* 308 */ +/* 309 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const utils = __webpack_require__(309); +const utils = __webpack_require__(310); const { CHAR_ASTERISK, /* * */ CHAR_AT, /* @ */ @@ -41420,7 +41558,7 @@ const { CHAR_RIGHT_CURLY_BRACE, /* } */ CHAR_RIGHT_PARENTHESES, /* ) */ CHAR_RIGHT_SQUARE_BRACKET /* ] */ -} = __webpack_require__(310); +} = __webpack_require__(311); const isPathSeparator = code => { return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; @@ -41787,7 +41925,7 @@ module.exports = scan; /***/ }), -/* 309 */ +/* 310 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -41800,7 +41938,7 @@ const { REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL -} = __webpack_require__(310); +} = __webpack_require__(311); exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); @@ -41858,7 +41996,7 @@ exports.wrapOutput = (input, state = {}, options = {}) => { /***/ }), -/* 310 */ +/* 311 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -42044,14 +42182,14 @@ module.exports = { /***/ }), -/* 311 */ +/* 312 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const constants = __webpack_require__(310); -const utils = __webpack_require__(309); +const constants = __webpack_require__(311); +const utils = __webpack_require__(310); /** * Constants @@ -43129,13 +43267,13 @@ module.exports = parse; /***/ }), -/* 312 */ +/* 313 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const merge2 = __webpack_require__(283); +const merge2 = __webpack_require__(284); function merge(streams) { const mergedStream = merge2(streams); streams.forEach((stream) => { @@ -43152,7 +43290,7 @@ function propagateCloseEventToSources(streams) { /***/ }), -/* 313 */ +/* 314 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43169,14 +43307,14 @@ exports.isEmpty = isEmpty; /***/ }), -/* 314 */ +/* 315 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(315); -const provider_1 = __webpack_require__(342); +const stream_1 = __webpack_require__(316); +const provider_1 = __webpack_require__(343); class ProviderAsync extends provider_1.default { constructor() { super(...arguments); @@ -43204,16 +43342,16 @@ exports.default = ProviderAsync; /***/ }), -/* 315 */ +/* 316 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(137); -const fsStat = __webpack_require__(316); -const fsWalk = __webpack_require__(321); -const reader_1 = __webpack_require__(341); +const stream_1 = __webpack_require__(138); +const fsStat = __webpack_require__(317); +const fsWalk = __webpack_require__(322); +const reader_1 = __webpack_require__(342); class ReaderStream extends reader_1.default { constructor() { super(...arguments); @@ -43266,15 +43404,15 @@ exports.default = ReaderStream; /***/ }), -/* 316 */ +/* 317 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(317); -const sync = __webpack_require__(318); -const settings_1 = __webpack_require__(319); +const async = __webpack_require__(318); +const sync = __webpack_require__(319); +const settings_1 = __webpack_require__(320); exports.Settings = settings_1.default; function stat(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43297,7 +43435,7 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 317 */ +/* 318 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43335,7 +43473,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 318 */ +/* 319 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43364,13 +43502,13 @@ exports.read = read; /***/ }), -/* 319 */ +/* 320 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(320); +const fs = __webpack_require__(321); class Settings { constructor(_options = {}) { this._options = _options; @@ -43387,13 +43525,13 @@ exports.default = Settings; /***/ }), -/* 320 */ +/* 321 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(133); +const fs = __webpack_require__(134); exports.FILE_SYSTEM_ADAPTER = { lstat: fs.lstat, stat: fs.stat, @@ -43410,16 +43548,16 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 321 */ +/* 322 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(322); -const stream_1 = __webpack_require__(337); -const sync_1 = __webpack_require__(338); -const settings_1 = __webpack_require__(340); +const async_1 = __webpack_require__(323); +const stream_1 = __webpack_require__(338); +const sync_1 = __webpack_require__(339); +const settings_1 = __webpack_require__(341); exports.Settings = settings_1.default; function walk(directory, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43449,13 +43587,13 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 322 */ +/* 323 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async_1 = __webpack_require__(323); +const async_1 = __webpack_require__(324); class AsyncProvider { constructor(_root, _settings) { this._root = _root; @@ -43486,17 +43624,17 @@ function callSuccessCallback(callback, entries) { /***/ }), -/* 323 */ +/* 324 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const events_1 = __webpack_require__(155); -const fsScandir = __webpack_require__(324); -const fastq = __webpack_require__(333); -const common = __webpack_require__(335); -const reader_1 = __webpack_require__(336); +const events_1 = __webpack_require__(156); +const fsScandir = __webpack_require__(325); +const fastq = __webpack_require__(334); +const common = __webpack_require__(336); +const reader_1 = __webpack_require__(337); class AsyncReader extends reader_1.default { constructor(_root, _settings) { super(_root, _settings); @@ -43586,15 +43724,15 @@ exports.default = AsyncReader; /***/ }), -/* 324 */ +/* 325 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const async = __webpack_require__(325); -const sync = __webpack_require__(330); -const settings_1 = __webpack_require__(331); +const async = __webpack_require__(326); +const sync = __webpack_require__(331); +const settings_1 = __webpack_require__(332); exports.Settings = settings_1.default; function scandir(path, optionsOrSettingsOrCallback, callback) { if (typeof optionsOrSettingsOrCallback === 'function') { @@ -43617,16 +43755,16 @@ function getSettings(settingsOrOptions = {}) { /***/ }), -/* 325 */ +/* 326 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(316); -const rpl = __webpack_require__(326); -const constants_1 = __webpack_require__(327); -const utils = __webpack_require__(328); +const fsStat = __webpack_require__(317); +const rpl = __webpack_require__(327); +const constants_1 = __webpack_require__(328); +const utils = __webpack_require__(329); function read(directory, settings, callback) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings, callback); @@ -43714,7 +43852,7 @@ function callSuccessCallback(callback, result) { /***/ }), -/* 326 */ +/* 327 */ /***/ (function(module, exports) { module.exports = runParallel @@ -43768,7 +43906,7 @@ function runParallel (tasks, cb) { /***/ }), -/* 327 */ +/* 328 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43788,18 +43926,18 @@ exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_B /***/ }), -/* 328 */ +/* 329 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(329); +const fs = __webpack_require__(330); exports.fs = fs; /***/ }), -/* 329 */ +/* 330 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -43824,15 +43962,15 @@ exports.createDirentFromStats = createDirentFromStats; /***/ }), -/* 330 */ +/* 331 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(316); -const constants_1 = __webpack_require__(327); -const utils = __webpack_require__(328); +const fsStat = __webpack_require__(317); +const constants_1 = __webpack_require__(328); +const utils = __webpack_require__(329); function read(directory, settings) { if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) { return readdirWithFileTypes(directory, settings); @@ -43883,15 +44021,15 @@ exports.readdir = readdir; /***/ }), -/* 331 */ +/* 332 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(316); -const fs = __webpack_require__(332); +const fsStat = __webpack_require__(317); +const fs = __webpack_require__(333); class Settings { constructor(_options = {}) { this._options = _options; @@ -43914,13 +44052,13 @@ exports.default = Settings; /***/ }), -/* 332 */ +/* 333 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(133); +const fs = __webpack_require__(134); exports.FILE_SYSTEM_ADAPTER = { lstat: fs.lstat, stat: fs.stat, @@ -43939,13 +44077,13 @@ exports.createFileSystemAdapter = createFileSystemAdapter; /***/ }), -/* 333 */ +/* 334 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var reusify = __webpack_require__(334) +var reusify = __webpack_require__(335) function fastqueue (context, worker, concurrency) { if (typeof context === 'function') { @@ -44119,7 +44257,7 @@ module.exports = fastqueue /***/ }), -/* 334 */ +/* 335 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44159,7 +44297,7 @@ module.exports = reusify /***/ }), -/* 335 */ +/* 336 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -44190,13 +44328,13 @@ exports.joinPathSegments = joinPathSegments; /***/ }), -/* 336 */ +/* 337 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const common = __webpack_require__(335); +const common = __webpack_require__(336); class Reader { constructor(_root, _settings) { this._root = _root; @@ -44208,14 +44346,14 @@ exports.default = Reader; /***/ }), -/* 337 */ +/* 338 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(137); -const async_1 = __webpack_require__(323); +const stream_1 = __webpack_require__(138); +const async_1 = __webpack_require__(324); class StreamProvider { constructor(_root, _settings) { this._root = _root; @@ -44245,13 +44383,13 @@ exports.default = StreamProvider; /***/ }), -/* 338 */ +/* 339 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(339); +const sync_1 = __webpack_require__(340); class SyncProvider { constructor(_root, _settings) { this._root = _root; @@ -44266,15 +44404,15 @@ exports.default = SyncProvider; /***/ }), -/* 339 */ +/* 340 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsScandir = __webpack_require__(324); -const common = __webpack_require__(335); -const reader_1 = __webpack_require__(336); +const fsScandir = __webpack_require__(325); +const common = __webpack_require__(336); +const reader_1 = __webpack_require__(337); class SyncReader extends reader_1.default { constructor() { super(...arguments); @@ -44332,14 +44470,14 @@ exports.default = SyncReader; /***/ }), -/* 340 */ +/* 341 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsScandir = __webpack_require__(324); +const fsScandir = __webpack_require__(325); class Settings { constructor(_options = {}) { this._options = _options; @@ -44365,15 +44503,15 @@ exports.default = Settings; /***/ }), -/* 341 */ +/* 342 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const fsStat = __webpack_require__(316); -const utils = __webpack_require__(286); +const fsStat = __webpack_require__(317); +const utils = __webpack_require__(287); class Reader { constructor(_settings) { this._settings = _settings; @@ -44405,17 +44543,17 @@ exports.default = Reader; /***/ }), -/* 342 */ +/* 343 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const path = __webpack_require__(4); -const deep_1 = __webpack_require__(343); -const entry_1 = __webpack_require__(346); -const error_1 = __webpack_require__(347); -const entry_2 = __webpack_require__(348); +const deep_1 = __webpack_require__(344); +const entry_1 = __webpack_require__(347); +const error_1 = __webpack_require__(348); +const entry_2 = __webpack_require__(349); class Provider { constructor(_settings) { this._settings = _settings; @@ -44460,14 +44598,14 @@ exports.default = Provider; /***/ }), -/* 343 */ +/* 344 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(286); -const partial_1 = __webpack_require__(344); +const utils = __webpack_require__(287); +const partial_1 = __webpack_require__(345); class DeepFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -44521,13 +44659,13 @@ exports.default = DeepFilter; /***/ }), -/* 344 */ +/* 345 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const matcher_1 = __webpack_require__(345); +const matcher_1 = __webpack_require__(346); class PartialMatcher extends matcher_1.default { match(filepath) { const parts = filepath.split('/'); @@ -44566,13 +44704,13 @@ exports.default = PartialMatcher; /***/ }), -/* 345 */ +/* 346 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(286); +const utils = __webpack_require__(287); class Matcher { constructor(_patterns, _settings, _micromatchOptions) { this._patterns = _patterns; @@ -44623,13 +44761,13 @@ exports.default = Matcher; /***/ }), -/* 346 */ +/* 347 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(286); +const utils = __webpack_require__(287); class EntryFilter { constructor(_settings, _micromatchOptions) { this._settings = _settings; @@ -44685,13 +44823,13 @@ exports.default = EntryFilter; /***/ }), -/* 347 */ +/* 348 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(286); +const utils = __webpack_require__(287); class ErrorFilter { constructor(_settings) { this._settings = _settings; @@ -44707,13 +44845,13 @@ exports.default = ErrorFilter; /***/ }), -/* 348 */ +/* 349 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const utils = __webpack_require__(286); +const utils = __webpack_require__(287); class EntryTransformer { constructor(_settings) { this._settings = _settings; @@ -44740,15 +44878,15 @@ exports.default = EntryTransformer; /***/ }), -/* 349 */ +/* 350 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const stream_1 = __webpack_require__(137); -const stream_2 = __webpack_require__(315); -const provider_1 = __webpack_require__(342); +const stream_1 = __webpack_require__(138); +const stream_2 = __webpack_require__(316); +const provider_1 = __webpack_require__(343); class ProviderStream extends provider_1.default { constructor() { super(...arguments); @@ -44778,14 +44916,14 @@ exports.default = ProviderStream; /***/ }), -/* 350 */ +/* 351 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const sync_1 = __webpack_require__(351); -const provider_1 = __webpack_require__(342); +const sync_1 = __webpack_require__(352); +const provider_1 = __webpack_require__(343); class ProviderSync extends provider_1.default { constructor() { super(...arguments); @@ -44808,15 +44946,15 @@ exports.default = ProviderSync; /***/ }), -/* 351 */ +/* 352 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsStat = __webpack_require__(316); -const fsWalk = __webpack_require__(321); -const reader_1 = __webpack_require__(341); +const fsStat = __webpack_require__(317); +const fsWalk = __webpack_require__(322); +const reader_1 = __webpack_require__(342); class ReaderSync extends reader_1.default { constructor() { super(...arguments); @@ -44858,14 +44996,14 @@ exports.default = ReaderSync; /***/ }), -/* 352 */ +/* 353 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(133); -const os = __webpack_require__(120); +const fs = __webpack_require__(134); +const os = __webpack_require__(121); const CPU_COUNT = os.cpus().length; exports.DEFAULT_FILE_SYSTEM_ADAPTER = { lstat: fs.lstat, @@ -44917,13 +45055,13 @@ exports.default = Settings; /***/ }), -/* 353 */ +/* 354 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(354); +const pathType = __webpack_require__(355); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -44999,13 +45137,13 @@ module.exports.sync = (input, options) => { /***/ }), -/* 354 */ +/* 355 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {promisify} = __webpack_require__(111); -const fs = __webpack_require__(133); +const {promisify} = __webpack_require__(112); +const fs = __webpack_require__(134); async function isType(fsStatType, statsMethodName, filePath) { if (typeof filePath !== 'string') { @@ -45049,17 +45187,17 @@ exports.isSymlinkSync = isTypeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 355 */ +/* 356 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {promisify} = __webpack_require__(111); -const fs = __webpack_require__(133); +const {promisify} = __webpack_require__(112); +const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(284); -const gitIgnore = __webpack_require__(356); -const slash = __webpack_require__(357); +const fastGlob = __webpack_require__(285); +const gitIgnore = __webpack_require__(357); +const slash = __webpack_require__(358); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -45173,7 +45311,7 @@ module.exports.sync = options => { /***/ }), -/* 356 */ +/* 357 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -45776,7 +45914,7 @@ if ( /***/ }), -/* 357 */ +/* 358 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -45794,12 +45932,12 @@ module.exports = path => { /***/ }), -/* 358 */ +/* 359 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {Transform} = __webpack_require__(137); +const {Transform} = __webpack_require__(138); class ObjectTransform extends Transform { constructor() { @@ -45846,60 +45984,6 @@ module.exports = { }; -/***/ }), -/* 359 */ -/***/ (function(module, exports, __webpack_require__) { - -/*! - * is-glob <https://github.com/jonschlinkert/is-glob> - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ - -var isExtglob = __webpack_require__(294); -var chars = { '{': '}', '(': ')', '[': ']'}; -var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; -var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; - -module.exports = function isGlob(str, options) { - if (typeof str !== 'string' || str === '') { - return false; - } - - if (isExtglob(str)) { - return true; - } - - var regex = strictRegex; - var match; - - // optionally relax regex - if (options && options.strict === false) { - regex = relaxedRegex; - } - - while ((match = regex.exec(str))) { - if (match[2]) return true; - var idx = match.index + match[0].length; - - // if an open bracket/brace/paren is escaped, - // set the index to the next closing character - var open = match[1]; - var close = open ? chars[open] : null; - if (open && close) { - var n = str.indexOf(close, idx); - if (n !== -1) { - idx = n + 1; - } - } - - str = str.slice(idx); - } - return false; -}; - - /***/ }), /* 360 */ /***/ (function(module, exports, __webpack_require__) { @@ -45954,12 +46038,12 @@ module.exports = (childPath, parentPath) => { /* 362 */ /***/ (function(module, exports, __webpack_require__) { -const assert = __webpack_require__(139) +const assert = __webpack_require__(140) const path = __webpack_require__(4) -const fs = __webpack_require__(133) +const fs = __webpack_require__(134) let glob = undefined try { - glob = __webpack_require__(146) + glob = __webpack_require__(147) } catch (_err) { // treat glob as optional. } @@ -46506,7 +46590,7 @@ module.exports = (string, count = 1, options) => { "use strict"; -const os = __webpack_require__(120); +const os = __webpack_require__(121); const extractPathRegex = /\s+at.*(?:\(|\s)(.*)\)?/; const pathRegex = /^(?:(?:(?:node|(?:internal\/[\w/]*|.*node_modules\/(?:babel-polyfill|pirates)\/.*)?\w+)\.js:\d+:\d+)|native)/; @@ -46555,13 +46639,13 @@ module.exports = (stack, options) => { const readline = __webpack_require__(368); const chalk = __webpack_require__(369); -const cliCursor = __webpack_require__(374); -const cliSpinners = __webpack_require__(376); -const logSymbols = __webpack_require__(378); -const stripAnsi = __webpack_require__(384); -const wcwidth = __webpack_require__(386); -const isInteractive = __webpack_require__(390); -const MuteStream = __webpack_require__(391); +const cliCursor = __webpack_require__(376); +const cliSpinners = __webpack_require__(380); +const logSymbols = __webpack_require__(382); +const stripAnsi = __webpack_require__(391); +const wcwidth = __webpack_require__(393); +const isInteractive = __webpack_require__(397); +const MuteStream = __webpack_require__(398); const TEXT = Symbol('text'); const PREFIX_TEXT = Symbol('prefixText'); @@ -46925,12 +47009,12 @@ module.exports = require("readline"); "use strict"; -const ansiStyles = __webpack_require__(113); -const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(370); +const ansiStyles = __webpack_require__(370); +const {stdout: stdoutColor, stderr: stderrColor} = __webpack_require__(120); const { stringReplaceAll, stringEncaseCRLFWithFirstIndex -} = __webpack_require__(372); +} = __webpack_require__(374); // `supportsColor.level` → `ansiStyles.color[name]` mapping const levelMapping = [ @@ -47131,7 +47215,7 @@ const chalkTag = (chalk, ...strings) => { } if (template === undefined) { - template = __webpack_require__(373); + template = __webpack_require__(375); } return template(chalk, parts.join('')); @@ -47164,1035 +47248,1263 @@ module.exports = chalk; /***/ (function(module, exports, __webpack_require__) { "use strict"; +/* WEBPACK VAR INJECTION */(function(module) { -const os = __webpack_require__(120); -const tty = __webpack_require__(121); -const hasFlag = __webpack_require__(371); +const wrapAnsi16 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${code + offset}m`; +}; -const {env} = process; +const wrapAnsi256 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${38 + offset};5;${code}m`; +}; -let forceColor; -if (hasFlag('no-color') || - hasFlag('no-colors') || - hasFlag('color=false') || - hasFlag('color=never')) { - forceColor = 0; -} else if (hasFlag('color') || - hasFlag('colors') || - hasFlag('color=true') || - hasFlag('color=always')) { - forceColor = 1; -} +const wrapAnsi16m = (fn, offset) => (...args) => { + const rgb = fn(...args); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; -if ('FORCE_COLOR' in env) { - if (env.FORCE_COLOR === 'true') { - forceColor = 1; - } else if (env.FORCE_COLOR === 'false') { - forceColor = 0; - } else { - forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); - } -} +const ansi2ansi = n => n; +const rgb2rgb = (r, g, b) => [r, g, b]; -function translateLevel(level) { - if (level === 0) { - return false; - } +const setLazyProperty = (object, property, get) => { + Object.defineProperty(object, property, { + get: () => { + const value = get(); - return { - level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; -} + Object.defineProperty(object, property, { + value, + enumerable: true, + configurable: true + }); -function supportsColor(haveStream, streamIsTTY) { - if (forceColor === 0) { - return 0; - } + return value; + }, + enumerable: true, + configurable: true + }); +}; - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; +/** @type {typeof import('color-convert')} */ +let colorConvert; +const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { + if (colorConvert === undefined) { + colorConvert = __webpack_require__(371); } - if (hasFlag('color=256')) { - return 2; - } + const offset = isBackground ? 10 : 0; + const styles = {}; - if (haveStream && !streamIsTTY && forceColor === undefined) { - return 0; + for (const [sourceSpace, suite] of Object.entries(colorConvert)) { + const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace; + if (sourceSpace === targetSpace) { + styles[name] = wrap(identity, offset); + } else if (typeof suite === 'object') { + styles[name] = wrap(suite[targetSpace], offset); + } } - const min = forceColor || 0; - - if (env.TERM === 'dumb') { - return min; - } + return styles; +}; - if (process.platform === 'win32') { - // 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(osRelease[0]) >= 10 && - Number(osRelease[2]) >= 10586 - ) { - return Number(osRelease[2]) >= 14931 ? 3 : 2; - } +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], - return 1; - } + // Bright color + blackBright: [90, 39], + 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], - if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { - return 1; + // 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] } + }; - return min; - } - - if ('TEAMCITY_VERSION' in env) { - return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; - } - - if ('GITHUB_ACTIONS' in env) { - return 1; - } + // Alias bright black as gray (and grey) + styles.color.gray = styles.color.blackBright; + styles.bgColor.bgGray = styles.bgColor.bgBlackBright; + styles.color.grey = styles.color.blackBright; + styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; - if (env.COLORTERM === 'truecolor') { - return 3; - } + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; - if ('TERM_PROGRAM' in env) { - const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + group[styleName] = styles[styleName]; - switch (env.TERM_PROGRAM) { - case 'iTerm.app': - return version >= 3 ? 3 : 2; - case 'Apple_Terminal': - return 2; - // No default + codes.set(style[0], style[1]); } - } - if (/-256(color)?$/i.test(env.TERM)) { - return 2; + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); } - if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { - return 1; - } + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); - if ('COLORTERM' in env) { - return 1; - } + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; - return min; -} + setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false)); + setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true)); -function getSupportLevel(stream) { - const level = supportsColor(stream, stream && stream.isTTY); - return translateLevel(level); + return styles; } -module.exports = { - supportsColor: getSupportLevel, - stdout: translateLevel(supportsColor(true, tty.isatty(1))), - stderr: translateLevel(supportsColor(true, tty.isatty(2))) -}; +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) /***/ }), /* 371 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - +const conversions = __webpack_require__(372); +const route = __webpack_require__(373); -module.exports = (flag, argv = process.argv) => { - const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const position = argv.indexOf(prefix + flag); - const terminatorPosition = argv.indexOf('--'); - return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); -}; +const convert = {}; +const models = Object.keys(conversions); -/***/ }), -/* 372 */ -/***/ (function(module, exports, __webpack_require__) { +function wrapRaw(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; + if (arg0 === undefined || arg0 === null) { + return arg0; + } -"use strict"; + if (arg0.length > 1) { + args = arg0; + } + return fn(args); + }; -const stringReplaceAll = (string, substring, replacer) => { - let index = string.indexOf(substring); - if (index === -1) { - return string; + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; } - 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; -}; + return wrappedFn; +} -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); +function wrapRounded(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; - returnValue += string.substr(endIndex); - return returnValue; -}; + if (arg0 === undefined || arg0 === null) { + return arg0; + } -module.exports = { - stringReplaceAll, - stringEncaseCRLFWithFirstIndex -}; + if (arg0.length > 1) { + args = arg0; + } + const result = fn(args); -/***/ }), -/* 373 */ -/***/ (function(module, exports, __webpack_require__) { + // 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 (let len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } -"use strict"; + return result; + }; -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; + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } -const ESCAPES = new Map([ - ['n', '\n'], - ['r', '\r'], - ['t', '\t'], - ['b', '\b'], - ['f', '\f'], - ['v', '\v'], - ['0', '\0'], - ['\\', '\\'], - ['e', '\u001B'], - ['a', '\u0007'] -]); + return wrappedFn; +} -function unescape(c) { - const u = c[0] === 'u'; - const bracket = c[1] === '{'; +models.forEach(fromModel => { + convert[fromModel] = {}; - if ((u && !bracket && c.length === 5) || (c[0] === 'x' && c.length === 3)) { - return String.fromCharCode(parseInt(c.slice(1), 16)); - } + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - if (u && bracket) { - return String.fromCodePoint(parseInt(c.slice(2, -1), 16)); - } + const routes = route(fromModel); + const routeModels = Object.keys(routes); - return ESCAPES.get(c) || c; -} + routeModels.forEach(toModel => { + const fn = routes[toModel]; -function parseArguments(name, arguments_) { - const results = []; - const chunks = arguments_.trim().split(/\s*,\s*/g); - let matches; + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); - 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}')`); - } - } +module.exports = convert; - return results; + +/***/ }), +/* 372 */ +/***/ (function(module, exports, __webpack_require__) { + +/* MIT license */ +/* eslint-disable no-mixed-operators */ +const cssKeywords = __webpack_require__(118); + +// 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.) + +const reverseKeywords = {}; +for (const key of Object.keys(cssKeywords)) { + reverseKeywords[cssKeywords[key]] = key; } -function parseStyle(style) { - STYLE_REGEX.lastIndex = 0; +const convert = { + 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']} +}; - const results = []; - let matches; +module.exports = convert; - while ((matches = STYLE_REGEX.exec(style)) !== null) { - const name = matches[1]; +// Hide .channels and .labels properties +for (const model of Object.keys(convert)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } - if (matches[2]) { - const args = parseArguments(name, matches[2]); - results.push([name].concat(args)); - } else { - results.push([name]); - } + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); } - return results; + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } + + const {channels, labels} = convert[model]; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); } -function buildStyle(chalk, styles) { - const enabled = {}; +convert.rgb.hsl = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const min = Math.min(r, g, b); + const max = Math.max(r, g, b); + const delta = max - min; + let h; + let s; - for (const layer of styles) { - for (const style of layer.styles) { - enabled[style[0]] = layer.inverse ? null : style.slice(1); - } + 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; } - let current = chalk; - for (const [styleName, styles] of Object.entries(enabled)) { - if (!Array.isArray(styles)) { - continue; - } + h = Math.min(h * 60, 360); - if (!(styleName in current)) { - throw new Error(`Unknown Chalk style: ${styleName}`); - } + if (h < 0) { + h += 360; + } - current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; + const l = (min + max) / 2; + + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); } - return current; -} + return [h, s * 100, l * 100]; +}; -module.exports = (chalk, temporary) => { - const styles = []; - const chunks = []; - let chunk = []; +convert.rgb.hsv = function (rgb) { + let rdif; + let gdif; + let bdif; + let h; + let s; - // 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'); - } + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const v = Math.max(r, g, b); + const diff = v - Math.min(r, g, b); + const diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; - chunks.push(buildStyle(chalk, styles)(chunk.join(''))); - chunk = []; - styles.pop(); - } else { - chunk.push(character); - } - }); + if (diff === 0) { + h = 0; + s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); - chunks.push(chunk.join('')); + 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 (styles.length > 0) { - const errMsg = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? '' : 's'} (\`}\`)`; - throw new Error(errMsg); + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } } - return chunks.join(''); + return [ + h * 360, + s * 100, + v * 100 + ]; }; +convert.rgb.hwb = function (rgb) { + const r = rgb[0]; + const g = rgb[1]; + let b = rgb[2]; + const h = convert.rgb.hsl(rgb)[0]; + const w = 1 / 255 * Math.min(r, Math.min(g, b)); -/***/ }), -/* 374 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); -const restoreCursor = __webpack_require__(375); + return [h, w * 100, b * 100]; +}; -let isHidden = false; +convert.rgb.cmyk = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; -exports.show = (writableStream = process.stderr) => { - if (!writableStream.isTTY) { - return; - } + const k = Math.min(1 - r, 1 - g, 1 - b); + const c = (1 - r - k) / (1 - k) || 0; + const m = (1 - g - k) / (1 - k) || 0; + const y = (1 - b - k) / (1 - k) || 0; - isHidden = false; - writableStream.write('\u001B[?25h'); + return [c * 100, m * 100, y * 100, k * 100]; }; -exports.hide = (writableStream = process.stderr) => { - if (!writableStream.isTTY) { - return; +function comparativeDistance(x, y) { + /* + See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + */ + return ( + ((x[0] - y[0]) ** 2) + + ((x[1] - y[1]) ** 2) + + ((x[2] - y[2]) ** 2) + ); +} + +convert.rgb.keyword = function (rgb) { + const reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; } - restoreCursor(); - isHidden = true; - writableStream.write('\u001B[?25l'); -}; + let currentClosestDistance = Infinity; + let currentClosestKeyword; -exports.toggle = (force, writableStream) => { - if (force !== undefined) { - isHidden = force; - } + for (const keyword of Object.keys(cssKeywords)) { + const value = cssKeywords[keyword]; - if (isHidden) { - exports.show(writableStream); - } else { - exports.hide(writableStream); + // Compute comparative distance + const distance = comparativeDistance(rgb, value); + + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } } + + return currentClosestKeyword; }; +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; -/***/ }), -/* 375 */ -/***/ (function(module, exports, __webpack_require__) { +convert.rgb.xyz = function (rgb) { + let r = rgb[0] / 255; + let g = rgb[1] / 255; + let b = rgb[2] / 255; -"use strict"; + // Assume sRGB + r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92); + g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92); + b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92); -const onetime = __webpack_require__(244); -const signalExit = __webpack_require__(217); + const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); -module.exports = onetime(() => { - signalExit(() => { - process.stderr.write('\u001B[?25h'); - }, {alwaysLast: true}); -}); + return [x * 100, y * 100, z * 100]; +}; +convert.rgb.lab = function (rgb) { + const xyz = convert.rgb.xyz(rgb); + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; -/***/ }), -/* 376 */ -/***/ (function(module, exports, __webpack_require__) { + x /= 95.047; + y /= 100; + z /= 108.883; -"use strict"; + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); -const spinners = Object.assign({}, __webpack_require__(377)); + return [l, a, b]; +}; -const spinnersList = Object.keys(spinners); +convert.hsl.rgb = function (hsl) { + const h = hsl[0] / 360; + const s = hsl[1] / 100; + const l = hsl[2] / 100; + let t2; + let t3; + let val; -Object.defineProperty(spinners, 'random', { - get() { - const randomIndex = Math.floor(Math.random() * spinnersList.length); - const spinnerName = spinnersList[randomIndex]; - return spinners[spinnerName]; + if (s === 0) { + val = l * 255; + return [val, val, val]; } -}); -module.exports = spinners; -// TODO: Remove this for the next major release -module.exports.default = spinners; + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } + const t1 = 2 * l - t2; -/***/ }), -/* 377 */ -/***/ (function(module) { + const rgb = [0, 0, 0]; + for (let i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } -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\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]}}"); + if (t3 > 1) { + t3--; + } -/***/ }), -/* 378 */ -/***/ (function(module, exports, __webpack_require__) { + 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; + } -"use strict"; + rgb[i] = val * 255; + } -const chalk = __webpack_require__(379); + return rgb; +}; -const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; +convert.hsl.hsv = function (hsl) { + const h = hsl[0]; + let s = hsl[1] / 100; + let l = hsl[2] / 100; + let smin = s; + const lmin = Math.max(l, 0.01); -const main = { - info: chalk.blue('ℹ'), - success: chalk.green('✔'), - warning: chalk.yellow('⚠'), - error: chalk.red('✖') -}; + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + const v = (l + s) / 2; + const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); -const fallbacks = { - info: chalk.blue('i'), - success: chalk.green('√'), - warning: chalk.yellow('‼'), - error: chalk.red('×') + return [h, sv * 100, v * 100]; }; -module.exports = isSupported ? main : fallbacks; +convert.hsv.rgb = function (hsv) { + const h = hsv[0] / 60; + const s = hsv[1] / 100; + let v = hsv[2] / 100; + const hi = Math.floor(h) % 6; + const f = h - Math.floor(h); + const p = 255 * v * (1 - s); + const q = 255 * v * (1 - (s * f)); + const t = 255 * v * (1 - (s * (1 - f))); + v *= 255; -/***/ }), -/* 379 */ -/***/ (function(module, exports, __webpack_require__) { + 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]; + } +}; -"use strict"; +convert.hsv.hsl = function (hsv) { + const h = hsv[0]; + const s = hsv[1] / 100; + const v = hsv[2] / 100; + const vmin = Math.max(v, 0.01); + let sl; + let l; -const escapeStringRegexp = __webpack_require__(178); -const ansiStyles = __webpack_require__(380); -const stdoutColor = __webpack_require__(381).stdout; + l = (2 - s) * v; + const lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; -const template = __webpack_require__(383); + return [h, sl * 100, l * 100]; +}; -const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + const h = hwb[0] / 360; + let wh = hwb[1] / 100; + let bl = hwb[2] / 100; + const ratio = wh + bl; + let f; -// `supportsColor.level` → `ansiStyles.color[name]` mapping -const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; + // Wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } -// `color-convert` models to exclude from the Chalk API due to conflicts and such -const skipModels = new Set(['gray']); + const i = Math.floor(6 * h); + const v = 1 - bl; + f = 6 * h - i; -const styles = Object.create(null); + if ((i & 0x01) !== 0) { + f = 1 - f; + } -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; + const n = wh + f * (v - wh); // Linear interpolation - return chalk.template; + let r; + let g; + let b; + /* eslint-disable max-statements-per-line,no-multi-spaces */ + 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; } + /* eslint-enable max-statements-per-line,no-multi-spaces */ - applyOptions(this, options); -} - -// Use bright blue on Windows as the normal blue color is illegible -if (isSimpleWindowsTerm) { - ansiStyles.blue.open = '\u001B[94m'; -} + return [r * 255, g * 255, b * 255]; +}; -for (const key of Object.keys(ansiStyles)) { - ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); +convert.cmyk.rgb = function (cmyk) { + const c = cmyk[0] / 100; + const m = cmyk[1] / 100; + const y = cmyk[2] / 100; + const k = cmyk[3] / 100; - styles[key] = { - get() { - const codes = ansiStyles[key]; - return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); - } - }; -} + const r = 1 - Math.min(1, c * (1 - k) + k); + const g = 1 - Math.min(1, m * (1 - k) + k); + const b = 1 - Math.min(1, y * (1 - k) + k); -styles.visible = { - get() { - return build.call(this, this._styles || [], true, 'visible'); - } + return [r * 255, g * 255, b * 255]; }; -ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); -for (const model of Object.keys(ansiStyles.color.ansi)) { - if (skipModels.has(model)) { - continue; - } +convert.xyz.rgb = function (xyz) { + const x = xyz[0] / 100; + const y = xyz[1] / 100; + const z = xyz[2] / 100; + let r; + let g; + let b; - 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); - }; - } - }; -} + 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); -ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g'); -for (const model of Object.keys(ansiStyles.bgColor.ansi)) { - if (skipModels.has(model)) { - continue; - } + // Assume sRGB + r = r > 0.0031308 + ? ((1.055 * (r ** (1.0 / 2.4))) - 0.055) + : r * 12.92; - 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); - }; - } - }; -} + g = g > 0.0031308 + ? ((1.055 * (g ** (1.0 / 2.4))) - 0.055) + : g * 12.92; -const proto = Object.defineProperties(() => {}, styles); + b = b > 0.0031308 + ? ((1.055 * (b ** (1.0 / 2.4))) - 0.055) + : b * 12.92; -function build(_styles, _empty, key) { - const builder = function () { - return applyStyle.apply(builder, arguments); - }; + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); - builder._styles = _styles; - builder._empty = _empty; + return [r * 255, g * 255, b * 255]; +}; - const self = this; +convert.xyz.lab = function (xyz) { + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; - Object.defineProperty(builder, 'level', { - enumerable: true, - get() { - return self.level; - }, - set(level) { - self.level = level; - } - }); + x /= 95.047; + y /= 100; + z /= 108.883; - Object.defineProperty(builder, 'enabled', { - enumerable: true, - get() { - return self.enabled; - }, - set(enabled) { - self.enabled = enabled; - } - }); + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); - // See below for fix regarding invisible grey/dim combination on Windows - builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey'; + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); - // `__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 [l, a, b]; +}; - return builder; -} +convert.lab.xyz = function (lab) { + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let x; + let y; + let z; -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]); + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; - if (argsLen === 0) { - return ''; - } + const y2 = y ** 3; + const x2 = x ** 3; + const z2 = 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; - if (argsLen > 1) { - // Don't slice `arguments`, it prevents V8 optimizations - for (let a = 1; a < argsLen; a++) { - str += ' ' + args[a]; - } - } + x *= 95.047; + y *= 100; + z *= 108.883; - if (!this.enabled || this.level <= 0 || !str) { - return this._empty ? '' : str; - } + return [x, y, z]; +}; - // 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 = ''; - } +convert.lab.lch = function (lab) { + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let h; - 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 hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; - // 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}`); + if (h < 0) { + h += 360; } - // Reset the original `dim` if we changed it to work around the Windows dimmed gray issue - ansiStyles.dim.open = originalDim; - - return str; -} - -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 c = Math.sqrt(a * a + b * b); - const args = [].slice.call(arguments, 2); - const parts = [strings.raw[0]]; + return [l, c, h]; +}; - for (let i = 1; i < strings.length; i++) { - parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); - parts.push(String(strings.raw[i])); - } +convert.lch.lab = function (lch) { + const l = lch[0]; + const c = lch[1]; + const h = lch[2]; - return template(chalk, parts.join('')); -} + const hr = h / 360 * 2 * Math.PI; + const a = c * Math.cos(hr); + const b = c * Math.sin(hr); -Object.defineProperties(Chalk.prototype, styles); + return [l, a, b]; +}; -module.exports = Chalk(); // eslint-disable-line new-cap -module.exports.supportsColor = stdoutColor; -module.exports.default = module.exports; // For TypeScript +convert.rgb.ansi16 = function (args, saturation = null) { + const [r, g, b] = args; + let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization + value = Math.round(value / 50); -/***/ }), -/* 380 */ -/***/ (function(module, exports, __webpack_require__) { + if (value === 0) { + return 30; + } -"use strict"; -/* WEBPACK VAR INJECTION */(function(module) { -const colorConvert = __webpack_require__(180); + let ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); -const wrapAnsi16 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${code + offset}m`; -}; + if (value === 2) { + ansi += 60; + } -const wrapAnsi256 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};5;${code}m`; + return ansi; }; -const wrapAnsi16m = (fn, offset) => function () { - const rgb = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +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]); }; -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], - - // 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], +convert.rgb.ansi256 = function (args) { + const r = args[0]; + const g = args[1]; + const b = args[2]; - // 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] + // 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; } - }; - // Fix humans - styles.color.grey = styles.color.gray; + if (r > 248) { + return 231; + } - for (const groupName of Object.keys(styles)) { - const group = styles[groupName]; + return Math.round(((r - 8) / 247) * 24) + 232; + } - for (const styleName of Object.keys(group)) { - const style = group[styleName]; + const ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); - styles[styleName] = { - open: `\u001B[${style[0]}m`, - close: `\u001B[${style[1]}m` - }; + return ansi; +}; - group[styleName] = styles[styleName]; +convert.ansi16.rgb = function (args) { + let color = args % 10; - codes.set(style[0], style[1]); + // Handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; } - Object.defineProperty(styles, groupName, { - value: group, - enumerable: false - }); + color = color / 10.5 * 255; - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); + return [color, color, color]; } - const ansi2ansi = n => n; - const rgb2rgb = (r, g, b) => [r, g, b]; - - styles.color.close = '\u001B[39m'; - styles.bgColor.close = '\u001B[49m'; + const mult = (~~(args > 50) + 1) * 0.5; + const r = ((color & 1) * mult) * 255; + const g = (((color >> 1) & 1) * mult) * 255; + const b = (((color >> 2) & 1) * mult) * 255; - styles.color.ansi = { - ansi: wrapAnsi16(ansi2ansi, 0) - }; - styles.color.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 0) - }; - styles.color.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 0) - }; + return [r, g, b]; +}; - styles.bgColor.ansi = { - ansi: wrapAnsi16(ansi2ansi, 10) - }; - styles.bgColor.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 10) - }; - styles.bgColor.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 10) - }; +convert.ansi256.rgb = function (args) { + // Handle greyscale + if (args >= 232) { + const c = (args - 232) * 10 + 8; + return [c, c, c]; + } - for (let key of Object.keys(colorConvert)) { - if (typeof colorConvert[key] !== 'object') { - continue; - } + args -= 16; - const suite = colorConvert[key]; + let rem; + const r = Math.floor(args / 36) / 5 * 255; + const g = Math.floor((rem = args % 36) / 6) / 5 * 255; + const b = (rem % 6) / 5 * 255; - if (key === 'ansi16') { - key = 'ansi'; - } + return [r, g, b]; +}; - if ('ansi16' in suite) { - styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); - styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); - } +convert.rgb.hex = function (args) { + const integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); - if ('ansi256' in suite) { - styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); - styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); - } + const string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; - if ('rgb' in suite) { - styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); - styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); - } +convert.hex.rgb = function (args) { + const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; } - return styles; -} - -// Make the export immutable -Object.defineProperty(module, 'exports', { - enumerable: true, - get: assembleStyles -}); + let colorString = match[0]; -/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(114)(module))) + if (match[0].length === 3) { + colorString = colorString.split('').map(char => { + return char + char; + }).join(''); + } -/***/ }), -/* 381 */ -/***/ (function(module, exports, __webpack_require__) { + const integer = parseInt(colorString, 16); + const r = (integer >> 16) & 0xFF; + const g = (integer >> 8) & 0xFF; + const b = integer & 0xFF; -"use strict"; - -const os = __webpack_require__(120); -const hasFlag = __webpack_require__(382); + return [r, g, b]; +}; -const env = process.env; +convert.rgb.hcg = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const max = Math.max(Math.max(r, g), b); + const min = Math.min(Math.min(r, g), b); + const chroma = (max - min); + let grayscale; + let hue; -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; -} + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } -function translateLevel(level) { - if (level === 0) { - return false; + 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; } - return { - level, - hasBasic: true, - has256: level >= 2, - has16m: level >= 3 - }; -} + hue /= 6; + hue %= 1; -function supportsColor(stream) { - if (forceColor === false) { - return 0; + return [hue * 360, chroma * 100, grayscale * 100]; +}; + +convert.hsl.hcg = function (hsl) { + const s = hsl[1] / 100; + const l = hsl[2] / 100; + + const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l)); + + let f = 0; + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); } - if (hasFlag('color=16m') || - hasFlag('color=full') || - hasFlag('color=truecolor')) { - return 3; + return [hsl[0], c * 100, f * 100]; +}; + +convert.hsv.hcg = function (hsv) { + const s = hsv[1] / 100; + const v = hsv[2] / 100; + + const c = s * v; + let f = 0; + + if (c < 1.0) { + f = (v - c) / (1 - c); } - if (hasFlag('color=256')) { - return 2; + return [hsv[0], c * 100, f * 100]; +}; + +convert.hcg.rgb = function (hcg) { + const h = hcg[0] / 360; + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; } - if (stream && !stream.isTTY && forceColor !== true) { - return 0; + const pure = [0, 0, 0]; + const hi = (h % 1) * 6; + const v = hi % 1; + const w = 1 - v; + let mg = 0; + + /* eslint-disable max-statements-per-line */ + 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; } + /* eslint-enable max-statements-per-line */ - const min = forceColor ? 1 : 0; + mg = (1.0 - c) * g; - 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 [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; - return 1; +convert.hcg.hsv = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + const v = c + g * (1.0 - c); + let f = 0; + + if (v > 0.0) { + f = c / v; } - if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { - return 1; - } + return [hcg[0], f * 100, v * 100]; +}; - return min; +convert.hcg.hsl = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + const l = g * (1.0 - c) + 0.5 * c; + let s = 0; + + 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 ('TEAMCITY_VERSION' in env) { - return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; + return [hcg[0], s * 100, l * 100]; +}; + +convert.hcg.hwb = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + const v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; +}; + +convert.hwb.hcg = function (hwb) { + const w = hwb[1] / 100; + const b = hwb[2] / 100; + const v = 1 - b; + const c = v - w; + let g = 0; + + if (c < 1) { + g = (v - c) / (1 - c); } - if (env.COLORTERM === 'truecolor') { - return 3; + return [hwb[0], c * 100, g * 100]; +}; + +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; + +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; + +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; + +convert.gray.hsl = function (args) { + return [0, 0, args[0]]; +}; + +convert.gray.hsv = convert.gray.hsl; + +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; + +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; + +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; + +convert.gray.hex = function (gray) { + const val = Math.round(gray[0] / 100 * 255) & 0xFF; + const integer = (val << 16) + (val << 8) + val; + + const string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.rgb.gray = function (rgb) { + const val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; + + +/***/ }), +/* 373 */ +/***/ (function(module, exports, __webpack_require__) { + +const conversions = __webpack_require__(372); + +/* + 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() { + const graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + const models = Object.keys(conversions); + + for (let 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 + }; } - if ('TERM_PROGRAM' in env) { - const version = parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10); + return graph; +} - switch (env.TERM_PROGRAM) { - case 'iTerm.app': - return version >= 3 ? 3 : 2; - case 'Apple_Terminal': - return 2; - // No default +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + const graph = buildGraph(); + const queue = [fromModel]; // Unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + const current = queue.pop(); + const adjacents = Object.keys(conversions[current]); + + for (let len = adjacents.length, i = 0; i < len; i++) { + const adjacent = adjacents[i]; + const node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } } } - if (/-256(color)?$/i.test(env.TERM)) { - return 2; - } + return graph; +} - if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { - return 1; - } +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} - if ('COLORTERM' in env) { - return 1; - } +function wrapConversion(toModel, graph) { + const path = [graph[toModel].parent, toModel]; + let fn = conversions[graph[toModel].parent][toModel]; - if (env.TERM === 'dumb') { - return min; + let 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; } - return min; + fn.conversion = path; + return fn; } -function getSupportLevel(stream) { - const level = supportsColor(stream); - return translateLevel(level); -} +module.exports = function (fromModel) { + const graph = deriveBFS(fromModel); + const conversion = {}; -module.exports = { - supportsColor: getSupportLevel, - stdout: getSupportLevel(process.stdout), - stderr: getSupportLevel(process.stderr) + const models = Object.keys(graph); + for (let len = models.length, i = 0; i < len; i++) { + const toModel = models[i]; + const 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; }; + /***/ }), -/* 382 */ +/* 374 */ /***/ (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 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 }; /***/ }), -/* 383 */ +/* 375 */ /***/ (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 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}|x[a-f\d]{2}|.)|([^\\])/gi; +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'], @@ -48208,23 +48520,31 @@ const ESCAPES = new Map([ ]); function unescape(c) { - if ((c[0] === 'u' && c.length === 5) || (c[0] === 'x' && c.length === 3)) { + const u = c[0] === 'u'; + const bracket = c[1] === '{'; + + 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; } -function parseArguments(name, args) { +function parseArguments(name, arguments_) { const results = []; - const chunks = args.trim().split(/\s*,\s*/g); + const chunks = arguments_.trim().split(/\s*,\s*/g); let matches; for (const chunk of chunks) { - if (!isNaN(chunk)) { - results.push(Number(chunk)); + 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, chr) => escape ? unescape(escape) : chr)); + 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}')`); } @@ -48263,36 +48583,34 @@ function buildStyle(chalk, styles) { } 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}`); - } + for (const [styleName, styles] of Object.entries(enabled)) { + if (!Array.isArray(styles)) { + continue; + } - if (enabled[styleName].length > 0) { - current = current[styleName].apply(current, enabled[styleName]); - } else { - current = current[styleName]; - } + if (!(styleName in current)) { + throw new Error(`Unknown Chalk style: ${styleName}`); } + + current = styles.length > 0 ? current[styleName](...styles) : current[styleName]; } return current; } -module.exports = (chalk, tmp) => { +module.exports = (chalk, temporary) => { 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)); + temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => { + if (escapeCharacter) { + chunk.push(unescape(escapeCharacter)); } else if (style) { - const str = chunk.join(''); + const string = chunk.join(''); chunk = []; - chunks.push(styles.length === 0 ? str : buildStyle(chalk, styles)(str)); + chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string)); styles.push({inverse, styles: parseStyle(style)}); } else if (close) { if (styles.length === 0) { @@ -48303,7 +48621,7 @@ module.exports = (chalk, tmp) => { chunk = []; styles.pop(); } else { - chunk.push(chr); + chunk.push(character); } }); @@ -48319,8317 +48637,10346 @@ module.exports = (chalk, tmp) => { /***/ }), -/* 384 */ +/* 376 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const ansiRegex = __webpack_require__(385); +const restoreCursor = __webpack_require__(377); -module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; +let isHidden = false; +exports.show = (writableStream = process.stderr) => { + if (!writableStream.isTTY) { + return; + } -/***/ }), -/* 385 */ -/***/ (function(module, exports, __webpack_require__) { + isHidden = false; + writableStream.write('\u001B[?25h'); +}; -"use strict"; +exports.hide = (writableStream = process.stderr) => { + if (!writableStream.isTTY) { + return; + } + restoreCursor(); + isHidden = true; + writableStream.write('\u001B[?25l'); +}; -module.exports = ({onlyFirst = false} = {}) => { - const pattern = [ - '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', - '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' - ].join('|'); +exports.toggle = (force, writableStream) => { + if (force !== undefined) { + isHidden = force; + } - return new RegExp(pattern, onlyFirst ? undefined : 'g'); + if (isHidden) { + exports.show(writableStream); + } else { + exports.hide(writableStream); + } }; /***/ }), -/* 386 */ +/* 377 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const onetime = __webpack_require__(378); +const signalExit = __webpack_require__(218); -var defaults = __webpack_require__(387) -var combining = __webpack_require__(389) - -var DEFAULTS = { - nul: 0, - control: 0 -} +module.exports = onetime(() => { + signalExit(() => { + process.stderr.write('\u001B[?25h'); + }, {alwaysLast: true}); +}); -module.exports = function wcwidth(str) { - return wcswidth(str, DEFAULTS) -} -module.exports.config = function(opts) { - opts = defaults(opts || {}, DEFAULTS) - return function wcwidth(str) { - return wcswidth(str, opts) - } -} - -/* - * 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. -*/ - -function wcswidth(str, opts) { - if (typeof str !== 'string') return wcwidth(str, opts) +/***/ }), +/* 378 */ +/***/ (function(module, exports, __webpack_require__) { - 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 - } +"use strict"; - return s -} +const mimicFn = __webpack_require__(379); -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 +const calledFunctions = new WeakMap(); - // binary search in table of non-spacing characters - if (bisearch(ucs)) return 0 +const oneTime = (fn, options = {}) => { + if (typeof fn !== 'function') { + throw new TypeError('Expected a function'); + } - // 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))); -} + let ret; + let isCalled = false; + let callCount = 0; + const functionName = fn.displayName || fn.name || '<anonymous>'; -function bisearch(ucs) { - var min = 0 - var max = combining.length - 1 - var mid + const onetime = function (...args) { + calledFunctions.set(onetime, ++callCount); - if (ucs < combining[0][0] || ucs > combining[max][1]) return false + if (isCalled) { + if (options.throw === true) { + throw new Error(`Function \`${functionName}\` can only be called once`); + } - 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 ret; + } - return false -} + isCalled = true; + ret = fn.apply(this, args); + fn = null; + return ret; + }; -/***/ }), -/* 387 */ -/***/ (function(module, exports, __webpack_require__) { + mimicFn(onetime, fn); + calledFunctions.set(onetime, callCount); -var clone = __webpack_require__(388); + return onetime; +}; -module.exports = function(options, defaults) { - options = options || {}; +module.exports = oneTime; +// TODO: Remove this for the next major release +module.exports.default = oneTime; - Object.keys(defaults).forEach(function(key) { - if (typeof options[key] === 'undefined') { - options[key] = clone(defaults[key]); - } - }); +module.exports.callCount = fn => { + if (!calledFunctions.has(fn)) { + throw new Error(`The given function \`${fn.name}\` is not wrapped by the \`onetime\` package`); + } - return options; + return calledFunctions.get(fn); }; + /***/ }), -/* 388 */ +/* 379 */ /***/ (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 = []; +"use strict"; - var useBuffer = typeof Buffer != 'undefined'; - if (typeof circular == 'undefined') - circular = true; +const mimicFn = (to, from) => { + for (const prop of Reflect.ownKeys(from)) { + Object.defineProperty(to, prop, Object.getOwnPropertyDescriptor(from, prop)); + } - if (typeof depth == 'undefined') - depth = Infinity; + return to; +}; - // 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; +module.exports = mimicFn; +// TODO: Remove this for the next major release +module.exports.default = mimicFn; - if (depth == 0) - return parent; - var child; - var proto; - if (typeof parent != 'object') { - return parent; - } +/***/ }), +/* 380 */ +/***/ (function(module, exports, __webpack_require__) { - 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; - } - } +"use strict"; - if (circular) { - var index = allParents.indexOf(parent); - if (index != -1) { - return allChildren[index]; - } - allParents.push(parent); - allChildren.push(child); - } +const spinners = Object.assign({}, __webpack_require__(381)); - for (var i in parent) { - var attrs; - if (proto) { - attrs = Object.getOwnPropertyDescriptor(proto, i); - } +const spinnersList = Object.keys(spinners); - if (attrs && attrs.set == null) { - continue; - } - child[i] = _clone(parent[i], depth - 1); - } +Object.defineProperty(spinners, 'random', { + get() { + const randomIndex = Math.floor(Math.random() * spinnersList.length); + const spinnerName = spinnersList[randomIndex]; + return spinners[spinnerName]; + } +}); - return child; - } +module.exports = spinners; +// TODO: Remove this for the next major release +module.exports.default = spinners; - return _clone(parent, depth); -} -/** - * 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; +/***/ }), +/* 381 */ +/***/ (function(module) { - var c = function () {}; - c.prototype = parent; - return new c(); -}; +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\":[\"ρββββββ\",\"βρβββββ\",\"ββρββββ\",\"βββρβββ\",\"ββββρββ\",\"βββββρβ\",\"ββββββρ\"]}}"); -// private utility functions +/***/ }), +/* 382 */ +/***/ (function(module, exports, __webpack_require__) { -function __objToStr(o) { - return Object.prototype.toString.call(o); -}; -clone.__objToStr = __objToStr; +"use strict"; -function __isDate(o) { - return typeof o === 'object' && __objToStr(o) === '[object Date]'; -}; -clone.__isDate = __isDate; +const chalk = __webpack_require__(383); -function __isArray(o) { - return typeof o === 'object' && __objToStr(o) === '[object Array]'; -}; -clone.__isArray = __isArray; +const isSupported = process.platform !== 'win32' || process.env.CI || process.env.TERM === 'xterm-256color'; -function __isRegExp(o) { - return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; +const main = { + info: chalk.blue('ℹ'), + success: chalk.green('✔'), + warning: chalk.yellow('⚠'), + error: chalk.red('✖') }; -clone.__isRegExp = __isRegExp; -function __getRegExpFlags(re) { - var flags = ''; - if (re.global) flags += 'g'; - if (re.ignoreCase) flags += 'i'; - if (re.multiline) flags += 'm'; - return flags; +const fallbacks = { + info: chalk.blue('i'), + success: chalk.green('√'), + warning: chalk.yellow('‼'), + error: chalk.red('×') }; -clone.__getRegExpFlags = __getRegExpFlags; - -return clone; -})(); - -if ( true && module.exports) { - module.exports = clone; -} - - -/***/ }), -/* 389 */ -/***/ (function(module, exports) { -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 ] -] +module.exports = isSupported ? main : fallbacks; /***/ }), -/* 390 */ +/* 383 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +const escapeStringRegexp = __webpack_require__(179); +const ansiStyles = __webpack_require__(384); +const stdoutColor = __webpack_require__(389).stdout; -module.exports = ({stream = process.stdout} = {}) => { - return Boolean( - stream && stream.isTTY && - process.env.TERM !== 'dumb' && - !('CI' in process.env) - ); -}; +const template = __webpack_require__(390); +const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm'); -/***/ }), -/* 391 */ -/***/ (function(module, exports, __webpack_require__) { +// `supportsColor.level` → `ansiStyles.color[name]` mapping +const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m']; -var Stream = __webpack_require__(137) +// `color-convert` models to exclude from the Chalk API due to conflicts and such +const skipModels = new Set(['gray']); -module.exports = MuteStream +const styles = Object.create(null); -// 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 +function applyOptions(obj, options) { + options = options || {}; - // 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 + // 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; } -MuteStream.prototype = Object.create(Stream.prototype) - -Object.defineProperty(MuteStream.prototype, 'constructor', { - value: MuteStream, - enumerable: false -}) - -MuteStream.prototype.mute = function () { - this.muted = true -} +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); -MuteStream.prototype.unmute = function () { - this.muted = false -} + chalk.template = function () { + const args = [].slice.call(arguments); + return chalkTag.apply(null, [chalk.template].concat(args)); + }; -Object.defineProperty(MuteStream.prototype, '_onpipe', { - value: onPipe, - enumerable: false, - writable: true, - configurable: true -}) + Object.setPrototypeOf(chalk, Chalk.prototype); + Object.setPrototypeOf(chalk.template, chalk); -function onPipe (src) { - this._src = src -} + chalk.template.constructor = Chalk; -Object.defineProperty(MuteStream.prototype, 'isTTY', { - get: getIsTTY, - set: setIsTTY, - enumerable: true, - configurable: true -}) + return chalk.template; + } -function getIsTTY () { - return( (this._dest) ? this._dest.isTTY - : (this._src) ? this._src.isTTY - : false - ) + applyOptions(this, options); } -// 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 - }) +// Use bright blue on Windows as the normal blue color is illegible +if (isSimpleWindowsTerm) { + ansiStyles.blue.open = '\u001B[94m'; } -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 }) - +for (const key of Object.keys(ansiStyles)) { + ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); -MuteStream.prototype.pipe = function (dest, options) { - this._dest = dest - return Stream.prototype.pipe.call(this, dest, options) + styles[key] = { + get() { + const codes = ansiStyles[key]; + return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key); + } + }; } -MuteStream.prototype.pause = function () { - if (this._src) return this._src.pause() -} +styles.visible = { + get() { + return build.call(this, this._styles || [], true, 'visible'); + } +}; -MuteStream.prototype.resume = function () { - if (this._src) return this._src.resume() -} +ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g'); +for (const model of Object.keys(ansiStyles.color.ansi)) { + if (skipModels.has(model)) { + continue; + } -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) + 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); + }; + } + }; } -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') -} +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 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) -}} + 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); + }; + } + }; +} -MuteStream.prototype.destroy = proxy('destroy') -MuteStream.prototype.destroySoon = proxy('destroySoon') -MuteStream.prototype.close = proxy('close') +const proto = Object.defineProperties(() => {}, styles); +function build(_styles, _empty, key) { + const builder = function () { + return applyStyle.apply(builder, arguments); + }; -/***/ }), -/* 392 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + builder._styles = _styles; + builder._empty = _empty; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RunCommand", function() { return RunCommand; }); -/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(162); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); -/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ + 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'; -const RunCommand = { - description: 'Run script defined in package.json in each package that contains that script.', - name: 'run', + // `__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 - async run(projects, projectGraph, { - extraArgs - }) { - const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_3__["topologicallyBatchProjects"])(projects, projectGraph); + return builder; +} - if (extraArgs.length === 0) { - throw new _utils_errors__WEBPACK_IMPORTED_MODULE_0__["CliError"]('No script specified'); - } +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]); - const scriptName = extraArgs[0]; - const scriptArgs = extraArgs.slice(1); - await Object(_utils_parallelize__WEBPACK_IMPORTED_MODULE_2__["parallelizeBatches"])(batchedProjects, async project => { - if (project.hasScript(scriptName)) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info(`[${project.name}] running "${scriptName}" script`); - await project.runScriptStreaming(scriptName, { - args: scriptArgs - }); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].success(`[${project.name}] complete`); - } - }); - } + if (argsLen === 0) { + return ''; + } -}; + if (argsLen > 1) { + // Don't slice `arguments`, it prevents V8 optimizations + for (let a = 1; a < argsLen; a++) { + str += ' ' + args[a]; + } + } -/***/ }), -/* 393 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + if (!this.enabled || this.level <= 0 || !str) { + return this._empty ? '' : str; + } -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatchCommand", function() { return WatchCommand; }); -/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(162); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); -/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(144); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(145); -/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(394); -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ + // 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; + // 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; + return str; +} +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(' '); + } -/** - * Name of the script in the package/project package.json file to run during `kbn watch`. - */ -const watchScriptName = 'kbn:watch'; -/** - * Name of the Kibana project. - */ + const args = [].slice.call(arguments, 2); + const parts = [strings.raw[0]]; -const kibanaProjectName = 'kibana'; -/** - * Command that traverses through list of available projects/packages that have `kbn:watch` script in their - * package.json files, groups them into topology aware batches and then processes theses batches one by one - * running `kbn:watch` scripts in parallel within the same batch. - * - * Command internally relies on the fact that most of the build systems that are triggered by `kbn:watch` - * will emit special "marker" once build/watch process is ready that we can use as completion condition for - * the `kbn:watch` script and eventually for the entire batch. Currently we support completion "markers" for - * `webpack` and `tsc` only, for the rest we rely on predefined timeouts. - */ + for (let i = 1; i < strings.length; i++) { + parts.push(String(args[i - 1]).replace(/[{}\\]/g, '\\$&')); + parts.push(String(strings.raw[i])); + } -const WatchCommand = { - description: 'Runs `kbn:watch` script for every project.', - name: 'watch', + return template(chalk, parts.join('')); +} - async run(projects, projectGraph) { - const projectsToWatch = new Map(); +Object.defineProperties(Chalk.prototype, styles); - for (const project of projects.values()) { - // We can't watch project that doesn't have `kbn:watch` script. - if (project.hasScript(watchScriptName)) { - projectsToWatch.set(project.name, project); - } - } +module.exports = Chalk(); // eslint-disable-line new-cap +module.exports.supportsColor = stdoutColor; +module.exports.default = module.exports; // For TypeScript - if (projectsToWatch.size === 0) { - throw new _utils_errors__WEBPACK_IMPORTED_MODULE_0__["CliError"](`There are no projects to watch found. Make sure that projects define 'kbn:watch' script in 'package.json'.`); - } - const projectNames = Array.from(projectsToWatch.keys()); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info(`Running ${watchScriptName} scripts for [${projectNames.join(', ')}].`); // Kibana should always be run the last, so we don't rely on automatic - // topological batching and push it to the last one-entry batch manually. +/***/ }), +/* 384 */ +/***/ (function(module, exports, __webpack_require__) { - const shouldWatchKibanaProject = projectsToWatch.delete(kibanaProjectName); - const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_3__["topologicallyBatchProjects"])(projectsToWatch, projectGraph); +"use strict"; +/* WEBPACK VAR INJECTION */(function(module) { +const colorConvert = __webpack_require__(385); - if (shouldWatchKibanaProject) { - batchedProjects.push([projects.get(kibanaProjectName)]); - } +const wrapAnsi16 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${code + offset}m`; +}; - await Object(_utils_parallelize__WEBPACK_IMPORTED_MODULE_2__["parallelizeBatches"])(batchedProjects, async pkg => { - const completionHint = await Object(_utils_watch__WEBPACK_IMPORTED_MODULE_4__["waitUntilWatchIsReady"])(pkg.runScriptStreaming(watchScriptName, { - debug: false - }).stdout); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].success(`[${pkg.name}] Initial build completed (${completionHint}).`); - }); - } +const wrapAnsi256 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};5;${code}m`; +}; +const wrapAnsi16m = (fn, offset) => function () { + const rgb = fn.apply(colorConvert, arguments); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; }; -/***/ }), -/* 394 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +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], -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitUntilWatchIsReady", function() { return waitUntilWatchIsReady; }); -/* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(8); -/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(395); -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ + // 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], + // 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] + } + }; -/** - * Number of milliseconds we wait before we fall back to the default watch handler. - */ + // Fix humans + styles.color.grey = styles.color.gray; -const defaultHandlerDelay = 3000; -/** - * If default watch handler is used, then it's the number of milliseconds we wait for - * any build output before we consider watch task ready. - */ + for (const groupName of Object.keys(styles)) { + const group = styles[groupName]; -const defaultHandlerReadinessTimeout = 2000; -/** - * Describes configurable watch options. - */ + for (const styleName of Object.keys(group)) { + const style = group[styleName]; -function getWatchHandlers(buildOutput$, { - handlerDelay = defaultHandlerDelay, - handlerReadinessTimeout = defaultHandlerReadinessTimeout -}) { - const typescriptHandler = buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('$ tsc')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["map"])(() => buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('Compilation complete.')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["mapTo"])('tsc')))); - const webpackHandler = buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('$ webpack')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["map"])(() => buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('Chunk Names')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["mapTo"])('webpack')))); - const defaultHandler = rxjs__WEBPACK_IMPORTED_MODULE_0__["of"](undefined).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["delay"])(handlerReadinessTimeout), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["map"])(() => buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["timeout"])(handlerDelay), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["catchError"])(() => rxjs__WEBPACK_IMPORTED_MODULE_0__["of"]('timeout'))))); - return [typescriptHandler, webpackHandler, defaultHandler]; -} + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; -function waitUntilWatchIsReady(stream, opts = {}) { - const buildOutput$ = new rxjs__WEBPACK_IMPORTED_MODULE_0__["Subject"](); + group[styleName] = styles[styleName]; - const onDataListener = data => buildOutput$.next(data.toString('utf-8')); + codes.set(style[0], style[1]); + } - const onEndListener = () => buildOutput$.complete(); + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); - const onErrorListener = e => buildOutput$.error(e); + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + } - stream.once('end', onEndListener); - stream.once('error', onErrorListener); - stream.on('data', onDataListener); - return rxjs__WEBPACK_IMPORTED_MODULE_0__["race"](getWatchHandlers(buildOutput$, opts)).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["mergeMap"])(whenReady => whenReady), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["finalize"])(() => { - stream.removeListener('data', onDataListener); - stream.removeListener('end', onEndListener); - stream.removeListener('error', onErrorListener); - buildOutput$.complete(); - })).toPromise(); -} + const ansi2ansi = n => n; + const rgb2rgb = (r, g, b) => [r, g, b]; -/***/ }), -/* 395 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(396); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__["audit"]; }); + styles.color.ansi = { + ansi: wrapAnsi16(ansi2ansi, 0) + }; + styles.color.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 0) + }; + styles.color.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 0) + }; -/* harmony import */ var _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(397); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return _internal_operators_auditTime__WEBPACK_IMPORTED_MODULE_1__["auditTime"]; }); + styles.bgColor.ansi = { + ansi: wrapAnsi16(ansi2ansi, 10) + }; + styles.bgColor.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 10) + }; + styles.bgColor.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 10) + }; -/* harmony import */ var _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(398); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return _internal_operators_buffer__WEBPACK_IMPORTED_MODULE_2__["buffer"]; }); + for (let key of Object.keys(colorConvert)) { + if (typeof colorConvert[key] !== 'object') { + continue; + } -/* harmony import */ var _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(399); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferCount", function() { return _internal_operators_bufferCount__WEBPACK_IMPORTED_MODULE_3__["bufferCount"]; }); + const suite = colorConvert[key]; -/* harmony import */ var _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(400); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return _internal_operators_bufferTime__WEBPACK_IMPORTED_MODULE_4__["bufferTime"]; }); + if (key === 'ansi16') { + key = 'ansi'; + } -/* harmony import */ var _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(401); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); + if ('ansi16' in suite) { + styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); + styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); + } -/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(402); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferWhen", function() { return _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__["bufferWhen"]; }); + if ('ansi256' in suite) { + styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); + styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); + } -/* harmony import */ var _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(403); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); + if ('rgb' in suite) { + styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); + styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); + } + } -/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(404); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); + return styles; +} -/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(405); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__["combineLatest"]; }); +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); -/* harmony import */ var _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(406); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return _internal_operators_concat__WEBPACK_IMPORTED_MODULE_10__["concat"]; }); +/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(115)(module))) -/* harmony import */ var _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(80); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); +/***/ }), +/* 385 */ +/***/ (function(module, exports, __webpack_require__) { -/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(407); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); +var conversions = __webpack_require__(386); +var route = __webpack_require__(388); -/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(408); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); +var convert = {}; -/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(409); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "count", function() { return _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__["count"]; }); +var models = Object.keys(conversions); -/* harmony import */ var _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(410); -/* 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__(411); -/* 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__(412); -/* 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__(413); -/* 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__(415); -/* 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__(416); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__["dematerialize"]; }); +function wrapRaw(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } -/* harmony import */ var _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__ = __webpack_require__(417); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } -/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(418); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); + return fn(args); + }; -/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(419); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__["distinctUntilKeyChanged"]; }); + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } -/* harmony import */ var _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__ = __webpack_require__(420); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); + return wrappedFn; +} -/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(423); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); +function wrapRounded(fn) { + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; + } -/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(424); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); + } -/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(425); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__["exhaust"]; }); + var result = fn(args); -/* harmony import */ var _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__ = __webpack_require__(426); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); + // 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]); + } + } -/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(427); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); + return result; + }; -/* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(104); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); + // preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } -/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(428); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__["finalize"]; }); + return wrappedFn; +} -/* harmony import */ var _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__ = __webpack_require__(429); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); +models.forEach(function (fromModel) { + convert[fromModel] = {}; -/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(430); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); -/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(431); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "first", function() { return _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__["first"]; }); + var routes = route(fromModel); + var routeModels = Object.keys(routes); -/* harmony import */ var _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__ = __webpack_require__(31); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "groupBy", function() { return _internal_operators_groupBy__WEBPACK_IMPORTED_MODULE_35__["groupBy"]; }); + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; -/* harmony import */ var _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__ = __webpack_require__(432); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ignoreElements", function() { return _internal_operators_ignoreElements__WEBPACK_IMPORTED_MODULE_36__["ignoreElements"]; }); + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); -/* harmony import */ var _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__ = __webpack_require__(433); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return _internal_operators_isEmpty__WEBPACK_IMPORTED_MODULE_37__["isEmpty"]; }); +module.exports = convert; -/* harmony import */ var _internal_operators_last__WEBPACK_IMPORTED_MODULE_38__ = __webpack_require__(434); -/* 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__(66); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); +/***/ }), +/* 386 */ +/***/ (function(module, exports, __webpack_require__) { -/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(436); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mapTo", function() { return _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__["mapTo"]; }); +/* MIT license */ +var cssKeywords = __webpack_require__(387); -/* harmony import */ var _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__ = __webpack_require__(437); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); +// 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.) -/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(438); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); +var reverseKeywords = {}; +for (var key in cssKeywords) { + if (cssKeywords.hasOwnProperty(key)) { + reverseKeywords[cssKeywords[key]] = key; + } +} -/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(441); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "merge", function() { return _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__["merge"]; }); +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']} +}; -/* harmony import */ var _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__ = __webpack_require__(81); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeAll", function() { return _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__["mergeAll"]; }); +// 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); + } -/* harmony import */ var _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(82); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "flatMap", function() { return _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__["mergeMap"]; }); + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } -/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(442); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); + 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}); + } +} -/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(443); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeScan", function() { return _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__["mergeScan"]; }); +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; -/* harmony import */ var _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__ = __webpack_require__(444); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); + 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; + } -/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(445); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "multicast", function() { return _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__["multicast"]; }); + h = Math.min(h * 60, 360); -/* harmony import */ var _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__ = __webpack_require__(41); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); + if (h < 0) { + h += 360; + } -/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(446); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); + l = (min + max) / 2; -/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(447); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__["pairwise"]; }); + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } -/* harmony import */ var _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__ = __webpack_require__(448); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "partition", function() { return _internal_operators_partition__WEBPACK_IMPORTED_MODULE_53__["partition"]; }); + return [h, s * 100, l * 100]; +}; -/* harmony import */ var _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__ = __webpack_require__(449); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "pluck", function() { return _internal_operators_pluck__WEBPACK_IMPORTED_MODULE_54__["pluck"]; }); +convert.rgb.hsv = function (rgb) { + var rdif; + var gdif; + var bdif; + var h; + var s; -/* harmony import */ var _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__ = __webpack_require__(450); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publish", function() { return _internal_operators_publish__WEBPACK_IMPORTED_MODULE_55__["publish"]; }); + 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; + }; -/* harmony import */ var _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__ = __webpack_require__(451); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishBehavior", function() { return _internal_operators_publishBehavior__WEBPACK_IMPORTED_MODULE_56__["publishBehavior"]; }); + if (diff === 0) { + h = s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); -/* harmony import */ var _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__ = __webpack_require__(452); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); + 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; + } + } -/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(453); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishReplay", function() { return _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__["publishReplay"]; }); + return [ + h * 360, + s * 100, + v * 100 + ]; +}; -/* harmony import */ var _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__ = __webpack_require__(454); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); +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)); -/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(439); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); -/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(455); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); + return [h, w * 100, b * 100]; +}; -/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(456); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__["repeatWhen"]; }); +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; -/* harmony import */ var _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__ = __webpack_require__(457); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); + 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; -/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(458); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); + return [c * 100, m * 100, y * 100, k * 100]; +}; -/* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(30); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "refCount", function() { return _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__["refCount"]; }); +/** + * 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) + ); +} -/* harmony import */ var _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__ = __webpack_require__(459); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); +convert.rgb.keyword = function (rgb) { + var reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } -/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(460); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); + var currentClosestDistance = Infinity; + var currentClosestKeyword; -/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(440); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__["scan"]; }); + for (var keyword in cssKeywords) { + if (cssKeywords.hasOwnProperty(keyword)) { + var value = cssKeywords[keyword]; -/* harmony import */ var _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__ = __webpack_require__(461); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); + // Compute comparative distance + var distance = comparativeDistance(rgb, value); -/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(462); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + } -/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(463); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); + return currentClosestKeyword; +}; -/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(464); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; -/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(465); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skip", function() { return _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__["skip"]; }); +convert.rgb.xyz = function (rgb) { + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; -/* harmony import */ var _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__ = __webpack_require__(466); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); + // 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); -/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(467); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); + 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); -/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(468); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__["skipWhile"]; }); + return [x * 100, y * 100, z * 100]; +}; -/* harmony import */ var _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__ = __webpack_require__(469); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); +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; -/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(470); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); + x /= 95.047; + y /= 100; + z /= 108.883; -/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(472); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); + 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); -/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(473); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); -/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(474); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMapTo", function() { return _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__["switchMapTo"]; }); + return [l, a, b]; +}; -/* harmony import */ var _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__ = __webpack_require__(422); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); +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; -/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(435); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); + if (s === 0) { + val = l * 255; + return [val, val, val]; + } -/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(475); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__["takeUntil"]; }); + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } -/* harmony import */ var _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__ = __webpack_require__(476); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return _internal_operators_takeWhile__WEBPACK_IMPORTED_MODULE_85__["takeWhile"]; }); + t1 = 2 * l - t2; -/* harmony import */ var _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__ = __webpack_require__(477); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); + 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--; + } -/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(478); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttle", function() { return _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__["throttle"]; }); + 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; + } -/* harmony import */ var _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__ = __webpack_require__(479); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return _internal_operators_throttleTime__WEBPACK_IMPORTED_MODULE_88__["throttleTime"]; }); + rgb[i] = val * 255; + } -/* harmony import */ var _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__ = __webpack_require__(421); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return _internal_operators_throwIfEmpty__WEBPACK_IMPORTED_MODULE_89__["throwIfEmpty"]; }); + return rgb; +}; -/* harmony import */ var _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__ = __webpack_require__(480); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeInterval", function() { return _internal_operators_timeInterval__WEBPACK_IMPORTED_MODULE_90__["timeInterval"]; }); +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; -/* harmony import */ var _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__ = __webpack_require__(481); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeout", function() { return _internal_operators_timeout__WEBPACK_IMPORTED_MODULE_91__["timeout"]; }); + 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); -/* harmony import */ var _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__ = __webpack_require__(482); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return _internal_operators_timeoutWith__WEBPACK_IMPORTED_MODULE_92__["timeoutWith"]; }); + return [h, sv * 100, v * 100]; +}; -/* harmony import */ var _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__ = __webpack_require__(483); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); +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; -/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(484); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); + 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; -/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(485); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "window", function() { return _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__["window"]; }); + 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]; + } +}; -/* harmony import */ var _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__ = __webpack_require__(486); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); +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; -/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(487); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__["windowTime"]; }); + l = (2 - s) * v; + lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; -/* harmony import */ var _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__ = __webpack_require__(488); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); + return [h, sl * 100, l * 100]; +}; -/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(489); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); +// 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; -/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(490); -/* 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__(491); -/* 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__(492); -/* 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 */ + // 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; + } + 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; + } + return [r * 255, g * 255, b * 255]; +}; +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; + 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 [r * 255, g * 255, b * 255]; +}; +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; + 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); + // assume sRGB + r = r > 0.0031308 + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + : r * 12.92; + g = g > 0.0031308 + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + : g * 12.92; + 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); + return [r * 255, g * 255, b * 255]; +}; +convert.xyz.lab = function (xyz) { + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; + x /= 95.047; + y /= 100; + z /= 108.883; + 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); + 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]; +}; +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; + if (h < 0) { + h += 360; + } + c = Math.sqrt(a * a + b * b); + return [l, c, h]; +}; +convert.lch.lab = function (lch) { + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); + return [l, a, b]; +}; +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 + value = Math.round(value / 50); + if (value === 0) { + return 30; + } + var ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + if (value === 2) { + ansi += 60; + } + return ansi; +}; +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]); +}; +convert.rgb.ansi256 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + // 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 (r > 248) { + return 231; + } + return Math.round(((r - 8) / 247) * 24) + 232; + } + var ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + return ansi; +}; +convert.ansi16.rgb = function (args) { + var color = args % 10; + // handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + color = color / 10.5 * 255; + return [color, color, color]; + } + 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; + return [r, g, b]; +}; +convert.ansi256.rgb = function (args) { + // handle greyscale + if (args >= 232) { + var c = (args - 232) * 10 + 8; + return [c, c, c]; + } + args -= 16; + 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; + return [r, g, b]; +}; +convert.rgb.hex = function (args) { + var integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + 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]; + } + var colorString = match[0]; + if (match[0].length === 3) { + colorString = colorString.split('').map(function (char) { + return char + char; + }).join(''); + } + var integer = parseInt(colorString, 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; + return [r, g, b]; +}; +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; + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } + 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; + } + hue /= 6; + hue %= 1; + return [hue * 360, chroma * 100, grayscale * 100]; +}; +convert.hsl.hcg = function (hsl) { + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var c = 1; + var f = 0; + 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); + } + return [hsl[0], c * 100, f * 100]; +}; +convert.hsv.hcg = function (hsv) { + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var c = s * v; + var f = 0; + if (c < 1.0) { + f = (v - c) / (1 - c); + } + return [hsv[0], c * 100, f * 100]; +}; +convert.hcg.rgb = function (hcg) { + var h = hcg[0] / 360; + var c = hcg[1] / 100; + var g = hcg[2] / 100; + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } + var pure = [0, 0, 0]; + var hi = (h % 1) * 6; + var v = hi % 1; + var w = 1 - v; + var mg = 0; + 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; + } + mg = (1.0 - c) * g; + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; +convert.hcg.hsv = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var v = c + g * (1.0 - c); + var f = 0; + if (v > 0.0) { + f = c / v; + } + return [hcg[0], f * 100, v * 100]; +}; +convert.hcg.hsl = function (hcg) { + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var l = g * (1.0 - c) + 0.5 * c; + var s = 0; + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else + if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } + 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]; +}; +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]; +}; +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; +convert.gray.hsl = convert.gray.hsv = function (args) { + return [0, 0, args[0]]; +}; +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; +convert.gray.hex = function (gray) { + var val = Math.round(gray[0] / 100 * 255) & 0xFF; + var integer = (val << 16) + (val << 8) + val; + var string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; -//# sourceMappingURL=index.js.map +convert.rgb.gray = function (rgb) { + var val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; /***/ }), -/* 396 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 387 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "audit", function() { return audit; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + + +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] +}; +/***/ }), +/* 388 */ +/***/ (function(module, exports, __webpack_require__) { -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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, duration); - 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 = null; - throttled.unsubscribe(); - } - if (hasValue) { - this.value = null; - this.hasValue = false; - this.destination.next(value); - } - }; - AuditSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex) { - this.clearThrottle(); - }; - AuditSubscriber.prototype.notifyComplete = function () { - this.clearThrottle(); - }; - return AuditSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=audit.js.map +var conversions = __webpack_require__(386); +/* + this function routes a model to all other models. -/***/ }), -/* 397 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + 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). -"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__(55); -/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(396); -/* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(107); -/** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ + 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 + }; + } -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); }); + return graph; } -//# sourceMappingURL=auditTime.js.map +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop -/***/ }), -/* 398 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + graph[fromModel].distance = 0; -"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__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + 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); + } + } + } -function buffer(closingNotifier) { - return function bufferOperatorFunction(source) { - return source.lift(new BufferOperator(closingNotifier)); - }; + return graph; } -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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, closingNotifier)); - return _this; - } - BufferSubscriber.prototype._next = function (value) { - this.buffer.push(value); - }; - BufferSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - var buffer = this.buffer; - this.buffer = []; - this.destination.next(buffer); - }; - return BufferSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=buffer.js.map - -/***/ }), -/* 399 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +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; + } -function bufferCount(bufferSize, startBufferEvery) { - if (startBufferEvery === void 0) { - startBufferEvery = null; - } - return function bufferCountOperatorFunction(source) { - return source.lift(new BufferCountOperator(bufferSize, startBufferEvery)); - }; + fn.conversion = path; + return fn; } -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 + +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; +}; + /***/ }), -/* 400 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 389 */ +/***/ (function(module, 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__(12); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(45); -/** PURE_IMPORTS_START tslib,_scheduler_async,_Subscriber,_util_isScheduler PURE_IMPORTS_END */ - +const os = __webpack_require__(121); +const hasFlag = __webpack_require__(186); +const env = process.env; -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 (!subscriber.closed) { - state.context = subscriber.openContext(); - state.context.closeAction = this.schedule(state, state.bufferTimeSpan); - } +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; } -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); - } +if ('FORCE_COLOR' in env) { + forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; } -function dispatchBufferClose(arg) { - var subscriber = arg.subscriber, context = arg.context; - subscriber.closeContext(context); + +function translateLevel(level) { + if (level === 0) { + return false; + } + + return { + level, + hasBasic: true, + has256: level >= 2, + has16m: level >= 3 + }; } -//# sourceMappingURL=bufferTime.js.map +function supportsColor(stream) { + if (forceColor === false) { + return 0; + } -/***/ }), -/* 401 */ -/***/ (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 export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return bufferToggle; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(17); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); -/** PURE_IMPORTS_START tslib,_Subscription,_util_subscribeToResult,_OuterSubscriber PURE_IMPORTS_END */ + 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; + } -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.openings = openings; - _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, outerIndex, innerIndex, innerSub) { - 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 + return 1; + } + if ('CI' in env) { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + return 1; + } -/***/ }), -/* 402 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + return min; + } -"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__(12); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(17); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subscription,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + 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; + } -function bufferWhen(closingSelector) { - return function (source) { - return source.lift(new BufferWhenOperator(closingSelector)); - }; + if ('COLORTERM' in env) { + return 1; + } + + if (env.TERM === 'dumb') { + return min; + } + + return min; } -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 = null; - this.subscribing = false; - }; - BufferWhenSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - 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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, closingNotifier)); - this.subscribing = false; - }; - return BufferWhenSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); -//# sourceMappingURL=bufferWhen.js.map + +function getSupportLevel(stream) { + const level = supportsColor(stream); + return translateLevel(level); +} + +module.exports = { + supportsColor: getSupportLevel, + stdout: getSupportLevel(process.stdout), + stderr: getSupportLevel(process.stderr) +}; /***/ }), -/* 403 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 390 */ +/***/ (function(module, exports, __webpack_require__) { "use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return catchError; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +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}')`); + } + } -function catchError(selector) { - return function catchErrorOperatorFunction(source) { - var operator = new CatchOperator(selector); - var caught = source.lift(operator); - return (operator.caught = caught); - }; + return results; } -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 _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](this, undefined, undefined); - this.add(innerSubscriber); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, result, undefined, undefined, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - this.add(innerSubscription); - } - } - }; - return CatchSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=catchError.js.map +function parseStyle(style) { + STYLE_REGEX.lastIndex = 0; -/***/ }), -/* 404 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + const results = []; + let matches; -"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__(68); -/** PURE_IMPORTS_START _observable_combineLatest PURE_IMPORTS_END */ + while ((matches = STYLE_REGEX.exec(style)) !== null) { + const name = matches[1]; -function combineAll(project) { - return function (source) { return source.lift(new _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__["CombineLatestOperator"](project)); }; -} -//# sourceMappingURL=combineAll.js.map + if (matches[2]) { + const args = parseArguments(name, matches[2]); + results.push([name].concat(args)); + } else { + results.push([name]); + } + } + return results; +} -/***/ }), -/* 405 */ -/***/ (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__, "combineLatest", function() { return combineLatest; }); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(18); -/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(68); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(83); -/** PURE_IMPORTS_START _util_isArray,_observable_combineLatest,_observable_from 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 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]; + } + } + } -var none = {}; -function combineLatest() { - var observables = []; - 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(); - } - 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)); }; + return current; } -//# sourceMappingURL=combineLatest.js.map +module.exports = (chalk, tmp) => { + const styles = []; + const chunks = []; + let chunk = []; -/***/ }), -/* 406 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + // 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'); + } -"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__(79); -/** PURE_IMPORTS_START _observable_concat PURE_IMPORTS_END */ + chunks.push(buildStyle(chalk, styles)(chunk.join(''))); + chunk = []; + styles.pop(); + } else { + chunk.push(chr); + } + }); -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 + 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(''); +}; /***/ }), -/* 407 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 391 */ +/***/ (function(module, 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__(82); -/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ -function concatMap(project, resultSelector) { - return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(project, resultSelector, 1); -} -//# sourceMappingURL=concatMap.js.map +const ansiRegex = __webpack_require__(392); + +module.exports = string => typeof string === 'string' ? string.replace(ansiRegex(), '') : string; /***/ }), -/* 408 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 392 */ +/***/ (function(module, 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__(407); -/** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ -function concatMapTo(innerObservable, resultSelector) { - return Object(_concatMap__WEBPACK_IMPORTED_MODULE_0__["concatMap"])(function () { return innerObservable; }, resultSelector); -} -//# sourceMappingURL=concatMapTo.js.map + +module.exports = ({onlyFirst = false} = {}) => { + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))' + ].join('|'); + + return new RegExp(pattern, onlyFirst ? undefined : 'g'); +}; /***/ }), -/* 409 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 393 */ +/***/ (function(module, 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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function count(predicate) { - return function (source) { return source.lift(new CountOperator(predicate, source)); }; +var defaults = __webpack_require__(394) +var combining = __webpack_require__(396) + +var DEFAULTS = { + nul: 0, + control: 0 } -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 +module.exports = function wcwidth(str) { + return wcswidth(str, DEFAULTS) +} -/***/ }), -/* 410 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +module.exports.config = function(opts) { + opts = defaults(opts || {}, DEFAULTS) + return function wcwidth(str) { + return wcswidth(str, opts) + } +} -"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__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* + * 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. +*/ +function wcswidth(str, opts) { + if (typeof str !== 'string') return wcwidth(str, opts) + 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 + } -function debounce(durationSelector) { - return function (source) { return source.lift(new DebounceOperator(durationSelector)); }; + return s } -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; - _this.durationSubscription = null; - 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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, duration); - if (subscription && !subscription.closed) { - this.add(this.durationSubscription = subscription); - } - }; - DebounceSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - 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 = null; - subscription.unsubscribe(); - this.remove(subscription); - } - this.value = null; - this.hasValue = false; - _super.prototype._next.call(this, value); - } - }; - return DebounceSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=debounce.js.map +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 -/***/ }), -/* 411 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + // binary search in table of non-spacing characters + if (bisearch(ucs)) return 0 -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ + // 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 + if (ucs < combining[0][0] || ucs > combining[max][1]) return false -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(); + 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 } -//# sourceMappingURL=debounceTime.js.map /***/ }), -/* 412 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 394 */ +/***/ (function(module, exports, __webpack_require__) { -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +var clone = __webpack_require__(395); +module.exports = function(options, defaults) { + options = options || {}; -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; + Object.keys(defaults).forEach(function(key) { + if (typeof options[key] === 'undefined') { + options[key] = clone(defaults[key]); } - 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 + }); + return options; +}; /***/ }), -/* 413 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 395 */ +/***/ (function(module, exports, __webpack_require__) { -"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__(12); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(414); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); -/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(42); -/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ +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; + if (typeof depth == 'undefined') + depth = Infinity; -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; + // 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; } - 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; + + 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; + } } - return DelayMessage; -}()); -//# sourceMappingURL=delay.js.map + if (circular) { + var index = allParents.indexOf(parent); -/***/ }), -/* 414 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + if (index != -1) { + return allChildren[index]; + } + allParents.push(parent); + allChildren.push(child); + } -"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 + for (var i in parent) { + var attrs; + if (proto) { + attrs = Object.getOwnPropertyDescriptor(proto, i); + } + if (attrs && attrs.set == null) { + continue; + } + child[i] = _clone(parent[i], depth - 1); + } -/***/ }), -/* 415 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { + return child; + } -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + return _clone(parent, depth); +} +/** + * 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; + var c = function () {}; + c.prototype = parent; + return new c(); +}; +// private utility functions +function __objToStr(o) { + return Object.prototype.toString.call(o); +}; +clone.__objToStr = __objToStr; -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 +function __isDate(o) { + return typeof o === 'object' && __objToStr(o) === '[object Date]'; +}; +clone.__isDate = __isDate; +function __isArray(o) { + return typeof o === 'object' && __objToStr(o) === '[object Array]'; +}; +clone.__isArray = __isArray; -/***/ }), -/* 416 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +function __isRegExp(o) { + return typeof o === 'object' && __objToStr(o) === '[object RegExp]'; +}; +clone.__isRegExp = __isRegExp; -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +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; +return clone; +})(); -function dematerialize() { - return function dematerializeOperatorFunction(source) { - return source.lift(new DeMaterializeOperator()); - }; +if ( true && module.exports) { + module.exports = clone; } -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 /***/ }), -/* 417 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 396 */ +/***/ (function(module, exports) { -"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__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +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 ] +] +/***/ }), +/* 397 */ +/***/ (function(module, exports, __webpack_require__) { -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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, flushes)); - } - return _this; - } - DistinctSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.values.clear(); - }; - DistinctSubscriber.prototype.notifyError = function (error, innerSub) { - 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; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); +"use strict"; -//# sourceMappingURL=distinct.js.map + +module.exports = ({stream = process.stdout} = {}) => { + return Boolean( + stream && stream.isTTY && + process.env.TERM !== 'dumb' && + !('CI' in process.env) + ); +}; /***/ }), -/* 418 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* 398 */ +/***/ (function(module, 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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +var Stream = __webpack_require__(138) +module.exports = MuteStream -function distinctUntilChanged(compare, keySelector) { - return function (source) { return source.lift(new DistinctUntilChangedOperator(compare, keySelector)); }; +// 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 } -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 +MuteStream.prototype = Object.create(Stream.prototype) -/***/ }), -/* 419 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +Object.defineProperty(MuteStream.prototype, 'constructor', { + value: MuteStream, + enumerable: false +}) -"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__(418); -/** PURE_IMPORTS_START _distinctUntilChanged PURE_IMPORTS_END */ +MuteStream.prototype.mute = function () { + this.muted = true +} -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]; }); +MuteStream.prototype.unmute = function () { + this.muted = false } -//# sourceMappingURL=distinctUntilKeyChanged.js.map +Object.defineProperty(MuteStream.prototype, '_onpipe', { + value: onPipe, + enumerable: false, + writable: true, + configurable: true +}) + +function onPipe (src) { + this._src = src +} -/***/ }), -/* 420 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +Object.defineProperty(MuteStream.prototype, 'isTTY', { + get: getIsTTY, + set: setIsTTY, + enumerable: true, + configurable: true +}) -"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__(62); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(412); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(422); -/** PURE_IMPORTS_START _util_ArgumentOutOfRangeError,_filter,_throwIfEmpty,_defaultIfEmpty,_take PURE_IMPORTS_END */ +function getIsTTY () { + return( (this._dest) ? this._dest.isTTY + : (this._src) ? this._src.isTTY + : false + ) +} +// 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 + }) +} +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 }) -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"](); })); - }; +MuteStream.prototype.pipe = function (dest, options) { + this._dest = dest + return Stream.prototype.pipe.call(this, dest, options) } -//# sourceMappingURL=elementAt.js.map - - -/***/ }), -/* 421 */ -/***/ (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__(12); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(63); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_util_EmptyError,_Subscriber PURE_IMPORTS_END */ +MuteStream.prototype.pause = function () { + if (this._src) return this._src.pause() +} +MuteStream.prototype.resume = function () { + if (this._src) return this._src.resume() +} -function throwIfEmpty(errorFactory) { - if (errorFactory === void 0) { - errorFactory = defaultErrorFactory; +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) } - return function (source) { - return source.lift(new ThrowIfEmptyOperator(errorFactory)); - }; + } + this.emit('data', c) } -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; + +MuteStream.prototype.end = function (c) { + if (this.muted) { + if (c && this.replace) { + c = c.toString().replace(/./g, this.replace) + } else { + c = null } - 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"](); + } + if (c) this.emit('data', c) + this.emit('end') } -//# sourceMappingURL=throwIfEmpty.js.map + +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) +}} + +MuteStream.prototype.destroy = proxy('destroy') +MuteStream.prototype.destroySoon = proxy('destroySoon') +MuteStream.prototype.close = proxy('close') /***/ }), -/* 422 */ +/* 399 */ /***/ (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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(62); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(43); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RunCommand", function() { return RunCommand; }); +/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(163); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); +/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(146); +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ -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; +const RunCommand = { + description: 'Run script defined in package.json in each package that contains that script.', + name: 'run', + + async run(projects, projectGraph, { + extraArgs + }) { + const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_3__["topologicallyBatchProjects"])(projects, projectGraph); + + if (extraArgs.length === 0) { + throw new _utils_errors__WEBPACK_IMPORTED_MODULE_0__["CliError"]('No script specified'); } - 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 + const scriptName = extraArgs[0]; + const scriptArgs = extraArgs.slice(1); + await Object(_utils_parallelize__WEBPACK_IMPORTED_MODULE_2__["parallelizeBatches"])(batchedProjects, async project => { + if (project.hasScript(scriptName)) { + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info(`[${project.name}] running "${scriptName}" script`); + await project.runScriptStreaming(scriptName, { + args: scriptArgs + }); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].success(`[${project.name}] complete`); + } + }); + } + +}; /***/ }), -/* 423 */ +/* 400 */ /***/ (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__(79); -/* harmony import */ var _observable_of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(44); -/** PURE_IMPORTS_START _observable_concat,_observable_of PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatchCommand", function() { return WatchCommand; }); +/* harmony import */ var _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(163); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); +/* harmony import */ var _utils_parallelize__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(146); +/* harmony import */ var _utils_watch__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(401); +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ -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 -/***/ }), -/* 424 */ -/***/ (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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/** + * Name of the script in the package/project package.json file to run during `kbn watch`. + */ +const watchScriptName = 'kbn:watch'; +/** + * Name of the Kibana project. + */ -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; +const kibanaProjectName = 'kibana'; +/** + * Command that traverses through list of available projects/packages that have `kbn:watch` script in their + * package.json files, groups them into topology aware batches and then processes theses batches one by one + * running `kbn:watch` scripts in parallel within the same batch. + * + * Command internally relies on the fact that most of the build systems that are triggered by `kbn:watch` + * will emit special "marker" once build/watch process is ready that we can use as completion condition for + * the `kbn:watch` script and eventually for the entire batch. Currently we support completion "markers" for + * `webpack` and `tsc` only, for the rest we rely on predefined timeouts. + */ + +const WatchCommand = { + description: 'Runs `kbn:watch` script for every project.', + name: 'watch', + + async run(projects, projectGraph) { + const projectsToWatch = new Map(); + + for (const project of projects.values()) { + // We can't watch project that doesn't have `kbn:watch` script. + if (project.hasScript(watchScriptName)) { + projectsToWatch.set(project.name, project); + } } - 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; + + if (projectsToWatch.size === 0) { + throw new _utils_errors__WEBPACK_IMPORTED_MODULE_0__["CliError"](`There are no projects to watch found. Make sure that projects define 'kbn:watch' script in 'package.json'.`); } - 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 + const projectNames = Array.from(projectsToWatch.keys()); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info(`Running ${watchScriptName} scripts for [${projectNames.join(', ')}].`); // Kibana should always be run the last, so we don't rely on automatic + // topological batching and push it to the last one-entry batch manually. + + const shouldWatchKibanaProject = projectsToWatch.delete(kibanaProjectName); + const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_3__["topologicallyBatchProjects"])(projectsToWatch, projectGraph); + + if (shouldWatchKibanaProject) { + batchedProjects.push([projects.get(kibanaProjectName)]); + } + + await Object(_utils_parallelize__WEBPACK_IMPORTED_MODULE_2__["parallelizeBatches"])(batchedProjects, async pkg => { + const completionHint = await Object(_utils_watch__WEBPACK_IMPORTED_MODULE_4__["waitUntilWatchIsReady"])(pkg.runScriptStreaming(watchScriptName, { + debug: false + }).stdout); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].success(`[${pkg.name}] Initial build completed (${completionHint}).`); + }); + } + +}; /***/ }), -/* 425 */ +/* 401 */ /***/ (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__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "waitUntilWatchIsReady", function() { return waitUntilWatchIsReady; }); +/* harmony import */ var rxjs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(8); +/* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(402); +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +/** + * Number of milliseconds we wait before we fall back to the default watch handler. + */ -function exhaust() { - return function (source) { return source.lift(new SwitchFirstOperator()); }; +const defaultHandlerDelay = 3000; +/** + * If default watch handler is used, then it's the number of milliseconds we wait for + * any build output before we consider watch task ready. + */ + +const defaultHandlerReadinessTimeout = 2000; +/** + * Describes configurable watch options. + */ + +function getWatchHandlers(buildOutput$, { + handlerDelay = defaultHandlerDelay, + handlerReadinessTimeout = defaultHandlerReadinessTimeout +}) { + const typescriptHandler = buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('$ tsc')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["map"])(() => buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('Compilation complete.')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["mapTo"])('tsc')))); + const webpackHandler = buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('$ webpack')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["map"])(() => buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["first"])(data => data.includes('Chunk Names')), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["mapTo"])('webpack')))); + const defaultHandler = rxjs__WEBPACK_IMPORTED_MODULE_0__["of"](undefined).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["delay"])(handlerReadinessTimeout), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["map"])(() => buildOutput$.pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["timeout"])(handlerDelay), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["catchError"])(() => rxjs__WEBPACK_IMPORTED_MODULE_0__["of"]('timeout'))))); + return [typescriptHandler, webpackHandler, defaultHandler]; } -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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, value)); - } - }; - SwitchFirstSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (!this.hasSubscription) { - this.destination.complete(); - } - }; - SwitchFirstSubscriber.prototype.notifyComplete = function (innerSub) { - this.remove(innerSub); - this.hasSubscription = false; - if (this.hasCompleted) { - this.destination.complete(); - } - }; - return SwitchFirstSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=exhaust.js.map +function waitUntilWatchIsReady(stream, opts = {}) { + const buildOutput$ = new rxjs__WEBPACK_IMPORTED_MODULE_0__["Subject"](); + + const onDataListener = data => buildOutput$.next(data.toString('utf-8')); + + const onEndListener = () => buildOutput$.complete(); + + const onErrorListener = e => buildOutput$.error(e); + + stream.once('end', onEndListener); + stream.once('error', onErrorListener); + stream.on('data', onDataListener); + return rxjs__WEBPACK_IMPORTED_MODULE_0__["race"](getWatchHandlers(buildOutput$, opts)).pipe(Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["mergeMap"])(whenReady => whenReady), Object(rxjs_operators__WEBPACK_IMPORTED_MODULE_1__["finalize"])(() => { + stream.removeListener('data', onDataListener); + stream.removeListener('end', onEndListener); + stream.removeListener('error', onErrorListener); + buildOutput$.complete(); + })).toPromise(); +} /***/ }), -/* 426 */ +/* 402 */ /***/ (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__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(66); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(83); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult,_map,_observable_from PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_audit__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(403); +/* 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__(404); +/* 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__(405); +/* 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__(406); +/* 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__(407); +/* 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__(408); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "bufferToggle", function() { return _internal_operators_bufferToggle__WEBPACK_IMPORTED_MODULE_5__["bufferToggle"]; }); -function exhaustMap(project, resultSelector) { - if (resultSelector) { - return function (source) { return source.pipe(exhaustMap(function (a, i) { return Object(_observable_from__WEBPACK_IMPORTED_MODULE_5__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_4__["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, value, index); - }; - ExhaustMapSubscriber.prototype._innerSub = function (result, value, index) { - var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](this, value, index); - var destination = this.destination; - destination.add(innerSubscriber); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, result, undefined, undefined, 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 (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.destination.next(innerValue); - }; - ExhaustMapSubscriber.prototype.notifyError = function (err) { - this.destination.error(err); - }; - ExhaustMapSubscriber.prototype.notifyComplete = function (innerSub) { - var destination = this.destination; - destination.remove(innerSub); - this.hasSubscription = false; - if (this.hasCompleted) { - this.destination.complete(); - } - }; - return ExhaustMapSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=exhaustMap.js.map +/* harmony import */ var _internal_operators_bufferWhen__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(409); +/* 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__(410); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return _internal_operators_catchError__WEBPACK_IMPORTED_MODULE_7__["catchError"]; }); -/***/ }), -/* 427 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(411); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return _internal_operators_combineAll__WEBPACK_IMPORTED_MODULE_8__["combineAll"]; }); -"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__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_combineLatest__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(412); +/* 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__(413); +/* 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__(80); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatAll", function() { return _internal_operators_concatAll__WEBPACK_IMPORTED_MODULE_11__["concatAll"]; }); -function expand(project, concurrent, scheduler) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; - } - if (scheduler === void 0) { - scheduler = undefined; - } - 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; -}()); +/* harmony import */ var _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(414); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return _internal_operators_concatMap__WEBPACK_IMPORTED_MODULE_12__["concatMap"]; }); -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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, result, value, index)); - }; - ExpandSubscriber.prototype._complete = function () { - this.hasCompleted = true; - if (this.hasCompleted && this.active === 0) { - this.destination.complete(); - } - this.unsubscribe(); - }; - ExpandSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this._next(innerValue); - }; - ExpandSubscriber.prototype.notifyComplete = function (innerSub) { - var buffer = this.buffer; - var destination = this.destination; - destination.remove(innerSub); - this.active--; - if (buffer && buffer.length > 0) { - this._next(buffer.shift()); - } - if (this.hasCompleted && this.active === 0) { - this.destination.complete(); - } - }; - return ExpandSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); +/* harmony import */ var _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(415); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return _internal_operators_concatMapTo__WEBPACK_IMPORTED_MODULE_13__["concatMapTo"]; }); -//# sourceMappingURL=expand.js.map +/* harmony import */ var _internal_operators_count__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(416); +/* 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__(417); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return _internal_operators_debounce__WEBPACK_IMPORTED_MODULE_15__["debounce"]; }); -/***/ }), -/* 428 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(418); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return _internal_operators_debounceTime__WEBPACK_IMPORTED_MODULE_16__["debounceTime"]; }); -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(17); -/** PURE_IMPORTS_START tslib,_Subscriber,_Subscription PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_defaultIfEmpty__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(419); +/* 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__(420); +/* 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__(422); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return _internal_operators_delayWhen__WEBPACK_IMPORTED_MODULE_19__["delayWhen"]; }); -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 +/* harmony import */ var _internal_operators_dematerialize__WEBPACK_IMPORTED_MODULE_20__ = __webpack_require__(423); +/* 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__(424); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinct", function() { return _internal_operators_distinct__WEBPACK_IMPORTED_MODULE_21__["distinct"]; }); -/***/ }), -/* 429 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__ = __webpack_require__(425); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return _internal_operators_distinctUntilChanged__WEBPACK_IMPORTED_MODULE_22__["distinctUntilChanged"]; }); -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_distinctUntilKeyChanged__WEBPACK_IMPORTED_MODULE_23__ = __webpack_require__(426); +/* 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__(427); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "elementAt", function() { return _internal_operators_elementAt__WEBPACK_IMPORTED_MODULE_24__["elementAt"]; }); -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; -}()); +/* harmony import */ var _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__ = __webpack_require__(430); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return _internal_operators_endWith__WEBPACK_IMPORTED_MODULE_25__["endWith"]; }); -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"])); +/* harmony import */ var _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__ = __webpack_require__(431); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "every", function() { return _internal_operators_every__WEBPACK_IMPORTED_MODULE_26__["every"]; }); -//# sourceMappingURL=find.js.map +/* harmony import */ var _internal_operators_exhaust__WEBPACK_IMPORTED_MODULE_27__ = __webpack_require__(432); +/* 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__(433); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return _internal_operators_exhaustMap__WEBPACK_IMPORTED_MODULE_28__["exhaustMap"]; }); -/***/ }), -/* 430 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__ = __webpack_require__(434); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "expand", function() { return _internal_operators_expand__WEBPACK_IMPORTED_MODULE_29__["expand"]; }); -"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__(429); -/** PURE_IMPORTS_START _operators_find PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__ = __webpack_require__(105); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "filter", function() { return _internal_operators_filter__WEBPACK_IMPORTED_MODULE_30__["filter"]; }); -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 +/* harmony import */ var _internal_operators_finalize__WEBPACK_IMPORTED_MODULE_31__ = __webpack_require__(435); +/* 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__(436); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "find", function() { return _internal_operators_find__WEBPACK_IMPORTED_MODULE_32__["find"]; }); -/***/ }), -/* 431 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__ = __webpack_require__(437); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return _internal_operators_findIndex__WEBPACK_IMPORTED_MODULE_33__["findIndex"]; }); -"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__(63); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(422); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(412); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(421); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); -/** PURE_IMPORTS_START _util_EmptyError,_filter,_take,_defaultIfEmpty,_throwIfEmpty,_util_identity PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_first__WEBPACK_IMPORTED_MODULE_34__ = __webpack_require__(438); +/* 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__(31); +/* 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__(439); +/* 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__(440); +/* 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__(441); +/* 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__(66); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "map", function() { return _internal_operators_map__WEBPACK_IMPORTED_MODULE_39__["map"]; }); -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 +/* harmony import */ var _internal_operators_mapTo__WEBPACK_IMPORTED_MODULE_40__ = __webpack_require__(443); +/* 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__(444); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return _internal_operators_materialize__WEBPACK_IMPORTED_MODULE_41__["materialize"]; }); -/***/ }), -/* 432 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__ = __webpack_require__(445); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "max", function() { return _internal_operators_max__WEBPACK_IMPORTED_MODULE_42__["max"]; }); -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_merge__WEBPACK_IMPORTED_MODULE_43__ = __webpack_require__(448); +/* 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__(81); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeAll", function() { return _internal_operators_mergeAll__WEBPACK_IMPORTED_MODULE_44__["mergeAll"]; }); -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 +/* harmony import */ var _internal_operators_mergeMap__WEBPACK_IMPORTED_MODULE_45__ = __webpack_require__(82); +/* 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"]; }); -/***/ }), -/* 433 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__ = __webpack_require__(449); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return _internal_operators_mergeMapTo__WEBPACK_IMPORTED_MODULE_46__["mergeMapTo"]; }); -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_mergeScan__WEBPACK_IMPORTED_MODULE_47__ = __webpack_require__(450); +/* 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__(451); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "min", function() { return _internal_operators_min__WEBPACK_IMPORTED_MODULE_48__["min"]; }); -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 +/* harmony import */ var _internal_operators_multicast__WEBPACK_IMPORTED_MODULE_49__ = __webpack_require__(452); +/* 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__(41); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "observeOn", function() { return _internal_operators_observeOn__WEBPACK_IMPORTED_MODULE_50__["observeOn"]; }); -/***/ }), -/* 434 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__ = __webpack_require__(453); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "onErrorResumeNext", function() { return _internal_operators_onErrorResumeNext__WEBPACK_IMPORTED_MODULE_51__["onErrorResumeNext"]; }); -"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__(63); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(435); -/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(421); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(412); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); -/** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_pairwise__WEBPACK_IMPORTED_MODULE_52__ = __webpack_require__(454); +/* 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__(455); +/* 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__(456); +/* 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__(457); +/* 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__(458); +/* 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__(459); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return _internal_operators_publishLast__WEBPACK_IMPORTED_MODULE_57__["publishLast"]; }); -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 +/* harmony import */ var _internal_operators_publishReplay__WEBPACK_IMPORTED_MODULE_58__ = __webpack_require__(460); +/* 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__(461); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "race", function() { return _internal_operators_race__WEBPACK_IMPORTED_MODULE_59__["race"]; }); -/***/ }), -/* 435 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__ = __webpack_require__(446); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return _internal_operators_reduce__WEBPACK_IMPORTED_MODULE_60__["reduce"]; }); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return takeLast; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(62); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(43); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__ = __webpack_require__(462); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return _internal_operators_repeat__WEBPACK_IMPORTED_MODULE_61__["repeat"]; }); +/* harmony import */ var _internal_operators_repeatWhen__WEBPACK_IMPORTED_MODULE_62__ = __webpack_require__(463); +/* 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__(464); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retry", function() { return _internal_operators_retry__WEBPACK_IMPORTED_MODULE_63__["retry"]; }); +/* harmony import */ var _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__ = __webpack_require__(465); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "retryWhen", function() { return _internal_operators_retryWhen__WEBPACK_IMPORTED_MODULE_64__["retryWhen"]; }); -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 TakeLastOperator = /*@__PURE__*/ (function () { - function TakeLastOperator(total) { - this.total = total; - if (this.total < 0) { - throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; - } - } - TakeLastOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeLastSubscriber(subscriber, this.total)); - }; - return TakeLastOperator; -}()); -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.total = total; - _this.ring = new Array(); - _this.count = 0; - 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); - } - else { - var index = count % total; - ring[index] = value; - } - }; - 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 TakeLastSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=takeLast.js.map +/* harmony import */ var _internal_operators_refCount__WEBPACK_IMPORTED_MODULE_65__ = __webpack_require__(30); +/* 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__(466); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sample", function() { return _internal_operators_sample__WEBPACK_IMPORTED_MODULE_66__["sample"]; }); -/***/ }), -/* 436 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__ = __webpack_require__(467); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sampleTime", function() { return _internal_operators_sampleTime__WEBPACK_IMPORTED_MODULE_67__["sampleTime"]; }); -"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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_scan__WEBPACK_IMPORTED_MODULE_68__ = __webpack_require__(447); +/* 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__(468); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "sequenceEqual", function() { return _internal_operators_sequenceEqual__WEBPACK_IMPORTED_MODULE_69__["sequenceEqual"]; }); -function mapTo(value) { - return function (source) { return source.lift(new MapToOperator(value)); }; -} -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 +/* harmony import */ var _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__ = __webpack_require__(469); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "share", function() { return _internal_operators_share__WEBPACK_IMPORTED_MODULE_70__["share"]; }); +/* harmony import */ var _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__ = __webpack_require__(470); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "shareReplay", function() { return _internal_operators_shareReplay__WEBPACK_IMPORTED_MODULE_71__["shareReplay"]; }); -/***/ }), -/* 437 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__ = __webpack_require__(471); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "single", function() { return _internal_operators_single__WEBPACK_IMPORTED_MODULE_72__["single"]; }); -"use strict"; -__webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "materialize", function() { return materialize; }); -/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(42); -/** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_skip__WEBPACK_IMPORTED_MODULE_73__ = __webpack_require__(472); +/* 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__(473); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipLast", function() { return _internal_operators_skipLast__WEBPACK_IMPORTED_MODULE_74__["skipLast"]; }); +/* harmony import */ var _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__ = __webpack_require__(474); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return _internal_operators_skipUntil__WEBPACK_IMPORTED_MODULE_75__["skipUntil"]; }); -function materialize() { - return function materializeOperatorFunction(source) { - return source.lift(new MaterializeOperator()); - }; -} -var MaterializeOperator = /*@__PURE__*/ (function () { - function MaterializeOperator() { - } - MaterializeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new MaterializeSubscriber(subscriber)); - }; - return MaterializeOperator; -}()); -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 +/* harmony import */ var _internal_operators_skipWhile__WEBPACK_IMPORTED_MODULE_76__ = __webpack_require__(475); +/* 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__(476); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return _internal_operators_startWith__WEBPACK_IMPORTED_MODULE_77__["startWith"]; }); -/***/ }), -/* 438 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { +/* harmony import */ var _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__ = __webpack_require__(477); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "subscribeOn", function() { return _internal_operators_subscribeOn__WEBPACK_IMPORTED_MODULE_78__["subscribeOn"]; }); -"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__(439); -/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ +/* harmony import */ var _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__ = __webpack_require__(479); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchAll", function() { return _internal_operators_switchAll__WEBPACK_IMPORTED_MODULE_79__["switchAll"]; }); -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 +/* harmony import */ var _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__ = __webpack_require__(480); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "switchMap", function() { return _internal_operators_switchMap__WEBPACK_IMPORTED_MODULE_80__["switchMap"]; }); +/* harmony import */ var _internal_operators_switchMapTo__WEBPACK_IMPORTED_MODULE_81__ = __webpack_require__(481); +/* 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__(429); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "take", function() { return _internal_operators_take__WEBPACK_IMPORTED_MODULE_82__["take"]; }); + +/* harmony import */ var _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__ = __webpack_require__(442); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "takeLast", function() { return _internal_operators_takeLast__WEBPACK_IMPORTED_MODULE_83__["takeLast"]; }); + +/* harmony import */ var _internal_operators_takeUntil__WEBPACK_IMPORTED_MODULE_84__ = __webpack_require__(482); +/* 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__(483); +/* 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__(484); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "tap", function() { return _internal_operators_tap__WEBPACK_IMPORTED_MODULE_86__["tap"]; }); + +/* harmony import */ var _internal_operators_throttle__WEBPACK_IMPORTED_MODULE_87__ = __webpack_require__(485); +/* 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__(486); +/* 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__(428); +/* 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__(487); +/* 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__(488); +/* 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__(489); +/* 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__(490); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "timestamp", function() { return _internal_operators_timestamp__WEBPACK_IMPORTED_MODULE_93__["timestamp"]; }); + +/* harmony import */ var _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__ = __webpack_require__(491); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "toArray", function() { return _internal_operators_toArray__WEBPACK_IMPORTED_MODULE_94__["toArray"]; }); + +/* harmony import */ var _internal_operators_window__WEBPACK_IMPORTED_MODULE_95__ = __webpack_require__(492); +/* 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__(493); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowCount", function() { return _internal_operators_windowCount__WEBPACK_IMPORTED_MODULE_96__["windowCount"]; }); + +/* harmony import */ var _internal_operators_windowTime__WEBPACK_IMPORTED_MODULE_97__ = __webpack_require__(494); +/* 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__(495); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return _internal_operators_windowToggle__WEBPACK_IMPORTED_MODULE_98__["windowToggle"]; }); + +/* harmony import */ var _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__ = __webpack_require__(496); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "windowWhen", function() { return _internal_operators_windowWhen__WEBPACK_IMPORTED_MODULE_99__["windowWhen"]; }); + +/* harmony import */ var _internal_operators_withLatestFrom__WEBPACK_IMPORTED_MODULE_100__ = __webpack_require__(497); +/* 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__(498); +/* 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__(499); +/* 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 */ -/***/ }), -/* 439 */ -/***/ (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__(440); -/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(435); -/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(412); -/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24); -/** 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); - }; - } - 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); - }; -} -//# sourceMappingURL=reduce.js.map + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +//# sourceMappingURL=index.js.map /***/ }), -/* 440 */ +/* 403 */ /***/ (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__, "audit", function() { return audit; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -function scan(accumulator, seed) { - var hasSeed = false; - if (arguments.length >= 2) { - hasSeed = true; - } - return function scanOperatorFunction(source) { - return source.lift(new ScanOperator(accumulator, seed, hasSeed)); +function audit(durationSelector) { + return function auditOperatorFunction(source) { + return source.lift(new AuditOperator(durationSelector)); }; } -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 AuditOperator = /*@__PURE__*/ (function () { + function AuditOperator(durationSelector) { + this.durationSelector = durationSelector; } - ScanOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ScanSubscriber(subscriber, this.accumulator, this.seed, this.hasSeed)); + AuditOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new AuditSubscriber(subscriber, this.durationSelector)); }; - return ScanOperator; + return AuditOperator; }()); -var ScanSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ScanSubscriber, _super); - function ScanSubscriber(destination, accumulator, _seed, hasSeed) { +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.accumulator = accumulator; - _this._seed = _seed; - _this.hasSeed = hasSeed; - _this.index = 0; + _this.durationSelector = durationSelector; + _this.hasValue = false; return _this; } - 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); - } - else { - return this._tryNext(value); + 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); + } } }; - ScanSubscriber.prototype._tryNext = function (value) { - var index = this.index++; - var result; - try { - result = this.accumulator(this.seed, value, index); + 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(); } - catch (err) { - this.destination.error(err); + if (hasValue) { + this.value = undefined; + this.hasValue = false; + this.destination.next(value); } - this.seed = result; - this.destination.next(result); }; - return ScanSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=scan.js.map + AuditSubscriber.prototype.notifyNext = function () { + this.clearThrottle(); + }; + AuditSubscriber.prototype.notifyComplete = function () { + this.clearThrottle(); + }; + return AuditSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=audit.js.map /***/ }), -/* 441 */ +/* 404 */ /***/ (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__(98); -/** PURE_IMPORTS_START _observable_merge PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "auditTime", function() { return auditTime; }); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(55); +/* harmony import */ var _audit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(403); +/* harmony import */ var _observable_timer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(108); +/** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ -function merge() { - var observables = []; - for (var _i = 0; _i < arguments.length; _i++) { - observables[_i] = arguments[_i]; + + +function auditTime(duration, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; } - return function (source) { return source.lift.call(_observable_merge__WEBPACK_IMPORTED_MODULE_0__["merge"].apply(void 0, [source].concat(observables))); }; + return Object(_audit__WEBPACK_IMPORTED_MODULE_1__["audit"])(function () { return Object(_observable_timer__WEBPACK_IMPORTED_MODULE_2__["timer"])(duration, scheduler); }); } -//# sourceMappingURL=merge.js.map +//# sourceMappingURL=auditTime.js.map /***/ }), -/* 442 */ +/* 405 */ /***/ (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__(82); -/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buffer", function() { return buffer; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe 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); + +function buffer(closingNotifier) { + return function bufferOperatorFunction(source) { + return source.lift(new BufferOperator(closingNotifier)); + }; +} +var BufferOperator = /*@__PURE__*/ (function () { + function BufferOperator(closingNotifier) { + this.closingNotifier = closingNotifier; } - if (typeof resultSelector === 'number') { - concurrent = resultSelector; + 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; } - return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(function () { return innerObservable; }, concurrent); -} -//# sourceMappingURL=mergeMapTo.js.map + 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 /***/ }), -/* 443 */ +/* 406 */ /***/ (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__, "bufferCount", function() { return bufferCount; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(70); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(71); -/** PURE_IMPORTS_START tslib,_util_subscribeToResult,_OuterSubscriber,_InnerSubscriber PURE_IMPORTS_END */ - - +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function mergeScan(accumulator, seed, concurrent) { - if (concurrent === void 0) { - concurrent = Number.POSITIVE_INFINITY; +function bufferCount(bufferSize, startBufferEvery) { + if (startBufferEvery === void 0) { + startBufferEvery = null; } - return function (source) { return source.lift(new MergeScanOperator(accumulator, seed, concurrent)); }; + return function bufferCountOperatorFunction(source) { + return source.lift(new BufferCountOperator(bufferSize, startBufferEvery)); + }; } -var MergeScanOperator = /*@__PURE__*/ (function () { - function MergeScanOperator(accumulator, seed, concurrent) { - this.accumulator = accumulator; - this.seed = seed; - this.concurrent = concurrent; +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; + } } - MergeScanOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new MergeScanSubscriber(subscriber, this.accumulator, this.seed, this.concurrent)); + BufferCountOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new this.subscriberClass(subscriber, this.bufferSize, this.startBufferEvery)); }; - return MergeScanOperator; + return BufferCountOperator; }()); - -var MergeScanSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](MergeScanSubscriber, _super); - function MergeScanSubscriber(destination, accumulator, acc, concurrent) { +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.accumulator = accumulator; - _this.acc = acc; - _this.concurrent = concurrent; - _this.hasValue = false; - _this.hasCompleted = false; + _this.bufferSize = bufferSize; _this.buffer = []; - _this.active = 0; - _this.index = 0; 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); - } - this.active++; - this._innerSub(ish, value, index); - } - else { - this.buffer.push(value); - } - }; - MergeScanSubscriber.prototype._innerSub = function (ish, value, index) { - var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_3__["InnerSubscriber"](this, value, index); - var destination = this.destination; - destination.add(innerSubscriber); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_1__["subscribeToResult"])(this, ish, undefined, undefined, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - destination.add(innerSubscription); - } - }; - 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(); + BufferCountSubscriber.prototype._next = function (value) { + var buffer = this.buffer; + buffer.push(value); + if (buffer.length == this.bufferSize) { + this.destination.next(buffer); + this.buffer = []; } - this.unsubscribe(); }; - MergeScanSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - var destination = this.destination; - this.acc = innerValue; - this.hasValue = true; - destination.next(innerValue); - }; - MergeScanSubscriber.prototype.notifyComplete = function (innerSub) { + BufferCountSubscriber.prototype._complete = function () { var buffer = this.buffer; - var destination = this.destination; - destination.remove(innerSub); - 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(); + this.destination.next(buffer); } + _super.prototype._complete.call(this); }; - return MergeScanSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); - -//# sourceMappingURL=mergeScan.js.map + 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 /***/ }), -/* 444 */ +/* 407 */ /***/ (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__(439); -/** 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 +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "bufferTime", function() { return bufferTime; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(45); +/** PURE_IMPORTS_START tslib,_scheduler_async,_Subscriber,_util_isScheduler PURE_IMPORTS_END */ -/***/ }), -/* 445 */ -/***/ (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__(26); -/** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */ -function multicast(subjectOrSubjectFactory, selector) { - return function multicastOperatorFunction(source) { - var subjectFactory; - if (typeof subjectOrSubjectFactory === 'function') { - subjectFactory = subjectOrSubjectFactory; +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 { - subjectFactory = function subjectFactory() { - return subjectOrSubjectFactory; - }; + 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)); } - if (typeof selector === 'function') { - return source.lift(new MulticastOperator(subjectFactory, selector)); + 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); } - var connectable = Object.create(source, _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__["connectableObservableDescriptor"]); - connectable.source = source; - connectable.subjectFactory = subjectFactory; - return connectable; }; + 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); + } } -var MulticastOperator = /*@__PURE__*/ (function () { - function MulticastOperator(subjectFactory, selector) { - this.subjectFactory = subjectFactory; - this.selector = selector; +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); } - 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 +} +function dispatchBufferClose(arg) { + var subscriber = arg.subscriber, context = arg.context; + subscriber.closeContext(context); +} +//# sourceMappingURL=bufferTime.js.map /***/ }), -/* 446 */ +/* 408 */ /***/ (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__, "bufferToggle", function() { return bufferToggle; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(83); -/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(17); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); /* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(71); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_observable_from,_util_isArray,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/** PURE_IMPORTS_START tslib,_Subscription,_util_subscribeToResult,_OuterSubscriber 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 = null; - 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, null).lift(new OnErrorResumeNextOperator(nextSources)); +function bufferToggle(openings, closingSelector) { + return function bufferToggleOperatorFunction(source) { + return source.lift(new BufferToggleOperator(openings, closingSelector)); + }; } -var OnErrorResumeNextOperator = /*@__PURE__*/ (function () { - function OnErrorResumeNextOperator(nextSources) { - this.nextSources = nextSources; +var BufferToggleOperator = /*@__PURE__*/ (function () { + function BufferToggleOperator(openings, closingSelector) { + this.openings = openings; + this.closingSelector = closingSelector; } - OnErrorResumeNextOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new OnErrorResumeNextSubscriber(subscriber, this.nextSources)); + BufferToggleOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new BufferToggleSubscriber(subscriber, this.openings, this.closingSelector)); }; - return OnErrorResumeNextOperator; + return BufferToggleOperator; }()); -var OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](OnErrorResumeNextSubscriber, _super); - function OnErrorResumeNextSubscriber(destination, nextSources) { +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.destination = destination; - _this.nextSources = nextSources; + _this.closingSelector = closingSelector; + _this.contexts = []; + _this.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(_this, openings)); return _this; } - OnErrorResumeNextSubscriber.prototype.notifyError = function (error, innerSub) { - this.subscribeToNextSource(); + 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); + } }; - OnErrorResumeNextSubscriber.prototype.notifyComplete = function (innerSub) { - this.subscribeToNextSource(); + 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); }; - OnErrorResumeNextSubscriber.prototype._error = function (err) { - this.subscribeToNextSource(); - this.unsubscribe(); + 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); }; - OnErrorResumeNextSubscriber.prototype._complete = function () { - this.subscribeToNextSource(); - this.unsubscribe(); + BufferToggleSubscriber.prototype.notifyNext = function (outerValue, innerValue) { + outerValue ? this.closeBuffer(outerValue) : this.openBuffer(innerValue); }; - OnErrorResumeNextSubscriber.prototype.subscribeToNextSource = function () { - var next = this.nextSources.shift(); - if (!!next) { - var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_4__["InnerSubscriber"](this, undefined, undefined); - var destination = this.destination; - destination.add(innerSubscriber); - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_5__["subscribeToResult"])(this, next, undefined, undefined, innerSubscriber); - if (innerSubscription !== innerSubscriber) { - destination.add(innerSubscription); + 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 { - this.destination.complete(); + innerSubscription.context = context; + this.add(innerSubscription); + subscription.add(innerSubscription); } }; - return OnErrorResumeNextSubscriber; + return BufferToggleSubscriber; }(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); -//# sourceMappingURL=onErrorResumeNext.js.map +//# sourceMappingURL=bufferToggle.js.map /***/ }), -/* 447 */ +/* 409 */ /***/ (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__, "bufferWhen", function() { return bufferWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(17); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_Subscription,_innerSubscribe PURE_IMPORTS_END */ -function pairwise() { - return function (source) { return source.lift(new PairwiseOperator()); }; + +function bufferWhen(closingSelector) { + return function (source) { + return source.lift(new BufferWhenOperator(closingSelector)); + }; } -var PairwiseOperator = /*@__PURE__*/ (function () { - function PairwiseOperator() { +var BufferWhenOperator = /*@__PURE__*/ (function () { + function BufferWhenOperator(closingSelector) { + this.closingSelector = closingSelector; } - PairwiseOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new PairwiseSubscriber(subscriber)); + BufferWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new BufferWhenSubscriber(subscriber, this.closingSelector)); }; - return PairwiseOperator; + return BufferWhenOperator; }()); -var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](PairwiseSubscriber, _super); - function PairwiseSubscriber(destination) { +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.hasPrev = false; + _this.closingSelector = closingSelector; + _this.subscribing = false; + _this.openBuffer(); return _this; } - PairwiseSubscriber.prototype._next = function (value) { - var pair; - if (this.hasPrev) { - pair = [this.prev, value]; + 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.hasPrev = true; + this.openBuffer(); } - this.prev = value; - if (pair) { - this.destination.next(pair); + }; + 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 PairwiseSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=pairwise.js.map + return BufferWhenSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_2__["SimpleOuterSubscriber"])); +//# sourceMappingURL=bufferWhen.js.map /***/ }), -/* 448 */ +/* 410 */ /***/ (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__(103); -/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(104); -/** PURE_IMPORTS_START _util_not,_filter PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "catchError", function() { return catchError; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe 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 catchError(selector) { + return function catchErrorOperatorFunction(source) { + var operator = new CatchOperator(selector); + var caught = source.lift(operator); + return (operator.caught = caught); }; } -//# sourceMappingURL=partition.js.map - - -/***/ }), -/* 449 */ -/***/ (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__(66); -/** PURE_IMPORTS_START _map 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.'); +var CatchOperator = /*@__PURE__*/ (function () { + function CatchOperator(selector) { + this.selector = selector; } - 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[props[i]]; - if (typeof p !== 'undefined') { - currentProp = p; + 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); } - else { - return undefined; + 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 currentProp; }; - return mapper; -} -//# sourceMappingURL=pluck.js.map + return CatchSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=catchError.js.map /***/ }), -/* 450 */ +/* 411 */ /***/ (function(module, __webpack_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__(27); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); -/** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineAll", function() { return combineAll; }); +/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(68); +/** PURE_IMPORTS_START _observable_combineLatest 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"]()); +function combineAll(project) { + return function (source) { return source.lift(new _observable_combineLatest__WEBPACK_IMPORTED_MODULE_0__["CombineLatestOperator"](project)); }; } -//# sourceMappingURL=publish.js.map +//# sourceMappingURL=combineAll.js.map /***/ }), -/* 451 */ +/* 412 */ /***/ (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__(32); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); -/** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "combineLatest", function() { return combineLatest; }); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(18); +/* harmony import */ var _observable_combineLatest__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(68); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(83); +/** PURE_IMPORTS_START _util_isArray,_observable_combineLatest,_observable_from PURE_IMPORTS_END */ -function publishBehavior(value) { - return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__["BehaviorSubject"](value))(source); }; + +var none = {}; +function combineLatest() { + var observables = []; + 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(); + } + 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)); }; } -//# sourceMappingURL=publishBehavior.js.map +//# sourceMappingURL=combineLatest.js.map /***/ }), -/* 452 */ +/* 413 */ /***/ (function(module, __webpack_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__(50); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); -/** PURE_IMPORTS_START _AsyncSubject,_multicast PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concat", function() { return concat; }); +/* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(79); +/** PURE_IMPORTS_START _observable_concat PURE_IMPORTS_END */ -function publishLast() { - return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__["AsyncSubject"]())(source); }; +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=publishLast.js.map +//# sourceMappingURL=concat.js.map /***/ }), -/* 453 */ +/* 414 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(33); -/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(445); -/** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMap", function() { return concatMap; }); +/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(82); +/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ -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); }; +function concatMap(project, resultSelector) { + return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(project, resultSelector, 1); } -//# sourceMappingURL=publishReplay.js.map +//# sourceMappingURL=concatMap.js.map /***/ }), -/* 454 */ +/* 415 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(18); -/* harmony import */ var _observable_race__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); -/** PURE_IMPORTS_START _util_isArray,_observable_race PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "concatMapTo", function() { return concatMapTo; }); +/* harmony import */ var _concatMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(414); +/** PURE_IMPORTS_START _concatMap PURE_IMPORTS_END */ -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))); - }; +function concatMapTo(innerObservable, resultSelector) { + return Object(_concatMap__WEBPACK_IMPORTED_MODULE_0__["concatMap"])(function () { return innerObservable; }, resultSelector); } -//# sourceMappingURL=race.js.map +//# sourceMappingURL=concatMapTo.js.map /***/ }), -/* 455 */ +/* 416 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeat", function() { return repeat; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "count", function() { return count; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(43); -/** PURE_IMPORTS_START tslib,_Subscriber,_observable_empty PURE_IMPORTS_END */ - +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -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)); - } - }; +function count(predicate) { + return function (source) { return source.lift(new CountOperator(predicate, source)); }; } -var RepeatOperator = /*@__PURE__*/ (function () { - function RepeatOperator(count, source) { - this.count = count; +var CountOperator = /*@__PURE__*/ (function () { + function CountOperator(predicate, source) { + this.predicate = predicate; this.source = source; } - RepeatOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RepeatSubscriber(subscriber, this.count, this.source)); + CountOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new CountSubscriber(subscriber, this.predicate, this.source)); }; - return RepeatOperator; + return CountOperator; }()); -var RepeatSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RepeatSubscriber, _super); - function RepeatSubscriber(destination, count, source) { +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.count = count; + _this.predicate = predicate; _this.source = source; + _this.count = 0; + _this.index = 0; 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()); + CountSubscriber.prototype._next = function (value) { + if (this.predicate) { + this._tryPredicate(value); + } + else { + this.count++; } }; - return RepeatSubscriber; + 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=repeat.js.map +//# sourceMappingURL=count.js.map /***/ }), -/* 456 */ +/* 417 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return repeatWhen; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "debounce", function() { return debounce; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ - -function repeatWhen(notifier) { - return function (source) { return source.lift(new RepeatWhenOperator(notifier)); }; +function debounce(durationSelector) { + return function (source) { return source.lift(new DebounceOperator(durationSelector)); }; } -var RepeatWhenOperator = /*@__PURE__*/ (function () { - function RepeatWhenOperator(notifier) { - this.notifier = notifier; +var DebounceOperator = /*@__PURE__*/ (function () { + function DebounceOperator(durationSelector) { + this.durationSelector = durationSelector; } - RepeatWhenOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RepeatWhenSubscriber(subscriber, this.notifier, source)); + DebounceOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DebounceSubscriber(subscriber, this.durationSelector)); }; - return RepeatWhenOperator; + return DebounceOperator; }()); -var RepeatWhenSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RepeatWhenSubscriber, _super); - function RepeatWhenSubscriber(destination, notifier, source) { +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.notifier = notifier; - _this.source = source; - _this.sourceIsBeingSubscribedTo = true; + _this.durationSelector = durationSelector; + _this.hasValue = false; return _this; } - RepeatWhenSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.sourceIsBeingSubscribedTo = true; - this.source.subscribe(this); - }; - RepeatWhenSubscriber.prototype.notifyComplete = function (innerSub) { - 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); + DebounceSubscriber.prototype._next = function (value) { + try { + var result = this.durationSelector.call(this, value); + if (result) { + this._tryNext(value, result); } - this._unsubscribeAndRecycle(); - this.notifications.next(); + } + catch (err) { + this.destination.error(err); } }; - RepeatWhenSubscriber.prototype._unsubscribe = function () { - var _a = this, notifications = _a.notifications, retriesSubscription = _a.retriesSubscription; - if (notifications) { - notifications.unsubscribe(); - this.notifications = null; + 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); } - if (retriesSubscription) { - retriesSubscription.unsubscribe(); - this.retriesSubscription = null; + 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); } - this.retries = null; }; - RepeatWhenSubscriber.prototype._unsubscribeAndRecycle = function () { - var _unsubscribe = this._unsubscribe; - this._unsubscribe = null; - _super.prototype._unsubscribeAndRecycle.call(this); - this._unsubscribe = _unsubscribe; - return this; + DebounceSubscriber.prototype.notifyNext = function () { + this.emitValue(); }; - 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); + 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); } - this.retries = retries; - this.retriesSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, retries); }; - return RepeatWhenSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); -//# sourceMappingURL=repeatWhen.js.map + return DebounceSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); +//# sourceMappingURL=debounce.js.map /***/ }), -/* 457 */ +/* 418 */ /***/ (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 export (binding) */ __webpack_require__.d(__webpack_exports__, "debounceTime", function() { return debounceTime; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); +/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ -function retry(count) { - if (count === void 0) { - count = -1; + +function debounceTime(dueTime, scheduler) { + if (scheduler === void 0) { + scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_2__["async"]; } - return function (source) { return source.lift(new RetryOperator(count, source)); }; + return function (source) { return source.lift(new DebounceTimeOperator(dueTime, scheduler)); }; } -var RetryOperator = /*@__PURE__*/ (function () { - function RetryOperator(count, source) { - this.count = count; - this.source = source; +var DebounceTimeOperator = /*@__PURE__*/ (function () { + function DebounceTimeOperator(dueTime, scheduler) { + this.dueTime = dueTime; + this.scheduler = scheduler; } - RetryOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RetrySubscriber(subscriber, this.count, this.source)); + DebounceTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DebounceTimeSubscriber(subscriber, this.dueTime, this.scheduler)); }; - return RetryOperator; + return DebounceTimeOperator; }()); -var RetrySubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RetrySubscriber, _super); - function RetrySubscriber(destination, count, source) { +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.count = count; - _this.source = source; + _this.dueTime = dueTime; + _this.scheduler = scheduler; + _this.debouncedSubscription = null; + _this.lastValue = null; + _this.hasValue = false; 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()); + 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); } }; - return RetrySubscriber; + 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"])); -//# sourceMappingURL=retry.js.map +function dispatchNext(subscriber) { + subscriber.debouncedNext(); +} +//# sourceMappingURL=debounceTime.js.map /***/ }), -/* 458 */ +/* 419 */ /***/ (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 export (binding) */ __webpack_require__.d(__webpack_exports__, "defaultIfEmpty", function() { return defaultIfEmpty; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function retryWhen(notifier) { - return function (source) { return source.lift(new RetryWhenOperator(notifier, source)); }; +function defaultIfEmpty(defaultValue) { + if (defaultValue === void 0) { + defaultValue = null; + } + return function (source) { return source.lift(new DefaultIfEmptyOperator(defaultValue)); }; } -var RetryWhenOperator = /*@__PURE__*/ (function () { - function RetryWhenOperator(notifier, source) { - this.notifier = notifier; - this.source = source; +var DefaultIfEmptyOperator = /*@__PURE__*/ (function () { + function DefaultIfEmptyOperator(defaultValue) { + this.defaultValue = defaultValue; } - RetryWhenOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new RetryWhenSubscriber(subscriber, this.notifier, this.source)); + DefaultIfEmptyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DefaultIfEmptySubscriber(subscriber, this.defaultValue)); }; - return RetryWhenOperator; + return DefaultIfEmptyOperator; }()); -var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](RetryWhenSubscriber, _super); - function RetryWhenSubscriber(destination, notifier, source) { +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.notifier = notifier; - _this.source = source; + _this.defaultValue = defaultValue; + _this.isEmpty = true; 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(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, retries); - } - else { - this.errors = null; - this.retriesSubscription = null; - } - this._unsubscribeAndRecycle(); - this.errors = errors; - this.retries = retries; - this.retriesSubscription = retriesSubscription; - errors.next(err); - } + DefaultIfEmptySubscriber.prototype._next = function (value) { + this.isEmpty = false; + this.destination.next(value); }; - RetryWhenSubscriber.prototype._unsubscribe = function () { - var _a = this, errors = _a.errors, retriesSubscription = _a.retriesSubscription; - if (errors) { - errors.unsubscribe(); - this.errors = null; - } - if (retriesSubscription) { - retriesSubscription.unsubscribe(); - this.retriesSubscription = null; + DefaultIfEmptySubscriber.prototype._complete = function () { + if (this.isEmpty) { + this.destination.next(this.defaultValue); } - this.retries = null; + this.destination.complete(); }; - RetryWhenSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - var _unsubscribe = this._unsubscribe; - this._unsubscribe = null; - this._unsubscribeAndRecycle(); - this._unsubscribe = _unsubscribe; - this.source.subscribe(this); - }; - return RetryWhenSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); -//# sourceMappingURL=retryWhen.js.map + return DefaultIfEmptySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=defaultIfEmpty.js.map /***/ }), -/* 459 */ +/* 420 */ /***/ (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 export (binding) */ __webpack_require__.d(__webpack_exports__, "delay", function() { return delay; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); +/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(42); +/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ -function sample(notifier) { - return function (source) { return source.lift(new SampleOperator(notifier)); }; + + +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 SampleOperator = /*@__PURE__*/ (function () { - function SampleOperator(notifier) { - this.notifier = notifier; +var DelayOperator = /*@__PURE__*/ (function () { + function DelayOperator(delay, scheduler) { + this.delay = delay; + this.scheduler = scheduler; } - SampleOperator.prototype.call = function (subscriber, source) { - var sampleSubscriber = new SampleSubscriber(subscriber); - var subscription = source.subscribe(sampleSubscriber); - subscription.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(sampleSubscriber, this.notifier)); - return subscription; + DelayOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DelaySubscriber(subscriber, this.delay, this.scheduler)); }; - return SampleOperator; + return DelayOperator; }()); -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; +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; } - SampleSubscriber.prototype._next = function (value) { - this.value = value; - this.hasValue = true; - }; - SampleSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.emitValue(); + 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; + } }; - SampleSubscriber.prototype.notifyComplete = function () { - this.emitValue(); + 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 + })); }; - SampleSubscriber.prototype.emitValue = function () { - if (this.hasValue) { - this.hasValue = false; - this.destination.next(this.value); + 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); } }; - return SampleSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=sample.js.map + 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 /***/ }), -/* 460 */ +/* 421 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ - - - -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; - } - 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); - } - }; - return SampleTimeSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -function dispatchNotification(state) { - var subscriber = state.subscriber, period = state.period; - subscriber.notifyNext(); - this.schedule(state, period); +/* 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=sampleTime.js.map +//# sourceMappingURL=isDate.js.map /***/ }), -/* 461 */ +/* 422 */ /***/ (function(module, __webpack_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 export (binding) */ __webpack_require__.d(__webpack_exports__, "delayWhen", function() { return delayWhen; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); +/** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ -function sequenceEqual(compareTo, comparator) { - return function (source) { return source.lift(new SequenceEqualOperator(compareTo, comparator)); }; + + + +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 SequenceEqualOperator = /*@__PURE__*/ (function () { - function SequenceEqualOperator(compareTo, comparator) { - this.compareTo = compareTo; - this.comparator = comparator; +var DelayWhenOperator = /*@__PURE__*/ (function () { + function DelayWhenOperator(delayDurationSelector) { + this.delayDurationSelector = delayDurationSelector; } - SequenceEqualOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SequenceEqualSubscriber(subscriber, this.compareTo, this.comparator)); + DelayWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DelayWhenSubscriber(subscriber, this.delayDurationSelector)); }; - return SequenceEqualOperator; + return DelayWhenOperator; }()); - -var SequenceEqualSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SequenceEqualSubscriber, _super); - function SequenceEqualSubscriber(destination, compareTo, comparator) { +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.compareTo = compareTo; - _this.comparator = comparator; - _this._a = []; - _this._b = []; - _this._oneComplete = false; - _this.destination.add(compareTo.subscribe(new SequenceEqualCompareToSubscriber(destination, _this))); + _this.delayDurationSelector = delayDurationSelector; + _this.completed = false; + _this.delayNotifierSubscriptions = []; + _this.index = 0; return _this; } - SequenceEqualSubscriber.prototype._next = function (value) { - if (this._oneComplete && this._b.length === 0) { - this.emit(false); - } - else { - this._a.push(value); - this.checkValues(); - } + DelayWhenSubscriber.prototype.notifyNext = function (outerValue, _innerValue, _outerIndex, _innerIndex, innerSub) { + this.destination.next(outerValue); + this.removeSubscription(innerSub); + this.tryComplete(); }; - SequenceEqualSubscriber.prototype._complete = function () { - if (this._oneComplete) { - this.emit(this._a.length === 0 && this._b.length === 0); - } - else { - this._oneComplete = true; + 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.unsubscribe(); + this.tryComplete(); }; - 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); + 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); + } }; - SequenceEqualSubscriber.prototype.emit = function (value) { - var destination = this.destination; - destination.next(value); - destination.complete(); + DelayWhenSubscriber.prototype._complete = function () { + this.completed = true; + this.tryComplete(); + this.unsubscribe(); }; - SequenceEqualSubscriber.prototype.nextB = function (value) { - if (this._oneComplete && this._a.length === 0) { - this.emit(false); - } - else { - this._b.push(value); - this.checkValues(); + DelayWhenSubscriber.prototype.removeSubscription = function (subscription) { + subscription.unsubscribe(); + var subscriptionIdx = this.delayNotifierSubscriptions.indexOf(subscription); + if (subscriptionIdx !== -1) { + this.delayNotifierSubscriptions.splice(subscriptionIdx, 1); } + return subscription.outerValue; }; - SequenceEqualSubscriber.prototype.completeB = function () { - if (this._oneComplete) { - this.emit(this._a.length === 0 && this._b.length === 0); + 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); } - else { - this._oneComplete = true; + }; + DelayWhenSubscriber.prototype.tryComplete = function () { + if (this.completed && this.delayNotifierSubscriptions.length === 0) { + this.destination.complete(); } }; - return SequenceEqualSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); - -var SequenceEqualCompareToSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SequenceEqualCompareToSubscriber, _super); - function SequenceEqualCompareToSubscriber(destination, parent) { - var _this = _super.call(this, destination) || this; + 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; } - SequenceEqualCompareToSubscriber.prototype._next = function (value) { - this.parent.nextB(value); + SubscriptionDelaySubscriber.prototype._next = function (unused) { + this.subscribeToSource(); }; - SequenceEqualCompareToSubscriber.prototype._error = function (err) { - this.parent.error(err); + SubscriptionDelaySubscriber.prototype._error = function (err) { this.unsubscribe(); + this.parent.error(err); }; - SequenceEqualCompareToSubscriber.prototype._complete = function () { - this.parent.completeB(); + SubscriptionDelaySubscriber.prototype._complete = function () { this.unsubscribe(); + this.subscribeToSource(); }; - return SequenceEqualCompareToSubscriber; + 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=sequenceEqual.js.map +//# sourceMappingURL=delayWhen.js.map /***/ }), -/* 462 */ +/* 423 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(445); -/* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); -/** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "dematerialize", function() { return dematerialize; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -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)); }; +function dematerialize() { + return function dematerializeOperatorFunction(source) { + return source.lift(new DeMaterializeOperator()); + }; } -//# sourceMappingURL=share.js.map - - -/***/ }), -/* 463 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"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__(33); -/** PURE_IMPORTS_START _ReplaySubject PURE_IMPORTS_END */ - -function shareReplay(configOrBufferSize, windowTime, scheduler) { - var config; - if (configOrBufferSize && typeof configOrBufferSize === 'object') { - config = configOrBufferSize; +var DeMaterializeOperator = /*@__PURE__*/ (function () { + function DeMaterializeOperator() { } - else { - config = { - bufferSize: configOrBufferSize, - windowTime: windowTime, - refCount: false, - scheduler: scheduler - }; + 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; } - 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++; - if (!subject || hasError) { - hasError = false; - subject = new _ReplaySubject__WEBPACK_IMPORTED_MODULE_0__["ReplaySubject"](bufferSize, windowTime, scheduler); - 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(); - }, - }); - } - var innerSub = subject.subscribe(this); - this.add(function () { - refCount--; - innerSub.unsubscribe(); - if (subscription && !isComplete && useRefCount && refCount === 0) { - subscription.unsubscribe(); - subscription = undefined; - subject = undefined; - } - }); + DeMaterializeSubscriber.prototype._next = function (value) { + value.observe(this.destination); }; -} -//# sourceMappingURL=shareReplay.js.map + return DeMaterializeSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=dematerialize.js.map /***/ }), -/* 464 */ +/* 424 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "single", function() { return single; }); +/* 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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_EmptyError PURE_IMPORTS_END */ - +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -function single(predicate) { - return function (source) { return source.lift(new SingleOperator(predicate, source)); }; +function distinct(keySelector, flushes) { + return function (source) { return source.lift(new DistinctOperator(keySelector, flushes)); }; } -var SingleOperator = /*@__PURE__*/ (function () { - function SingleOperator(predicate, source) { - this.predicate = predicate; - this.source = source; +var DistinctOperator = /*@__PURE__*/ (function () { + function DistinctOperator(keySelector, flushes) { + this.keySelector = keySelector; + this.flushes = flushes; } - SingleOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SingleSubscriber(subscriber, this.predicate, this.source)); + DistinctOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DistinctSubscriber(subscriber, this.keySelector, this.flushes)); }; - return SingleOperator; + return DistinctOperator; }()); -var SingleSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SingleSubscriber, _super); - function SingleSubscriber(destination, predicate, source) { +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.predicate = predicate; - _this.source = source; - _this.seenValue = false; - _this.index = 0; + _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; } - SingleSubscriber.prototype.applySingleValue = function (value) { - if (this.seenValue) { - this.destination.error('Sequence contains more than one element'); - } - else { - this.seenValue = true; - this.singleValue = value; - } + DistinctSubscriber.prototype.notifyNext = function () { + this.values.clear(); }; - SingleSubscriber.prototype._next = function (value) { - var index = this.index++; - if (this.predicate) { - this.tryNext(value, index); + DistinctSubscriber.prototype.notifyError = function (error) { + this._error(error); + }; + DistinctSubscriber.prototype._next = function (value) { + if (this.keySelector) { + this._useKeySelector(value); } else { - this.applySingleValue(value); + this._finalizeNext(value, value); } }; - SingleSubscriber.prototype.tryNext = function (value, index) { + DistinctSubscriber.prototype._useKeySelector = function (value) { + var key; + var destination = this.destination; try { - if (this.predicate(value, index, this.source)) { - this.applySingleValue(value); - } + key = this.keySelector(value); } catch (err) { - this.destination.error(err); + destination.error(err); + return; } + this._finalizeNext(key, value); }; - 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"]); + DistinctSubscriber.prototype._finalizeNext = function (key, value) { + var values = this.values; + if (!values.has(key)) { + values.add(key); + this.destination.next(value); } }; - return SingleSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=single.js.map + return DistinctSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); + +//# sourceMappingURL=distinct.js.map /***/ }), -/* 465 */ +/* 425 */ /***/ (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 export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilChanged", function() { return distinctUntilChanged; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function skip(count) { - return function (source) { return source.lift(new SkipOperator(count)); }; +function distinctUntilChanged(compare, keySelector) { + return function (source) { return source.lift(new DistinctUntilChangedOperator(compare, keySelector)); }; } -var SkipOperator = /*@__PURE__*/ (function () { - function SkipOperator(total) { - this.total = total; +var DistinctUntilChangedOperator = /*@__PURE__*/ (function () { + function DistinctUntilChangedOperator(compare, keySelector) { + this.compare = compare; + this.keySelector = keySelector; } - SkipOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SkipSubscriber(subscriber, this.total)); + DistinctUntilChangedOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DistinctUntilChangedSubscriber(subscriber, this.compare, this.keySelector)); }; - return SkipOperator; + return DistinctUntilChangedOperator; }()); -var SkipSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipSubscriber, _super); - function SkipSubscriber(destination, total) { +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.total = total; - _this.count = 0; + _this.keySelector = keySelector; + _this.hasKey = false; + if (typeof compare === 'function') { + _this.compare = compare; + } return _this; } - SkipSubscriber.prototype._next = function (x) { - if (++this.count > this.total) { - this.destination.next(x); + 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 SkipSubscriber; + return DistinctUntilChangedSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=skip.js.map +//# sourceMappingURL=distinctUntilChanged.js.map /***/ }), -/* 466 */ +/* 426 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(62); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "distinctUntilKeyChanged", function() { return distinctUntilKeyChanged; }); +/* harmony import */ var _distinctUntilChanged__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(425); +/** 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 -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; +/***/ }), +/* 427 */ +/***/ (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__(62); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(428); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(419); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(429); +/** 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"](); } - 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); - } + 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"](); })); }; - return SkipLastSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=skipLast.js.map +} +//# sourceMappingURL=elementAt.js.map /***/ }), -/* 467 */ +/* 428 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return skipUntil; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throwIfEmpty", function() { return throwIfEmpty; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(63); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_util_EmptyError,_Subscriber PURE_IMPORTS_END */ -function skipUntil(notifier) { - return function (source) { return source.lift(new SkipUntilOperator(notifier)); }; +function throwIfEmpty(errorFactory) { + if (errorFactory === void 0) { + errorFactory = defaultErrorFactory; + } + return function (source) { + return source.lift(new ThrowIfEmptyOperator(errorFactory)); + }; } -var SkipUntilOperator = /*@__PURE__*/ (function () { - function SkipUntilOperator(notifier) { - this.notifier = notifier; +var ThrowIfEmptyOperator = /*@__PURE__*/ (function () { + function ThrowIfEmptyOperator(errorFactory) { + this.errorFactory = errorFactory; } - SkipUntilOperator.prototype.call = function (destination, source) { - return source.subscribe(new SkipUntilSubscriber(destination, this.notifier)); + ThrowIfEmptyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ThrowIfEmptySubscriber(subscriber, this.errorFactory)); }; - return SkipUntilOperator; + return ThrowIfEmptyOperator; }()); -var SkipUntilSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipUntilSubscriber, _super); - function SkipUntilSubscriber(destination, notifier) { +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; - var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](_this, undefined, undefined); - _this.add(innerSubscriber); - _this.innerSubscription = innerSubscriber; - var innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(_this, notifier, undefined, undefined, 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 (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + ThrowIfEmptySubscriber.prototype._next = function (value) { this.hasValue = true; - if (this.innerSubscription) { - this.innerSubscription.unsubscribe(); - } + this.destination.next(value); }; - SkipUntilSubscriber.prototype.notifyComplete = function () { + 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 SkipUntilSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=skipUntil.js.map + return ThrowIfEmptySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_2__["Subscriber"])); +function defaultErrorFactory() { + return new _util_EmptyError__WEBPACK_IMPORTED_MODULE_1__["EmptyError"](); +} +//# sourceMappingURL=throwIfEmpty.js.map /***/ }), -/* 468 */ +/* 429 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return skipWhile; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "take", function() { return take; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(62); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(43); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ -function skipWhile(predicate) { - return function (source) { return source.lift(new SkipWhileOperator(predicate)); }; + + +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 SkipWhileOperator = /*@__PURE__*/ (function () { - function SkipWhileOperator(predicate) { - this.predicate = predicate; +var TakeOperator = /*@__PURE__*/ (function () { + function TakeOperator(total) { + this.total = total; + if (this.total < 0) { + throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; + } } - SkipWhileOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SkipWhileSubscriber(subscriber, this.predicate)); + TakeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeSubscriber(subscriber, this.total)); }; - return SkipWhileOperator; + return TakeOperator; }()); -var SkipWhileSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SkipWhileSubscriber, _super); - function SkipWhileSubscriber(destination, predicate) { +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.predicate = predicate; - _this.skipping = true; - _this.index = 0; + _this.total = total; + _this.count = 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); + 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 SkipWhileSubscriber; + return TakeSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=skipWhile.js.map +//# sourceMappingURL=take.js.map /***/ }), -/* 469 */ +/* 430 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "startWith", function() { return startWith; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "endWith", function() { return endWith; }); /* harmony import */ var _observable_concat__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(79); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(45); -/** PURE_IMPORTS_START _observable_concat,_util_isScheduler PURE_IMPORTS_END */ +/* harmony import */ var _observable_of__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(44); +/** PURE_IMPORTS_START _observable_concat,_observable_of PURE_IMPORTS_END */ -function startWith() { +function endWith() { 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); }; - } + 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=startWith.js.map +//# sourceMappingURL=endWith.js.map /***/ }), -/* 470 */ +/* 431 */ /***/ (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__(471); -/** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "every", function() { return every; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function subscribeOn(scheduler, delay) { - if (delay === void 0) { - delay = 0; - } - return function subscribeOnOperatorFunction(source) { - return source.lift(new SubscribeOnOperator(scheduler, delay)); - }; + +function every(predicate, thisArg) { + return function (source) { return source.lift(new EveryOperator(predicate, thisArg, source)); }; } -var SubscribeOnOperator = /*@__PURE__*/ (function () { - function SubscribeOnOperator(scheduler, delay) { - this.scheduler = scheduler; - this.delay = delay; +var EveryOperator = /*@__PURE__*/ (function () { + function EveryOperator(predicate, thisArg, source) { + this.predicate = predicate; + this.thisArg = thisArg; + this.source = source; } - SubscribeOnOperator.prototype.call = function (subscriber, source) { - return new _observable_SubscribeOnObservable__WEBPACK_IMPORTED_MODULE_0__["SubscribeOnObservable"](source, this.delay, this.scheduler).subscribe(subscriber); + EveryOperator.prototype.call = function (observer, source) { + return source.subscribe(new EverySubscriber(observer, this.predicate, this.thisArg, this.source)); }; - return SubscribeOnOperator; + return EveryOperator; }()); -//# sourceMappingURL=subscribeOn.js.map - - -/***/ }), -/* 471 */ -/***/ (function(module, __webpack_exports__, __webpack_require__) { - -"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__(12); -/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); -/* harmony import */ var _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(51); -/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(97); -/** PURE_IMPORTS_START tslib,_Observable,_scheduler_asap,_util_isNumeric PURE_IMPORTS_END */ - - - - -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; +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.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"]; - } + _this.index = 0; + _this.thisArg = thisArg || _this; return _this; } - SubscribeOnObservable.create = function (source, delay, scheduler) { - if (delay === void 0) { - delay = 0; + 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); } - if (scheduler === void 0) { - scheduler = _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__["asap"]; + catch (err) { + this.destination.error(err); + return; + } + if (!result) { + this.notifyComplete(false); } - 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 - }); + EverySubscriber.prototype._complete = function () { + this.notifyComplete(true); }; - return SubscribeOnObservable; -}(_Observable__WEBPACK_IMPORTED_MODULE_1__["Observable"])); - -//# sourceMappingURL=SubscribeOnObservable.js.map + return EverySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=every.js.map /***/ }), -/* 472 */ +/* 432 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(473); -/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(25); -/** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaust", function() { return exhaust; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -function switchAll() { - return Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(_util_identity__WEBPACK_IMPORTED_MODULE_1__["identity"]); +function exhaust() { + return function (source) { return source.lift(new SwitchFirstOperator()); }; } -//# sourceMappingURL=switchAll.js.map - +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 + /***/ }), -/* 473 */ +/* 433 */ /***/ (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 export (binding) */ __webpack_require__.d(__webpack_exports__, "exhaustMap", function() { return exhaustMap; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(71); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(66); -/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(83); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_InnerSubscriber,_util_subscribeToResult,_map,_observable_from PURE_IMPORTS_END */ - - +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(66); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(83); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ -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_5__["from"])(project(a, i)).pipe(Object(_map__WEBPACK_IMPORTED_MODULE_4__["map"])(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; +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 SwitchMapOperator(project)); }; + return function (source) { + return source.lift(new ExhaustMapOperator(project)); + }; } -var SwitchMapOperator = /*@__PURE__*/ (function () { - function SwitchMapOperator(project) { +var ExhaustMapOperator = /*@__PURE__*/ (function () { + function ExhaustMapOperator(project) { this.project = project; } - SwitchMapOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new SwitchMapSubscriber(subscriber, this.project)); + ExhaustMapOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ExhaustMapSubscriber(subscriber, this.project)); }; - return SwitchMapOperator; + return ExhaustMapOperator; }()); -var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](SwitchMapSubscriber, _super); - function SwitchMapSubscriber(destination, project) { +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; } - SwitchMapSubscriber.prototype._next = function (value) { + 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 (error) { - this.destination.error(error); + catch (err) { + this.destination.error(err); return; } - this._innerSub(result, value, index); + this.hasSubscription = true; + this._innerSub(result); }; - SwitchMapSubscriber.prototype._innerSub = function (result, value, index) { - var innerSubscription = this.innerSubscription; - if (innerSubscription) { - innerSubscription.unsubscribe(); - } - var innerSubscriber = new _InnerSubscriber__WEBPACK_IMPORTED_MODULE_2__["InnerSubscriber"](this, value, index); + ExhaustMapSubscriber.prototype._innerSub = function (result) { + var innerSubscriber = new _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleInnerSubscriber"](this); var destination = this.destination; destination.add(innerSubscriber); - this.innerSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(this, result, undefined, undefined, innerSubscriber); - if (this.innerSubscription !== innerSubscriber) { - destination.add(this.innerSubscription); + var innerSubscription = Object(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["innerSubscribe"])(result, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + destination.add(innerSubscription); } }; - SwitchMapSubscriber.prototype._complete = function () { - var innerSubscription = this.innerSubscription; - if (!innerSubscription || innerSubscription.closed) { - _super.prototype._complete.call(this); + ExhaustMapSubscriber.prototype._complete = function () { + this.hasCompleted = true; + if (!this.hasSubscription) { + this.destination.complete(); } this.unsubscribe(); }; - SwitchMapSubscriber.prototype._unsubscribe = function () { - this.innerSubscription = null; + ExhaustMapSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); }; - SwitchMapSubscriber.prototype.notifyComplete = function (innerSub) { - var destination = this.destination; - destination.remove(innerSub); - this.innerSubscription = null; - if (this.isStopped) { - _super.prototype._complete.call(this); - } + ExhaustMapSubscriber.prototype.notifyError = function (err) { + this.destination.error(err); }; - SwitchMapSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.destination.next(innerValue); + ExhaustMapSubscriber.prototype.notifyComplete = function () { + this.hasSubscription = false; + if (this.hasCompleted) { + this.destination.complete(); + } }; - return SwitchMapSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=switchMap.js.map + return ExhaustMapSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); +//# sourceMappingURL=exhaustMap.js.map /***/ }), -/* 474 */ +/* 434 */ /***/ (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__(473); -/** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ +/* 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__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe 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; }); + +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)); }; } -//# sourceMappingURL=switchMapTo.js.map +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 /***/ }), -/* 475 */ +/* 435 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeUntil", function() { return takeUntil; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "finalize", function() { return finalize; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(17); +/** PURE_IMPORTS_START tslib,_Subscriber,_Subscription PURE_IMPORTS_END */ -function takeUntil(notifier) { - return function (source) { return source.lift(new TakeUntilOperator(notifier)); }; +function finalize(callback) { + return function (source) { return source.lift(new FinallyOperator(callback)); }; } -var TakeUntilOperator = /*@__PURE__*/ (function () { - function TakeUntilOperator(notifier) { - this.notifier = notifier; +var FinallyOperator = /*@__PURE__*/ (function () { + function FinallyOperator(callback) { + this.callback = callback; } - TakeUntilOperator.prototype.call = function (subscriber, source) { - var takeUntilSubscriber = new TakeUntilSubscriber(subscriber); - var notifierSubscription = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(takeUntilSubscriber, this.notifier); - if (notifierSubscription && !takeUntilSubscriber.seenValue) { - takeUntilSubscriber.add(notifierSubscription); - return source.subscribe(takeUntilSubscriber); - } - return takeUntilSubscriber; + FinallyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new FinallySubscriber(subscriber, this.callback)); }; - return TakeUntilOperator; + return FinallyOperator; }()); -var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeUntilSubscriber, _super); - function TakeUntilSubscriber(destination) { +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.seenValue = false; + _this.add(new _Subscription__WEBPACK_IMPORTED_MODULE_2__["Subscription"](callback)); return _this; } - TakeUntilSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.seenValue = true; - this.complete(); - }; - TakeUntilSubscriber.prototype.notifyComplete = function () { - }; - return TakeUntilSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=takeUntil.js.map + return FinallySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=finalize.js.map /***/ }), -/* 476 */ +/* 436 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "takeWhile", function() { return takeWhile; }); +/* 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__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function takeWhile(predicate, inclusive) { - if (inclusive === void 0) { - inclusive = false; +function find(predicate, thisArg) { + if (typeof predicate !== 'function') { + throw new TypeError('predicate is not a function'); } - return function (source) { - return source.lift(new TakeWhileOperator(predicate, inclusive)); - }; + return function (source) { return source.lift(new FindValueOperator(predicate, source, false, thisArg)); }; } -var TakeWhileOperator = /*@__PURE__*/ (function () { - function TakeWhileOperator(predicate, inclusive) { +var FindValueOperator = /*@__PURE__*/ (function () { + function FindValueOperator(predicate, source, yieldIndex, thisArg) { this.predicate = predicate; - this.inclusive = inclusive; + this.source = source; + this.yieldIndex = yieldIndex; + this.thisArg = thisArg; } - TakeWhileOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TakeWhileSubscriber(subscriber, this.predicate, this.inclusive)); + FindValueOperator.prototype.call = function (observer, source) { + return source.subscribe(new FindValueSubscriber(observer, this.predicate, this.source, this.yieldIndex, this.thisArg)); }; - return TakeWhileOperator; + return FindValueOperator; }()); -var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TakeWhileSubscriber, _super); - function TakeWhileSubscriber(destination, predicate, inclusive) { + +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.inclusive = inclusive; + _this.source = source; + _this.yieldIndex = yieldIndex; + _this.thisArg = thisArg; _this.index = 0; return _this; } - TakeWhileSubscriber.prototype._next = function (value) { + FindValueSubscriber.prototype.notifyComplete = function (value) { var destination = this.destination; - var result; + 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 { - result = this.predicate(value, this.index++); + var result = predicate.call(thisArg || this, value, index, this.source); + if (result) { + this.notifyComplete(this.yieldIndex ? index : value); + } } catch (err) { - destination.error(err); - return; + this.destination.error(err); } - 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(); - } + FindValueSubscriber.prototype._complete = function () { + this.notifyComplete(this.yieldIndex ? -1 : undefined); }; - return TakeWhileSubscriber; + return FindValueSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=takeWhile.js.map + +//# sourceMappingURL=find.js.map /***/ }), -/* 477 */ +/* 437 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(60); -/* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13); -/** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "findIndex", function() { return findIndex; }); +/* harmony import */ var _operators_find__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(436); +/** 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 +/***/ }), +/* 438 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -function tap(nextOrObserver, error, complete) { - return function tapOperatorFunction(source) { - return source.lift(new DoOperator(nextOrObserver, error, complete)); +"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__(63); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); +/* harmony import */ var _take__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(429); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(419); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(428); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); +/** 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 + + +/***/ }), +/* 439 */ +/***/ (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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + + +function ignoreElements() { + return function ignoreElementsOperatorFunction(source) { + return source.lift(new IgnoreElementsOperator()); }; } -var DoOperator = /*@__PURE__*/ (function () { - function DoOperator(nextOrObserver, error, complete) { - this.nextOrObserver = nextOrObserver; - this.error = error; - this.complete = complete; +var IgnoreElementsOperator = /*@__PURE__*/ (function () { + function IgnoreElementsOperator() { } - DoOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); + IgnoreElementsOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new IgnoreElementsSubscriber(subscriber)); }; - return DoOperator; + return IgnoreElementsOperator; }()); -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; +var IgnoreElementsSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](IgnoreElementsSubscriber, _super); + function IgnoreElementsSubscriber() { + return _super !== null && _super.apply(this, arguments) || 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(); + IgnoreElementsSubscriber.prototype._next = function (unused) { }; - return TapSubscriber; + return IgnoreElementsSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=tap.js.map +//# sourceMappingURL=ignoreElements.js.map /***/ }), -/* 478 */ +/* 440 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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 export (binding) */ __webpack_require__.d(__webpack_exports__, "isEmpty", function() { return isEmpty; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -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)); }; +function isEmpty() { + return function (source) { return source.lift(new IsEmptyOperator()); }; } -var ThrottleOperator = /*@__PURE__*/ (function () { - function ThrottleOperator(durationSelector, leading, trailing) { - this.durationSelector = durationSelector; - this.leading = leading; - this.trailing = trailing; +var IsEmptyOperator = /*@__PURE__*/ (function () { + function IsEmptyOperator() { } - ThrottleOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ThrottleSubscriber(subscriber, this.durationSelector, this.leading, this.trailing)); + IsEmptyOperator.prototype.call = function (observer, source) { + return source.subscribe(new IsEmptySubscriber(observer)); }; - return ThrottleOperator; + return IsEmptyOperator; }()); -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; +var IsEmptySubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](IsEmptySubscriber, _super); + function IsEmptySubscriber(destination) { + return _super.call(this, destination) || 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 = null; - }; - ThrottleSubscriber.prototype.throttle = function (value) { - var duration = this.tryDurationSelector(value); - if (!!duration) { - this.add(this._throttled = Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__["subscribeToResult"])(this, duration)); - } - }; - 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 = null; - if (_trailing) { - this.send(); - } + IsEmptySubscriber.prototype.notifyComplete = function (isEmpty) { + var destination = this.destination; + destination.next(isEmpty); + destination.complete(); }; - ThrottleSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.throttlingDone(); + IsEmptySubscriber.prototype._next = function (value) { + this.notifyComplete(false); }; - ThrottleSubscriber.prototype.notifyComplete = function () { - this.throttlingDone(); + IsEmptySubscriber.prototype._complete = function () { + this.notifyComplete(true); }; - return ThrottleSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__["OuterSubscriber"])); -//# sourceMappingURL=throttle.js.map + return IsEmptySubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=isEmpty.js.map /***/ }), -/* 479 */ +/* 441 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "throttleTime", function() { return throttleTime; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "last", function() { return last; }); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(63); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(442); +/* harmony import */ var _throwIfEmpty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(428); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(419); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(25); +/** PURE_IMPORTS_START _util_EmptyError,_filter,_takeLast,_throwIfEmpty,_defaultIfEmpty,_util_identity 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"](); })); }; +} +//# sourceMappingURL=last.js.map + + +/***/ }), +/* 442 */ +/***/ (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 import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); /* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(478); -/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(62); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(43); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ -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)); }; +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 ThrottleTimeOperator = /*@__PURE__*/ (function () { - function ThrottleTimeOperator(duration, scheduler, leading, trailing) { - this.duration = duration; - this.scheduler = scheduler; - this.leading = leading; - this.trailing = trailing; +var TakeLastOperator = /*@__PURE__*/ (function () { + function TakeLastOperator(total) { + this.total = total; + if (this.total < 0) { + throw new _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__["ArgumentOutOfRangeError"]; + } } - ThrottleTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new ThrottleTimeSubscriber(subscriber, this.duration, this.scheduler, this.leading, this.trailing)); + TakeLastOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeLastSubscriber(subscriber, this.total)); }; - return ThrottleTimeOperator; + return TakeLastOperator; }()); -var ThrottleTimeSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](ThrottleTimeSubscriber, _super); - function ThrottleTimeSubscriber(destination, duration, scheduler, leading, trailing) { +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.duration = duration; - _this.scheduler = scheduler; - _this.leading = leading; - _this.trailing = trailing; - _this._hasTrailingValue = false; - _this._trailingValue = null; + _this.total = total; + _this.ring = new Array(); + _this.count = 0; 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(); + TakeLastSubscriber.prototype._next = function (value) { + var ring = this.ring; + var total = this.total; + var count = this.count++; + if (ring.length < total) { + ring.push(value); } else { - this.destination.complete(); + var index = count % total; + ring[index] = value; } }; - 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; + 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]); } - throttled.unsubscribe(); - this.remove(throttled); - this.throttled = null; } + destination.complete(); }; - return ThrottleTimeSubscriber; + return TakeLastSubscriber; }(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -function dispatchNext(arg) { - var subscriber = arg.subscriber; - subscriber.clearThrottle(); -} -//# sourceMappingURL=throttleTime.js.map +//# sourceMappingURL=takeLast.js.map /***/ }), -/* 480 */ +/* 443 */ /***/ (function(module, __webpack_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__(55); -/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(440); -/* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(66); -/** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map 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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +function mapTo(value) { + return function (source) { return source.lift(new MapToOperator(value)); }; +} +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 -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); - })); - }); +/***/ }), +/* 444 */ +/***/ (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 import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _Notification__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(42); +/** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ + + + +function materialize() { + return function materializeOperatorFunction(source) { + return source.lift(new MaterializeOperator()); }; } -var TimeInterval = /*@__PURE__*/ (function () { - function TimeInterval(value, interval) { - this.value = value; - this.interval = interval; +var MaterializeOperator = /*@__PURE__*/ (function () { + function MaterializeOperator() { } - return TimeInterval; + MaterializeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new MaterializeSubscriber(subscriber)); + }; + return MaterializeOperator; }()); +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 -//# sourceMappingURL=timeInterval.js.map + +/***/ }), +/* 445 */ +/***/ (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__(446); +/** 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 /***/ }), -/* 481 */ +/* 446 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(55); -/* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); -/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(482); -/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(49); -/** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "reduce", function() { return reduce; }); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(447); +/* harmony import */ var _takeLast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(442); +/* harmony import */ var _defaultIfEmpty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(419); +/* harmony import */ var _util_pipe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(24); +/** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ -function timeout(due, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; +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); + }; } - 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 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); + }; } -//# sourceMappingURL=timeout.js.map +//# sourceMappingURL=reduce.js.map /***/ }), -/* 482 */ +/* 447 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "timeoutWith", function() { return timeoutWith; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "scan", function() { return scan; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); -/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(414); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - - +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function timeoutWith(due, withObservable, scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_1__["async"]; +function scan(accumulator, seed) { + var hasSeed = false; + if (arguments.length >= 2) { + hasSeed = true; } - 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 function scanOperatorFunction(source) { + return source.lift(new ScanOperator(accumulator, seed, hasSeed)); }; } -var TimeoutWithOperator = /*@__PURE__*/ (function () { - function TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler) { - this.waitFor = waitFor; - this.absoluteTimeout = absoluteTimeout; - this.withObservable = withObservable; - this.scheduler = 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; } - TimeoutWithOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new TimeoutWithSubscriber(subscriber, this.absoluteTimeout, this.waitFor, this.withObservable, this.scheduler)); + ScanOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ScanSubscriber(subscriber, this.accumulator, this.seed, this.hasSeed)); }; - return TimeoutWithOperator; + return ScanOperator; }()); -var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](TimeoutWithSubscriber, _super); - function TimeoutWithSubscriber(destination, absoluteTimeout, waitFor, withObservable, scheduler) { +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.absoluteTimeout = absoluteTimeout; - _this.waitFor = waitFor; - _this.withObservable = withObservable; - _this.scheduler = scheduler; - _this.action = null; - _this.scheduleTimeout(); + _this.accumulator = accumulator; + _this._seed = _seed; + _this.hasSeed = hasSeed; + _this.index = 0; return _this; } - TimeoutWithSubscriber.dispatchTimeout = function (subscriber) { - var withObservable = subscriber.withObservable; - subscriber._unsubscribeAndRecycle(); - subscriber.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__["subscribeToResult"])(subscriber, withObservable)); - }; - TimeoutWithSubscriber.prototype.scheduleTimeout = function () { - var action = this.action; - if (action) { - this.action = action.schedule(this, this.waitFor); + 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); } else { - this.add(this.action = this.scheduler.schedule(TimeoutWithSubscriber.dispatchTimeout, this.waitFor, this)); + return this._tryNext(value); } }; - TimeoutWithSubscriber.prototype._next = function (value) { - if (!this.absoluteTimeout) { - this.scheduleTimeout(); + ScanSubscriber.prototype._tryNext = function (value) { + var index = this.index++; + var result; + try { + result = this.accumulator(this.seed, value, index); } - _super.prototype._next.call(this, value); - }; - TimeoutWithSubscriber.prototype._unsubscribe = function () { - this.action = null; - this.scheduler = null; - this.withObservable = null; + catch (err) { + this.destination.error(err); + } + this.seed = result; + this.destination.next(result); }; - return TimeoutWithSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__["OuterSubscriber"])); -//# sourceMappingURL=timeoutWith.js.map + return ScanSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=scan.js.map /***/ }), -/* 483 */ +/* 448 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(55); -/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(66); -/** PURE_IMPORTS_START _scheduler_async,_map 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__(99); +/** PURE_IMPORTS_START _observable_merge PURE_IMPORTS_END */ -function timestamp(scheduler) { - if (scheduler === void 0) { - scheduler = _scheduler_async__WEBPACK_IMPORTED_MODULE_0__["async"]; +function merge() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; } - return Object(_map__WEBPACK_IMPORTED_MODULE_1__["map"])(function (value) { return new Timestamp(value, scheduler.now()); }); + return function (source) { return source.lift.call(_observable_merge__WEBPACK_IMPORTED_MODULE_0__["merge"].apply(void 0, [source].concat(observables))); }; } -var Timestamp = /*@__PURE__*/ (function () { - function Timestamp(value, timestamp) { - this.value = value; - this.timestamp = timestamp; - } - return Timestamp; -}()); - -//# sourceMappingURL=timestamp.js.map +//# sourceMappingURL=merge.js.map /***/ }), -/* 484 */ +/* 449 */ /***/ (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__(439); -/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "mergeMapTo", function() { return mergeMapTo; }); +/* harmony import */ var _mergeMap__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(82); +/** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ -function toArrayReducer(arr, item, index) { - if (index === 0) { - return [item]; +function mergeMapTo(innerObservable, resultSelector, concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; } - arr.push(item); - return arr; + if (typeof resultSelector === 'function') { + return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(function () { return innerObservable; }, resultSelector, concurrent); + } + if (typeof resultSelector === 'number') { + concurrent = resultSelector; + } + return Object(_mergeMap__WEBPACK_IMPORTED_MODULE_0__["mergeMap"])(function () { return innerObservable; }, concurrent); } -function toArray() { - return Object(_reduce__WEBPACK_IMPORTED_MODULE_0__["reduce"])(toArrayReducer, []); -} -//# sourceMappingURL=toArray.js.map +//# sourceMappingURL=mergeMapTo.js.map /***/ }), -/* 485 */ +/* 450 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "window", function() { return window; }); +/* 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__(12); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ - -function window(windowBoundaries) { - return function windowOperatorFunction(source) { - return source.lift(new WindowOperator(windowBoundaries)); - }; +function mergeScan(accumulator, seed, concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; + } + return function (source) { return source.lift(new MergeScanOperator(accumulator, seed, concurrent)); }; } -var WindowOperator = /*@__PURE__*/ (function () { - function WindowOperator(windowBoundaries) { - this.windowBoundaries = windowBoundaries; +var MergeScanOperator = /*@__PURE__*/ (function () { + function MergeScanOperator(accumulator, seed, concurrent) { + this.accumulator = accumulator; + this.seed = seed; + this.concurrent = concurrent; } - WindowOperator.prototype.call = function (subscriber, source) { - var windowSubscriber = new WindowSubscriber(subscriber); - var sourceSubscription = source.subscribe(windowSubscriber); - if (!sourceSubscription.closed) { - windowSubscriber.add(Object(_util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__["subscribeToResult"])(windowSubscriber, this.windowBoundaries)); - } - return sourceSubscription; + MergeScanOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new MergeScanSubscriber(subscriber, this.accumulator, this.seed, this.concurrent)); }; - return WindowOperator; + return MergeScanOperator; }()); -var WindowSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowSubscriber, _super); - function WindowSubscriber(destination) { + +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.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - destination.next(_this.window); + _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; } - WindowSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - this.openWindow(); - }; - WindowSubscriber.prototype.notifyError = function (error, innerSub) { - this._error(error); - }; - WindowSubscriber.prototype.notifyComplete = function (innerSub) { - this._complete(); - }; - WindowSubscriber.prototype._next = function (value) { - this.window.next(value); + 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); + } + else { + this.buffer.push(value); + } }; - WindowSubscriber.prototype._error = function (err) { - this.window.error(err); - this.destination.error(err); + 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); + } }; - WindowSubscriber.prototype._complete = function () { - this.window.complete(); - this.destination.complete(); + 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(); }; - WindowSubscriber.prototype._unsubscribe = function () { - this.window = null; + MergeScanSubscriber.prototype.notifyNext = function (innerValue) { + var destination = this.destination; + this.acc = innerValue; + this.hasValue = true; + destination.next(innerValue); }; - WindowSubscriber.prototype.openWindow = function () { - var prevWindow = this.window; - if (prevWindow) { - prevWindow.complete(); + 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(); } - var destination = this.destination; - var newWindow = this.window = new _Subject__WEBPACK_IMPORTED_MODULE_1__["Subject"](); - destination.next(newWindow); }; - return WindowSubscriber; -}(_OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__["OuterSubscriber"])); -//# sourceMappingURL=window.js.map + return MergeScanSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_1__["SimpleOuterSubscriber"])); + +//# sourceMappingURL=mergeScan.js.map /***/ }), -/* 486 */ +/* 451 */ /***/ (function(module, __webpack_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__(12); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); -/** PURE_IMPORTS_START tslib,_Subscriber,_Subject 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__(446); +/** 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 -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); +/***/ }), +/* 452 */ +/***/ (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__(26); +/** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */ + +function multicast(subjectOrSubjectFactory, selector) { + return function multicastOperatorFunction(source) { + var subjectFactory; + if (typeof subjectOrSubjectFactory === 'function') { + subjectFactory = subjectOrSubjectFactory; } - }; - WindowCountSubscriber.prototype._error = function (err) { - var windows = this.windows; - if (windows) { - while (windows.length > 0 && !this.closed) { - windows.shift().error(err); - } + else { + subjectFactory = function subjectFactory() { + return subjectOrSubjectFactory; + }; } - this.destination.error(err); - }; - WindowCountSubscriber.prototype._complete = function () { - var windows = this.windows; - if (windows) { - while (windows.length > 0 && !this.closed) { - windows.shift().complete(); - } + if (typeof selector === 'function') { + return source.lift(new MulticastOperator(subjectFactory, selector)); } - this.destination.complete(); + var connectable = Object.create(source, _observable_ConnectableObservable__WEBPACK_IMPORTED_MODULE_0__["connectableObservableDescriptor"]); + connectable.source = source; + connectable.subjectFactory = subjectFactory; + return connectable; }; - WindowCountSubscriber.prototype._unsubscribe = function () { - this.count = 0; - this.windows = null; +} +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 WindowCountSubscriber; -}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); -//# sourceMappingURL=windowCount.js.map + return MulticastOperator; +}()); + +//# sourceMappingURL=multicast.js.map /***/ }), -/* 487 */ +/* 453 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowTime", function() { return windowTime; }); +/* 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__(12); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); -/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); -/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); -/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(97); -/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(45); -/** PURE_IMPORTS_START tslib,_Subject,_scheduler_async,_Subscriber,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ - - +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(83); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(18); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_observable_from,_util_isArray,_innerSubscribe PURE_IMPORTS_END */ -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]; +function onErrorResumeNext() { + var nextSources = []; + for (var _i = 0; _i < arguments.length; _i++) { + nextSources[_i] = arguments[_i]; } - else if (Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_4__["isNumeric"])(arguments[2])) { - maxWindowSize = arguments[2]; + if (nextSources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(nextSources[0])) { + nextSources = nextSources[0]; } - if (Object(_util_isScheduler__WEBPACK_IMPORTED_MODULE_5__["isScheduler"])(arguments[1])) { - scheduler = arguments[1]; + 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]; } - else if (Object(_util_isNumeric__WEBPACK_IMPORTED_MODULE_4__["isNumeric"])(arguments[1])) { - windowCreationInterval = arguments[1]; + var source = undefined; + if (nextSources.length === 1 && Object(_util_isArray__WEBPACK_IMPORTED_MODULE_2__["isArray"])(nextSources[0])) { + nextSources = nextSources[0]; } - return function windowTimeOperatorFunction(source) { - return source.lift(new WindowTimeOperator(windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler)); - }; + source = nextSources.shift(); + return Object(_observable_from__WEBPACK_IMPORTED_MODULE_1__["from"])(source).lift(new OnErrorResumeNextOperator(nextSources)); } -var WindowTimeOperator = /*@__PURE__*/ (function () { - function WindowTimeOperator(windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler) { - this.windowTimeSpan = windowTimeSpan; - this.windowCreationInterval = windowCreationInterval; - this.maxWindowSize = maxWindowSize; - this.scheduler = scheduler; +var OnErrorResumeNextOperator = /*@__PURE__*/ (function () { + function OnErrorResumeNextOperator(nextSources) { + this.nextSources = nextSources; } - WindowTimeOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WindowTimeSubscriber(subscriber, this.windowTimeSpan, this.windowCreationInterval, this.maxWindowSize, this.scheduler)); + OnErrorResumeNextOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new OnErrorResumeNextSubscriber(subscriber, this.nextSources)); }; - return WindowTimeOperator; + return OnErrorResumeNextOperator; }()); -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 OnErrorResumeNextSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](OnErrorResumeNextSubscriber, _super); + function OnErrorResumeNextSubscriber(destination, nextSources) { 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)); - } + _this.nextSources = nextSources; 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); - } - } - } + OnErrorResumeNextSubscriber.prototype.notifyError = function () { + this.subscribeToNextSource(); }; - WindowTimeSubscriber.prototype._error = function (err) { - var windows = this.windows; - while (windows.length > 0) { - windows.shift().error(err); - } - this.destination.error(err); + OnErrorResumeNextSubscriber.prototype.notifyComplete = function () { + this.subscribeToNextSource(); }; - 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(); + OnErrorResumeNextSubscriber.prototype._error = function (err) { + this.subscribeToNextSource(); + this.unsubscribe(); }; - WindowTimeSubscriber.prototype.openWindow = function () { - var window = new CountedSubject(); - this.windows.push(window); - var destination = this.destination; - destination.next(window); - return window; + OnErrorResumeNextSubscriber.prototype._complete = function () { + this.subscribeToNextSource(); + this.unsubscribe(); }; - WindowTimeSubscriber.prototype.closeWindow = function (window) { - window.complete(); - var windows = this.windows; - windows.splice(windows.indexOf(window), 1); + 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.destination.complete(); + } }; - 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 + return OnErrorResumeNextSubscriber; +}(_innerSubscribe__WEBPACK_IMPORTED_MODULE_3__["SimpleOuterSubscriber"])); +//# sourceMappingURL=onErrorResumeNext.js.map /***/ }), -/* 488 */ +/* 454 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "windowToggle", function() { return windowToggle; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "pairwise", function() { return pairwise; }); /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); -/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(17); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subject,_Subscription,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ - - - +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -function windowToggle(openings, closingSelector) { - return function (source) { return source.lift(new WindowToggleOperator(openings, closingSelector)); }; +function pairwise() { + return function (source) { return source.lift(new PairwiseOperator()); }; } -var WindowToggleOperator = /*@__PURE__*/ (function () { - function WindowToggleOperator(openings, closingSelector) { - this.openings = openings; - this.closingSelector = closingSelector; +var PairwiseOperator = /*@__PURE__*/ (function () { + function PairwiseOperator() { } - WindowToggleOperator.prototype.call = function (subscriber, source) { - return source.subscribe(new WindowToggleSubscriber(subscriber, this.openings, this.closingSelector)); + PairwiseOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new PairwiseSubscriber(subscriber)); }; - return WindowToggleOperator; + return PairwiseOperator; }()); -var WindowToggleSubscriber = /*@__PURE__*/ (function (_super) { - tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](WindowToggleSubscriber, _super); - function WindowToggleSubscriber(destination, openings, closingSelector) { +var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { + tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](PairwiseSubscriber, _super); + function PairwiseSubscriber(destination) { 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)); + _this.hasPrev = false; 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); - } + 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); } }; - 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 + return PairwiseSubscriber; +}(_Subscriber__WEBPACK_IMPORTED_MODULE_1__["Subscriber"])); +//# sourceMappingURL=pairwise.js.map /***/ }), -/* 489 */ +/* 455 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); -/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult 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__(104); +/* harmony import */ var _filter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(105); +/** PURE_IMPORTS_START _util_not,_filter PURE_IMPORTS_END */ -function windowWhen(closingSelector) { - return function windowWhenOperatorFunction(source) { - return source.lift(new WindowOperator(closingSelector)); +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) + ]; }; } -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, innerSub) { - 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 +//# sourceMappingURL=partition.js.map /***/ }), -/* 490 */ +/* 456 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); -/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); -/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); -/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult 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__(66); +/** PURE_IMPORTS_START _map PURE_IMPORTS_END */ -function withLatestFrom() { - var args = []; +function pluck() { + var properties = []; 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 WithLatestFromOperator = /*@__PURE__*/ (function () { - function WithLatestFromOperator(observables, project) { - this.observables = observables; - this.project = project; + properties[_i] = arguments[_i]; } - 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, observable, i)); - } - return _this; + var length = properties.length; + if (length === 0) { + throw new Error('list of properties cannot be empty.'); } - WithLatestFromSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { - 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); + 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 { - this.destination.next(args); + return undefined; } } + return currentProp; }; - 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 + return mapper; +} +//# sourceMappingURL=pluck.js.map /***/ }), -/* 491 */ +/* 457 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(109); -/** PURE_IMPORTS_START _observable_zip 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__(27); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/** PURE_IMPORTS_START _Subject,_multicast PURE_IMPORTS_END */ -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))); - }; + +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=zip.js.map +//# sourceMappingURL=publish.js.map /***/ }), -/* 492 */ +/* 458 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(109); -/** PURE_IMPORTS_START _observable_zip 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__(32); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/** PURE_IMPORTS_START _BehaviorSubject,_multicast PURE_IMPORTS_END */ -function zipAll(project) { - return function (source) { return source.lift(new _observable_zip__WEBPACK_IMPORTED_MODULE_0__["ZipOperator"](project)); }; + +function publishBehavior(value) { + return function (source) { return Object(_multicast__WEBPACK_IMPORTED_MODULE_1__["multicast"])(new _BehaviorSubject__WEBPACK_IMPORTED_MODULE_0__["BehaviorSubject"](value))(source); }; } -//# sourceMappingURL=zipAll.js.map +//# sourceMappingURL=publishBehavior.js.map /***/ }), -/* 493 */ +/* 459 */ /***/ (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 _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(162); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(143); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(145); -/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(494); -/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(495); -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; } - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "publishLast", function() { return publishLast; }); +/* harmony import */ var _AsyncSubject__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(50); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/** 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 -async function runCommand(command, config) { - try { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(`Running [${command.name}] command from [${config.rootPath}]`); - const kbn = await _utils_kibana__WEBPACK_IMPORTED_MODULE_4__["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_1__["log"].error(`There are no projects found. Double check project name(s) in '-i/--include' and '-e/--exclude' filters.`); - return process.exit(1); - } +/***/ }), +/* 460 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_2__["buildProjectGraph"])(projects); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(`Found ${projects.size.toString()} projects`); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(Object(_utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__["renderProjectsTree"])(config.rootPath, projects)); - await command.run(projects, projectGraph, _objectSpread(_objectSpread({}, config), {}, { - kbn - })); - } catch (error) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(`[${command.name}] failed:`); +"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__(33); +/* harmony import */ var _multicast__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(452); +/** PURE_IMPORTS_START _ReplaySubject,_multicast PURE_IMPORTS_END */ - if (error instanceof _utils_errors__WEBPACK_IMPORTED_MODULE_0__["CliError"]) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(error.message); - const metaOutput = Object.entries(error.meta).map(([key, value]) => `${key}: ${value}`).join('\n'); - if (metaOutput) { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info('Additional debugging info:\n'); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].indent(2); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info(metaOutput); - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].indent(-2); - } - } else { - _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(error); +function publishReplay(bufferSize, windowTime, selectorOrScheduler, scheduler) { + if (selectorOrScheduler && typeof selectorOrScheduler !== 'function') { + scheduler = selectorOrScheduler; } - - process.exit(1); - } + 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 toArray(value) { - if (value == null) { - return []; - } - - return Array.isArray(value) ? value : [value]; -} /***/ }), -/* 494 */ +/* 461 */ /***/ (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 import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(112); -/* 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__); -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "race", function() { return race; }); +/* harmony import */ var _util_isArray__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(18); +/* harmony import */ var _observable_race__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(106); +/** PURE_IMPORTS_START _util_isArray,_observable_race 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 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))); + }; } +//# sourceMappingURL=race.js.map -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; -} +/***/ }), +/* 462 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -function createTreeStructure(tree) { - let name; - const children = []; +"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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _observable_empty__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(43); +/** PURE_IMPORTS_START tslib,_Subscriber,_observable_empty PURE_IMPORTS_END */ - 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; +function repeat(count) { + if (count === void 0) { + count = -1; } - - 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; - } - - children.push({ - children: subtree.children, - name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) - }); - } - - return { - name, - children - }; -} - -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}})}`; + 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)); + } + }; } - -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); +var RepeatOperator = /*@__PURE__*/ (function () { + function RepeatOperator(count, source) { + this.count = count; + this.source = source; } - } - - return tree; -} - -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()); + 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 - const subtree = tree.get(currentDir); - addProjectToTree(subtree, rest, project); - } -} /***/ }), -/* 495 */ +/* 463 */ /***/ (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 multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(496); -/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(361); -/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); -/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(274); -/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(145); -/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(271); -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; } +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "repeatWhen", function() { return repeatWhen; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ -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; } -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ +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 +/***/ }), +/* 464 */ +/***/ (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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ +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 -/** - * 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. - */ -class Kibana { - static async loadFrom(rootPath) { - return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_4__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"])({ - rootPath - }))); - } +/***/ }), +/* 465 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - constructor(allWorkspaceProjects) { - this.allWorkspaceProjects = allWorkspaceProjects; +"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__(12); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ - _defineProperty(this, "kibanaProject", void 0); - const kibanaProject = allWorkspaceProjects.get('kibana'); - if (!kibanaProject) { - throw new TypeError('Unable to create Kibana object without all projects, including the Kibana project.'); +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; } - - this.kibanaProject = kibanaProject; - } - /** make an absolute path by resolving subPath relative to the kibana repo */ + 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 - 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 */ +/***/ }), +/* 466 */ +/***/ (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__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ - 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 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 - getAllProjects() { - return new Map(this.allWorkspaceProjects); - } - /** determine if a project with the given name exists */ +/***/ }), +/* 467 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - hasProject(name) { - return this.allWorkspaceProjects.has(name); - } - /** get a specific project, throws if the name is not known (use hasProject() first) */ +"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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); +/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ - getProject(name) { - const project = this.allWorkspaceProjects.get(name); - if (!project) { - throw new Error(`No package with name "${name}" in the workspace`); +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; } + 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); + } + }; + 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 - return project; - } - /** get a project and all of the projects it depends on in a ProjectMap */ +/***/ }), +/* 468 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - getProjectAndDeps(name) { - const project = this.getProject(name); - return Object(_projects__WEBPACK_IMPORTED_MODULE_4__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); - } - /** filter the projects to just those matching certain paths/include/exclude tags */ +"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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - 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_5__["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_1___default()(pkgJsonPaths, filteredPkgJsonGlobs); +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; +}()); - 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); - - if (pathMatches && notExcluded && isIncluded) { - filteredProjects.set(project.name, project); - } +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"])); - return filteredProjects; - } - - isPartOfRepo(project) { - return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_2___default()(project.path, this.kibanaProject.path); - } - - isOutsideRepo(project) { - return !this.isPartOfRepo(project); - } - - resolveAllProductionDependencies(yarnLock, log) { - const kibanaDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ - project: this.kibanaProject, - yarnLock, - kbn: this, - includeDependentProject: true, - productionDepsOnly: true, - log - }); - const xpackDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ - project: this.getProject('x-pack'), - yarnLock, - kbn: this, - includeDependentProject: true, - productionDepsOnly: true, - log - }); - return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); - } +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 -} /***/ }), -/* 496 */ -/***/ (function(module, exports, __webpack_require__) { +/* 469 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(452); +/* harmony import */ var _refCount__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); +/** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ -const minimatch = __webpack_require__(149); -const arrayUnion = __webpack_require__(497); -const arrayDiffer = __webpack_require__(498); -const arrify = __webpack_require__(499); - -module.exports = (list, patterns, options = {}) => { - list = arrify(list); - patterns = arrify(patterns); - - if (list.length === 0 || patterns.length === 0) { - return []; - } - - return patterns.reduce((result, pattern) => { - let process = arrayUnion; - if (pattern[0] === '!') { - pattern = pattern.slice(1); - process = arrayDiffer; - } - return process(result, minimatch.match(list, pattern, options)); - }, []); -}; +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 /***/ }), -/* 497 */ -/***/ (function(module, exports, __webpack_require__) { +/* 470 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(33); +/** PURE_IMPORTS_START _ReplaySubject PURE_IMPORTS_END */ - -module.exports = (...arguments_) => { - return [...new Set([].concat(...arguments_))]; -}; +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 /***/ }), -/* 498 */ -/***/ (function(module, exports, __webpack_require__) { +/* 471 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _util_EmptyError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(63); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_EmptyError PURE_IMPORTS_END */ -const arrayDiffer = (array, ...values) => { - const rest = new Set([].concat(...values)); - return array.filter(element => !rest.has(element)); -}; -module.exports = arrayDiffer; +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 /***/ }), -/* 499 */ -/***/ (function(module, exports, __webpack_require__) { +/* 472 */ +/***/ (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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ -const arrify = value => { - if (value === null || value === undefined) { - return []; - } +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 - if (Array.isArray(value)) { - return value; - } - if (typeof value === 'string') { - return [value]; - } +/***/ }), +/* 473 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - if (typeof value[Symbol.iterator] === 'function') { - return [...value]; - } +"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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _util_ArgumentOutOfRangeError__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(62); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError PURE_IMPORTS_END */ - return [value]; -}; -module.exports = arrify; + +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 /***/ }), -/* 500 */ +/* 474 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(501); -/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipUntil", function() { return skipUntil; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ + +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 /***/ }), -/* 501 */ +/* 475 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); -/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); -/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(502); -/* 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__(280); -/* 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__(271); -/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(130); -/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(143); -/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(164); -/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(145); -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - - +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "skipWhile", function() { return skipWhile; }); +/* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - - - -async function buildProductionProjects({ - kibanaRoot, - buildRoot, - onlyOSS -}) { - const projects = await getProductionProjects(kibanaRoot, onlyOSS); - const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["buildProjectGraph"])(projects); - const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["topologicallyBatchProjects"])(projects, projectGraph); - const projectNames = [...projects.values()].map(project => project.name); - _utils_log__WEBPACK_IMPORTED_MODULE_5__["log"].info(`Preparing production build for [${projectNames.join(', ')}]`); - - for (const batch of batchedProjects) { - for (const project of batch) { - await deleteTarget(project); - await buildProject(project); - await copyToBuild(project, kibanaRoot, buildRoot); - } - } +function skipWhile(predicate) { + return function (source) { return source.lift(new SkipWhileOperator(predicate)); }; } -/** - * Returns the subset of projects that should be built into the production - * bundle. As we copy these into Kibana's `node_modules` during the build step, - * and let Kibana's build process be responsible for installing dependencies, - * we only include Kibana's transitive _production_ dependencies. If onlyOSS - * is supplied, we omit projects with build.oss in their package.json set to false. - */ - -async function getProductionProjects(rootPath, onlyOSS) { - const projectPaths = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])({ - rootPath - }); - const projects = await Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["getProjects"])(rootPath, projectPaths); - const projectsSubset = [projects.get('kibana')]; - - if (projects.has('x-pack')) { - projectsSubset.push(projects.get('x-pack')); - } - - const productionProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["includeTransitiveProjects"])(projectsSubset, projects, { - onlyProductionDependencies: true - }); // We remove Kibana, as we're already building Kibana - - productionProjects.delete('kibana'); +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 (onlyOSS) { - productionProjects.forEach(project => { - if (project.getBuildConfig().oss === false) { - productionProjects.delete(project.json.name); - } - }); - } - return productionProjects; -} +/***/ }), +/* 476 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -async function deleteTarget(project) { - const targetDir = project.targetLocation; +"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__(79); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(45); +/** PURE_IMPORTS_START _observable_concat,_util_isScheduler PURE_IMPORTS_END */ - if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_4__["isDirectory"])(targetDir)) { - await del__WEBPACK_IMPORTED_MODULE_1___default()(targetDir, { - force: true - }); - } -} -async function buildProject(project) { - if (project.hasScript('build')) { - await project.runScript('build'); - } +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); }; + } } -/** - * Copy all the project's files from its "intermediate build directory" and - * into the build. The intermediate directory can either be the root of the - * project or some other location defined in the project's `package.json`. - * - * 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. - */ - - -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()(['**/*', '!node_modules/**'], buildProjectPath, { - cwd: project.getIntermediateBuildDirectory(), - dot: true, - nodir: 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`. +//# sourceMappingURL=startWith.js.map - const packageJson = (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_4__["isFile"])(Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(buildProjectPath, 'package.json'))) ? await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_6__["readPackageJson"])(buildProjectPath) : project.json; - await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_6__["writePackageJson"])(buildProjectPath, packageJson); -} /***/ }), -/* 502 */ -/***/ (function(module, exports, __webpack_require__) { +/* 477 */ +/***/ (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__(478); +/** PURE_IMPORTS_START _observable_SubscribeOnObservable PURE_IMPORTS_END */ -const EventEmitter = __webpack_require__(155); -const path = __webpack_require__(4); -const os = __webpack_require__(120); -const pAll = __webpack_require__(503); -const arrify = __webpack_require__(505); -const globby = __webpack_require__(506); -const isGlob = __webpack_require__(704); -const cpFile = __webpack_require__(705); -const junk = __webpack_require__(717); -const CpyError = __webpack_require__(718); - -const defaultOptions = { - ignoreJunk: true -}; - -const preprocessSourcePath = (source, options) => options.cwd ? path.resolve(options.cwd, source) : source; - -const preprocessDestinationPath = (source, destination, options) => { - let basename = path.basename(source); - const dirname = path.dirname(source); - - if (typeof options.rename === 'string') { - basename = options.rename; - } else if (typeof options.rename === 'function') { - basename = options.rename(basename); - } - - if (options.cwd) { - destination = path.resolve(options.cwd, destination); - } - - if (options.parents) { - return path.join(destination, dirname, basename); - } +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 - return path.join(destination, basename); -}; -module.exports = (source, destination, { - concurrency = (os.cpus().length || 1) * 2, - ...options -} = {}) => { - const progressEmitter = new EventEmitter(); +/***/ }), +/* 478 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - options = { - ...defaultOptions, - ...options - }; +"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__(12); +/* harmony import */ var _Observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); +/* harmony import */ var _scheduler_asap__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(51); +/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(98); +/** PURE_IMPORTS_START tslib,_Observable,_scheduler_asap,_util_isNumeric PURE_IMPORTS_END */ - const promise = (async () => { - source = arrify(source); - if (source.length === 0 || !destination) { - throw new CpyError('`source` and `destination` required'); - } - const copyStatus = new Map(); - let completedFiles = 0; - let completedSize = 0; - let files; - try { - files = await globby(source, options); +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"])); - if (options.ignoreJunk) { - files = files.filter(file => junk.not(path.basename(file))); - } - } catch (error) { - throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error); - } +//# sourceMappingURL=SubscribeOnObservable.js.map - const sourcePaths = source.filter(value => !isGlob(value)); - if (files.length === 0 || (sourcePaths.length > 0 && !sourcePaths.every(value => files.includes(value)))) { - throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`); - } +/***/ }), +/* 479 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - const fileProgressHandler = event => { - const fileStatus = copyStatus.get(event.src) || {written: 0, percent: 0}; +"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__(480); +/* harmony import */ var _util_identity__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(25); +/** PURE_IMPORTS_START _switchMap,_util_identity PURE_IMPORTS_END */ - if (fileStatus.written !== event.written || fileStatus.percent !== event.percent) { - completedSize -= fileStatus.written; - completedSize += event.written; - if (event.percent === 1 && fileStatus.percent !== 1) { - completedFiles++; - } +function switchAll() { + return Object(_switchMap__WEBPACK_IMPORTED_MODULE_0__["switchMap"])(_util_identity__WEBPACK_IMPORTED_MODULE_1__["identity"]); +} +//# sourceMappingURL=switchAll.js.map - copyStatus.set(event.src, { - written: event.written, - percent: event.percent - }); - progressEmitter.emit('progress', { - totalFiles: files.length, - percent: completedFiles / files.length, - completedFiles, - completedSize - }); - } - }; +/***/ }), +/* 480 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - return pAll(files.map(sourcePath => { - return async () => { - const from = preprocessSourcePath(sourcePath, options); - const to = preprocessDestinationPath(sourcePath, destination, options); +"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__(12); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(66); +/* harmony import */ var _observable_from__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(83); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ - try { - await cpFile(from, to, options).on('progress', fileProgressHandler); - } catch (error) { - throw new CpyError(`Cannot copy from \`${from}\` to \`${to}\`: ${error.message}`, error); - } - return to; - }; - }), {concurrency}); - })(); - promise.on = (...arguments_) => { - progressEmitter.on(...arguments_); - return promise; - }; - return promise; -}; +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 /***/ }), -/* 503 */ -/***/ (function(module, exports, __webpack_require__) { +/* 481 */ +/***/ (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__(480); +/** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ -const pMap = __webpack_require__(504); - -module.exports = (iterable, options) => pMap(iterable, element => element(), options); -// TODO: Remove this for the next major release -module.exports.default = module.exports; +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 /***/ }), -/* 504 */ -/***/ (function(module, exports, __webpack_require__) { +/* 482 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -const pMap = (iterable, mapper, options) => new Promise((resolve, reject) => { - options = Object.assign({ - concurrency: Infinity - }, options); - - if (typeof mapper !== 'function') { - throw new TypeError('Mapper function is required'); - } - - const {concurrency} = options; - - if (!(typeof concurrency === 'number' && concurrency >= 1)) { - throw new TypeError(`Expected \`concurrency\` to be a number from 1 and up, got \`${concurrency}\` (${typeof concurrency})`); - } +function takeUntil(notifier) { + return function (source) { return source.lift(new TakeUntilOperator(notifier)); }; +} +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 - const ret = []; - const iterator = iterable[Symbol.iterator](); - let isRejected = false; - let isIterableDone = false; - let resolvingCount = 0; - let currentIndex = 0; - const next = () => { - if (isRejected) { - return; - } +/***/ }), +/* 483 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - const nextItem = iterator.next(); - const i = currentIndex; - currentIndex++; +"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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ - if (nextItem.done) { - isIterableDone = true; - if (resolvingCount === 0) { - resolve(ret); - } +function takeWhile(predicate, inclusive) { + if (inclusive === void 0) { + inclusive = false; + } + return function (source) { + return source.lift(new TakeWhileOperator(predicate, inclusive)); + }; +} +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 - return; - } - resolvingCount++; +/***/ }), +/* 484 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - Promise.resolve(nextItem.value) - .then(element => mapper(element, i)) - .then( - value => { - ret[i] = value; - resolvingCount--; - next(); - }, - error => { - isRejected = true; - reject(error); - } - ); - }; +"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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _util_noop__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(60); +/* harmony import */ var _util_isFunction__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13); +/** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ - for (let i = 0; i < concurrency; i++) { - next(); - if (isIterableDone) { - break; - } - } -}); -module.exports = pMap; -// TODO: Remove this for the next major release -module.exports.default = pMap; + +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 /***/ }), -/* 505 */ -/***/ (function(module, exports, __webpack_require__) { +/* 485 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ -const arrify = value => { - if (value === null || value === undefined) { - return []; - } - - if (Array.isArray(value)) { - return value; - } - - if (typeof value === 'string') { - return [value]; - } - - if (typeof value[Symbol.iterator] === 'function') { - return [...value]; - } - - return [value]; +var defaultThrottleConfig = { + leading: true, + trailing: false }; - -module.exports = arrify; +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 /***/ }), -/* 506 */ -/***/ (function(module, exports, __webpack_require__) { +/* 486 */ +/***/ (function(module, __webpack_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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); +/* harmony import */ var _throttle__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(485); +/** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ -const fs = __webpack_require__(133); -const arrayUnion = __webpack_require__(507); -const glob = __webpack_require__(146); -const fastGlob = __webpack_require__(509); -const dirGlob = __webpack_require__(697); -const gitignore = __webpack_require__(700); -const DEFAULT_FILTER = () => false; -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'); - } -}; +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 -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); +/***/ }), +/* 487 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - const globTasks = []; +"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__(55); +/* harmony import */ var _scan__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(447); +/* harmony import */ var _observable_defer__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(91); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(66); +/** PURE_IMPORTS_START _scheduler_async,_scan,_observable_defer,_map PURE_IMPORTS_END */ - taskOptions = Object.assign({ - ignore: [], - expandDirectories: true - }, taskOptions); - patterns.forEach((pattern, i) => { - if (isNegative(pattern)) { - return; - } - const ignore = patterns - .slice(i) - .filter(isNegative) - .map(pattern => pattern.slice(1)); - const options = Object.assign({}, taskOptions, { - ignore: taskOptions.ignore.concat(ignore) - }); +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); + })); + }); + }; +} +var TimeInterval = /*@__PURE__*/ (function () { + function TimeInterval(value, interval) { + this.value = value; + this.interval = interval; + } + return TimeInterval; +}()); - globTasks.push({pattern, options}); - }); +//# sourceMappingURL=timeInterval.js.map - return globTasks; -}; -const globDirs = (task, fn) => { - let options = {}; - if (task.options.cwd) { - options.cwd = task.options.cwd; - } +/***/ }), +/* 488 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - 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); - } +"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__(55); +/* harmony import */ var _util_TimeoutError__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(64); +/* harmony import */ var _timeoutWith__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(489); +/* harmony import */ var _observable_throwError__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(49); +/** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ - return fn(task.pattern, options); -}; -const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; -const globToTask = task => glob => { - const {options} = task; - if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { - options.ignore = dirGlob.sync(options.ignore); - } - return { - pattern: glob, - options - }; -}; +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=timeout.js.map -const globby = (patterns, options) => { - let globTasks; - try { - globTasks = generateGlobTasks(patterns, options); - } catch (error) { - return Promise.reject(error); - } +/***/ }), +/* 489 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - const getTasks = Promise.all(globTasks.map(task => Promise.resolve(getPattern(task, dirGlob)) - .then(globs => Promise.all(globs.map(globToTask(task)))) - )) - .then(tasks => arrayUnion(...tasks)); +"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__(12); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(55); +/* harmony import */ var _util_isDate__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(421); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ - 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); +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 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 - 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); - }, []); +/***/ }), +/* 490 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - const filter = getFilter(); - return tasks.reduce( - (matches, task) => arrayUnion(matches, fastGlob.sync(task.pattern, task.options)), - [] - ).filter(p => !filter(p)); -}; +"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__(55); +/* harmony import */ var _map__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(66); +/** PURE_IMPORTS_START _scheduler_async,_map PURE_IMPORTS_END */ -module.exports.generateGlobTasks = generateGlobTasks; -module.exports.hasMagic = (patterns, options) => [] - .concat(patterns) - .some(pattern => glob.hasMagic(pattern, options)); +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; +}()); -module.exports.gitignore = gitignore; +//# sourceMappingURL=timestamp.js.map /***/ }), -/* 507 */ -/***/ (function(module, exports, __webpack_require__) { +/* 491 */ +/***/ (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__(446); +/** PURE_IMPORTS_START _reduce PURE_IMPORTS_END */ -var arrayUniq = __webpack_require__(508); - -module.exports = function () { - return arrayUniq([].concat.apply([], arguments)); -}; +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 /***/ }), -/* 508 */ -/***/ (function(module, exports, __webpack_require__) { +/* 492 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "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__(12); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); +/* harmony import */ var _innerSubscribe__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(90); +/** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ -// there's 3 implementations written in increasing order of efficiency -// 1 - no Set type is defined -function uniqNoSet(arr) { - var ret = []; +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 - for (var i = 0; i < arr.length; i++) { - if (ret.indexOf(arr[i]) === -1) { - ret.push(arr[i]); - } - } - return ret; -} +/***/ }), +/* 493 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -// 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; - } +"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__(12); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(27); +/** PURE_IMPORTS_START tslib,_Subscriber,_Subject PURE_IMPORTS_END */ - return false; - }); + + +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 -// 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); - }); +/***/ }), +/* 494 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - return ret; -} +"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__(12); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); +/* harmony import */ var _scheduler_async__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(55); +/* harmony import */ var _Subscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(11); +/* harmony import */ var _util_isNumeric__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(98); +/* harmony import */ var _util_isScheduler__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(45); +/** PURE_IMPORTS_START tslib,_Subject,_scheduler_async,_Subscriber,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ -// V8 currently has a broken implementation -// https://github.com/joyent/node/issues/8449 -function doesForEachActuallyWork() { - var ret = false; - (new Set([true])).forEach(function (el) { - ret = el; - }); - return ret === true; -} -if ('Set' in global) { - if (typeof Set.prototype.forEach === 'function' && doesForEachActuallyWork()) { - module.exports = uniqSetWithForEach; - } else { - module.exports = uniqSet; - } -} else { - module.exports = uniqNoSet; + + +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 /***/ }), -/* 509 */ -/***/ (function(module, exports, __webpack_require__) { - -const pkg = __webpack_require__(510); +/* 495 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { -module.exports = pkg.async; -module.exports.default = pkg.async; +"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__(12); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); +/* harmony import */ var _Subscription__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(17); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(69); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(70); +/** PURE_IMPORTS_START tslib,_Subject,_Subscription,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ -module.exports.async = pkg.async; -module.exports.sync = pkg.sync; -module.exports.stream = pkg.stream; -module.exports.generateTasks = pkg.generateTasks; -/***/ }), -/* 510 */ -/***/ (function(module, exports, __webpack_require__) { -"use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var optionsManager = __webpack_require__(511); -var taskManager = __webpack_require__(512); -var reader_async_1 = __webpack_require__(668); -var reader_stream_1 = __webpack_require__(692); -var reader_sync_1 = __webpack_require__(693); -var arrayUtils = __webpack_require__(695); -var streamUtils = __webpack_require__(696); -/** - * 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'; -} +function windowToggle(openings, closingSelector) { + return function (source) { return source.lift(new WindowToggleOperator(openings, closingSelector)); }; +} +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 /***/ }), -/* 511 */ -/***/ (function(module, exports, __webpack_require__) { +/* 496 */ +/***/ (function(module, __webpack_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; +__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__(12); +/* harmony import */ var _Subject__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(69); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(70); +/** PURE_IMPORTS_START tslib,_Subject,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + + + + +function windowWhen(closingSelector) { + return function windowWhenOperatorFunction(source) { + return source.lift(new WindowOperator(closingSelector)); + }; +} +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 /***/ }), -/* 512 */ -/***/ (function(module, exports, __webpack_require__) { +/* 497 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var patternUtils = __webpack_require__(513); -/** - * 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; +__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__(12); +/* harmony import */ var _OuterSubscriber__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(69); +/* harmony import */ var _util_subscribeToResult__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(70); +/** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + + + +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 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 /***/ }), -/* 513 */ -/***/ (function(module, exports, __webpack_require__) { +/* 498 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; - -Object.defineProperty(exports, "__esModule", { value: true }); -var path = __webpack_require__(4); -var globParent = __webpack_require__(514); -var isGlob = __webpack_require__(517); -var micromatch = __webpack_require__(518); -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. +__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__(110); +/** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ + +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=zip.js.map + + +/***/ }), +/* 499 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"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__(110); +/** PURE_IMPORTS_START _observable_zip PURE_IMPORTS_END */ + +function zipAll(project) { + return function (source) { return source.lift(new _observable_zip__WEBPACK_IMPORTED_MODULE_0__["ZipOperator"](project)); }; +} +//# sourceMappingURL=zipAll.js.map + + +/***/ }), +/* 500 */ +/***/ (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 _utils_errors__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(163); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(144); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(146); +/* harmony import */ var _utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(501); +/* harmony import */ var _utils_kibana__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(502); +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; } + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + + + +async function runCommand(command, config) { + try { + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(`Running [${command.name}] command from [${config.rootPath}]`); + const kbn = await _utils_kibana__WEBPACK_IMPORTED_MODULE_4__["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_1__["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_2__["buildProjectGraph"])(projects); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(`Found ${projects.size.toString()} projects`); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].debug(Object(_utils_projects_tree__WEBPACK_IMPORTED_MODULE_3__["renderProjectsTree"])(config.rootPath, projects)); + await command.run(projects, projectGraph, _objectSpread(_objectSpread({}, config), {}, { + kbn + })); + } catch (error) { + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(`[${command.name}] failed:`); + + if (error instanceof _utils_errors__WEBPACK_IMPORTED_MODULE_0__["CliError"]) { + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(error.message); + const metaOutput = Object.entries(error.meta).map(([key, value]) => `${key}: ${value}`).join('\n'); + + if (metaOutput) { + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info('Additional debugging info:\n'); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].indent(2); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].info(metaOutput); + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].indent(-2); + } + } else { + _utils_log__WEBPACK_IMPORTED_MODULE_1__["log"].error(error); + } + + process.exit(1); + } +} + +function toArray(value) { + if (value == null) { + return []; + } + + return Array.isArray(value) ? value : [value]; +} + +/***/ }), +/* 501 */ +/***/ (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 import */ var chalk__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(113); +/* 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__); +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +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 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; + } + + 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; + } + + children.push({ + children: subtree.children, + name: chalk__WEBPACK_IMPORTED_MODULE_0___default.a.dim(dir.toString()) + }); + } + + return { + name, + children + }; +} + +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}})}`; +} + +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 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()); + } + + const subtree = tree.get(currentDir); + addProjectToTree(subtree, rest, project); + } +} + +/***/ }), +/* 502 */ +/***/ (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 multimatch__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(503); +/* harmony import */ var multimatch__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(multimatch__WEBPACK_IMPORTED_MODULE_1__); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(361); +/* harmony import */ var is_path_inside__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(is_path_inside__WEBPACK_IMPORTED_MODULE_2__); +/* harmony import */ var _yarn_lock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(275); +/* harmony import */ var _projects__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(146); +/* harmony import */ var _config__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(272); +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; } + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + + + + +/** + * 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. + */ + +class Kibana { + static async loadFrom(rootPath) { + return new Kibana(await Object(_projects__WEBPACK_IMPORTED_MODULE_4__["getProjects"])(rootPath, Object(_config__WEBPACK_IMPORTED_MODULE_5__["getProjectPaths"])({ + rootPath + }))); + } + + constructor(allWorkspaceProjects) { + this.allWorkspaceProjects = allWorkspaceProjects; + + _defineProperty(this, "kibanaProject", void 0); + + const kibanaProject = allWorkspaceProjects.get('kibana'); + + if (!kibanaProject) { + throw new TypeError('Unable to create Kibana object without all projects, including the Kibana project.'); + } + + this.kibanaProject = kibanaProject; + } + /** make an absolute path by resolving subPath relative to the kibana repo */ + + + 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 */ + + + 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 */ + + + getAllProjects() { + return new Map(this.allWorkspaceProjects); + } + /** determine if a project with the given name exists */ + + + hasProject(name) { + return this.allWorkspaceProjects.has(name); + } + /** get a specific project, throws if the name is not known (use hasProject() first) */ + + + getProject(name) { + const project = this.allWorkspaceProjects.get(name); + + if (!project) { + throw new Error(`No package with name "${name}" in the workspace`); + } + + return project; + } + /** get a project and all of the projects it depends on in a ProjectMap */ + + + getProjectAndDeps(name) { + const project = this.getProject(name); + return Object(_projects__WEBPACK_IMPORTED_MODULE_4__["includeTransitiveProjects"])([project], this.allWorkspaceProjects); + } + /** filter the projects to just those matching certain paths/include/exclude tags */ + + + 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_5__["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_1___default()(pkgJsonPaths, filteredPkgJsonGlobs); + + 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); + + if (pathMatches && notExcluded && isIncluded) { + filteredProjects.set(project.name, project); + } + } + + return filteredProjects; + } + + isPartOfRepo(project) { + return project.path === this.kibanaProject.path || is_path_inside__WEBPACK_IMPORTED_MODULE_2___default()(project.path, this.kibanaProject.path); + } + + isOutsideRepo(project) { + return !this.isPartOfRepo(project); + } + + resolveAllProductionDependencies(yarnLock, log) { + const kibanaDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ + project: this.kibanaProject, + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + const xpackDeps = Object(_yarn_lock__WEBPACK_IMPORTED_MODULE_3__["resolveDepsForProject"])({ + project: this.getProject('x-pack'), + yarnLock, + kbn: this, + includeDependentProject: true, + productionDepsOnly: true, + log + }); + return new Map([...kibanaDeps.entries(), ...xpackDeps.entries()]); + } + +} + +/***/ }), +/* 503 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const minimatch = __webpack_require__(150); +const arrayUnion = __webpack_require__(504); +const arrayDiffer = __webpack_require__(505); +const arrify = __webpack_require__(506); + +module.exports = (list, patterns, options = {}) => { + list = arrify(list); + patterns = arrify(patterns); + + if (list.length === 0 || patterns.length === 0) { + return []; + } + + return patterns.reduce((result, pattern) => { + let process = arrayUnion; + + if (pattern[0] === '!') { + pattern = pattern.slice(1); + process = arrayDiffer; + } + + return process(result, minimatch.match(list, pattern, options)); + }, []); +}; + + +/***/ }), +/* 504 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +module.exports = (...arguments_) => { + return [...new Set([].concat(...arguments_))]; +}; + + +/***/ }), +/* 505 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const arrayDiffer = (array, ...values) => { + const rest = new Set([].concat(...values)); + return array.filter(element => !rest.has(element)); +}; + +module.exports = arrayDiffer; + + +/***/ }), +/* 506 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const arrify = value => { + if (value === null || value === undefined) { + return []; + } + + if (Array.isArray(value)) { + return value; + } + + if (typeof value === 'string') { + return [value]; + } + + if (typeof value[Symbol.iterator] === 'function') { + return [...value]; + } + + return [value]; +}; + +module.exports = arrify; + + +/***/ }), +/* 507 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony import */ var _build_production_projects__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(508); +/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return _build_production_projects__WEBPACK_IMPORTED_MODULE_0__["buildProductionProjects"]; }); + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +/***/ }), +/* 508 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "buildProductionProjects", function() { return buildProductionProjects; }); +/* harmony import */ var cpy__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(509); +/* 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__(281); +/* 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__(272); +/* harmony import */ var _utils_fs__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(131); +/* harmony import */ var _utils_log__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(144); +/* harmony import */ var _utils_package_json__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(165); +/* harmony import */ var _utils_projects__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(146); +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + + + + + + + +async function buildProductionProjects({ + kibanaRoot, + buildRoot, + onlyOSS +}) { + const projects = await getProductionProjects(kibanaRoot, onlyOSS); + const projectGraph = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["buildProjectGraph"])(projects); + const batchedProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["topologicallyBatchProjects"])(projects, projectGraph); + const projectNames = [...projects.values()].map(project => project.name); + _utils_log__WEBPACK_IMPORTED_MODULE_5__["log"].info(`Preparing production build for [${projectNames.join(', ')}]`); + + for (const batch of batchedProjects) { + for (const project of batch) { + await deleteTarget(project); + await buildProject(project); + await copyToBuild(project, kibanaRoot, buildRoot); + } + } +} +/** + * Returns the subset of projects that should be built into the production + * bundle. As we copy these into Kibana's `node_modules` during the build step, + * and let Kibana's build process be responsible for installing dependencies, + * we only include Kibana's transitive _production_ dependencies. If onlyOSS + * is supplied, we omit projects with build.oss in their package.json set to false. + */ + +async function getProductionProjects(rootPath, onlyOSS) { + const projectPaths = Object(_config__WEBPACK_IMPORTED_MODULE_3__["getProjectPaths"])({ + rootPath + }); + const projects = await Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["getProjects"])(rootPath, projectPaths); + const projectsSubset = [projects.get('kibana')]; + + if (projects.has('x-pack')) { + projectsSubset.push(projects.get('x-pack')); + } + + const productionProjects = Object(_utils_projects__WEBPACK_IMPORTED_MODULE_7__["includeTransitiveProjects"])(projectsSubset, projects, { + onlyProductionDependencies: true + }); // We remove Kibana, as we're already building Kibana + + productionProjects.delete('kibana'); + + if (onlyOSS) { + productionProjects.forEach(project => { + if (project.getBuildConfig().oss === false) { + productionProjects.delete(project.json.name); + } + }); + } + + return productionProjects; +} + +async function deleteTarget(project) { + const targetDir = project.targetLocation; + + if (await Object(_utils_fs__WEBPACK_IMPORTED_MODULE_4__["isDirectory"])(targetDir)) { + await del__WEBPACK_IMPORTED_MODULE_1___default()(targetDir, { + force: true + }); + } +} + +async function buildProject(project) { + if (project.hasScript('build')) { + await project.runScript('build'); + } +} +/** + * Copy all the project's files from its "intermediate build directory" and + * into the build. The intermediate directory can either be the root of the + * project or some other location defined in the project's `package.json`. + * + * 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. + */ + + +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()(['**/*', '!node_modules/**'], buildProjectPath, { + cwd: project.getIntermediateBuildDirectory(), + dot: true, + nodir: 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_4__["isFile"])(Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(buildProjectPath, 'package.json'))) ? await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_6__["readPackageJson"])(buildProjectPath) : project.json; + await Object(_utils_package_json__WEBPACK_IMPORTED_MODULE_6__["writePackageJson"])(buildProjectPath, packageJson); +} + +/***/ }), +/* 509 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const EventEmitter = __webpack_require__(156); +const path = __webpack_require__(4); +const os = __webpack_require__(121); +const pAll = __webpack_require__(510); +const arrify = __webpack_require__(512); +const globby = __webpack_require__(513); +const isGlob = __webpack_require__(294); +const cpFile = __webpack_require__(713); +const junk = __webpack_require__(723); +const CpyError = __webpack_require__(724); + +const defaultOptions = { + ignoreJunk: true +}; + +const preprocessSourcePath = (source, options) => options.cwd ? path.resolve(options.cwd, source) : source; + +const preprocessDestinationPath = (source, destination, options) => { + let basename = path.basename(source); + const dirname = path.dirname(source); + + if (typeof options.rename === 'string') { + basename = options.rename; + } else if (typeof options.rename === 'function') { + basename = options.rename(basename); + } + + if (options.cwd) { + destination = path.resolve(options.cwd, destination); + } + + if (options.parents) { + return path.join(destination, dirname, basename); + } + + return path.join(destination, basename); +}; + +module.exports = (source, destination, { + concurrency = (os.cpus().length || 1) * 2, + ...options +} = {}) => { + const progressEmitter = new EventEmitter(); + + options = { + ...defaultOptions, + ...options + }; + + const promise = (async () => { + source = arrify(source); + + if (source.length === 0 || !destination) { + throw new CpyError('`source` and `destination` required'); + } + + const copyStatus = new Map(); + let completedFiles = 0; + let completedSize = 0; + + let files; + try { + files = await globby(source, options); + + if (options.ignoreJunk) { + files = files.filter(file => junk.not(path.basename(file))); + } + } catch (error) { + throw new CpyError(`Cannot glob \`${source}\`: ${error.message}`, error); + } + + const sourcePaths = source.filter(value => !isGlob(value)); + + if (files.length === 0 || (sourcePaths.length > 0 && !sourcePaths.every(value => files.includes(value)))) { + throw new CpyError(`Cannot copy \`${source}\`: the file doesn't exist`); + } + + 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; + + 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 + }); + } + }; + + return pAll(files.map(sourcePath => { + return async () => { + const from = preprocessSourcePath(sourcePath, options); + const to = preprocessDestinationPath(sourcePath, destination, options); + + try { + await cpFile(from, to, options).on('progress', fileProgressHandler); + } catch (error) { + throw new CpyError(`Cannot copy from \`${from}\` to \`${to}\`: ${error.message}`, error); + } + + return to; + }; + }), {concurrency}); + })(); + + promise.on = (...arguments_) => { + progressEmitter.on(...arguments_); + return promise; + }; + + return promise; +}; + + +/***/ }), +/* 510 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const pMap = __webpack_require__(511); + +module.exports = (iterable, options) => pMap(iterable, element => element(), options); +// TODO: Remove this for the next major release +module.exports.default = module.exports; + + +/***/ }), +/* 511 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const pMap = (iterable, mapper, options) => new Promise((resolve, reject) => { + options = Object.assign({ + concurrency: Infinity + }, options); + + if (typeof mapper !== 'function') { + throw new TypeError('Mapper function is required'); + } + + const {concurrency} = options; + + 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; + + const next = () => { + if (isRejected) { + return; + } + + const nextItem = iterator.next(); + const i = currentIndex; + currentIndex++; + + if (nextItem.done) { + isIterableDone = true; + + if (resolvingCount === 0) { + resolve(ret); + } + + return; + } + + resolvingCount++; + + Promise.resolve(nextItem.value) + .then(element => mapper(element, i)) + .then( + value => { + ret[i] = value; + resolvingCount--; + next(); + }, + error => { + isRejected = true; + reject(error); + } + ); + }; + + for (let i = 0; i < concurrency; i++) { + next(); + + if (isIterableDone) { + break; + } + } +}); + +module.exports = pMap; +// TODO: Remove this for the next major release +module.exports.default = pMap; + + +/***/ }), +/* 512 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +const arrify = value => { + if (value === null || value === undefined) { + return []; + } + + if (Array.isArray(value)) { + return value; + } + + if (typeof value === 'string') { + return [value]; + } + + if (typeof value[Symbol.iterator] === 'function') { + return [...value]; + } + + return [value]; +}; + +module.exports = arrify; + + +/***/ }), +/* 513 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +const fs = __webpack_require__(134); +const arrayUnion = __webpack_require__(514); +const glob = __webpack_require__(147); +const fastGlob = __webpack_require__(516); +const dirGlob = __webpack_require__(706); +const gitignore = __webpack_require__(709); + +const DEFAULT_FILTER = () => false; + +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'); + } +}; + +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); + + const globTasks = []; + + taskOptions = Object.assign({ + ignore: [], + expandDirectories: true + }, taskOptions); + + patterns.forEach((pattern, i) => { + if (isNegative(pattern)) { + return; + } + + const ignore = patterns + .slice(i) + .filter(isNegative) + .map(pattern => pattern.slice(1)); + + const options = Object.assign({}, taskOptions, { + ignore: taskOptions.ignore.concat(ignore) + }); + + globTasks.push({pattern, options}); + }); + + return globTasks; +}; + +const globDirs = (task, fn) => { + let options = {}; + if (task.options.cwd) { + options.cwd = task.options.cwd; + } + + 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); + } + + return fn(task.pattern, options); +}; + +const getPattern = (task, fn) => task.options.expandDirectories ? globDirs(task, fn) : [task.pattern]; + +const globToTask = task => glob => { + const {options} = task; + if (options.ignore && Array.isArray(options.ignore) && options.expandDirectories) { + options.ignore = dirGlob.sync(options.ignore); + } + + return { + pattern: glob, + options + }; +}; + +const globby = (patterns, options) => { + let globTasks; + + try { + globTasks = generateGlobTasks(patterns, options); + } catch (error) { + return Promise.reject(error); + } + + 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 + ); + }; + + 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; + + +/***/ }), +/* 514 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +var arrayUniq = __webpack_require__(515); + +module.exports = function () { + return arrayUniq([].concat.apply([], arguments)); +}; + + +/***/ }), +/* 515 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +// there's 3 implementations written in increasing order of efficiency + +// 1 - no Set type is defined +function uniqNoSet(arr) { + var ret = []; + + for (var i = 0; i < arr.length; i++) { + if (ret.indexOf(arr[i]) === -1) { + ret.push(arr[i]); + } + } + + return ret; +} + +// 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; + }); +} + +// 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); + }); + + return ret; +} + +// V8 currently has a broken implementation +// https://github.com/joyent/node/issues/8449 +function doesForEachActuallyWork() { + var ret = false; + + (new Set([true])).forEach(function (el) { + ret = el; + }); + + return ret === true; +} + +if ('Set' in global) { + if (typeof Set.prototype.forEach === 'function' && doesForEachActuallyWork()) { + module.exports = uniqSetWithForEach; + } else { + module.exports = uniqSet; + } +} else { + module.exports = uniqNoSet; +} + + +/***/ }), +/* 516 */ +/***/ (function(module, exports, __webpack_require__) { + +const pkg = __webpack_require__(517); + +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; + + +/***/ }), +/* 517 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var optionsManager = __webpack_require__(518); +var taskManager = __webpack_require__(519); +var reader_async_1 = __webpack_require__(677); +var reader_stream_1 = __webpack_require__(701); +var reader_sync_1 = __webpack_require__(702); +var arrayUtils = __webpack_require__(704); +var streamUtils = __webpack_require__(705); +/** + * 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'; +} + + +/***/ }), +/* 518 */ +/***/ (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; + + +/***/ }), +/* 519 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var patternUtils = __webpack_require__(520); +/** + * 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; + + +/***/ }), +/* 520 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + +Object.defineProperty(exports, "__esModule", { value: true }); +var path = __webpack_require__(4); +var globParent = __webpack_require__(521); +var isGlob = __webpack_require__(294); +var micromatch = __webpack_require__(524); +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; @@ -56750,1679 +59097,2874 @@ function matchAny(entry, patternsRe) { exports.matchAny = matchAny; -/***/ }), -/* 514 */ -/***/ (function(module, exports, __webpack_require__) { +/***/ }), +/* 521 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var path = __webpack_require__(4); +var isglob = __webpack_require__(522); +var pathDirname = __webpack_require__(523); +var isWin32 = __webpack_require__(121).platform() === 'win32'; + +module.exports = function globParent(str) { + // flip windows path separators + if (isWin32 && str.indexOf('/') < 0) str = str.split('\\').join('/'); + + // special case for strings ending in enclosure containing path separator + if (/[\{\[].*[\/]*.*[\}\]]$/.test(str)) str += '/'; + + // preserves full path in case of trailing path separator + str += 'a'; + + // remove path parts that are globby + do {str = pathDirname.posix(str)} + while (isglob(str) || /(^|[^\\])([\{\[]|\([^\)]+$)/.test(str)); + + // remove escape chars and return result + return str.replace(/\\([\*\?\|\[\]\(\)\{\}])/g, '$1'); +}; + + +/***/ }), +/* 522 */ +/***/ (function(module, exports, __webpack_require__) { + +/*! + * is-glob <https://github.com/jonschlinkert/is-glob> + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + */ + +var isExtglob = __webpack_require__(295); + +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; +}; + + +/***/ }), +/* 523 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var path = __webpack_require__(4); +var inspect = __webpack_require__(112).inspect; + +function assertPath(path) { + if (typeof path !== 'string') { + throw new TypeError('Path must be a string. Received ' + inspect(path)); + } +} + +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 (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); + + // Try to match a root + if (len > 1) { + if (code === 47/*/*/ || code === 92/*\*/) { + // Possible UNC root + + rootEnd = offset = 1; + + 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 + + // 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 + + 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]; + } + + 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; + } + } + + if (end === -1) { + if (rootEnd === -1) + return '.'; + else + end = rootEnd; + } + return path.slice(0, end); +} + +module.exports = process.platform === 'win32' ? win32 : posix; +module.exports.posix = posix; +module.exports.win32 = win32; + + +/***/ }), +/* 524 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Module dependencies + */ + +var util = __webpack_require__(112); +var braces = __webpack_require__(525); +var toRegex = __webpack_require__(526); +var extend = __webpack_require__(638); + +/** + * Local dependencies + */ + +var compilers = __webpack_require__(640); +var parsers = __webpack_require__(672); +var cache = __webpack_require__(673); +var utils = __webpack_require__(674); +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 + */ + +function micromatch(list, patterns, options) { + patterns = utils.arrayify(patterns); + list = utils.arrayify(list); + + var len = patterns.length; + if (list.length === 0 || len === 0) { + return []; + } + + if (len === 1) { + return micromatch.match(list, patterns[0], options); + } + + 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, 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'); + } + + 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; + + 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); + } + + 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]; + } + } + + // 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 + */ + +micromatch.isMatch = function(str, pattern, options) { + if (typeof str !== 'string') { + throw new TypeError('expected a string: "' + util.inspect(str) + '"'); + } + + if (isEmptyString(str) || isEmptyString(pattern)) { + return false; + } + + var equals = utils.equalsPattern(options); + if (equals(str)) { + return true; + } + + var isMatch = memoize('isMatch', pattern, options, micromatch.matcher); + return isMatch(str); +}; + +/** + * 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 + */ + +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; +}; + +/** + * 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 + */ + +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; +}; + +/** + * 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 + */ + +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)); + } + + return opts.nodupes !== false ? utils.unique(matches) : matches; +}; + +/** + * 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 + */ + +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 opts = extend({}, options, {contains: true}); + return micromatch.any(str, patterns, opts); +}; + +/** + * 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; +}; + +/** + * 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'); + } + 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 + */ + +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'); + } + + // 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 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); + } + + function test(regex) { + var equals = utils.equalsPattern(options); + var unixify = utils.unixify(options); + + return function(str) { + if (equals(str)) { + return true; + } + + if (regex.test(unixify(str))) { + return true; + } + return false; + }; + } + + var fn = test(re); + Object.defineProperty(fn, 'result', { + configurable: true, + enumerable: false, + value: re.result + }); + return fn; +}; + +/** + * 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 + */ + +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); +}; + +/** + * 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'); + } + + if (pattern.length > MAX_LENGTH) { + throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); + } + + 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 regex = toRegex(output.join('|'), options); + Object.defineProperty(regex, 'result', { + configurable: true, + enumerable: false, + value: ast_array + }); + return regex; + } + + return memoize('makeRe', pattern, options, makeRe); +}; + +/** + * 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'); + } + + function expand() { + if (options && options.nobrace === true || !/\{.*\}/.test(pattern)) { + return utils.arrayify(pattern); + } + return braces(pattern, options); + } + + return memoize('braces', pattern, options, expand); +}; + +/** + * Proxy to the [micromatch.braces](#method), for parity with + * minimatch. + */ + +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 + */ + +micromatch.create = function(pattern, options) { + return memoize('create', pattern, options, function() { + function create(str, opts) { + return micromatch.compile(micromatch.parse(str, opts), opts); + } + + pattern = micromatch.braces(pattern, options); + var len = pattern.length; + var idx = -1; + var res = []; + + 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 + */ + +micromatch.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); +}; + +/** + * 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); + } + + return memoize('compile', ast.input, options, function() { + var snapdragon = utils.instantiate(ast, options); + compilers(snapdragon, options); + return snapdragon.compile(ast, options); + }); +}; + +/** + * Clear the regex cache. + * + * ```js + * mm.clearCache(); + * ``` + * @api public + */ + +micromatch.clearCache = function() { + micromatch.cache.caches = {}; +}; + +/** + * Returns true if the given value is effectively an empty string + */ + +function isEmptyString(val) { + return String(val) === '' || String(val) === './'; +} + +/** + * Compose a matcher function with the given patterns. + * This allows matcher functions to be compiled once and + * called multiple times. + */ + +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; + }; + }); +} + +/** + * Memoize a generated regex or function. A unique key is generated + * from the `type` (usually method name), the `pattern`, and + * user-defined options. + */ + +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; +} + +/** + * Expose compiler, parser and cache on `micromatch` + */ + +micromatch.compilers = compilers; +micromatch.parsers = parsers; +micromatch.caches = cache.caches; + +/** + * Expose `micromatch` + * @type {Function} + */ + +module.exports = micromatch; + + +/***/ }), +/* 525 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Module dependencies + */ + +var toRegex = __webpack_require__(526); +var unique = __webpack_require__(548); +var extend = __webpack_require__(549); + +/** + * Local dependencies + */ + +var compilers = __webpack_require__(551); +var parsers = __webpack_require__(564); +var Braces = __webpack_require__(568); +var utils = __webpack_require__(552); +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 + */ + +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 + */ + +braces.optimize = function(pattern, options) { + return braces.create(pattern, options); +}; + +/** + * 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. + * + * ```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 + */ + +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)]; + } + + 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); + } + + // filter out duplicates if specified + if (options && options.nodupes === true) { + arr = unique(arr); + } + + Object.defineProperty(arr, 'result', { + enumerable: false, + value: result + }); + + return arr; + } + + return memoize('create', pattern, options, create); +}; + +/** + * 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 + */ + +braces.makeRe = 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 makeRe() { + var arr = braces(pattern, options); + var opts = extend({strictErrors: false}, options); + return toRegex(arr, opts); + } + + return memoize('makeRe', pattern, options, makeRe); +}; + +/** + * 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 + */ + +braces.parse = function(pattern, options) { + var proto = new Braces(options); + return proto.parse(pattern, options); +}; + +/** + * 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 + */ + +braces.compile = function(ast, options) { + var proto = new Braces(options); + return proto.compile(ast, options); +}; + +/** + * Clear the regex cache. + * + * ```js + * braces.clearCache(); + * ``` + * @api public + */ + +braces.clearCache = function() { + cache = braces.cache = {}; +}; + +/** + * 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. + */ + +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); + } + + if (cache.hasOwnProperty(key)) { + return cache[key]; + } + + var res = fn(pattern, options); + cache[key] = res; + return res; +} + +/** + * Expose `Braces` constructor and methods + * @type {Function} + */ + +braces.Braces = Braces; +braces.compilers = compilers; +braces.parsers = parsers; +braces.cache = cache; + +/** + * Expose `braces` + * @type {Function} + */ + +module.exports = braces; + + +/***/ }), +/* 526 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var safe = __webpack_require__(527); +var define = __webpack_require__(533); +var extend = __webpack_require__(541); +var not = __webpack_require__(545); +var MAX_LENGTH = 1024 * 64; + +/** + * Session cache + */ + +var cache = {}; + +/** + * 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 + */ + +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'); + } -"use strict"; + 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); -var path = __webpack_require__(4); -var isglob = __webpack_require__(515); -var pathDirname = __webpack_require__(516); -var isWin32 = __webpack_require__(120).platform() === 'win32'; + if (cache.hasOwnProperty(key)) { + return cache[key]; + } + } -module.exports = function globParent(str) { - // flip windows path separators - if (isWin32 && str.indexOf('/') < 0) str = str.split('\\').join('/'); + var opts = extend({}, options); + if (opts.contains === true) { + if (opts.negate === true) { + opts.strictNegate = false; + } else { + opts.strict = false; + } + } - // special case for strings ending in enclosure containing path separator - if (/[\{\[].*[\/]*.*[\}\]]$/.test(str)) str += '/'; + if (opts.strict === false) { + opts.strictOpen = false; + opts.strictClose = false; + } - // preserves full path in case of trailing path separator - str += 'a'; + var open = opts.strictOpen !== false ? '^' : ''; + var close = opts.strictClose !== false ? '$' : ''; + var flags = opts.flags || ''; + var regex; - // remove path parts that are globby - do {str = pathDirname.posix(str)} - while (isglob(str) || /(^|[^\\])([\{\[]|\([^\)]+$)/.test(str)); + if (opts.nocase === true && !/i/.test(flags)) { + flags += 'i'; + } - // remove escape chars and return result - return str.replace(/\\([\*\?\|\[\]\(\)\{\}])/g, '$1'); -}; + try { + if (opts.negate || typeof opts.strictNegate === 'boolean') { + pattern = not.create(pattern, opts); + } + var str = open + '(?:' + pattern + ')' + close; + regex = new RegExp(str, flags); -/***/ }), -/* 515 */ -/***/ (function(module, exports, __webpack_require__) { + if (opts.safe === true && safe(regex) === false) { + throw new Error('potentially unsafe regular expression: ' + regex.source); + } -/*! - * is-glob <https://github.com/jonschlinkert/is-glob> - * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License. - */ + } catch (err) { + if (opts.strictErrors === true || opts.safe === true) { + err.key = key; + err.pattern = pattern; + err.originalOptions = options; + err.createdOptions = opts; + throw err; + } -var isExtglob = __webpack_require__(294); + try { + regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$'); + } catch (err) { + regex = /.^/; //<= match nothing + } + } -module.exports = function isGlob(str) { - if (typeof str !== 'string' || str === '') { - return false; + if (opts.cache !== false) { + memoize(regex, key, pattern, opts); } + return regex; +} - if (isExtglob(str)) return 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. + */ - var regex = /(\\).|([*?]|\[.*\]|\{.*\}|\(.*\|.*\)|^!)/; - var match; +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; +} - while ((match = regex.exec(str))) { - if (match[2]) return true; - str = str.slice(match.index + match[0].length); +/** + * 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. + */ + +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 false; + return key; +} + +/** + * Expose `makeRe` + */ + +module.exports.makeRe = makeRe; + + +/***/ }), +/* 527 */ +/***/ (function(module, exports, __webpack_require__) { + +var parse = __webpack_require__(528); +var types = parse.types; + +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]'; +} + /***/ }), -/* 516 */ +/* 528 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +var util = __webpack_require__(529); +var types = __webpack_require__(530); +var sets = __webpack_require__(531); +var positions = __webpack_require__(532); -var path = __webpack_require__(4); -var inspect = __webpack_require__(111).inspect; +module.exports = function(regexpStr) { + var i = 0, l, c, + start = { type: types.ROOT, stack: []}, -function assertPath(path) { - if (typeof path !== 'string') { - throw new TypeError('Path must be a string. Received ' + inspect(path)); - } -} + // Keep track of last clause/group and stack. + lastGroup = start, + last = start.stack, + groupStack = []; -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 (end === -1) - return hasRoot ? '/' : '.'; - if (hasRoot && end === 1) - return '//'; - return path.slice(0, end); -} + var repeatErr = function(i) { + util.error(regexpStr, 'Nothing to repeat at column ' + (i - 1)); + }; -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); + // Decode a few escaped characters. + var str = util.strToChars(regexpStr); + l = str.length; - // Try to match a root - if (len > 1) { - if (code === 47/*/*/ || code === 92/*\*/) { - // Possible UNC root + // Iterate through each character in string. + while (i < l) { + c = str[i++]; - rootEnd = offset = 1; + switch (c) { + // Handle escaped characters, inclues a few sets. + case '\\': + c = str[i++]; - 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/*\*/) + switch (c) { + case 'b': + last.push(positions.wordBoundary()); + break; + + case 'B': + last.push(positions.nonWordBoundary()); + break; + + case 'w': + last.push(sets.words()); + break; + + case 'W': + last.push(sets.notWords()); + break; + + case 'd': + last.push(sets.ints()); + break; + + case 'D': + last.push(sets.notInts()); + break; + + case 's': + last.push(sets.whitespace()); + break; + + case 'S': + last.push(sets.notWhitespace()); break; + + 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) }); + + // Escaped character. + } else { + last.push({ type: types.CHAR, value: c.charCodeAt(0) }); + } } - 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; + + break; + + + // Positionals. + case '^': + last.push(positions.begin()); + break; + + case '$': + last.push(positions.end()); + break; + + + // Handle custom sets. + case '[': + // Check if this class is 'anti' i.e. [^abc]. + var not; + if (str[i] === '^') { + not = true; + i++; + } else { + not = false; + } + + // Get all the characters in class. + var classTokens = util.tokenizeClass(str.slice(i), regexpStr); + + // Increase index by length of class. + i += classTokens[1]; + last.push({ + type: types.SET, + set: classTokens[0], + not: not, + }); + + break; + + + // Class of any character except \n. + case '.': + last.push(sets.anyChar()); + break; + + + // Push group onto stack. + case '(': + // Create group. + var group = { + type: types.GROUP, + stack: [], + remember: true, + }; + + c = str[i]; + + // If if this is a special kind of group. + if (c === '?') { + c = str[i + 1]; + i += 2; + + // Match if followed by. + if (c === '=') { + group.followedBy = true; + + // Match if not followed by. + } else if (c === '!') { + group.notFollowedBy = true; + + } else if (c !== ':') { + util.error(regexpStr, + 'Invalid group, character \'' + c + + '\' after \'?\' at column ' + (i - 1)); } - 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 - // 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 + group.remember = false; + } + + // 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; + + + // Pop group out of stack. + case ')': + if (groupStack.length === 0) { + util.error(regexpStr, 'Unmatched ) at column ' + (i - 1)); + } + lastGroup = groupStack.pop(); + + // 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; + + + // 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; + } + + // Create a new stack and add to options for rest of clause. + var stack = []; + lastGroup.options.push(stack); + last = stack; + break; + + + // 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; + + case '?': + if (last.length === 0) { + repeatErr(i); + } + last.push({ + type: types.REPETITION, + min: 0, + max: 1, + value: last.pop(), + }); + break; + + case '+': + if (last.length === 0) { + repeatErr(i); + } + last.push({ + type: types.REPETITION, + min: 1, + max: Infinity, + value: last.pop(), + }); + break; + + case '*': + if (last.length === 0) { + repeatErr(i); + } + last.push({ + type: types.REPETITION, + min: 0, + max: Infinity, + value: last.pop(), + }); + break; + - 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; - } - } + // Default is a character that is not `\[](){}?+*^$`. + default: + last.push({ + type: types.CHAR, + value: c.charCodeAt(0), + }); } - } else if (code === 47/*/*/ || code === 92/*\*/) { - return path[0]; - } - 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; - } } - if (end === -1) { - if (rootEnd === -1) - return '.'; - else - end = rootEnd; + // Check if any groups have not been closed. + if (groupStack.length !== 0) { + util.error(regexpStr, 'Unterminated group'); } - return path.slice(0, end); -} -module.exports = process.platform === 'win32' ? win32 : posix; -module.exports.posix = posix; -module.exports.win32 = win32; + return start; +}; + +module.exports.types = types; /***/ }), -/* 517 */ +/* 529 */ /***/ (function(module, exports, __webpack_require__) { -/*! - * is-glob <https://github.com/jonschlinkert/is-glob> - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ +var types = __webpack_require__(530); +var sets = __webpack_require__(531); -var isExtglob = __webpack_require__(294); -var chars = { '{': '}', '(': ')', '[': ']'}; -module.exports = function isGlob(str, options) { - if (typeof str !== 'string' || str === '') { - return false; - } +// All of these are private and only used by randexp. +// It's assumed that they will always be called with the correct input. - if (isExtglob(str)) { - return true; - } +var CTRL = '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ ?'; +var SLSH = { '0': 0, 't': 9, 'n': 10, 'v': 11, 'f': 12, 'r': 13 }; - var regex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; - var match; +/** + * 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; + } - // optionally relax regex - if (options && options.strict === false) { - regex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; - } + var code = b ? 8 : + a16 ? parseInt(a16, 16) : + b16 ? parseInt(b16, 16) : + c8 ? parseInt(c8, 8) : + dctrl ? CTRL.indexOf(dctrl) : + SLSH[eslsh]; - while ((match = regex.exec(str))) { - if (match[2]) return true; - var idx = match.index + match[0].length; + var c = String.fromCharCode(code); - // if an open bracket/brace/paren is escaped, - // set the index to the next closing character - var open = match[1]; - var close = open ? chars[open] : null; - if (open && close) { - var n = str.indexOf(close, idx); - if (n !== -1) { - idx = n + 1; - } + // Escape special regex characters. + if (/[\[\]{}\^$.|?*+()]/.test(c)) { + c = '\\' + c; } - str = str.slice(idx); - } - return false; -}; - - -/***/ }), -/* 518 */ -/***/ (function(module, exports, __webpack_require__) { + return c; + }); -"use strict"; + return str; +}; /** - * Module dependencies + * turns class into tokens + * reads str until it encounters a ] not preceeded by a \ + * + * @param {String} str + * @param {String} regexpStr + * @return {Array.<Array.<Object>, Number>} */ +exports.tokenizeClass = function(str, regexpStr) { + /* jshint maxlen: false */ + var tokens = []; + var regexp = /\\(?:(w)|(d)|(s)|(W)|(D)|(S))|((?:(?:\\)(.)|([^\]\\]))-(?:\\)?([^\]]))|(\])|(?:\\)?(.)/g; + var rs, c; -var util = __webpack_require__(111); -var braces = __webpack_require__(519); -var toRegex = __webpack_require__(621); -var extend = __webpack_require__(629); -/** - * Local dependencies - */ + while ((rs = regexp.exec(str)) != null) { + if (rs[1]) { + tokens.push(sets.words()); -var compilers = __webpack_require__(632); -var parsers = __webpack_require__(664); -var cache = __webpack_require__(665); -var utils = __webpack_require__(666); -var MAX_LENGTH = 1024 * 64; + } else if (rs[2]) { + tokens.push(sets.ints()); -/** - * 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 - */ + } else if (rs[3]) { + tokens.push(sets.whitespace()); -function micromatch(list, patterns, options) { - patterns = utils.arrayify(patterns); - list = utils.arrayify(list); + } else if (rs[4]) { + tokens.push(sets.notWords()); - var len = patterns.length; - if (list.length === 0 || len === 0) { - return []; - } + } else if (rs[5]) { + tokens.push(sets.notInts()); - if (len === 1) { - return micromatch.match(list, patterns[0], options); - } + } else if (rs[6]) { + tokens.push(sets.notWhitespace()); - var omit = []; - var keep = []; - var idx = -1; + } else if (rs[7]) { + tokens.push({ + type: types.RANGE, + from: (rs[8] || rs[9]).charCodeAt(0), + to: rs[10].charCodeAt(0), + }); - while (++idx < len) { - var pattern = patterns[idx]; + } else if (c = rs[12]) { + tokens.push({ + type: types.CHAR, + value: c.charCodeAt(0), + }); - 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)); + return [tokens, regexp.lastIndex]; } } - var matches = utils.diff(keep, omit); - if (!options || options.nodupes !== false) { - return utils.unique(matches); - } + exports.error(regexpStr, 'Unterminated character class'); +}; - return matches; -} /** - * Similar to the main function, but `pattern` must be a string. - * - * ```js - * var mm = require('micromatch'); - * mm.match(list, pattern[, options]); + * Shortcut to throw errors. * - * 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 + * @param {String} regexp + * @param {String} msg */ +exports.error = function(regexp, msg) { + throw new SyntaxError('Invalid regular expression: /' + regexp + '/: ' + msg); +}; -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; - - 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); - } - - 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]; - } - } - // if `opts.ignore` was defined, diff ignored list - if (options.ignore) { - matches = micromatch.not(matches, options.ignore, options); - } +/***/ }), +/* 530 */ +/***/ (function(module, exports) { - return options.nodupes !== false ? utils.unique(matches) : matches; +module.exports = { + ROOT : 0, + GROUP : 1, + POSITION : 2, + SET : 3, + RANGE : 4, + REPETITION : 5, + REFERENCE : 6, + CHAR : 7, }; -/** - * 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 - */ - -micromatch.isMatch = function(str, pattern, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } - if (isEmptyString(str) || isEmptyString(pattern)) { - return false; - } +/***/ }), +/* 531 */ +/***/ (function(module, exports, __webpack_require__) { - var equals = utils.equalsPattern(options); - if (equals(str)) { - return true; - } +var types = __webpack_require__(530); - var isMatch = memoize('isMatch', pattern, options, micromatch.matcher); - return isMatch(str); +var INTS = function() { + return [{ type: types.RANGE , from: 48, to: 57 }]; }; -/** - * 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 - */ - -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; +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()); }; -/** - * 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 - */ - -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; +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 } + ]; }; -/** - * 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]; - } +var NOTANYCHAR = function() { + return [ + { type: types.CHAR, value: 10 }, + { type: types.CHAR, value: 13 }, + { type: types.CHAR, value: 8232 }, + { type: types.CHAR, value: 8233 }, + ]; +}; - for (var i = 0; i < patterns.length; i++) { - if (micromatch.isMatch(str, patterns[i], options)) { - return true; - } - } - return false; +// Predefined class objects. +exports.words = function() { + return { type: types.SET, set: WORDS(), not: 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 - */ +exports.notWords = function() { + return { type: types.SET, set: WORDS(), not: true }; +}; -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; +exports.ints = function() { + return { type: types.SET, set: INTS(), not: false }; }; -/** - * 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 - */ - -micromatch.not = function(list, patterns, options) { - var opts = extend({}, options); - var ignore = opts.ignore; - delete opts.ignore; +exports.notInts = function() { + return { type: types.SET, set: INTS(), not: true }; +}; - var unixify = utils.unixify(opts); - list = utils.arrayify(list).map(unixify); +exports.whitespace = function() { + return { type: types.SET, set: WHITESPACE(), not: false }; +}; - var matches = utils.diff(list, micromatch(list, patterns, opts)); - if (ignore) { - matches = utils.diff(matches, micromatch(list, ignore)); - } +exports.notWhitespace = function() { + return { type: types.SET, set: WHITESPACE(), not: true }; +}; - return opts.nodupes !== false ? utils.unique(matches) : matches; +exports.anyChar = function() { + return { type: types.SET, set: NOTANYCHAR(), not: true }; }; -/** - * 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 - */ -micromatch.contains = function(str, patterns, options) { - if (typeof str !== 'string') { - throw new TypeError('expected a string: "' + util.inspect(str) + '"'); - } +/***/ }), +/* 532 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof patterns === 'string') { - if (isEmptyString(str) || isEmptyString(patterns)) { - return false; - } +var types = __webpack_require__(530); - var equals = utils.equalsPattern(patterns, options); - if (equals(str)) { - return true; - } - var contains = utils.containsPattern(patterns, options); - if (contains(str)) { - return true; - } - } +exports.wordBoundary = function() { + return { type: types.POSITION, value: 'b' }; +}; - var opts = extend({}, options, {contains: true}); - return micromatch.any(str, patterns, opts); +exports.nonWordBoundary = function() { + return { type: types.POSITION, value: 'B' }; }; -/** - * Returns true if the given pattern and options should enable - * the `matchBase` option. - * @return {Boolean} - * @api private - */ +exports.begin = function() { + return { type: types.POSITION, value: '^' }; +}; -micromatch.matchBase = function(pattern, options) { - if (pattern && pattern.indexOf('/') !== -1 || !options) return false; - return options.basename === true || options.matchBase === true; +exports.end = function() { + return { type: types.POSITION, value: '$' }; }; -/** - * 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'); - } - var keys = micromatch(Object.keys(obj), patterns, options); - return utils.pick(obj, keys); -}; +/***/ }), +/* 533 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * 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]); +"use strict"; +/*! + * define-property <https://github.com/jonschlinkert/define-property> * - * 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-2018, Jon Schlinkert. + * Released 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'); - } +var isobject = __webpack_require__(534); +var isDescriptor = __webpack_require__(535); +var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) + ? Reflect.defineProperty + : Object.defineProperty; - // if pattern is a non-glob string - if (!utils.hasSpecialChars(pattern)) { - if (options && options.nocase === true) { - pattern = pattern.toLowerCase(); - } - return utils.matchPath(pattern, options); +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 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 key !== 'string') { + throw new TypeError('expected "key" to be a string'); } - function test(regex) { - var equals = utils.equalsPattern(options); - var unixify = utils.unixify(options); - - return function(str) { - if (equals(str)) { - return true; - } - - if (regex.test(unixify(str))) { - return true; - } - return false; - }; + if (isDescriptor(val)) { + define(obj, key, val); + return obj; } - var fn = test(re); - Object.defineProperty(fn, 'result', { + define(obj, key, { configurable: true, enumerable: false, - value: re.result + writable: true, + value: val }); - return fn; -}; - -/** - * 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 - */ - -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 obj; +}; - return match.slice(1); - }; - } - var capture = memoize('capture', pattern, options, match); - return capture(str); -}; +/***/ }), +/* 534 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Create a regular expression from the given glob `pattern`. - * - * ```js - * var mm = require('micromatch'); - * mm.makeRe(pattern[, options]); +"use strict"; +/*! + * isobject <https://github.com/jonschlinkert/isobject> * - * 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 + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. */ -micromatch.makeRe = function(pattern, options) { - 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'); - } - 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; - }); +module.exports = function isObject(val) { + return val != null && typeof val === 'object' && Array.isArray(val) === false; +}; - var regex = toRegex(output.join('|'), options); - Object.defineProperty(regex, 'result', { - configurable: true, - enumerable: false, - value: ast_array - }); - return regex; - } - return memoize('makeRe', pattern, options, makeRe); -}; +/***/ }), +/* 535 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Expand the given brace `pattern`. - * - * ```js - * var mm = require('micromatch'); - * console.log(mm.braces('foo/{a,b}/bar')); - * //=> ['foo/(a|b)/bar'] +"use strict"; +/*! + * is-descriptor <https://github.com/jonschlinkert/is-descriptor> * - * 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 + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -micromatch.braces = function(pattern, options) { - if (typeof pattern !== 'string' && !Array.isArray(pattern)) { - throw new TypeError('expected pattern to be an array or string'); - } - - function expand() { - if (options && options.nobrace === true || !/\{.*\}/.test(pattern)) { - return utils.arrayify(pattern); - } - return braces(pattern, options); - } - return memoize('braces', pattern, options, expand); -}; -/** - * Proxy to the [micromatch.braces](#method), for parity with - * minimatch. - */ +var typeOf = __webpack_require__(536); +var isAccessor = __webpack_require__(537); +var isData = __webpack_require__(539); -micromatch.braceExpand = function(pattern, options) { - var opts = extend({}, options, {expand: true}); - return micromatch.braces(pattern, opts); +module.exports = function isDescriptor(obj, key) { + if (typeOf(obj) !== 'object') { + return false; + } + if ('get' in obj) { + return isAccessor(obj, key); + } + return isData(obj, key); }; -/** - * 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 - */ - -micromatch.create = function(pattern, options) { - return memoize('create', pattern, options, function() { - function create(str, opts) { - return micromatch.compile(micromatch.parse(str, opts), opts); - } - pattern = micromatch.braces(pattern, options); - var len = pattern.length; - var idx = -1; - var res = []; +/***/ }), +/* 536 */ +/***/ (function(module, exports) { - while (++idx < len) { - res.push(create(pattern[idx], options)); - } - return res; - }); -}; +var toString = Object.prototype.toString; -/** - * 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 - */ +module.exports = function kindOf(val) { + if (val === void 0) return 'undefined'; + if (val === null) return 'null'; -micromatch.parse = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); + 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 parse() { - var snapdragon = utils.instantiate(null, options); - parsers(snapdragon, options); + 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'; - var ast = snapdragon.parse(pattern, options); - utils.define(ast, 'snapdragon', snapdragon); - ast.input = pattern; - return ast; - } + switch (ctorName(val)) { + case 'Symbol': return 'symbol'; + case 'Promise': return 'promise'; - return memoize('parse', pattern, options, parse); -}; + // Set, Map, WeakSet, WeakMap + case 'WeakMap': return 'weakmap'; + case 'WeakSet': return 'weakset'; + case 'Map': return 'map'; + case 'Set': return 'set'; -/** - * 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 - */ + // 8-bit typed arrays + case 'Int8Array': return 'int8array'; + case 'Uint8Array': return 'uint8array'; + case 'Uint8ClampedArray': return 'uint8clampedarray'; -micromatch.compile = function(ast, options) { - if (typeof ast === 'string') { - ast = micromatch.parse(ast, options); + // 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'; } - return memoize('compile', ast.input, options, function() { - var snapdragon = utils.instantiate(ast, options); - compilers(snapdragon, options); - return snapdragon.compile(ast, options); - }); -}; + if (isGeneratorObj(val)) { + return 'generator'; + } -/** - * Clear the regex cache. - * - * ```js - * mm.clearCache(); - * ``` - * @api public - */ + // 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'; + } -micromatch.clearCache = function() { - micromatch.cache.caches = {}; + // other + return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); }; -/** - * Returns true if the given value is effectively an empty string - */ - -function isEmptyString(val) { - return String(val) === '' || String(val) === './'; +function ctorName(val) { + return typeof val.constructor === 'function' ? val.constructor.name : null; } -/** - * Compose a matcher function with the given patterns. - * This allows matcher functions to be compiled once and - * called multiple times. - */ - -function compose(patterns, options, matcher) { - var matchers; +function isArray(val) { + if (Array.isArray) return Array.isArray(val); + return val instanceof Array; +} - 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)); - } - } +function isError(val) { + return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); +} - var len = matchers.length; - while (len--) { - if (matchers[len](file) === true) { - return true; - } - } - return false; - }; - }); +function isDate(val) { + if (val instanceof Date) return true; + return typeof val.toDateString === 'function' + && typeof val.getDate === 'function' + && typeof val.setDate === 'function'; } -/** - * Memoize a generated regex or function. A unique key is generated - * from the `type` (usually method name), the `pattern`, and - * user-defined options. - */ +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 memoize(type, pattern, options, fn) { - var key = utils.createKey(type + '=' + pattern, options); +function isGeneratorFn(name, val) { + return ctorName(name) === 'GeneratorFunction'; +} - if (options && options.cache === false) { - return fn(pattern, options); - } +function isGeneratorObj(val) { + return typeof val.throw === 'function' + && typeof val.return === 'function' + && typeof val.next === 'function'; +} - if (cache.has(type, key)) { - return cache.get(type, key); +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; + } } - - var val = fn(pattern, options); - cache.set(type, key, val); - return val; + return false; } /** - * Expose compiler, parser and cache on `micromatch` - */ - -micromatch.compilers = compilers; -micromatch.parsers = parsers; -micromatch.caches = cache.caches; - -/** - * Expose `micromatch` - * @type {Function} + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer */ -module.exports = micromatch; +function isBuffer(val) { + if (val.constructor && typeof val.constructor.isBuffer === 'function') { + return val.constructor.isBuffer(val); + } + return false; +} /***/ }), -/* 519 */ +/* 537 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -/** - * Module dependencies +/*! + * is-accessor-descriptor <https://github.com/jonschlinkert/is-accessor-descriptor> + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -var toRegex = __webpack_require__(520); -var unique = __webpack_require__(532); -var extend = __webpack_require__(529); -/** - * Local dependencies - */ -var compilers = __webpack_require__(533); -var parsers = __webpack_require__(548); -var Braces = __webpack_require__(558); -var utils = __webpack_require__(534); -var MAX_LENGTH = 1024 * 64; -var cache = {}; +var typeOf = __webpack_require__(538); -/** - * 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 - */ +// accessor descriptor properties +var accessor = { + get: 'function', + set: 'function', + configurable: 'boolean', + enumerable: 'boolean' +}; -function braces(pattern, options) { - var key = utils.createKey(String(pattern), options); - var arr = []; +function isAccessorDescriptor(obj, prop) { + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } - var disabled = options && options.cache === false; - if (!disabled && cache.hasOwnProperty(key)) { - return cache[key]; + if (typeOf(obj) !== 'object') { + return false; } - 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 (has(obj, 'value') || has(obj, 'writable')) { + return false; } - if (options && options.nodupes === true) { - arr = unique(arr); + if (!has(obj, 'get') || typeof obj.get !== 'function') { + return false; } - if (!disabled) { - cache[key] = arr; + // 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; } - 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 - */ + for (var key in obj) { + if (!accessor.hasOwnProperty(key)) { + continue; + } -braces.expand = function(pattern, options) { - return braces.create(pattern, extend({}, options, {expand: true})); -}; + if (typeOf(obj[key]) === accessor[key]) { + continue; + } -/** - * 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 - */ + if (typeof obj[key] !== 'undefined') { + return false; + } + } + return true; +} -braces.optimize = function(pattern, options) { - return braces.create(pattern, options); -}; +function has(obj, key) { + return {}.hasOwnProperty.call(obj, key); +} /** - * 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. - * - * ```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 + * Expose `isAccessorDescriptor` */ -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]; - } +module.exports = isAccessorDescriptor; - if (utils.isEmptySets(pattern)) { - return []; - } - if (utils.isQuotedString(pattern)) { - return [pattern.slice(1, -1)]; - } +/***/ }), +/* 538 */ +/***/ (function(module, exports) { - var proto = new Braces(options); - var result = !options || options.expand !== true - ? proto.optimize(pattern, options) - : proto.expand(pattern, options); +var toString = Object.prototype.toString; - // get the generated pattern(s) - var arr = result.output; +module.exports = function kindOf(val) { + if (val === void 0) return 'undefined'; + if (val === null) return 'null'; - // filter out empty strings if specified - if (options && options.noempty === true) { - arr = arr.filter(Boolean); - } + 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'; + } - // filter out duplicates if specified - if (options && options.nodupes === true) { - arr = unique(arr); - } + 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'; - Object.defineProperty(arr, 'result', { - enumerable: false, - value: result - }); + switch (ctorName(val)) { + case 'Symbol': return 'symbol'; + case 'Promise': return 'promise'; - return arr; - } + // Set, Map, WeakSet, WeakMap + case 'WeakMap': return 'weakmap'; + case 'WeakSet': return 'weakset'; + case 'Map': return 'map'; + case 'Set': return 'set'; - return memoize('create', pattern, options, create); -}; + // 8-bit typed arrays + case 'Int8Array': return 'int8array'; + case 'Uint8Array': return 'uint8array'; + case 'Uint8ClampedArray': return 'uint8clampedarray'; -/** - * 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 - */ + // 16-bit typed arrays + case 'Int16Array': return 'int16array'; + case 'Uint16Array': return 'uint16array'; -braces.makeRe = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); + // 32-bit typed arrays + case 'Int32Array': return 'int32array'; + case 'Uint32Array': return 'uint32array'; + case 'Float32Array': return 'float32array'; + case 'Float64Array': return 'float64array'; } - var maxLength = (options && options.maxLength) || MAX_LENGTH; - if (pattern.length >= maxLength) { - throw new Error('expected pattern to be less than ' + maxLength + ' characters'); + if (isGeneratorObj(val)) { + return 'generator'; } - function makeRe() { - var arr = braces(pattern, options); - var opts = extend({strictErrors: false}, options); - return toRegex(arr, opts); + // 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'; } - return memoize('makeRe', pattern, options, makeRe); -}; - -/** - * 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 - */ - -braces.parse = function(pattern, options) { - var proto = new Braces(options); - return proto.parse(pattern, options); -}; - -/** - * 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 - */ - -braces.compile = function(ast, options) { - var proto = new Braces(options); - return proto.compile(ast, options); + // other + return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); }; -/** - * Clear the regex cache. - * - * ```js - * braces.clearCache(); - * ``` - * @api public - */ - -braces.clearCache = function() { - cache = braces.cache = {}; -}; +function ctorName(val) { + return typeof val.constructor === 'function' ? val.constructor.name : null; +} -/** - * 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. - */ +function isArray(val) { + if (Array.isArray) return Array.isArray(val); + return val instanceof Array; +} -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); - } +function isError(val) { + return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); +} - if (cache.hasOwnProperty(key)) { - return cache[key]; - } +function isDate(val) { + if (val instanceof Date) return true; + return typeof val.toDateString === 'function' + && typeof val.getDate === 'function' + && typeof val.setDate === 'function'; +} - var res = fn(pattern, options); - cache[key] = res; - return res; +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'; } -/** - * Expose `Braces` constructor and methods - * @type {Function} - */ +function isGeneratorFn(name, val) { + return ctorName(name) === 'GeneratorFunction'; +} -braces.Braces = Braces; -braces.compilers = compilers; -braces.parsers = parsers; -braces.cache = cache; +function isGeneratorObj(val) { + return typeof val.throw === 'function' + && typeof val.return === 'function' + && typeof val.next === 'function'; +} + +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; +} /** - * Expose `braces` - * @type {Function} + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer */ -module.exports = braces; +function isBuffer(val) { + if (val.constructor && typeof val.constructor.isBuffer === 'function') { + return val.constructor.isBuffer(val); + } + return false; +} /***/ }), -/* 520 */ +/* 539 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -var define = __webpack_require__(521); -var extend = __webpack_require__(529); -var not = __webpack_require__(531); -var MAX_LENGTH = 1024 * 64; - -/** - * Session cache +/*! + * is-data-descriptor <https://github.com/jonschlinkert/is-data-descriptor> + * + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -var cache = {}; -/** - * 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 - */ -module.exports = function(patterns, options) { - if (!Array.isArray(patterns)) { - return makeRe(patterns, options); - } - return makeRe(patterns.join('|'), options); -}; +var typeOf = __webpack_require__(540); -/** - * 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 - */ +module.exports = function isDataDescriptor(obj, prop) { + // data descriptor properties + var data = { + configurable: 'boolean', + enumerable: 'boolean', + writable: 'boolean' + }; -function makeRe(pattern, options) { - if (pattern instanceof RegExp) { - return pattern; + if (typeOf(obj) !== 'object') { + return false; } - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; } - if (pattern.length > MAX_LENGTH) { - throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); + if (!('value' in obj) && !('writable' in obj)) { + return false; } - var key = pattern; - // do this before shallow cloning options, it's a lot faster - if (!options || (options && options.cache !== false)) { - key = createKey(pattern, options); + for (var key in obj) { + if (key === 'value') continue; - if (cache.hasOwnProperty(key)) { - return cache[key]; + if (!data.hasOwnProperty(key)) { + continue; } - } - var opts = extend({}, options); - if (opts.contains === true) { - if (opts.negate === true) { - opts.strictNegate = false; - } else { - opts.strict = false; + if (typeOf(obj[key]) === data[key]) { + continue; } - } - if (opts.strict === false) { - opts.strictOpen = false; - opts.strictClose = false; + if (typeof obj[key] !== 'undefined') { + return false; + } } + return true; +}; - var open = opts.strictOpen !== false ? '^' : ''; - var close = opts.strictClose !== false ? '$' : ''; - var flags = opts.flags || ''; - var regex; - if (opts.nocase === true && !/i/.test(flags)) { - flags += 'i'; +/***/ }), +/* 540 */ +/***/ (function(module, exports) { + +var toString = Object.prototype.toString; + +module.exports = function kindOf(val) { + if (val === void 0) return 'undefined'; + if (val === null) return 'null'; + + 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'; } - try { - if (opts.negate || typeof opts.strictNegate === 'boolean') { - pattern = not.create(pattern, opts); - } - var str = open + '(?:' + pattern + ')' + close; - regex = new RegExp(str, flags); - } catch (err) { - if (opts.strictErrors === true) { - err.key = key; - err.pattern = pattern; - err.originalOptions = options; - err.createdOptions = opts; - throw err; - } + 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'; - try { - regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$'); - } catch (err) { - regex = /.^/; //<= match nothing - } + 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'; } - if (opts.cache !== false) { - cacheRegex(regex, key, pattern, opts); + if (isGeneratorObj(val)) { + return 'generator'; } - return regex; + + // 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; } -/** - * Cache 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. - */ +function isArray(val) { + if (Array.isArray) return Array.isArray(val); + return val instanceof Array; +} -function cacheRegex(regex, key, pattern, options) { - define(regex, 'cached', true); - define(regex, 'pattern', pattern); - define(regex, 'options', options); - define(regex, 'key', key); - cache[key] = regex; +function isError(val) { + return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); } -/** - * 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. - */ +function isDate(val) { + if (val instanceof Date) return true; + return typeof val.toDateString === 'function' + && typeof val.getDate === 'function' + && typeof val.setDate === 'function'; +} -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 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 isGeneratorFn(name, val) { + return ctorName(name) === 'GeneratorFunction'; +} + +function isGeneratorObj(val) { + return typeof val.throw === 'function' + && typeof val.return === 'function' + && typeof val.next === 'function'; +} + +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 key; + return false; } /** - * Expose `makeRe` + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer */ -module.exports.makeRe = makeRe; +function isBuffer(val) { + if (val.constructor && typeof val.constructor.isBuffer === 'function') { + return val.constructor.isBuffer(val); + } + return false; +} /***/ }), -/* 521 */ +/* 541 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * define-property <https://github.com/jonschlinkert/define-property> - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ +var isExtendable = __webpack_require__(542); +var assignSymbols = __webpack_require__(544); -var isDescriptor = __webpack_require__(522); - -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); +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 (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } } +} - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); +function isString(val) { + return (val && typeof val === 'string'); +} + +function toObject(str) { + var obj = {}; + for (var i in str) { + obj[i] = str[i]; } + return obj; +} - return Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); -}; +function isObject(val) { + return (val && typeof val === 'object') || isExtendable(val); +} + +/** + * Returns true if the given `key` is an own property of `obj`. + */ + +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +function isEnum(obj, key) { + return Object.prototype.propertyIsEnumerable.call(obj, key); +} /***/ }), -/* 522 */ +/* 542 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-descriptor <https://github.com/jonschlinkert/is-descriptor> + * is-extendable <https://github.com/jonschlinkert/is-extendable> * * Copyright (c) 2015-2017, Jon Schlinkert. * Released under the MIT License. @@ -58430,181 +61972,64 @@ module.exports = function defineProperty(obj, prop, val) { -var typeOf = __webpack_require__(523); -var isAccessor = __webpack_require__(524); -var isData = __webpack_require__(527); +var isPlainObject = __webpack_require__(543); -module.exports = function isDescriptor(obj, key) { - if (typeOf(obj) !== 'object') { - return false; - } - if ('get' in obj) { - return isAccessor(obj, key); - } - return isData(obj, key); +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; /***/ }), -/* 523 */ -/***/ (function(module, exports) { - -var toString = Object.prototype.toString; +/* 543 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Get the native `typeof` a value. +"use strict"; +/*! + * is-plain-object <https://github.com/jonschlinkert/is-plain-object> * - * @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'; - } - - // functions - if (type === 'function' || val instanceof Function) { - if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') { - return 'generatorfunction'; - } - return 'function'; - } - - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } - - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - 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'; - } + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ - // 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); +var isObject = __webpack_require__(534); + +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; + } + + // Most likely a plain Object + return true; +}; + /***/ }), -/* 524 */ +/* 544 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-accessor-descriptor <https://github.com/jonschlinkert/is-accessor-descriptor> + * assign-symbols <https://github.com/jonschlinkert/assign-symbols> * * Copyright (c) 2015, Jon Schlinkert. * Licensed under the MIT License. @@ -58612,409 +62037,264 @@ function isBuffer(val) { -var typeOf = __webpack_require__(525); - -// 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; +module.exports = function(receiver, objects) { + if (receiver === null || typeof receiver === 'undefined') { + throw new TypeError('expected first argument to be an object.'); } - if (has(obj, 'value') || has(obj, 'writable')) { - return false; + if (typeof objects === 'undefined' || typeof Symbol === 'undefined') { + return receiver; } - if (!has(obj, 'get') || typeof obj.get !== 'function') { - return false; + if (typeof Object.getOwnPropertySymbols !== 'function') { + return receiver; } - // 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; - } + var isEnumerable = Object.prototype.propertyIsEnumerable; + var target = Object(receiver); + var len = arguments.length, i = 0; - for (var key in obj) { - if (!accessor.hasOwnProperty(key)) { - continue; - } + while (++i < len) { + var provider = Object(arguments[i]); + var names = Object.getOwnPropertySymbols(provider); - if (typeOf(obj[key]) === accessor[key]) { - continue; - } + for (var j = 0; j < names.length; j++) { + var key = names[j]; - if (typeof obj[key] !== 'undefined') { - return false; + if (isEnumerable.call(provider, key)) { + target[key] = provider[key]; + } } } - return true; -} + return target; +}; -function has(obj, key) { - return {}.hasOwnProperty.call(obj, key); -} -/** - * Expose `isAccessorDescriptor` - */ +/***/ }), +/* 545 */ +/***/ (function(module, exports, __webpack_require__) { -module.exports = isAccessorDescriptor; +"use strict"; -/***/ }), -/* 525 */ -/***/ (function(module, exports, __webpack_require__) { +var extend = __webpack_require__(546); +var safe = __webpack_require__(527); -var isBuffer = __webpack_require__(526); -var toString = Object.prototype.toString; +/** + * 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 + */ + +function toRegex(pattern, options) { + return new RegExp(toRegex.create(pattern, options)); +} /** - * Get the native `typeof` a value. + * Create a regex-compatible string from the given `pattern` and `options`. * - * @param {*} `val` - * @return {*} Native javascript type + * ```js + & var not = require('regex-not'); + & console.log(not.create('foo')); + & //=> '^(?:(?!^(?:foo)$).)*$' + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {String} + * @api public */ -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'; +toRegex.create = function(pattern, options) { + if (typeof pattern !== 'string') { + throw new TypeError('expected a string'); } - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; + var opts = extend({}, options); + if (opts.contains === true) { + opts.strictNegate = false; } - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; - } + var open = opts.strictOpen !== false ? '^' : ''; + var close = opts.strictClose !== false ? '$' : ''; + var endChar = opts.endChar ? opts.endChar : '+'; + var str = pattern; - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; + if (opts.strictNegate === false) { + str = '(?:(?!(?:' + pattern + ')).)' + endChar; + } else { + str = '(?:(?!^(?:' + pattern + ')$).)' + endChar; } - if (val instanceof Date) { - return 'date'; + + var res = open + str + close; + if (opts.safe === true && safe(res) === false) { + throw new Error('potentially unsafe regular expression: ' + res); } - // other objects - var type = toString.call(val); + return res; +}; - if (type === '[object RegExp]') { - return 'regexp'; - } - if (type === '[object Date]') { - return 'date'; - } - if (type === '[object Arguments]') { - return 'arguments'; - } - if (type === '[object Error]') { - return 'error'; - } +/** + * Expose `toRegex` + */ - // buffer - if (isBuffer(val)) { - return 'buffer'; - } +module.exports = toRegex; - // 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'; +/***/ }), +/* 546 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isExtendable = __webpack_require__(547); +var assignSymbols = __webpack_require__(544); + +module.exports = Object.assign || function(obj/*, objects*/) { + if (obj === null || typeof obj === 'undefined') { + throw new TypeError('Cannot convert undefined or null to object'); } - if (type === '[object Float32Array]') { - return 'float32array'; + if (!isObject(obj)) { + obj = {}; } - if (type === '[object Float64Array]') { - return 'float64array'; + 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); + } } - - // must be a plain object - return 'object'; + return obj; }; +function assign(a, b) { + for (var key in b) { + if (hasOwn(b, key)) { + a[key] = b[key]; + } + } +} -/***/ }), -/* 526 */ -/***/ (function(module, exports) { +function isString(val) { + return (val && typeof val === 'string'); +} -/*! - * Determine if an object is a Buffer - * - * @author Feross Aboukhadijeh <https://feross.org> - * @license MIT - */ +function toObject(str) { + var obj = {}; + for (var i in str) { + obj[i] = str[i]; + } + return obj; +} -// 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 isObject(val) { + return (val && typeof val === 'object') || isExtendable(val); } -function isBuffer (obj) { - return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) +/** + * Returns true if the given `key` is an own property of `obj`. + */ + +function hasOwn(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); } -// 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)) +function isEnum(obj, key) { + return Object.prototype.propertyIsEnumerable.call(obj, key); } /***/ }), -/* 527 */ +/* 547 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * is-data-descriptor <https://github.com/jonschlinkert/is-data-descriptor> + * is-extendable <https://github.com/jonschlinkert/is-extendable> * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -var typeOf = __webpack_require__(528); +var isPlainObject = __webpack_require__(543); -// data descriptor properties -var data = { - configurable: 'boolean', - enumerable: 'boolean', - writable: 'boolean' +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; -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; - - if (!data.hasOwnProperty(key)) { - continue; - } - - if (typeOf(obj[key]) === data[key]) { - continue; - } - - if (typeof obj[key] !== 'undefined') { - return false; - } - } - return true; -} - -/** - * Expose `isDataDescriptor` - */ - -module.exports = isDataDescriptor; - /***/ }), -/* 528 */ +/* 548 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(526); -var toString = Object.prototype.toString; - -/** - * Get the native `typeof` a value. +"use strict"; +/*! + * array-unique <https://github.com/jonschlinkert/array-unique> * - * @param {*} `val` - * @return {*} Native javascript type + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -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'; - } - // functions - if (typeof val === 'function' || val instanceof Function) { - return 'function'; - } - // array - if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { - return 'array'; +module.exports = function unique(arr) { + if (!Array.isArray(arr)) { + throw new TypeError('array-unique expects an array.'); } - // check for instances of RegExp and Date before calling `toString` - if (val instanceof RegExp) { - return 'regexp'; - } - if (val instanceof Date) { - return 'date'; - } + var len = arr.length; + var i = -1; - // other objects - var type = toString.call(val); + while (i++ < len) { + var j = i + 1; - 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 (; j < arr.length; ++j) { + if (arr[i] === arr[j]) { + arr.splice(j--, 1); + } + } } + return arr; +}; - // buffer - if (isBuffer(val)) { - return 'buffer'; +module.exports.immutable = function uniqueImmutable(arr) { + if (!Array.isArray(arr)) { + throw new TypeError('array-unique expects an array.'); } - // 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 arrLen = arr.length; + var newArr = new Array(arrLen); - // 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'; + for (var i = 0; i < arrLen; i++) { + newArr[i] = arr[i]; } - // must be a plain object - return 'object'; + return module.exports(newArr); }; /***/ }), -/* 529 */ +/* 549 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(530); +var isObject = __webpack_require__(550); module.exports = function extend(o/*, objects*/) { if (!isObject(o)) { o = {}; } @@ -59048,7 +62328,7 @@ function hasOwn(obj, key) { /***/ }), -/* 530 */ +/* 550 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59068,136 +62348,13 @@ module.exports = function isExtendable(val) { /***/ }), -/* 531 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var extend = __webpack_require__(529); - -/** - * 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 - */ - -function toRegex(pattern, options) { - return new RegExp(toRegex.create(pattern, options)); -} - -/** - * 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` - * @return {String} - * @api public - */ - -toRegex.create = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } - - var opts = extend({}, options); - if (opts && opts.contains === true) { - opts.strictNegate = false; - } - - var open = opts.strictOpen !== false ? '^' : ''; - var close = opts.strictClose !== false ? '$' : ''; - var endChar = opts.endChar ? opts.endChar : '+'; - var str = pattern; - - if (opts && opts.strictNegate === false) { - str = '(?:(?!(?:' + pattern + ')).)' + endChar; - } else { - str = '(?:(?!^(?:' + pattern + ')$).)' + endChar; - } - - return open + str + close; -}; - -/** - * Expose `toRegex` - */ - -module.exports = toRegex; - - -/***/ }), -/* 532 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * array-unique <https://github.com/jonschlinkert/array-unique> - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - - - -module.exports = function unique(arr) { - if (!Array.isArray(arr)) { - throw new TypeError('array-unique expects an array.'); - } - - var len = arr.length; - var i = -1; - - while (i++ < len) { - var j = i + 1; - - for (; j < arr.length; ++j) { - if (arr[i] === arr[j]) { - arr.splice(j--, 1); - } - } - } - return arr; -}; - -module.exports.immutable = function uniqueImmutable(arr) { - if (!Array.isArray(arr)) { - throw new TypeError('array-unique expects an array.'); - } - - var arrLen = arr.length; - var newArr = new Array(arrLen); - - for (var i = 0; i < arrLen; i++) { - newArr[i] = arr[i]; - } - - return module.exports(newArr); -}; - - -/***/ }), -/* 533 */ +/* 551 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(534); +var utils = __webpack_require__(552); module.exports = function(braces, options) { braces.compiler @@ -59480,25 +62637,25 @@ function hasQueue(node) { /***/ }), -/* 534 */ +/* 552 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var splitString = __webpack_require__(535); +var splitString = __webpack_require__(553); var utils = module.exports; /** * Module dependencies */ -utils.extend = __webpack_require__(529); -utils.flatten = __webpack_require__(541); -utils.isObject = __webpack_require__(539); -utils.fillRange = __webpack_require__(542); -utils.repeat = __webpack_require__(547); -utils.unique = __webpack_require__(532); +utils.extend = __webpack_require__(549); +utils.flatten = __webpack_require__(556); +utils.isObject = __webpack_require__(534); +utils.fillRange = __webpack_require__(557); +utils.repeat = __webpack_require__(563); +utils.unique = __webpack_require__(548); utils.define = function(obj, key, val) { Object.defineProperty(obj, key, { @@ -59830,7 +62987,7 @@ utils.escapeRegex = function(str) { /***/ }), -/* 535 */ +/* 553 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -59843,7 +63000,7 @@ utils.escapeRegex = function(str) { -var extend = __webpack_require__(536); +var extend = __webpack_require__(554); module.exports = function(str, options, fn) { if (typeof str !== 'string') { @@ -60008,14 +63165,14 @@ function keepEscaping(opts, str, idx) { /***/ }), -/* 536 */ +/* 554 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(537); -var assignSymbols = __webpack_require__(540); +var isExtendable = __webpack_require__(555); +var assignSymbols = __webpack_require__(544); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -60075,7 +63232,7 @@ function isEnum(obj, key) { /***/ }), -/* 537 */ +/* 555 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60088,7 +63245,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(538); +var isPlainObject = __webpack_require__(543); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -60096,117 +63253,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 538 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-plain-object <https://github.com/jonschlinkert/is-plain-object> - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var isObject = __webpack_require__(539); - -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; - } - - // Most likely a plain Object - return true; -}; - - -/***/ }), -/* 539 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * isobject <https://github.com/jonschlinkert/isobject> - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -module.exports = function isObject(val) { - return val != null && typeof val === 'object' && Array.isArray(val) === false; -}; - - -/***/ }), -/* 540 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * assign-symbols <https://github.com/jonschlinkert/assign-symbols> - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed 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.'); - } - - if (typeof objects === 'undefined' || typeof Symbol === 'undefined') { - return receiver; - } - - if (typeof Object.getOwnPropertySymbols !== 'function') { - return receiver; - } - - var isEnumerable = Object.prototype.propertyIsEnumerable; - var target = Object(receiver); - var len = arguments.length, i = 0; - - while (++i < len) { - var provider = Object(arguments[i]); - var names = Object.getOwnPropertySymbols(provider); - - for (var j = 0; j < names.length; j++) { - var key = names[j]; - - if (isEnumerable.call(provider, key)) { - target[key] = provider[key]; - } - } - } - return target; -}; - - -/***/ }), -/* 541 */ +/* 556 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60235,7 +63282,7 @@ function flat(arr, res) { /***/ }), -/* 542 */ +/* 557 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60248,11 +63295,11 @@ function flat(arr, res) { -var util = __webpack_require__(111); -var isNumber = __webpack_require__(543); -var extend = __webpack_require__(529); -var repeat = __webpack_require__(545); -var toRegex = __webpack_require__(546); +var util = __webpack_require__(112); +var isNumber = __webpack_require__(558); +var extend = __webpack_require__(549); +var repeat = __webpack_require__(561); +var toRegex = __webpack_require__(562); /** * Return a range of numbers or letters. @@ -60450,7 +63497,7 @@ module.exports = fillRange; /***/ }), -/* 543 */ +/* 558 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60463,7 +63510,7 @@ module.exports = fillRange; -var typeOf = __webpack_require__(544); +var typeOf = __webpack_require__(559); module.exports = function isNumber(num) { var type = typeOf(num); @@ -60479,10 +63526,10 @@ module.exports = function isNumber(num) { /***/ }), -/* 544 */ +/* 559 */ /***/ (function(module, exports, __webpack_require__) { -var isBuffer = __webpack_require__(526); +var isBuffer = __webpack_require__(560); var toString = Object.prototype.toString; /** @@ -60595,13 +63642,40 @@ module.exports = function kindOf(val) { return 'float64array'; } - // must be a plain object - return 'object'; -}; + // must be a plain object + return 'object'; +}; + + +/***/ }), +/* 560 */ +/***/ (function(module, exports) { + +/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh <https://feross.org> + * @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)) +} /***/ }), -/* 545 */ +/* 561 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60678,7 +63752,7 @@ function repeat(str, num) { /***/ }), -/* 546 */ +/* 562 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -60691,8 +63765,8 @@ function repeat(str, num) { -var repeat = __webpack_require__(545); -var isNumber = __webpack_require__(543); +var repeat = __webpack_require__(561); +var isNumber = __webpack_require__(558); var cache = {}; function toRegexRange(min, max, options) { @@ -60979,7 +64053,7 @@ module.exports = toRegexRange; /***/ }), -/* 547 */ +/* 563 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -61004,14 +64078,14 @@ module.exports = function repeat(ele, num) { /***/ }), -/* 548 */ +/* 564 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var Node = __webpack_require__(549); -var utils = __webpack_require__(534); +var Node = __webpack_require__(565); +var utils = __webpack_require__(552); /** * Braces parsers @@ -61269,157 +64343,653 @@ module.exports = function(braces, options) { var prev = this.prev(); var val = m[0]; - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } + if (isInside && prev.type === 'brace') { + prev.text = prev.text || ''; + prev.text += val; + } + + 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 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); +} + + +/***/ }), +/* 565 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var isObject = __webpack_require__(534); +var define = __webpack_require__(566); +var utils = __webpack_require__(567); +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; + } + + define(this, 'parent', parent); + define(this, 'isNode', true); + define(this, 'expect', null); + + 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; + } +} + +/** + * 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 + */ + +Node.isNode = function(node) { + return utils.isNode(node); +}; + +/** + * 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 + */ + +Node.prototype.define = function(name, val) { + define(this, name, val); + return this; +}; + +/** + * 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 + */ + +Node.prototype.isEmpty = function(fn) { + return utils.isEmpty(this, fn); +}; + +/** + * 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 + */ + +Node.prototype.push = function(node) { + assert(Node.isNode(node), 'expected node to be an instance of Node'); + define(node, 'parent', this); + + this.nodes = this.nodes || []; + return this.nodes.push(node); +}; + +/** + * 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 + */ + +Node.prototype.unshift = function(node) { + assert(Node.isNode(node), 'expected node to be an instance of Node'); + define(node, 'parent', this); + + this.nodes = this.nodes || []; + return this.nodes.unshift(node); +}; + +/** + * 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 + */ + +Node.prototype.pop = function() { + return this.nodes && this.nodes.pop(); +}; + +/** + * 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 + */ + +Node.prototype.shift = function() { + return this.nodes && this.nodes.shift(); +}; + +/** + * Remove `node` from `node.nodes`. + * + * ```js + * node.remove(childNode); + * ``` + * @param {Object} `node` + * @return {Object} Returns the removed node. + * @api public + */ + +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; +}; + +/** + * 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 + */ + +Node.prototype.find = function(type) { + return utils.findNode(this.nodes, type); +}; + +/** + * 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 + */ + +Node.prototype.isType = function(type) { + return utils.isType(this, type); +}; + +/** + * 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 + */ + +Node.prototype.hasType = function(type) { + return utils.hasType(this, type); +}; + +/** + * 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 + */ + +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 + */ + +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 + */ + +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; + } +}); + +/** + * 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; + } +}); + +/** + * 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 + */ - return pos(new Node({ - type: 'text', - multiplier: 0, - val: val - })); - }) +Object.defineProperty(Node.prototype, 'first', { + get: function() { + return this.nodes ? this.nodes[0] : null; + } +}); - /** - * Text - */ +/** + * 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 + */ - .set('text', function() { - var isInside = this.isInside('brace'); - var pos = this.position(); - var m = this.match(/^((?!\\)[^${}[\]])+/); - if (!m) return; +Object.defineProperty(Node.prototype, 'last', { + get: function() { + return this.nodes ? utils.last(this.nodes) : null; + } +}); - var prev = this.prev(); - var val = m[0]; +/** + * 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 + */ - if (isInside && prev.type === 'brace') { - prev.text = prev.text || ''; - prev.text += val; - } +Object.defineProperty(Node.prototype, 'scope', { + get: function() { + if (this.isScope !== true) { + return this.parent ? this.parent.scope : this; + } + return this; + } +}); - var node = pos(new Node({ - type: 'text', - multiplier: 1, - val: val - })); +/** + * Get own property names from Node prototype, but only the + * first time `Node` is instantiated + */ - return concatNodes.call(this, pos, node, prev, options); - }); -}; +function lazyKeys() { + if (!ownNames) { + ownNames = Object.getOwnPropertyNames(Node.prototype); + } +} /** - * Returns true if the character is an extglob character. + * Simplified assertion. Throws an error is `val` is falsey. */ -function isExtglobChar(ch) { - return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+'; +function assert(val, message) { + if (!val) throw new Error(message); } /** - * Combine text nodes, and calculate empty sets (`{,,}`) - * @param {Function} `pos` Function to calculate node position - * @param {Object} `node` AST node - * @return {Object} + * Expose `Node` */ -function concatNodes(pos, node, parent, options) { - node.orig = node.val; - var prev = this.prev(); - var last = utils.last(prev.nodes); - var isEscaped = false; +exports = module.exports = Node; - if (node.val.length > 1) { - var a = node.val.charAt(0); - var b = node.val.slice(-1); - isEscaped = (a === '"' && b === '"') - || (a === "'" && b === "'") - || (a === '`' && b === '`'); - } +/***/ }), +/* 566 */ +/***/ (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 <https://github.com/jonschlinkert/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__(535); + +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 + }); +}; /***/ }), -/* 549 */ +/* 567 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isObject = __webpack_require__(539); -var define = __webpack_require__(550); -var utils = __webpack_require__(557); -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; - } - - define(this, 'parent', parent); - define(this, 'isNode', true); - define(this, 'expect', null); - - 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; - } -} +var typeOf = __webpack_require__(559); +var utils = module.exports; /** * Returns true if the given value is a node. @@ -61427,3874 +64997,3545 @@ function Node(val, type, parent) { * ```js * var Node = require('snapdragon-node'); * var node = new Node({type: 'foo'}); - * console.log(Node.isNode(node)); //=> true - * console.log(Node.isNode({})); //=> false + * console.log(utils.isNode(node)); //=> true + * console.log(utils.isNode({})); //=> false * ``` - * @param {Object} `node` + * @param {Object} `node` Instance of [snapdragon-node][] * @returns {Boolean} * @api public */ -Node.isNode = function(node) { - return utils.isNode(node); +utils.isNode = function(node) { + return typeOf(node) === 'object' && node.isNode === true; }; /** - * Define a non-enumberable property on the node instance. - * Useful for adding properties that shouldn't be extended - * or visible during debugging. + * Emit an empty string for the given `node`. * * ```js - * var node = new Node(); - * node.define('foo', 'something non-enumerable'); + * // do nothing for beginning-of-string + * snapdragon.compiler.set('bos', utils.noop); * ``` - * @param {String} `name` - * @param {any} `val` - * @return {Object} returns the node instance + * @param {Object} `node` Instance of [snapdragon-node][] + * @returns {undefined} * @api public */ -Node.prototype.define = function(name, val) { - define(this, name, val); - return this; +utils.noop = function(node) { + append(this, '', node); }; /** - * Returns true if `node.val` is an empty string, or `node.nodes` does - * not contain any non-empty text nodes. + * Appdend `node.val` to `compiler.output`, exactly as it was created + * by the parser. * * ```js - * var node = new Node({type: 'text'}); - * node.isEmpty(); //=> true - * node.val = 'foo'; - * node.isEmpty(); //=> false + * snapdragon.compiler.set('text', utils.identity); * ``` - * @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} + * @param {Object} `node` Instance of [snapdragon-node][] + * @returns {undefined} * @api public */ -Node.prototype.isEmpty = function(fn) { - return utils.isEmpty(this, fn); +utils.identity = function(node) { + append(this, node.val, node); }; /** - * Given node `foo` and node `bar`, push node `bar` onto `foo.nodes`, and - * set `foo` as `bar.parent`. + * 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 - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * foo.push(bar); + * snapdragon.compiler + * .set('i', function(node) { + * this.mapVisit(node); + * }) + * .set('i.open', utils.append('<i>')) + * .set('i.close', utils.append('</i>')) * ``` - * @param {Object} `node` - * @return {Number} Returns the length of `node.nodes` + * @param {Object} `node` Instance of [snapdragon-node][] + * @returns {Function} Returns a compiler middleware function. * @api public */ -Node.prototype.push = function(node) { - assert(Node.isNode(node), 'expected node to be an instance of Node'); - define(node, 'parent', this); - - this.nodes = this.nodes || []; - return this.nodes.push(node); +utils.append = function(val) { + return function(node) { + append(this, val, node); + }; }; /** - * Given node `foo` and node `bar`, unshift node `bar` onto `foo.nodes`, and - * set `foo` as `bar.parent`. + * 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 - * var foo = new Node({type: 'foo'}); - * var bar = new Node({type: 'bar'}); - * foo.unshift(bar); + * utils.toNoop(node); + * // convert `node.nodes` to the given value instead of deleting it + * utils.toNoop(node, []); * ``` - * @param {Object} `node` - * @return {Number} Returns the length of `node.nodes` + * @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 */ -Node.prototype.unshift = function(node) { - assert(Node.isNode(node), 'expected node to be an instance of Node'); - define(node, 'parent', this); - - this.nodes = this.nodes || []; - return this.nodes.unshift(node); +utils.toNoop = function(node, nodes) { + if (nodes) { + node.nodes = nodes; + } else { + delete node.nodes; + node.type = 'text'; + node.val = ''; + } }; /** - * Pop a node from `node.nodes`. + * 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 - * 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 + * snapdragon.compiler.set('i', function(node) { + * utils.visit(node, function(childNode) { + * // do stuff with "childNode" + * return childNode; + * }); + * }); * ``` - * @return {Number} Returns the popped `node` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Function} `fn` + * @return {Object} returns the node after recursively visiting all child nodes. * @api public */ -Node.prototype.pop = function() { - return this.nodes && this.nodes.pop(); +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; }; /** - * Shift a node from `node.nodes`. + * 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. * * ```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 + * snapdragon.compiler.set('i', function(node) { + * utils.mapVisit(node, function(childNode) { + * // do stuff with "childNode" + * return childNode; + * }); + * }); * ``` - * @return {Object} Returns the shifted `node` + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {Object} `options` + * @param {Function} `fn` + * @return {Object} returns the node * @api public */ -Node.prototype.shift = function() { - return this.nodes && this.nodes.shift(); +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'); + + for (var i = 0; i < node.nodes.length; i++) { + utils.visit(node.nodes[i], fn); + } + return node; }; /** - * Remove `node` from `node.nodes`. + * Unshift an `*.open` node onto `node.nodes`. * * ```js - * node.remove(childNode); + * 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` - * @return {Object} Returns the removed node. + * @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 */ -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); +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 (typeof val === 'function') { + filter = val; + val = ''; } - return null; + + 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); + } + return open; }; /** - * 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. + * Push a `*.close` node onto `node.nodes`. * * ```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) + * 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 {String} `type` - * @return {Object} Returns a child node or undefined. + * @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 */ -Node.prototype.find = function(type) { - return utils.findNode(this.nodes, type); +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; }; /** - * Return true if the node is the given `type`. + * Wraps the given `node` with `*.open` and `*.close` nodes. * - * ```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} + * @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 */ -Node.prototype.isType = function(type) { - return utils.isType(this, type); +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; }; /** - * Return true if the `node.nodes` has the given `type`. + * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. * * ```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 + * 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 {String} `type` - * @return {Boolean} + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Object} Returns the child node * @api public */ -Node.prototype.hasType = function(type) { - return utils.hasType(this, type); +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; }; /** - * Get the siblings array, or `null` if it doesn't exist. + * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. * * ```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 + * 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' * ``` - * @return {Array} + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {undefined} * @api public */ -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; - } -}); +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); +}; /** - * Get the node's current index from `node.parent.nodes`. - * This should always be correct, even when the parent adds nodes. + * 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 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 + * 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 * ``` - * @return {Number} + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. * @api public */ -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; +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 previous node from the siblings array or `null`. + * 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 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' + * 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 * ``` - * @return {Object} + * @param {Object} `parent` + * @param {Object} `node` Instance of [snapdragon-node][] + * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. * @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; +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(); +}; /** - * Get the siblings array, or `null` if it doesn't exist. + * Remove the specified `node` from `parent.nodes`. * * ```js + * var parent = new Node({type: 'abc'}); * 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 + * 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 * ``` - * @return {Object} + * @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 */ -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; - } +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; } -}); -/** - * 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 - */ + if (typeof parent.remove === 'function') { + return parent.remove(node); + } -Object.defineProperty(Node.prototype, 'first', { - get: function() { - return this.nodes ? this.nodes[0] : null; + var idx = parent.nodes.indexOf(node); + if (idx !== -1) { + return parent.nodes.splice(idx, 1); } -}); +}; /** - * Get the last node from `node.nodes`. + * Returns true if `node.type` matches the given `type`. Throws a + * `TypeError` if `node` is not an instance of `Node`. * * ```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' + * 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 * ``` - * @return {Object} The last node, or undefiend + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {String} `type` + * @return {Boolean} * @api public */ -Object.defineProperty(Node.prototype, 'last', { - get: function() { - return this.nodes ? utils.last(this.nodes) : null; +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'); + } } -}); +}; /** - * Get the last node from `node.nodes`. + * 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 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' + * 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 * ``` - * @return {Object} The last node, or undefiend + * @param {Object} `node` Instance of [snapdragon-node][] + * @param {String} `type` + * @return {Boolean} * @api public */ -Object.defineProperty(Node.prototype, 'scope', { - get: function() { - if (this.isScope !== true) { - return this.parent ? this.parent.scope : this; +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; } - return this; - } -}); - -/** - * Get own property names from Node prototype, but only the - * first time `Node` is instantiated - */ - -function lazyKeys() { - if (!ownNames) { - ownNames = Object.getOwnPropertyNames(Node.prototype); } -} - -/** - * Simplified assertion. Throws an error is `val` is falsey. - */ - -function assert(val, message) { - if (!val) throw new Error(message); -} + return false; +}; /** - * Expose `Node` - */ - -exports = module.exports = Node; - - -/***/ }), -/* 550 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * define-property <https://github.com/jonschlinkert/define-property> + * Returns the first node from `node.nodes` of the given `type` * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var isDescriptor = __webpack_require__(551); - -module.exports = function defineProperty(obj, prop, val) { - if (typeof obj !== 'object' && typeof obj !== 'function') { - throw new TypeError('expected an object or function.'); - } - - 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 - }); -}; - - -/***/ }), -/* 551 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-descriptor <https://github.com/jonschlinkert/is-descriptor> + * ```js + * var node = new Node({ + * type: 'foo', + * nodes: [ + * new Node({type: 'text', val: 'abc'}), + * new Node({type: 'text', val: 'xyz'}) + * ] + * }); * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var typeOf = __webpack_require__(552); -var isAccessor = __webpack_require__(553); -var isData = __webpack_require__(555); - -module.exports = function isDescriptor(obj, key) { - if (typeOf(obj) !== 'object') { - return false; - } - if ('get' in obj) { - return isAccessor(obj, key); - } - return isData(obj, key); -}; - - -/***/ }), -/* 552 */ -/***/ (function(module, exports) { - -var toString = Object.prototype.toString; - -module.exports = function kindOf(val) { - if (val === void 0) return 'undefined'; - if (val === null) return 'null'; - - 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'; - } - - 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'; - } - - if (isGeneratorObj(val)) { - return 'generator'; - } - - // 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 isArray(val) { - if (Array.isArray) return Array.isArray(val); - return val instanceof Array; -} - -function isError(val) { - return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); -} - -function isDate(val) { - if (val instanceof Date) return true; - return typeof val.toDateString === 'function' - && typeof val.getDate === 'function' - && typeof val.setDate === 'function'; -} - -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 isGeneratorFn(name, val) { - return ctorName(name) === 'GeneratorFunction'; -} - -function isGeneratorObj(val) { - return typeof val.throw === 'function' - && typeof val.return === 'function' - && typeof val.next === 'function'; -} + * 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 isArguments(val) { - try { - if (typeof val.length === 'number' && typeof val.callee === 'function') { - return true; - } - } catch (err) { - if (err.message.indexOf('callee') !== -1) { - return true; +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 false; -} +}; /** - * If you need to support Safari 5-7 (8-10 yr-old browser), - * take a look at https://github.com/feross/is-buffer + * 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 */ -function isBuffer(val) { - if (val.constructor && typeof val.constructor.isBuffer === 'function') { - return val.constructor.isBuffer(val); +utils.findNode = function(nodes, type) { + if (!Array.isArray(nodes)) { + return null; } - return false; -} - - -/***/ }), -/* 553 */ -/***/ (function(module, exports, __webpack_require__) { + if (typeof type === 'number') { + return nodes[type]; + } + return utils.firstOfType(nodes, type); +}; -"use strict"; -/*! - * is-accessor-descriptor <https://github.com/jonschlinkert/is-accessor-descriptor> +/** + * Returns true if the given node is 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'}); + * 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 */ - - -var typeOf = __webpack_require__(554); - -// accessor descriptor properties -var accessor = { - get: 'function', - set: 'function', - configurable: 'boolean', - enumerable: 'boolean' +utils.isOpen = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + return node.type.slice(-5) === '.open'; }; -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; - } +/** + * 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 + */ - for (var key in obj) { - if (!accessor.hasOwnProperty(key)) { - continue; - } +utils.isClose = function(node) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); + return node.type.slice(-6) === '.close'; +}; - if (typeOf(obj[key]) === accessor[key]) { - continue; - } +/** + * 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 + */ - if (typeof obj[key] !== 'undefined') { - return false; - } +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 true; -} - -function has(obj, key) { - return {}.hasOwnProperty.call(obj, key); -} + return false; +}; /** - * Expose `isAccessorDescriptor` + * 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 */ -module.exports = isAccessorDescriptor; - - -/***/ }), -/* 554 */ -/***/ (function(module, exports) { - -var toString = Object.prototype.toString; - -module.exports = function kindOf(val) { - if (val === void 0) return 'undefined'; - if (val === null) return 'null'; - - 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'; +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; +}; - 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'; +/** + * 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 + */ - // Set, Map, WeakSet, WeakMap - case 'WeakMap': return 'weakmap'; - case 'WeakSet': return 'weakset'; - case 'Map': return 'map'; - case 'Set': return 'set'; +utils.hasOpenAndClose = function(node) { + return utils.hasOpen(node) && utils.hasClose(node); +}; - // 8-bit typed arrays - case 'Int8Array': return 'int8array'; - case 'Uint8Array': return 'uint8array'; - case 'Uint8ClampedArray': return 'uint8clampedarray'; +/** + * 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`. + * + * ```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 + */ - // 16-bit typed arrays - case 'Int16Array': return 'int16array'; - case 'Uint16Array': return 'uint16array'; +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'); - // 32-bit typed arrays - case 'Int32Array': return 'int32array'; - case 'Uint32Array': return 'uint32array'; - case 'Float32Array': return 'float32array'; - case 'Float64Array': return 'float64array'; - } + var type = node.parent + ? node.parent.type + : node.type.replace(/\.open$/, ''); - if (isGeneratorObj(val)) { - return 'generator'; + if (!state.hasOwnProperty('inside')) { + state.inside = {}; } - - // 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'; + if (!state.inside.hasOwnProperty(type)) { + state.inside[type] = []; } - // other - return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); + var arr = state.inside[type]; + arr.push(node); + return arr; }; -function ctorName(val) { - return typeof val.constructor === 'function' ? val.constructor.name : null; -} - -function isArray(val) { - if (Array.isArray) return Array.isArray(val); - return val instanceof Array; -} +/** + * 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`. + * + * ```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 + */ -function isError(val) { - return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); -} +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'); -function isDate(val) { - if (val instanceof Date) return true; - return typeof val.toDateString === 'function' - && typeof val.getDate === 'function' - && typeof val.setDate === 'function'; -} + var type = node.parent + ? node.parent.type + : node.type.replace(/\.close$/, ''); -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'; -} + if (state.inside.hasOwnProperty(type)) { + return state.inside[type].pop(); + } +}; -function isGeneratorFn(name, val) { - return ctorName(name) === 'GeneratorFunction'; -} +/** + * 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'}); + * 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 isGeneratorObj(val) { - return typeof val.throw === 'function' - && typeof val.return === 'function' - && typeof val.next === 'function'; -} +utils.isEmpty = function(node, fn) { + assert(utils.isNode(node), 'expected node to be an instance of Node'); -function isArguments(val) { - try { - if (typeof val.length === 'number' && typeof val.callee === 'function') { + if (!Array.isArray(node.nodes)) { + if (node.type !== 'text') { return true; } - } catch (err) { - if (err.message.indexOf('callee') !== -1) { - return true; + if (typeof fn === 'function') { + return fn(node, node.parent); } + return !utils.trim(node.val); } - return false; -} - -/** - * 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); + 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 false; -} + return true; +}; -/***/ }), -/* 555 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-data-descriptor <https://github.com/jonschlinkert/is-data-descriptor> +/** + * Returns true if the `state.inside` stack for the given type exists + * and has one or more nodes on it. * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. + * ```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'); - -var typeOf = __webpack_require__(556); - -module.exports = function isDataDescriptor(obj, prop) { - // data descriptor properties - var data = { - configurable: 'boolean', - enumerable: 'boolean', - writable: 'boolean' - }; - - if (typeOf(obj) !== 'object') { + if (!state.hasOwnProperty('inside')) { return false; } - if (typeof prop === 'string') { - var val = Object.getOwnPropertyDescriptor(obj, prop); - return typeof val !== 'undefined'; + if (!state.inside.hasOwnProperty(type)) { + return false; } - if (!('value' in obj) && !('writable' in obj)) { + return state.inside[type].length > 0; +}; + +/** + * 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 + */ + +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; + } + } return false; } - for (var key in obj) { - if (key === 'value') continue; + var parent = node.parent; + if (typeof type === 'string') { + return (parent && parent.type === type) || utils.isInsideType(state, type); + } - if (!data.hasOwnProperty(key)) { - continue; + if (typeOf(type) === 'regexp') { + if (parent && parent.type && type.test(parent.type)) { + return true; } - if (typeOf(obj[key]) === data[key]) { - continue; - } + 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 (typeof obj[key] !== 'undefined') { - return false; + if (Array.isArray(val) && val.length !== 0 && type.test(key)) { + return true; + } } } - 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 + */ -/***/ }), -/* 556 */ -/***/ (function(module, exports) { - -var toString = Object.prototype.toString; +utils.last = function(arr, n) { + return arr[arr.length - (n || 1)]; +}; -module.exports = function kindOf(val) { - if (val === void 0) return 'undefined'; - if (val === null) return 'null'; +/** + * 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 + */ - 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'; +utils.arrayify = function(val) { + if (typeof val === 'string' && val !== '') { + return [val]; } - - 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'; + if (!Array.isArray(val)) { + return []; } + return val; +}; - if (isGeneratorObj(val)) { - return 'generator'; - } +/** + * 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 + */ - // 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'; - } +utils.stringify = function(val) { + return utils.arrayify(val).join(','); +}; - // other - return type.slice(8, -1).toLowerCase().replace(/\s/g, ''); +/** + * 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 + */ + +utils.trim = function(str) { + return typeof str === 'string' ? str.trim() : ''; }; -function ctorName(val) { - return typeof val.constructor === 'function' ? val.constructor.name : null; -} +/** + * Return true if val is an object + */ -function isArray(val) { - if (Array.isArray) return Array.isArray(val); - return val instanceof Array; +function isObject(val) { + return typeOf(val) === 'object'; } -function isError(val) { - return val instanceof Error || (typeof val.message === 'string' && val.constructor && typeof val.constructor.stackTraceLimit === 'number'); -} +/** + * Return true if val is a string + */ -function isDate(val) { - if (val instanceof Date) return true; - return typeof val.toDateString === 'function' - && typeof val.getDate === 'function' - && typeof val.setDate === 'function'; +function isString(val) { + return typeof val === 'string'; } -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'; -} +/** + * Return true if val is a function + */ -function isGeneratorFn(name, val) { - return ctorName(name) === 'GeneratorFunction'; +function isFunction(val) { + return typeof val === 'function'; } -function isGeneratorObj(val) { - return typeof val.throw === 'function' - && typeof val.return === 'function' - && typeof val.next === 'function'; +/** + * Return true if val is an array + */ + +function isArray(val) { + return Array.isArray(val); } -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; - } +/** + * Shim to ensure the `.append` methods work with any version of snapdragon + */ + +function append(compiler, val, node) { + if (typeof compiler.append !== 'function') { + return compiler.emit(val, node); } - return false; + return compiler.append(val, node); } /** - * If you need to support Safari 5-7 (8-10 yr-old browser), - * take a look at https://github.com/feross/is-buffer + * Simplified assertion. Throws an error is `val` is falsey. */ -function isBuffer(val) { - if (val.constructor && typeof val.constructor.isBuffer === 'function') { - return val.constructor.isBuffer(val); - } - return false; +function assert(val, message) { + if (!val) throw new Error(message); } /***/ }), -/* 557 */ +/* 568 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(544); -var utils = module.exports; +var extend = __webpack_require__(549); +var Snapdragon = __webpack_require__(569); +var compilers = __webpack_require__(551); +var parsers = __webpack_require__(564); +var utils = __webpack_require__(552); /** - * 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 + * Customize Snapdragon parser and renderer */ -utils.isNode = function(node) { - return typeOf(node) === 'object' && node.isNode === true; -}; +function Braces(options) { + this.options = extend({}, options); +} /** - * 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 + * Initialize braces */ -utils.noop = function(node) { - append(this, '', node); -}; +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; -/** - * 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 - */ + compilers(this.snapdragon, opts); + parsers(this.snapdragon, opts); -utils.identity = function(node) { - append(this, node.val, node); + /** + * 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; + }); }; /** - * 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('<i>')) - * .set('i.close', utils.append('</i>')) - * ``` - * @param {Object} `node` Instance of [snapdragon-node][] - * @returns {Function} Returns a compiler middleware function. - * @api public + * Decorate `.parse` method */ -utils.append = function(val) { - return function(node) { - append(this, val, node); - }; +Braces.prototype.parse = function(ast, options) { + if (ast && typeof ast === 'object' && ast.nodes) return ast; + this.init(options); + return this.snapdragon.parse(ast, options); }; /** - * 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 + * Decorate `.compile` method */ -utils.toNoop = function(node, nodes) { - if (nodes) { - node.nodes = nodes; +Braces.prototype.compile = function(ast, options) { + if (typeof ast === 'string') { + ast = this.parse(ast, options); } else { - delete node.nodes; - node.type = 'text'; - node.val = ''; + this.init(options); } + return this.snapdragon.compile(ast, options); }; /** - * 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 + * Expand */ -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; +Braces.prototype.expand = function(pattern) { + var ast = this.parse(pattern, {expand: true}); + return this.compile(ast, {expand: true}); }; /** - * 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. - * - * ```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 + * Optimize */ -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'); - - for (var i = 0; i < node.nodes.length; i++) { - utils.visit(node.nodes[i], fn); - } - return node; +Braces.prototype.optimize = function(pattern) { + var ast = this.parse(pattern, {optimize: true}); + return this.compile(ast, {optimize: true}); }; /** - * 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 + * Expose `Braces` */ -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 (typeof val === 'function') { - filter = val; - val = ''; - } +module.exports = Braces; - 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); - } - return open; -}; -/** - * 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 - */ +/***/ }), +/* 569 */ +/***/ (function(module, exports, __webpack_require__) { -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'); +"use strict"; - 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; -}; +var Base = __webpack_require__(570); +var define = __webpack_require__(596); +var Compiler = __webpack_require__(606); +var Parser = __webpack_require__(635); +var utils = __webpack_require__(615); +var regexCache = {}; +var cache = {}; /** - * Wraps the given `node` with `*.open` and `*.close` nodes. + * Create a new instance of `Snapdragon` with the given `options`. * - * @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 + * ```js + * var snapdragon = new Snapdragon(); + * ``` + * + * @param {Object} `options` * @api public */ -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'); +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); - utils.addOpen(node, Node, filter); - utils.addClose(node, Node, filter); - return node; -}; + 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; + } + }); +} /** - * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. + * 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 - * 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' + * 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 {Object} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Object} Returns the child node + * @param {String} `type` + * @param {RegExp|Function} `regex` + * @return {Object} Returns the parser instance for chaining * @api public */ -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; +Snapdragon.prototype.capture = function() { + return this.parser.capture.apply(this.parser, arguments); }; /** - * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. + * Register a plugin `fn`. * * ```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' + * 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} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {undefined} + * @param {Object} `fn` * @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'); - - node.define('parent', parent); - parent.nodes = parent.nodes || []; - parent.nodes.unshift(node); +Snapdragon.prototype.use = function(fn) { + fn.call(this, this); + return this; }; /** - * 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`. + * Parse the given `str`. * * ```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 + * var snapdragon = new Snapdgragon([options]); + * // register parsers + * snapdragon.parser.use(function() {}); + * + * // parse + * var ast = snapdragon.parse('foo/bar'); + * console.log(ast); * ``` - * @param {Object} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. + * @param {String} `str` + * @param {Object} `options` Set `options.sourcemap` to true to enable source maps. + * @return {Object} Returns an AST. * @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(); - } - return node.nodes && node.nodes.pop(); +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; }; /** - * 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`. + * Compile the given `AST`. * * ```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 + * 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} `parent` - * @param {Object} `node` Instance of [snapdragon-node][] - * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. + * @param {Object} `ast` + * @param {Object} `options` + * @return {Object} Returns an object with an `output` property with the rendered string. * @api public */ -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(); +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; }; /** - * Remove the specified `node` from `parent.nodes`. - * - * ```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 + * Expose `Snapdragon` */ -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'); +module.exports = Snapdragon; - if (!parent.nodes) { - return null; - } +/** + * Expose `Parser` and `Compiler` + */ - if (typeof parent.remove === 'function') { - return parent.remove(node); - } +module.exports.Compiler = Compiler; +module.exports.Parser = Parser; - var idx = parent.nodes.indexOf(node); - if (idx !== -1) { - return parent.nodes.splice(idx, 1); - } -}; + +/***/ }), +/* 570 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var util = __webpack_require__(112); +var define = __webpack_require__(571); +var CacheBase = __webpack_require__(572); +var Emitter = __webpack_require__(573); +var isObject = __webpack_require__(534); +var merge = __webpack_require__(590); +var pascal = __webpack_require__(593); +var cu = __webpack_require__(594); /** - * 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 + * Optionally define a custom `cache` namespace to use. */ -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'); +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 + */ + + Base.prototype.define = function(key, val) { + if (isObject(key)) { + return this.visit('define', key); + } + define(this, key, val); + return this; + }; + + /** + * 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; + }; + + /** + * Non-enumberable mixin array, used by the static [Base.mixin]() method. + */ + + 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; } - } -}; + }); -/** - * 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 - */ + /** + * 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 + */ -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; - } - } - return false; -}; + define(Base, 'use', function(fn) { + fns.push(fn); + return Base; + }); -/** - * 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 - */ + /** + * 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. + */ -utils.firstOfType = function(nodes, type) { - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i]; - if (utils.isType(node, type)) { - return node; + define(Base, 'run', function(obj, prop, arr) { + var len = arr.length, i = 0; + while (len--) { + obj[prop](arr[i++]); } - } -}; + return Base; + }); -/** - * 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 - */ + /** + * 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 + */ -utils.findNode = function(nodes, type) { - if (!Array.isArray(nodes)) { - return null; - } - if (typeof type === 'number') { - return nodes[type]; - } - return utils.firstOfType(nodes, type); -}; + define(Base, 'extend', cu.extend(Base, function(Ctor, Parent) { + Ctor.prototype.mixins = Ctor.prototype.mixins || []; -/** - * 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 - */ + define(Ctor, 'mixin', function(fn) { + var mixin = fn(Ctor.prototype, Ctor); + if (typeof mixin === 'function') { + Ctor.prototype.mixins.push(mixin); + } + return Ctor; + }); -utils.isOpen = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - return node.type.slice(-5) === '.open'; -}; + define(Ctor, 'mixins', function(Child) { + Base.run(Child, 'mixin', Ctor.prototype.mixins); + return Ctor; + }); -/** - * 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 - */ + Ctor.prototype.mixin = function(key, value) { + Ctor.prototype[key] = value; + return this; + }; + return Base; + })); -utils.isClose = function(node) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); - return node.type.slice(-6) === '.close'; -}; + /** + * 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 + */ -/** - * 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 - */ + define(Base, 'mixin', function(fn) { + var mixin = fn(Base.prototype, Base); + if (typeof mixin === 'function') { + Base.prototype.mixins.push(mixin); + } + return Base; + }); -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; -}; + /** + * 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 + */ -/** - * 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 - */ + define(Base, 'mixins', function(Child) { + Base.run(Child, 'mixin', Base.prototype.mixins); + return Base; + }); -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; -}; + /** + * 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 `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 + * Expose `Base` with default settings */ -utils.hasOpenAndClose = function(node) { - return utils.hasOpen(node) && utils.hasClose(node); -}; +module.exports = namespace(); /** - * 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`. - * - * ```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 + * Allow users to define a namespace */ -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.namespace = namespace; - if (!state.hasOwnProperty('inside')) { - state.inside = {}; - } - if (!state.inside.hasOwnProperty(type)) { - state.inside[type] = []; - } - var arr = state.inside[type]; - arr.push(node); - return arr; -}; +/***/ }), +/* 571 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * 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`. +"use strict"; +/*! + * define-property <https://github.com/jonschlinkert/define-property> * - * ```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) 2015, 2017, Jon Schlinkert. + * Released 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(); - } -}; -/** - * 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'}); - * 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 - */ +var isDescriptor = __webpack_require__(535); -utils.isEmpty = function(node, fn) { - assert(utils.isNode(node), 'expected node to be an instance of Node'); +module.exports = function defineProperty(obj, prop, val) { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError('expected an object or function.'); + } - 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); + if (typeof prop !== 'string') { + throw new TypeError('expected `prop` to be a string.'); } - 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; - } + if (isDescriptor(val) && ('set' in val || 'get' in val)) { + return Object.defineProperty(obj, prop, val); } - return true; + return Object.defineProperty(obj, prop, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); }; -/** - * 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'); +/***/ }), +/* 572 */ +/***/ (function(module, exports, __webpack_require__) { - if (!state.hasOwnProperty('inside')) { - return false; - } +"use strict"; - if (!state.inside.hasOwnProperty(type)) { - return false; - } - return state.inside[type].length > 0; -}; +var isObject = __webpack_require__(534); +var Emitter = __webpack_require__(573); +var visit = __webpack_require__(574); +var toPath = __webpack_require__(577); +var union = __webpack_require__(578); +var del = __webpack_require__(582); +var get = __webpack_require__(580); +var has = __webpack_require__(587); +var set = __webpack_require__(581); /** - * Returns true if `node` is either a child or grand-child of the given `type`, - * or `state.inside[type]` is a non-empty array. + * Create a `Cache` constructor that when instantiated will + * store values on the given `prop`. * * ```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 + * var Cache = require('cache-base').namespace('data'); + * var cache = new Cache(); + * + * cache.set('foo', 'bar'); + * //=> {data: {foo: 'bar'}} * ``` - * @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} + * @param {String} `prop` The property name to use for storing values. + * @return {Function} Returns a custom `Cache` constructor * @api public */ -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'); +function namespace(prop) { - if (Array.isArray(type)) { - for (var i = 0; i < type.length; i++) { - if (utils.isInside(state, node, type[i])) { - return true; - } + /** + * 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); } - return false; } - var parent = node.parent; - if (typeof type === 'string') { - return (parent && parent.type === type) || utils.isInsideType(state, type); - } + /** + * Inherit Emitter + */ - if (typeOf(type) === 'regexp') { - if (parent && parent.type && type.test(parent.type)) { - return true; + 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; + }; - 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]; + /** + * 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 + */ - if (Array.isArray(val) && val.length !== 0 && type.test(key)) { - return true; - } + Cache.prototype.union = function(key, val) { + if (Array.isArray(key) && arguments.length === 2) { + key = toPath(key); } - } - return false; -}; + var ctx = prop ? this[prop] : this; + union(ctx, key, arrayify(val)); + this.emit('union', val); + return this; + }; -/** - * 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 - */ + /** + * 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 + */ -utils.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; + Cache.prototype.get = function(key) { + key = toPath(arguments); -/** - * 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 - */ + var ctx = prop ? this[prop] : this; + var val = get(ctx, key); -utils.arrayify = function(val) { - if (typeof val === 'string' && val !== '') { - return [val]; - } - if (!Array.isArray(val)) { - return []; - } - return val; -}; + this.emit('get', key, val); + 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 - */ + /** + * 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 + */ -utils.stringify = function(val) { - return utils.arrayify(val).join(','); -}; + Cache.prototype.has = function(key) { + key = toPath(arguments); -/** - * 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 ctx = prop ? this[prop] : this; + var val = get(ctx, key); -utils.trim = function(str) { - return typeof str === 'string' ? str.trim() : ''; -}; + var has = typeof val !== 'undefined'; + this.emit('has', key, has); + return has; + }; -/** - * Return true if val is an object - */ + /** + * 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 + */ -function isObject(val) { - return typeOf(val) === 'object'; -} + 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; + }; -/** - * Return true if val is a string - */ + /** + * Reset the entire cache to an empty object. + * + * ```js + * app.clear(); + * ``` + * @api public + */ -function isString(val) { - return typeof val === 'string'; -} + Cache.prototype.clear = function() { + if (prop) { + this[prop] = {}; + } + }; -/** - * Return true if val is a function - */ + /** + * 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 + */ -function isFunction(val) { - return typeof val === 'function'; + Cache.prototype.visit = function(method, val) { + visit(this, method, val); + return this; + }; + + return Cache; } /** - * Return true if val is an array + * Cast val to an array */ -function isArray(val) { - return Array.isArray(val); +function arrayify(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; } /** - * Shim to ensure the `.append` methods work with any version of snapdragon + * Expose `Cache` */ -function append(compiler, val, node) { - if (typeof compiler.append !== 'function') { - return compiler.emit(val, node); - } - return compiler.append(val, node); -} +module.exports = namespace(); /** - * Simplified assertion. Throws an error is `val` is falsey. + * Expose `Cache.namespace` */ -function assert(val, message) { - if (!val) throw new Error(message); -} +module.exports.namespace = namespace; /***/ }), -/* 558 */ +/* 573 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; - - -var extend = __webpack_require__(529); -var Snapdragon = __webpack_require__(559); -var compilers = __webpack_require__(533); -var parsers = __webpack_require__(548); -var utils = __webpack_require__(534); + +/** + * 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; +}; -/** - * Customize Snapdragon parser and renderer - */ -function Braces(options) { - this.options = extend({}, options); -} +/***/ }), +/* 574 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Initialize braces +"use strict"; +/*! + * collection-visit <https://github.com/jonschlinkert/collection-visit> + * + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -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); -}; +var visit = __webpack_require__(575); +var mapVisit = __webpack_require__(576); -/** - * Decorate `.compile` method - */ +module.exports = function(collection, method, val) { + var result; -Braces.prototype.compile = function(ast, options) { - if (typeof ast === 'string') { - ast = this.parse(ast, options); + 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 { - this.init(options); + result = visit.apply(null, arguments); } - 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 - */ + if (typeof result !== 'undefined') { + return result; + } -Braces.prototype.optimize = function(pattern) { - var ast = this.parse(pattern, {optimize: true}); - return this.compile(ast, {optimize: true}); + return collection; }; -/** - * Expose `Braces` - */ - -module.exports = Braces; - /***/ }), -/* 559 */ +/* 575 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -var Base = __webpack_require__(560); -var define = __webpack_require__(521); -var Compiler = __webpack_require__(589); -var Parser = __webpack_require__(618); -var utils = __webpack_require__(598); -var regexCache = {}; -var cache = {}; - -/** - * Create a new instance of `Snapdragon` with the given `options`. - * - * ```js - * var snapdragon = new Snapdragon(); - * ``` +/*! + * object-visit <https://github.com/jonschlinkert/object-visit> * - * @param {Object} `options` - * @api public + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -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; - } - }); +var isObject = __webpack_require__(534); - Object.defineProperty(this, 'regex', { - get: function() { - return this.parser.regex; - } - }); -} +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.'); + } -/** - * Inherit Base - */ + if (typeof method !== 'string') { + throw new Error('object-visit expects `method` name to be a string'); + } -Base.extend(Snapdragon); + if (typeof thisArg[method] !== 'function') { + return thisArg; + } -/** - * 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 - */ + var args = [].slice.call(arguments, 3); + target = target || {}; -Snapdragon.prototype.capture = function() { - return this.parser.capture.apply(this.parser, arguments); + for (var key in target) { + var arr = [key, target[key]].concat(args); + thisArg[method].apply(thisArg, arr); + } + return thisArg; }; -/** - * 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; -}; +/***/ }), +/* 576 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * 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 - */ +"use strict"; -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; -}; +var util = __webpack_require__(112); +var visit = __webpack_require__(575); /** - * 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'); + * Map `visit` over an array of objects. * - * // 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 + * @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. */ -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; -}; +module.exports = function mapVisit(collection, method, val) { + if (isObject(val)) { + return visit.apply(null, arguments); + } -/** - * Expose `Snapdragon` - */ + if (!Array.isArray(val)) { + throw new TypeError('expected an array: ' + util.inspect(val)); + } -module.exports = Snapdragon; + var args = [].slice.call(arguments, 3); -/** - * Expose `Parser` and `Compiler` - */ + 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)); + } + } +}; -module.exports.Compiler = Compiler; -module.exports.Parser = Parser; +function isObject(val) { + return val && (typeof val === 'function' || (!Array.isArray(val) && typeof val === 'object')); +} /***/ }), -/* 560 */ +/* 577 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -var util = __webpack_require__(111); -var define = __webpack_require__(561); -var CacheBase = __webpack_require__(562); -var Emitter = __webpack_require__(563); -var isObject = __webpack_require__(539); -var merge = __webpack_require__(580); -var pascal = __webpack_require__(583); -var cu = __webpack_require__(584); - -/** - * Optionally define a custom `cache` namespace to use. +/*! + * to-object-path <https://github.com/jonschlinkert/to-object-path> + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -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 - */ +var typeOf = __webpack_require__(559); - Base.prototype.use = function(fn) { - fn.call(this, this); - return this; - }; +module.exports = function toPath(args) { + if (typeOf(args) !== 'arguments') { + args = arguments; + } + return filter(args).join('.'); +}; - /** - * 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 - */ +function filter(arr) { + var len = arr.length; + var idx = -1; + var res = []; - Base.prototype.define = function(key, val) { - if (isObject(key)) { - return this.visit('define', key); + 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); } - define(this, key, val); - return this; - }; + } + return res; +} - /** - * 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; - }; +/***/ }), +/* 578 */ +/***/ (function(module, exports, __webpack_require__) { - /** - * Non-enumberable mixin array, used by the static [Base.mixin]() method. - */ +"use strict"; - 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 - */ +var isObject = __webpack_require__(550); +var union = __webpack_require__(579); +var get = __webpack_require__(580); +var set = __webpack_require__(581); - Object.defineProperty(Base.prototype, 'base', { - configurable: true, - get: function() { - return this.parent ? this.parent.base : this; - } - }); +module.exports = function unionValue(obj, prop, value) { + if (!isObject(obj)) { + throw new TypeError('union-value expects the first argument to be an object.'); + } - /** - * 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 - */ + if (typeof prop !== 'string') { + throw new TypeError('union-value expects `prop` to be a string.'); + } - define(Base, 'use', function(fn) { - fns.push(fn); - return Base; - }); + var arr = arrayify(get(obj, prop)); + set(obj, prop, union(arr, arrayify(value))); + return obj; +}; - /** - * 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. - */ +function arrayify(val) { + if (val === null || typeof val === 'undefined') { + return []; + } + if (Array.isArray(val)) { + return val; + } + return [val]; +} - define(Base, 'run', function(obj, prop, arr) { - var len = arr.length, i = 0; - while (len--) { - obj[prop](arr[i++]); - } - 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 - */ +/***/ }), +/* 579 */ +/***/ (function(module, exports, __webpack_require__) { - define(Base, 'extend', cu.extend(Base, function(Ctor, Parent) { - Ctor.prototype.mixins = Ctor.prototype.mixins || []; +"use strict"; - define(Ctor, 'mixin', function(fn) { - var mixin = fn(Ctor.prototype, Ctor); - if (typeof mixin === 'function') { - Ctor.prototype.mixins.push(mixin); - } - return Ctor; - }); - define(Ctor, 'mixins', function(Child) { - Base.run(Child, 'mixin', Ctor.prototype.mixins); - return Ctor; - }); +module.exports = function union(init) { + if (!Array.isArray(init)) { + throw new TypeError('arr-union expects the first argument to be an array.'); + } - Ctor.prototype.mixin = function(key, value) { - Ctor.prototype[key] = value; - return this; - }; - return Base; - })); + var len = arguments.length; + var i = 0; - /** - * 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 - */ + while (++i < len) { + var arg = arguments[i]; + if (!arg) continue; - define(Base, 'mixin', function(fn) { - var mixin = fn(Base.prototype, Base); - if (typeof mixin === 'function') { - Base.prototype.mixins.push(mixin); + if (!Array.isArray(arg)) { + arg = [arg]; } - return Base; - }); - /** - * 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 - */ + for (var j = 0; j < arg.length; j++) { + var ele = arg[j]; - define(Base, 'mixins', function(Child) { - Base.run(Child, 'mixin', Base.prototype.mixins); - return Base; - }); + if (init.indexOf(ele) >= 0) { + continue; + } + init.push(ele); + } + } + return init; +}; - /** - * 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; -} +/***/ }), +/* 580 */ +/***/ (function(module, exports) { -/** - * Expose `Base` with default settings +/*! + * get-value <https://github.com/jonschlinkert/get-value> + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -module.exports = namespace(); +module.exports = function(obj, prop, a, b, c) { + if (!isObject(obj) || !prop) { + return obj; + } -/** - * Allow users to define a namespace - */ + prop = toString(prop); -module.exports.namespace = namespace; + // 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; +} /***/ }), -/* 561 */ +/* 581 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * define-property <https://github.com/jonschlinkert/define-property> + * set-value <https://github.com/jonschlinkert/set-value> * - * Copyright (c) 2015, 2017, Jon Schlinkert. + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. * Released under the MIT License. */ -var isDescriptor = __webpack_require__(551); +var split = __webpack_require__(553); +var extend = __webpack_require__(549); +var isPlainObject = __webpack_require__(543); +var isObject = __webpack_require__(550); -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(obj, prop, val) { + if (!isObject(obj)) { + return obj; + } + + if (Array.isArray(prop)) { + prop = [].concat.apply([], prop).join('.'); } if (typeof prop !== 'string') { - throw new TypeError('expected `prop` to be a string.'); + return obj; } - if (isDescriptor(val) && ('set' in val || 'get' in val)) { - return Object.defineProperty(obj, prop, val); + 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 Object.defineProperty(obj, prop, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); + return obj; }; +function isValidKey(key) { + return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; +} + /***/ }), -/* 562 */ +/* 582 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; - - -var isObject = __webpack_require__(539); -var Emitter = __webpack_require__(563); -var visit = __webpack_require__(564); -var toPath = __webpack_require__(567); -var union = __webpack_require__(568); -var del = __webpack_require__(572); -var get = __webpack_require__(570); -var has = __webpack_require__(577); -var set = __webpack_require__(571); - -/** - * Create a `Cache` constructor that when instantiated will - * store values on the given `prop`. - * - * ```js - * var Cache = require('cache-base').namespace('data'); - * var cache = new Cache(); +/*! + * unset-value <https://github.com/jonschlinkert/unset-value> * - * cache.set('foo', 'bar'); - * //=> {data: {foo: 'bar'}} - * ``` - * @param {String} `prop` The property name to use for storing values. - * @return {Function} Returns a custom `Cache` constructor - * @api public + * Copyright (c) 2015, 2017, Jon Schlinkert. + * Released under the MIT License. */ -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); - } +var isObject = __webpack_require__(534); +var has = __webpack_require__(583); + +module.exports = function unset(obj, prop) { + if (!isObject(obj)) { + throw new TypeError('expected an object.'); + } + if (obj.hasOwnProperty(prop)) { + delete obj[prop]; + return true; } - /** - * Inherit Emitter - */ + 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; +}; - 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 - */ +/***/ }), +/* 583 */ +/***/ (function(module, exports, __webpack_require__) { - 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; - }; +"use strict"; +/*! + * has-value <https://github.com/jonschlinkert/has-value> + * + * Copyright (c) 2014-2016, Jon Schlinkert. + * Licensed under the MIT License. + */ - /** - * 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 - */ - 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; - }; - /** - * 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 isObject = __webpack_require__(584); +var hasValues = __webpack_require__(586); +var get = __webpack_require__(580); - Cache.prototype.get = function(key) { - key = toPath(arguments); +module.exports = function(obj, prop, noZero) { + if (isObject(obj)) { + return hasValues(get(obj, prop), noZero); + } + return hasValues(obj, prop); +}; - var ctx = prop ? this[prop] : this; - var val = get(ctx, key); - this.emit('get', key, val); - return val; - }; +/***/ }), +/* 584 */ +/***/ (function(module, exports, __webpack_require__) { - /** - * 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 - */ +"use strict"; +/*! + * isobject <https://github.com/jonschlinkert/isobject> + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. + */ - Cache.prototype.has = function(key) { - key = toPath(arguments); - var ctx = prop ? this[prop] : this; - var val = get(ctx, key); - var has = typeof val !== 'undefined'; - this.emit('has', key, has); - return has; - }; +var isArray = __webpack_require__(585); - /** - * 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 - */ +module.exports = function isObject(val) { + return val != null && typeof val === 'object' && isArray(val) === false; +}; - 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; - }; - /** - * Reset the entire cache to an empty object. - * - * ```js - * app.clear(); - * ``` - * @api public - */ +/***/ }), +/* 585 */ +/***/ (function(module, exports) { - Cache.prototype.clear = function() { - if (prop) { - this[prop] = {}; - } - }; +var toString = {}.toString; - /** - * 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 - */ +module.exports = Array.isArray || function (arr) { + return toString.call(arr) == '[object Array]'; +}; - Cache.prototype.visit = function(method, val) { - visit(this, method, val); - return this; - }; - return Cache; -} +/***/ }), +/* 586 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Cast val to an array +"use strict"; +/*! + * has-values <https://github.com/jonschlinkert/has-values> + * + * Copyright (c) 2014-2015, Jon Schlinkert. + * Licensed under the MIT License. */ -function arrayify(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; -} -/** - * Expose `Cache` - */ -module.exports = namespace(); +module.exports = function hasValue(o, noZero) { + if (o === null || o === undefined) { + return false; + } -/** - * Expose `Cache.namespace` - */ + if (typeof o === 'boolean') { + return true; + } -module.exports.namespace = namespace; + if (typeof o === 'number') { + if (o === 0 && noZero === true) { + return false; + } + return true; + } + + if (o.length !== undefined) { + return o.length !== 0; + } + + for (var key in o) { + if (o.hasOwnProperty(key)) { + return true; + } + } + return false; +}; /***/ }), -/* 563 */ +/* 587 */ /***/ (function(module, exports, __webpack_require__) { - -/** - * 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; -}; +"use strict"; +/*! + * has-value <https://github.com/jonschlinkert/has-value> + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Licensed under the MIT License. + */ + + + +var isObject = __webpack_require__(534); +var hasValues = __webpack_require__(588); +var get = __webpack_require__(580); + +module.exports = function(val, prop) { + return hasValues(isObject(val) && prop ? get(val, prop) : val); +}; /***/ }), -/* 564 */ +/* 588 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * collection-visit <https://github.com/jonschlinkert/collection-visit> + * has-values <https://github.com/jonschlinkert/has-values> * - * Copyright (c) 2015, 2017, Jon Schlinkert. + * Copyright (c) 2014-2015, 2017, Jon Schlinkert. * Released under the MIT License. */ -var visit = __webpack_require__(565); -var mapVisit = __webpack_require__(566); +var typeOf = __webpack_require__(589); +var isNumber = __webpack_require__(558); -module.exports = function(collection, method, val) { - var result; - - 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); +module.exports = function hasValue(val) { + // is-number checks for NaN and other edge cases + if (isNumber(val)) { + return true; } - if (typeof result !== 'undefined') { - return result; + 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 collection; }; /***/ }), -/* 565 */ +/* 589 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; -/*! - * object-visit <https://github.com/jonschlinkert/object-visit> +var isBuffer = __webpack_require__(560); +var toString = Object.prototype.toString; + +/** + * Get the native `typeof` a value. * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * @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'; + } + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; + } -var isObject = __webpack_require__(539); + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; + } -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.'); + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; + } + if (val instanceof Date) { + return 'date'; } - if (typeof method !== 'string') { - throw new Error('object-visit expects `method` name to be a string'); + // 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'; + } + 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'; + } + + // 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 (typeof thisArg[method] !== 'function') { - return thisArg; + if (type === '[object Float64Array]') { + return 'float64array'; } - 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; + // must be a plain object + return 'object'; }; /***/ }), -/* 566 */ +/* 590 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var util = __webpack_require__(111); -var visit = __webpack_require__(565); +var isExtendable = __webpack_require__(591); +var forIn = __webpack_require__(592); + +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; +} /** - * Map `visit` over an array of objects. + * Copy properties from the source object to the + * target object. * - * @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` + * @param {String} `key` */ -module.exports = function mapVisit(collection, method, val) { - if (isObject(val)) { - return visit.apply(null, arguments); +function copy(val, key) { + if (!isValidKey(key)) { + return; } - if (!Array.isArray(val)) { - throw new TypeError('expected an array: ' + util.inspect(val)); + var obj = this[key]; + if (isObject(val) && isObject(obj)) { + mixinDeep(obj, val); + } else { + this[key] = val; } +} - 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)); - } - } -}; +/** + * Returns true if `val` is an object or function. + * + * @param {any} val + * @return {Boolean} + */ function isObject(val) { - return val && (typeof val === 'function' || (!Array.isArray(val) && typeof val === 'object')); + 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'; +}; + +/** + * Expose `mixinDeep` + */ + +module.exports = mixinDeep; + /***/ }), -/* 567 */ +/* 591 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * to-object-path <https://github.com/jonschlinkert/to-object-path> + * is-extendable <https://github.com/jonschlinkert/is-extendable> * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. + * Copyright (c) 2015-2017, Jon Schlinkert. + * Released under the MIT License. */ -var typeOf = __webpack_require__(544); +var isPlainObject = __webpack_require__(543); -module.exports = function toPath(args) { - if (typeOf(args) !== 'arguments') { - args = arguments; - } - return filter(args).join('.'); +module.exports = function isExtendable(val) { + return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); }; -function filter(arr) { - var len = arr.length; - var idx = -1; - var res = []; - - 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; -} - /***/ }), -/* 568 */ +/* 592 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; +/*! + * for-in <https://github.com/jonschlinkert/for-in> + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ -var isObject = __webpack_require__(530); -var union = __webpack_require__(569); -var get = __webpack_require__(570); -var set = __webpack_require__(571); -module.exports = function unionValue(obj, prop, value) { - if (!isObject(obj)) { - throw new TypeError('union-value expects the first argument to be an object.'); +module.exports = function forIn(obj, fn, thisArg) { + for (var key in obj) { + if (fn.call(thisArg, obj[key], key, obj) === false) { + break; + } } +}; - 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; -}; +/***/ }), +/* 593 */ +/***/ (function(module, exports) { -function arrayify(val) { - if (val === null || typeof val === 'undefined') { - return []; - } - if (Array.isArray(val)) { - return val; +/*! + * pascalcase <https://github.com/jonschlinkert/pascalcase> + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. + */ + +function pascalcase(str) { + if (typeof str !== 'string') { + throw new TypeError('expected a string.'); } - return [val]; + 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; + /***/ }), -/* 569 */ +/* 594 */ /***/ (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 util = __webpack_require__(112); +var utils = __webpack_require__(595); - var len = arguments.length; - var i = 0; +/** + * Expose class utils + */ - while (++i < len) { - var arg = arguments[i]; - if (!arg) continue; +var cu = module.exports; - if (!Array.isArray(arg)) { - arg = [arg]; - } +/** + * Expose class utils: `cu` + */ - for (var j = 0; j < arg.length; j++) { - var ele = arg[j]; +cu.isObject = function isObject(val) { + return utils.isObj(val) || typeof val === 'function'; +}; - if (init.indexOf(ele) >= 0) { - continue; +/** + * 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 + */ + +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; } - init.push(ele); } + + var keys = cu.nativeKeys(obj); + return cu.has(keys, val); } - return init; -}; + if (Array.isArray(obj)) { + var arr = obj; + while (len--) { + if (arr.indexOf(val[len]) > -1) { + return true; + } + } + return false; + } -/***/ }), -/* 570 */ -/***/ (function(module, exports) { + throw new TypeError('expected an array or object.'); +}; -/*! - * get-value <https://github.com/jonschlinkert/get-value> +/** + * Returns true if an array or object has all of the given values. * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. + * ```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 */ -module.exports = function(obj, prop, a, b, c) { - if (!isObject(obj) || !prop) { - return obj; +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; +}; - 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); +/** + * Cast the given value to an array. + * + * ```js + * cu.arrayify('foo'); + * //=> ['foo'] + * + * cu.arrayify(['foo']); + * //=> ['foo'] + * ``` + * + * @param {String|Array} `val` + * @return {Array} + * @api public + */ - if (prop in obj) { - return obj[prop]; - } +cu.arrayify = function arrayify(val) { + return val ? (Array.isArray(val) ? val : [val]) : []; +}; - var segs = prop.split('.'); - var len = segs.length; - var i = -1; +/** + * Noop + */ - 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; +cu.noop = function noop() { + return; }; -function isObject(val) { - return val !== null && (typeof val === 'object' || typeof val === 'function'); -} +/** + * Returns the first argument passed to the function. + */ -function toString(val) { - if (!val) return ''; - if (Array.isArray(val)) { - return val.join('.'); - } +cu.identity = function identity(val) { return val; -} - - -/***/ }), -/* 571 */ -/***/ (function(module, exports, __webpack_require__) { +}; -"use strict"; -/*! - * set-value <https://github.com/jonschlinkert/set-value> +/** + * Returns true if a value has a `contructor` * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * cu.hasConstructor({}); + * //=> true + * + * cu.hasConstructor(Object.create(null)); + * //=> false + * ``` + * @param {Object} `value` + * @return {Boolean} + * @api public */ +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 + */ -var split = __webpack_require__(535); -var extend = __webpack_require__(529); -var isPlainObject = __webpack_require__(538); -var isObject = __webpack_require__(530); +cu.nativeKeys = function nativeKeys(val) { + if (!cu.hasConstructor(val)) return []; + return Object.getOwnPropertyNames(val); +}; -module.exports = function(obj, prop, val) { - if (!isObject(obj)) { - return obj; - } +/** + * 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 + */ - if (Array.isArray(prop)) { - prop = [].concat.apply([], prop).join('.'); +cu.getDescriptor = function getDescriptor(obj, key) { + if (!cu.isObject(obj)) { + throw new TypeError('expected an object.'); } - - if (typeof prop !== 'string') { - return obj; + if (typeof key !== 'string') { + throw new TypeError('expected key to be a string.'); } + return Object.getOwnPropertyDescriptor(obj, key); +}; - 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; - } +/** + * 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 + */ - if (isPlainObject(current[key]) && isPlainObject(val)) { - current[key] = extend({}, current[key], val); - } else { - current[key] = val; - } +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.'); } - return obj; + var val = cu.getDescriptor(provider, name); + if (val) Object.defineProperty(receiver, name, val); }; -function isValidKey(key) { - return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'; -} +/** + * 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 + */ + +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]; -/***/ }), -/* 572 */ -/***/ (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); + } + } +}; -"use strict"; -/*! - * unset-value <https://github.com/jonschlinkert/unset-value> +/** + * Inherit the static properties, prototype properties, and descriptors + * from of an object. * - * Copyright (c) 2015, 2017, Jon Schlinkert. - * Released under the MIT License. + * @param {Object} `receiver` + * @param {Object} `provider` + * @param {String|Array} `omit` One or more properties to omit + * @return {Object} + * @api public */ - - -var isObject = __webpack_require__(539); -var has = __webpack_require__(573); - -module.exports = function unset(obj, prop) { - if (!isObject(obj)) { - throw new TypeError('expected an object.'); +cu.inherit = function inherit(receiver, provider, omit) { + if (!cu.isObject(receiver)) { + throw new TypeError('expected receiving object to be an object.'); } - if (obj.hasOwnProperty(prop)) { - delete obj[prop]; - return true; + if (!cu.isObject(provider)) { + throw new TypeError('expected providing object to be an object.'); } - 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]); + var keys = []; + for (var key in provider) { + keys.push(key); + receiver[key] = provider[key]; } - return true; -}; + keys = keys.concat(cu.arrayify(omit)); -/***/ }), -/* 573 */ -/***/ (function(module, exports, __webpack_require__) { + var a = provider.prototype || provider; + var b = receiver.prototype || receiver; + cu.copy(b, a, keys); +}; -"use strict"; -/*! - * has-value <https://github.com/jonschlinkert/has-value> +/** + * Returns a function for extending the static properties, + * prototype properties, and descriptors from the `Parent` + * constructor onto `Child` constructors. * - * Copyright (c) 2014-2016, Jon Schlinkert. - * Licensed under the MIT License. + * ```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 + */ -var isObject = __webpack_require__(574); -var hasValues = __webpack_require__(576); -var get = __webpack_require__(570); - -module.exports = function(obj, prop, noZero) { - if (isObject(obj)) { - return hasValues(get(obj, prop), noZero); - } - return hasValues(obj, prop); +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); + }; }; /***/ }), -/* 574 */ +/* 595 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * isobject <https://github.com/jonschlinkert/isobject> - * - * Copyright (c) 2014-2015, Jon Schlinkert. - * Licensed under the MIT License. - */ +var utils = {}; -var isArray = __webpack_require__(575); -module.exports = function isObject(val) { - return val != null && typeof val === 'object' && isArray(val) === false; -}; +/** + * Lazily required module dependencies + */ -/***/ }), -/* 575 */ -/***/ (function(module, exports) { +utils.union = __webpack_require__(579); +utils.define = __webpack_require__(596); +utils.isObj = __webpack_require__(534); +utils.staticExtend = __webpack_require__(603); -var toString = {}.toString; -module.exports = Array.isArray || function (arr) { - return toString.call(arr) == '[object Array]'; -}; +/** + * Expose `utils` + */ + +module.exports = utils; /***/ }), -/* 576 */ +/* 596 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * has-values <https://github.com/jonschlinkert/has-values> + * define-property <https://github.com/jonschlinkert/define-property> * - * Copyright (c) 2014-2015, Jon Schlinkert. + * Copyright (c) 2015, Jon Schlinkert. * Licensed under the MIT License. */ -module.exports = function hasValue(o, noZero) { - if (o === null || o === undefined) { - return false; - } - - if (typeof o === 'boolean') { - return true; - } +var isDescriptor = __webpack_require__(597); - if (typeof o === 'number') { - if (o === 0 && noZero === true) { - return false; - } - return true; +module.exports = function defineProperty(obj, prop, val) { + if (typeof obj !== 'object' && typeof obj !== 'function') { + throw new TypeError('expected an object or function.'); } - if (o.length !== undefined) { - return o.length !== 0; + if (typeof prop !== 'string') { + throw new TypeError('expected `prop` to be a string.'); } - for (var key in o) { - if (o.hasOwnProperty(key)) { - return true; - } + if (isDescriptor(val) && ('set' in val || 'get' in val)) { + return Object.defineProperty(obj, prop, val); } - return false; -}; - - -/***/ }), -/* 577 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * has-value <https://github.com/jonschlinkert/has-value> - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Licensed under the MIT License. - */ - - -var isObject = __webpack_require__(539); -var hasValues = __webpack_require__(578); -var get = __webpack_require__(570); - -module.exports = function(val, prop) { - return hasValues(isObject(val) && prop ? get(val, prop) : val); + return Object.defineProperty(obj, prop, { + configurable: true, + enumerable: false, + writable: true, + value: val + }); }; /***/ }), -/* 578 */ +/* 597 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * has-values <https://github.com/jonschlinkert/has-values> + * is-descriptor <https://github.com/jonschlinkert/is-descriptor> * - * Copyright (c) 2014-2015, 2017, Jon Schlinkert. + * Copyright (c) 2015-2017, Jon Schlinkert. * Released under the MIT License. */ -var typeOf = __webpack_require__(579); -var isNumber = __webpack_require__(543); +var typeOf = __webpack_require__(598); +var isAccessor = __webpack_require__(599); +var isData = __webpack_require__(601); -module.exports = function hasValue(val) { - // is-number checks for NaN and other edge cases - if (isNumber(val)) { - return true; +module.exports = function isDescriptor(obj, key) { + if (typeOf(obj) !== 'object') { + return false; } - - 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; - } + if ('get' in obj) { + return isAccessor(obj, key); } + return isData(obj, key); }; /***/ }), -/* 579 */ -/***/ (function(module, exports, __webpack_require__) { +/* 598 */ +/***/ (function(module, exports) { -var isBuffer = __webpack_require__(526); var toString = Object.prototype.toString; /** @@ -65305,8 +68546,10 @@ var toString = Object.prototype.toString; */ module.exports = function kindOf(val) { + var type = typeof val; + // primitivies - if (typeof val === 'undefined') { + if (type === 'undefined') { return 'undefined'; } if (val === null) { @@ -65315,15 +68558,18 @@ module.exports = function kindOf(val) { if (val === true || val === false || val instanceof Boolean) { return 'boolean'; } - if (typeof val === 'string' || val instanceof String) { + if (type === 'string' || val instanceof String) { return 'string'; } - if (typeof val === 'number' || val instanceof Number) { + if (type === 'number' || val instanceof Number) { return 'number'; } // functions - if (typeof val === 'function' || val instanceof Function) { + if (type === 'function' || val instanceof Function) { + if (typeof val.constructor.name !== 'undefined' && val.constructor.name.slice(0, 9) === 'Generator') { + return 'generatorfunction'; + } return 'function'; } @@ -65341,7 +68587,7 @@ module.exports = function kindOf(val) { } // other objects - var type = toString.call(val); + type = toString.call(val); if (type === '[object RegExp]') { return 'regexp'; @@ -65380,7 +68626,20 @@ module.exports = function kindOf(val) { 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'; @@ -65414,551 +68673,402 @@ module.exports = function kindOf(val) { return 'object'; }; - -/***/ }), -/* 580 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var isExtendable = __webpack_require__(581); -var forIn = __webpack_require__(582); - -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; -} - -/** - * Copy properties from the source object to the - * target object. - * - * @param {*} `val` - * @param {String} `key` - */ - -function copy(val, key) { - if (!isValidKey(key)) { - return; - } - - var obj = this[key]; - if (isObject(val) && isObject(obj)) { - mixinDeep(obj, val); - } else { - this[key] = val; - } -} - /** - * Returns true if `val` is an object or function. - * - * @param {any} val - * @return {Boolean} + * If you need to support Safari 5-7 (8-10 yr-old browser), + * take a look at https://github.com/feross/is-buffer */ -function isObject(val) { - return isExtendable(val) && !Array.isArray(val); +function isBuffer(val) { + return val.constructor + && typeof val.constructor.isBuffer === 'function' + && val.constructor.isBuffer(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'; -}; - -/** - * Expose `mixinDeep` - */ - -module.exports = mixinDeep; - /***/ }), -/* 581 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; -/*! - * is-extendable <https://github.com/jonschlinkert/is-extendable> - * - * Copyright (c) 2015-2017, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var isPlainObject = __webpack_require__(538); - -module.exports = function isExtendable(val) { - return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); -}; - - -/***/ }), -/* 582 */ +/* 599 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /*! - * for-in <https://github.com/jonschlinkert/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; - } - } -}; - - -/***/ }), -/* 583 */ -/***/ (function(module, exports) { - -/*! - * pascalcase <https://github.com/jonschlinkert/pascalcase> + * is-accessor-descriptor <https://github.com/jonschlinkert/is-accessor-descriptor> * * 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; - - -/***/ }), -/* 584 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var util = __webpack_require__(111); -var utils = __webpack_require__(585); - -/** - * Expose class utils - */ -var cu = module.exports; -/** - * Expose class utils: `cu` - */ +var typeOf = __webpack_require__(600); -cu.isObject = function isObject(val) { - return utils.isObj(val) || typeof val === 'function'; +// accessor descriptor properties +var accessor = { + get: 'function', + set: 'function', + configurable: 'boolean', + enumerable: 'boolean' }; -/** - * 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 - */ - -cu.has = function has(obj, val) { - val = cu.arrayify(val); - var len = val.length; +function isAccessorDescriptor(obj, prop) { + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; + } - if (cu.isObject(obj)) { - for (var key in obj) { - if (val.indexOf(key) > -1) { - return true; - } - } + if (typeOf(obj) !== 'object') { + return false; + } - var keys = cu.nativeKeys(obj); - return cu.has(keys, val); + if (has(obj, 'value') || has(obj, 'writable')) { + return false; } - if (Array.isArray(obj)) { - var arr = obj; - while (len--) { - if (arr.indexOf(val[len]) > -1) { - return true; - } - } + if (!has(obj, 'get') || typeof obj.get !== 'function') { return false; } - throw new TypeError('expected an array or object.'); -}; + // 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; + } -/** - * 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 - */ + for (var key in obj) { + if (!accessor.hasOwnProperty(key)) { + continue; + } -cu.hasAll = function hasAll(val, values) { - values = cu.arrayify(values); - var len = values.length; - while (len--) { - if (!cu.has(val, values[len])) { + if (typeOf(obj[key]) === accessor[key]) { + continue; + } + + if (typeof obj[key] !== 'undefined') { return false; } } return true; -}; - -/** - * Cast the given value to an array. - * - * ```js - * cu.arrayify('foo'); - * //=> ['foo'] - * - * cu.arrayify(['foo']); - * //=> ['foo'] - * ``` - * - * @param {String|Array} `val` - * @return {Array} - * @api public - */ +} -cu.arrayify = function arrayify(val) { - return val ? (Array.isArray(val) ? val : [val]) : []; -}; +function has(obj, key) { + return {}.hasOwnProperty.call(obj, key); +} /** - * Noop + * Expose `isAccessorDescriptor` */ -cu.noop = function noop() { - return; -}; - -/** - * Returns the first argument passed to the function. - */ +module.exports = isAccessorDescriptor; -cu.identity = function identity(val) { - return val; -}; -/** - * 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 - */ +/***/ }), +/* 600 */ +/***/ (function(module, exports, __webpack_require__) { -cu.hasConstructor = function hasConstructor(val) { - return cu.isObject(val) && typeof val.constructor !== 'undefined'; -}; +var isBuffer = __webpack_require__(560); +var toString = Object.prototype.toString; /** - * 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'] - * ``` + * Get the native `typeof` a value. * - * @param {Object} `obj` Object that has a `constructor`. - * @return {Array} Array of keys. - * @api public + * @param {*} `val` + * @return {*} Native javascript type */ -cu.nativeKeys = function nativeKeys(val) { - if (!cu.hasConstructor(val)) return []; - return Object.getOwnPropertyNames(val); -}; +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'; + } -/** - * 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 - */ + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; + } -cu.getDescriptor = function getDescriptor(obj, key) { - if (!cu.isObject(obj)) { - throw new TypeError('expected an object.'); + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; } - if (typeof key !== 'string') { - throw new TypeError('expected key to be a string.'); + + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; + } + if (val instanceof Date) { + return 'date'; } - return Object.getOwnPropertyDescriptor(obj, key); -}; -/** - * 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 - */ + // other objects + var type = toString.call(val); -cu.copyDescriptor = function copyDescriptor(receiver, provider, name) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); + if (type === '[object RegExp]') { + return 'regexp'; } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); + if (type === '[object Date]') { + return 'date'; } - if (typeof name !== 'string') { - throw new TypeError('expected name to be a string.'); + if (type === '[object Arguments]') { + return 'arguments'; + } + if (type === '[object Error]') { + return 'error'; } - var val = cu.getDescriptor(provider, name); - if (val) Object.defineProperty(receiver, name, val); -}; - -/** - * 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 - */ + // buffer + if (isBuffer(val)) { + return 'buffer'; + } -cu.copy = function copy(receiver, provider, omit) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); + // es6: Map, WeakMap, Set, WeakSet + if (type === '[object Set]') { + return 'set'; } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); + 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 props = Object.getOwnPropertyNames(provider); - var keys = Object.keys(provider); - var len = props.length, - key; - omit = cu.arrayify(omit); - - while (len--) { - key = props[len]; - 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); - } + // 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'; }; -/** - * Inherit the static properties, prototype properties, and descriptors - * from of an object. + +/***/ }), +/* 601 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * is-data-descriptor <https://github.com/jonschlinkert/is-data-descriptor> * - * @param {Object} `receiver` - * @param {Object} `provider` - * @param {String|Array} `omit` One or more properties to omit - * @return {Object} - * @api public + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. */ -cu.inherit = function inherit(receiver, provider, omit) { - if (!cu.isObject(receiver)) { - throw new TypeError('expected receiving object to be an object.'); + + +var typeOf = __webpack_require__(602); + +// data descriptor properties +var data = { + configurable: 'boolean', + enumerable: 'boolean', + writable: 'boolean' +}; + +function isDataDescriptor(obj, prop) { + if (typeOf(obj) !== 'object') { + return false; } - if (!cu.isObject(provider)) { - throw new TypeError('expected providing object to be an object.'); + + if (typeof prop === 'string') { + var val = Object.getOwnPropertyDescriptor(obj, prop); + return typeof val !== 'undefined'; } - var keys = []; - for (var key in provider) { - keys.push(key); - receiver[key] = provider[key]; + if (!('value' in obj) && !('writable' in obj)) { + return false; } - keys = keys.concat(cu.arrayify(omit)); + for (var key in obj) { + if (key === 'value') continue; - var a = provider.prototype || provider; - var b = receiver.prototype || receiver; - cu.copy(b, a, keys); -}; + if (!data.hasOwnProperty(key)) { + continue; + } -/** - * 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 - */ + if (typeOf(obj[key]) === data[key]) { + continue; + } -cu.extend = function() { - // keep it lazy, instead of assigning to `cu.extend` - return utils.staticExtend.apply(null, arguments); -}; + if (typeof obj[key] !== 'undefined') { + return false; + } + } + return true; +} /** - * Bubble up events emitted from static methods on the Parent ctor. - * - * @param {Object} `Parent` - * @param {Array} `events` Event names to bubble up - * @api public + * Expose `isDataDescriptor` */ -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); - }; -}; +module.exports = isDataDescriptor; /***/ }), -/* 585 */ +/* 602 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +var isBuffer = __webpack_require__(560); +var toString = Object.prototype.toString; +/** + * Get the native `typeof` a value. + * + * @param {*} `val` + * @return {*} Native javascript type + */ -var utils = {}; +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'; + } + // functions + if (typeof val === 'function' || val instanceof Function) { + return 'function'; + } + // array + if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) { + return 'array'; + } -/** - * Lazily required module dependencies - */ + // check for instances of RegExp and Date before calling `toString` + if (val instanceof RegExp) { + return 'regexp'; + } + if (val instanceof Date) { + return 'date'; + } -utils.union = __webpack_require__(569); -utils.define = __webpack_require__(521); -utils.isObj = __webpack_require__(539); -utils.staticExtend = __webpack_require__(586); + // 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'; + } -/** - * Expose `utils` - */ + // buffer + if (isBuffer(val)) { + return 'buffer'; + } -module.exports = utils; + // 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'; +}; /***/ }), -/* 586 */ +/* 603 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -65971,9 +69081,9 @@ module.exports = utils; -var copy = __webpack_require__(587); -var define = __webpack_require__(521); -var util = __webpack_require__(111); +var copy = __webpack_require__(604); +var define = __webpack_require__(596); +var util = __webpack_require__(112); /** * Returns a function for extending the static properties, @@ -66055,15 +69165,15 @@ module.exports = extend; /***/ }), -/* 587 */ +/* 604 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var typeOf = __webpack_require__(544); -var copyDescriptor = __webpack_require__(588); -var define = __webpack_require__(521); +var typeOf = __webpack_require__(559); +var copyDescriptor = __webpack_require__(605); +var define = __webpack_require__(596); /** * Copy static properties, prototype properties, and descriptors from one object to another. @@ -66236,7 +69346,7 @@ module.exports.has = has; /***/ }), -/* 588 */ +/* 605 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66324,16 +69434,16 @@ function isObject(val) { /***/ }), -/* 589 */ +/* 606 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var use = __webpack_require__(590); -var define = __webpack_require__(521); -var debug = __webpack_require__(592)('snapdragon:compiler'); -var utils = __webpack_require__(598); +var use = __webpack_require__(607); +var define = __webpack_require__(596); +var debug = __webpack_require__(609)('snapdragon:compiler'); +var utils = __webpack_require__(615); /** * Create a new `Compiler` with the given `options`. @@ -66487,7 +69597,7 @@ Compiler.prototype = { // source map support if (opts.sourcemap) { - var sourcemaps = __webpack_require__(617); + var sourcemaps = __webpack_require__(634); sourcemaps(this); this.mapVisit(this.ast.nodes); this.applySourceMaps(); @@ -66508,7 +69618,7 @@ module.exports = Compiler; /***/ }), -/* 590 */ +/* 607 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66521,7 +69631,7 @@ module.exports = Compiler; -var utils = __webpack_require__(591); +var utils = __webpack_require__(608); module.exports = function base(app, opts) { if (!utils.isObject(app) && typeof app !== 'function') { @@ -66636,7 +69746,7 @@ module.exports = function base(app, opts) { /***/ }), -/* 591 */ +/* 608 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -66650,8 +69760,8 @@ var utils = {}; * Lazily required module dependencies */ -utils.define = __webpack_require__(521); -utils.isObject = __webpack_require__(539); +utils.define = __webpack_require__(596); +utils.isObject = __webpack_require__(534); utils.isString = function(val) { @@ -66666,7 +69776,7 @@ module.exports = utils; /***/ }), -/* 592 */ +/* 609 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -66675,14 +69785,14 @@ module.exports = utils; */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(593); + module.exports = __webpack_require__(610); } else { - module.exports = __webpack_require__(596); + module.exports = __webpack_require__(613); } /***/ }), -/* 593 */ +/* 610 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -66691,7 +69801,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(594); +exports = module.exports = __webpack_require__(611); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -66873,7 +69983,7 @@ function localstorage() { /***/ }), -/* 594 */ +/* 611 */ /***/ (function(module, exports, __webpack_require__) { @@ -66889,7 +69999,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(595); +exports.humanize = __webpack_require__(612); /** * The currently active debug mode names, and names to skip. @@ -67081,7 +70191,7 @@ function coerce(val) { /***/ }), -/* 595 */ +/* 612 */ /***/ (function(module, exports) { /** @@ -67171,1046 +70281,405 @@ function parse(str) { 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'; -} - - -/***/ }), -/* 596 */ -/***/ (function(module, exports, __webpack_require__) { - -/** - * Module dependencies. - */ - -var tty = __webpack_require__(121); -var util = __webpack_require__(111); - -/** - * This is the Node.js implementation of `debug()`. - * - * Expose `debug()` as the module. - */ - -exports = module.exports = __webpack_require__(594); -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]; - -/** - * Build up the default `inspectOpts` object from the environment variables. - * - * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js - */ - -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() }); - - // 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); - - obj[prop] = val; - return obj; -}, {}); - -/** - * The file descriptor to write the `debug()` calls to. - * Set the `DEBUG_FD` env variable to override with another value. i.e.: - * - * $ DEBUG_FD=3 node script.js 3>debug.log - */ - -var fd = parseInt(process.env.DEBUG_FD, 10) || 2; - -if (1 !== fd && 2 !== fd) { - util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')() -} - -var stream = 1 === fd ? process.stdout : - 2 === fd ? process.stderr : - createWritableStdioStream(fd); - -/** - * Is stdout a TTY? Colored output is enabled when `true`. - */ - -function useColors() { - return 'colors' in exports.inspectOpts - ? Boolean(exports.inspectOpts.colors) - : tty.isatty(fd); -} - -/** - * Map %o to `util.inspect()`, all on a single line. - */ - -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. - */ - -exports.formatters.O = function(v) { - this.inspectOpts.colors = this.useColors; - return util.inspect(v, this.inspectOpts); -}; - -/** - * Adds ANSI color escape codes if enabled. - * - * @api public - */ - -function formatArgs(args) { - var name = this.namespace; - var useColors = this.useColors; - - if (useColors) { - var c = this.color; - var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m'; - - args[0] = prefix + args[0].split('\n').join('\n' + prefix); - args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); - } else { - args[0] = new Date().toUTCString() - + ' ' + name + ' ' + args[0]; - } -} - -/** - * Invokes `util.format()` with the specified arguments and writes to `stream`. - */ - -function log() { - return stream.write(util.format.apply(util, arguments) + '\n'); -} - -/** - * Save `namespaces`. - * - * @param {String} namespaces - * @api private - */ - -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; - } -} - -/** - * Load `namespaces`. - * - * @return {String} returns the previously persisted debug modes - * @api private - */ - -function load() { - return process.env.DEBUG; -} - -/** - * Copied from `node/src/node.js`. - * - * XXX: It's lame that node doesn't expose this API out-of-the-box. It also - * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. - */ - -function createWritableStdioStream (fd) { - var stream; - var tty_wrap = process.binding('tty_wrap'); - - // Note stream._type is used for test-module-load-list.js - - switch (tty_wrap.guessHandleType(fd)) { - case 'TTY': - stream = new tty.WriteStream(fd); - stream._type = 'tty'; - - // Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - case 'FILE': - var fs = __webpack_require__(133); - stream = new fs.SyncWriteStream(fd, { autoClose: false }); - stream._type = 'fs'; - break; - - case 'PIPE': - case 'TCP': - var net = __webpack_require__(597); - stream = new net.Socket({ - fd: fd, - readable: false, - writable: true - }); - - // FIXME Should probably have an option in net.Socket to create a - // stream from an existing fd which is writable only. But for now - // we'll just add this hack and set the `readable` member to false. - // Test: ./node test/fixtures/echo.js < /etc/passwd - stream.readable = false; - stream.read = null; - stream._type = 'pipe'; - - // FIXME Hack to have stream not keep the event loop alive. - // See https://github.com/joyent/node/issues/1726 - if (stream._handle && stream._handle.unref) { - stream._handle.unref(); - } - break; - - default: - // Probably an error on in uv_guess_handle() - throw new Error('Implement me. Unknown stream file type!'); - } - - // For supporting legacy API we put the FD here. - stream.fd = fd; - - stream._isStdio = true; - - return stream; -} - -/** - * Init logic for `debug` instances. - * - * Create a new `inspectOpts` object in case `useColors` is set - * differently for a particular `debug` instance. - */ - -function init (debug) { - debug.inspectOpts = {}; - - var keys = Object.keys(exports.inspectOpts); - for (var i = 0; i < keys.length; i++) { - debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; - } -} - -/** - * Enable namespaces listed in `process.env.DEBUG` initially. - */ - -exports.enable(load()); - - -/***/ }), -/* 597 */ -/***/ (function(module, exports) { - -module.exports = require("net"); - -/***/ }), -/* 598 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -/** - * Module dependencies - */ - -exports.extend = __webpack_require__(529); -exports.SourceMap = __webpack_require__(599); -exports.sourceMapResolve = __webpack_require__(610); - -/** - * Convert backslash in the given string to forward slashes - */ - -exports.unixify = function(fp) { - return fp.split(/\\+/).join('/'); -}; + return n * s; + case 'milliseconds': + case 'millisecond': + case 'msecs': + case 'msec': + case 'ms': + return n; + default: + return undefined; + } +} /** - * Return true if `val` is a non-empty string + * Short format for `ms`. * - * @param {String} `str` - * @return {Boolean} + * @param {Number} ms + * @return {String} + * @api private */ -exports.isString = function(str) { - return str && typeof str === 'string'; -}; +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'; +} /** - * Cast `val` to an array - * @return {Array} + * Long format for `ms`. + * + * @param {Number} ms + * @return {String} + * @api private */ -exports.arrayify = function(val) { - if (typeof val === 'string') return [val]; - return val ? (Array.isArray(val) ? val : [val]) : []; -}; +function fmtLong(ms) { + return plural(ms, d, 'day') || + plural(ms, h, 'hour') || + plural(ms, m, 'minute') || + plural(ms, s, 'second') || + ms + ' ms'; +} /** - * Get the last `n` element from the given `array` - * @param {Array} `array` - * @return {*} + * Pluralization helper. */ -exports.last = function(arr, n) { - return arr[arr.length - (n || 1)]; -}; +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'; +} /***/ }), -/* 599 */ +/* 613 */ /***/ (function(module, exports, __webpack_require__) { -/* - * Copyright 2009-2011 Mozilla Foundation and contributors - * Licensed under the New BSD license. See LICENSE.txt or: - * http://opensource.org/licenses/BSD-3-Clause +/** + * Module dependencies. */ -exports.SourceMapGenerator = __webpack_require__(600).SourceMapGenerator; -exports.SourceMapConsumer = __webpack_require__(606).SourceMapConsumer; -exports.SourceNode = __webpack_require__(609).SourceNode; +var tty = __webpack_require__(122); +var util = __webpack_require__(112); -/***/ }), -/* 600 */ -/***/ (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 +/** + * This is the Node.js implementation of `debug()`. + * + * Expose `debug()` as the module. */ -var base64VLQ = __webpack_require__(601); -var util = __webpack_require__(603); -var ArraySet = __webpack_require__(604).ArraySet; -var MappingList = __webpack_require__(605).MappingList; +exports = module.exports = __webpack_require__(611); +exports.init = init; +exports.log = log; +exports.formatArgs = formatArgs; +exports.save = save; +exports.load = load; +exports.useColors = useColors; /** - * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. You may pass an object with the following - * properties: - * - * - file: The filename of the generated source. - * - sourceRoot: A root for all relative URLs in this source map. + * Colors. */ -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; +exports.colors = [6, 2, 3, 4, 5, 1]; /** - * Creates a new SourceMapGenerator based on a SourceMapConsumer + * Build up the default `inspectOpts` object from the environment variables. * - * @param aSourceMapConsumer The SourceMap. + * $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js */ -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 - } - }; - if (mapping.source != null) { - newMapping.source = mapping.source; - if (sourceRoot != null) { - newMapping.source = util.relative(sourceRoot, newMapping.source); - } - - newMapping.original = { - line: mapping.originalLine, - column: mapping.originalColumn - }; +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() }); - if (mapping.name != null) { - newMapping.name = mapping.name; - } - } + // 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); - generator.addMapping(newMapping); - }); - aSourceMapConsumer.sources.forEach(function (sourceFile) { - var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content != null) { - generator.setSourceContent(sourceFile, content); - } - }); - return generator; - }; + obj[prop] = val; + return obj; +}, {}); /** - * 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: + * The file descriptor to write the `debug()` calls to. + * Set the `DEBUG_FD` env variable to override with another value. i.e.: * - * - 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. + * $ DEBUG_FD=3 node script.js 3>debug.log */ -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); - } - if (source != null) { - source = String(source); - if (!this._sources.has(source)) { - this._sources.add(source); - } - } +var fd = parseInt(process.env.DEBUG_FD, 10) || 2; - if (name != null) { - name = String(name); - if (!this._names.has(name)) { - this._names.add(name); - } - } +if (1 !== fd && 2 !== fd) { + util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')() +} - this._mappings.add({ - generatedLine: generated.line, - generatedColumn: generated.column, - originalLine: original != null && original.line, - originalColumn: original != null && original.column, - source: source, - name: name - }); - }; +var stream = 1 === fd ? process.stdout : + 2 === fd ? process.stderr : + createWritableStdioStream(fd); /** - * Set the source content for a source file. + * Is stdout a TTY? Colored output is enabled when `true`. */ -SourceMapGenerator.prototype.setSourceContent = - function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { - var source = aSourceFile; - if (this._sourceRoot != null) { - source = util.relative(this._sourceRoot, source); - } - 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; - } - } - }; +function useColors() { + return 'colors' in exports.inspectOpts + ? Boolean(exports.inspectOpts.colors) + : tty.isatty(fd); +} /** - * 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 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. + * Map %o to `util.inspect()`, all on a single line. */ -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(); - - // 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 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); - }; +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(' '); +}; /** - * 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. + * Map %o to `util.inspect()`, allowing multiple lines if needed. */ -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.' - ); - } - 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 - })); - } - }; +exports.formatters.O = function(v) { + this.inspectOpts.colors = this.useColors; + return util.inspect(v, this.inspectOpts); +}; /** - * Serialize the accumulated mappings in to the stream of base 64 VLQs - * specified by the source map format. + * Adds ANSI color escape codes if enabled. + * + * @api public */ -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 = '' - - 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; - if (mapping.source != null) { - sourceIdx = this._sources.indexOf(mapping.source); - next += base64VLQ.encode(sourceIdx - previousSource); - previousSource = sourceIdx; +function formatArgs(args) { + var name = this.namespace; + var useColors = this.useColors; - // lines are stored 0-based in SourceMap spec version 3 - next += base64VLQ.encode(mapping.originalLine - 1 - - previousOriginalLine); - previousOriginalLine = mapping.originalLine - 1; + if (useColors) { + var c = this.color; + var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m'; - next += base64VLQ.encode(mapping.originalColumn - - previousOriginalColumn); - previousOriginalColumn = mapping.originalColumn; + args[0] = prefix + args[0].split('\n').join('\n' + prefix); + args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m'); + } else { + args[0] = new Date().toUTCString() + + ' ' + name + ' ' + args[0]; + } +} - if (mapping.name != null) { - nameIdx = this._names.indexOf(mapping.name); - next += base64VLQ.encode(nameIdx - previousName); - previousName = nameIdx; - } - } +/** + * Invokes `util.format()` with the specified arguments and writes to `stream`. + */ - result += next; - } +function log() { + return stream.write(util.format.apply(util, arguments) + '\n'); +} - return result; - }; +/** + * Save `namespaces`. + * + * @param {String} namespaces + * @api private + */ -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); - }; +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; + } +} /** - * Externalize the source map. + * Load `namespaces`. + * + * @return {String} returns the previously persisted debug modes + * @api private */ -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; - }; +function load() { + return process.env.DEBUG; +} /** - * Render the source map being generated to a string. + * Copied from `node/src/node.js`. + * + * XXX: It's lame that node doesn't expose this API out-of-the-box. It also + * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame. */ -SourceMapGenerator.prototype.toString = - function SourceMapGenerator_toString() { - return JSON.stringify(this.toJSON()); - }; -exports.SourceMapGenerator = SourceMapGenerator; +function createWritableStdioStream (fd) { + var stream; + var tty_wrap = process.binding('tty_wrap'); + // Note stream._type is used for test-module-load-list.js -/***/ }), -/* 601 */ -/***/ (function(module, exports, __webpack_require__) { + switch (tty_wrap.guessHandleType(fd)) { + case 'TTY': + stream = new tty.WriteStream(fd); + stream._type = 'tty'; -/* -*- 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. - */ + // Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; -var base64 = __webpack_require__(602); + case 'FILE': + var fs = __webpack_require__(134); + stream = new fs.SyncWriteStream(fd, { autoClose: false }); + stream._type = 'fs'; + break; -// 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 + case 'PIPE': + case 'TCP': + var net = __webpack_require__(614); + stream = new net.Socket({ + fd: fd, + readable: false, + writable: true + }); -var VLQ_BASE_SHIFT = 5; + // FIXME Should probably have an option in net.Socket to create a + // stream from an existing fd which is writable only. But for now + // we'll just add this hack and set the `readable` member to false. + // Test: ./node test/fixtures/echo.js < /etc/passwd + stream.readable = false; + stream.read = null; + stream._type = 'pipe'; -// binary: 100000 -var VLQ_BASE = 1 << VLQ_BASE_SHIFT; + // FIXME Hack to have stream not keep the event loop alive. + // See https://github.com/joyent/node/issues/1726 + if (stream._handle && stream._handle.unref) { + stream._handle.unref(); + } + break; -// binary: 011111 -var VLQ_BASE_MASK = VLQ_BASE - 1; + default: + // Probably an error on in uv_guess_handle() + throw new Error('Implement me. Unknown stream file type!'); + } -// binary: 100000 -var VLQ_CONTINUATION_BIT = VLQ_BASE; + // For supporting legacy API we put the FD here. + stream.fd = fd; -/** - * 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; -} + stream._isStdio = true; -/** - * 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; + return stream; } /** - * Returns the base 64 VLQ encoded value. + * Init logic for `debug` instances. + * + * Create a new `inspectOpts` object in case `useColors` is set + * differently for a particular `debug` instance. */ -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 init (debug) { + debug.inspectOpts = {}; - return encoded; -}; + var keys = Object.keys(exports.inspectOpts); + for (var i = 0; i < keys.length; i++) { + debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]]; + } +} /** - * 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. + * Enable namespaces listed in `process.env.DEBUG` initially. */ -exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { - var strLen = aStr.length; - var result = 0; - var shift = 0; - var continuation, digit; - - 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)); - } +exports.enable(load()); - 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; -}; +/***/ }), +/* 614 */ +/***/ (function(module, exports) { +module.exports = require("net"); /***/ }), -/* 602 */ -/***/ (function(module, exports) { +/* 615 */ +/***/ (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 intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); /** - * Encode an integer in the range of 0 to 63 to a single base 64 digit. + * Module dependencies */ -exports.encode = function (number) { - if (0 <= number && number < intToCharMap.length) { - return intToCharMap[number]; - } - throw new TypeError("Must be between 0 and 63: " + number); -}; + +exports.extend = __webpack_require__(549); +exports.SourceMap = __webpack_require__(616); +exports.sourceMapResolve = __webpack_require__(627); /** - * Decode a single base 64 character code digit to an integer. Returns -1 on - * failure. + * Convert backslash in the given string to forward slashes */ -exports.decode = function (charCode) { - var bigA = 65; // 'A' - var bigZ = 90; // 'Z' - var littleA = 97; // 'a' - var littleZ = 122; // 'z' +exports.unixify = function(fp) { + return fp.split(/\\+/).join('/'); +}; - var zero = 48; // '0' - var nine = 57; // '9' +/** + * Return true if `val` is a non-empty string + * + * @param {String} `str` + * @return {Boolean} + */ - var plus = 43; // '+' - var slash = 47; // '/' +exports.isString = function(str) { + return str && typeof str === 'string'; +}; - var littleOffset = 26; - var numberOffset = 52; +/** + * Cast `val` to an array + * @return {Array} + */ - // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ - if (bigA <= charCode && charCode <= bigZ) { - return (charCode - bigA); - } +exports.arrayify = function(val) { + if (typeof val === 'string') return [val]; + return val ? (Array.isArray(val) ? val : [val]) : []; +}; - // 26 - 51: abcdefghijklmnopqrstuvwxyz - if (littleA <= charCode && charCode <= littleZ) { - return (charCode - littleA + littleOffset); - } +/** + * Get the last `n` element from the given `array` + * @param {Array} `array` + * @return {*} + */ - // 52 - 61: 0123456789 - if (zero <= charCode && charCode <= nine) { - return (charCode - zero + numberOffset); - } +exports.last = function(arr, n) { + return arr[arr.length - (n || 1)]; +}; - // 62: + - if (charCode == plus) { - return 62; - } - // 63: / - if (charCode == slash) { - return 63; - } +/***/ }), +/* 616 */ +/***/ (function(module, exports, __webpack_require__) { - // Invalid base64 digit. - return -1; -}; +/* + * 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__(617).SourceMapGenerator; +exports.SourceMapConsumer = __webpack_require__(623).SourceMapConsumer; +exports.SourceNode = __webpack_require__(626).SourceNode; /***/ }), -/* 603 */ -/***/ (function(module, exports) { +/* 617 */ +/***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ /* @@ -68219,420 +70688,419 @@ exports.decode = function (charCode) { * http://opensource.org/licenses/BSD-3-Clause */ -/** - * This is a helper function for getting values from parameter/options - * objects. - * - * @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. - */ -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; - -var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; -var dataUrlRegexp = /^data:.+\,.+$/; - -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; - -function urlGenerate(aParsedUrl) { - var url = ''; - if (aParsedUrl.scheme) { - url += aParsedUrl.scheme + ':'; - } - url += '//'; - if (aParsedUrl.auth) { - url += aParsedUrl.auth + '@'; - } - if (aParsedUrl.host) { - url += aParsedUrl.host; - } - if (aParsedUrl.port) { - url += ":" + aParsedUrl.port - } - if (aParsedUrl.path) { - url += aParsedUrl.path; - } - return url; -} -exports.urlGenerate = urlGenerate; +var base64VLQ = __webpack_require__(618); +var util = __webpack_require__(620); +var ArraySet = __webpack_require__(621).ArraySet; +var MappingList = __webpack_require__(622).MappingList; /** - * Normalizes a path, or the path portion of a URL: - * - * - Replaces consecutive slashes with one slash. - * - Removes unnecessary '.' parts. - * - Removes unnecessary '<dir>/..' parts. - * - * Based on code in the Node.js 'path' core module. + * An instance of the SourceMapGenerator represents a source map which is + * being built incrementally. You may pass an object with the following + * properties: * - * @param aPath The path or url to normalize. + * - file: The filename of the generated source. + * - sourceRoot: A root for all relative URLs in this source map. */ -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); - - 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('/'); - - if (path === '') { - path = isAbsolute ? '/' : '.'; - } - - if (url) { - url.path = path; - return urlGenerate(url); +function SourceMapGenerator(aArgs) { + if (!aArgs) { + aArgs = {}; } - return path; + 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; } -exports.normalize = normalize; + +SourceMapGenerator.prototype._version = 3; /** - * Joins two paths/URLs. - * - * @param aRoot The root path or URL. - * @param aPath The path or URL to be joined with the root. + * Creates a new SourceMapGenerator based on a SourceMapConsumer * - * - 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. + * @param aSourceMapConsumer The SourceMap. */ -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); - } - - if (aPathUrl || aPath.match(dataUrlRegexp)) { - return aPath; - } +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 + } + }; - // `join('http://', 'www.example.com')` - if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { - aRootUrl.host = aPath; - return urlGenerate(aRootUrl); - } + if (mapping.source != null) { + newMapping.source = mapping.source; + if (sourceRoot != null) { + newMapping.source = util.relative(sourceRoot, newMapping.source); + } - var joined = aPath.charAt(0) === '/' - ? aPath - : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); + newMapping.original = { + line: mapping.originalLine, + column: mapping.originalColumn + }; - if (aRootUrl) { - aRootUrl.path = joined; - return urlGenerate(aRootUrl); - } - return joined; -} -exports.join = join; + if (mapping.name != null) { + newMapping.name = mapping.name; + } + } -exports.isAbsolute = function (aPath) { - return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp); -}; + generator.addMapping(newMapping); + }); + aSourceMapConsumer.sources.forEach(function (sourceFile) { + var content = aSourceMapConsumer.sourceContentFor(sourceFile); + if (content != null) { + generator.setSourceContent(sourceFile, content); + } + }); + return generator; + }; /** - * Make a path relative to a URL or another path. + * 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 aRoot The root path or URL. - * @param aPath The path or URL to be made relative to aRoot. + * - 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. */ -function relative(aRoot, aPath) { - if (aRoot === "") { - aRoot = "."; - } - - aRoot = aRoot.replace(/\/$/, ''); +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); - // 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 (!this._skipValidation) { + this._validateMapping(generated, original, source, name); } - // 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; + if (source != null) { + source = String(source); + if (!this._sources.has(source)) { + this._sources.add(source); + } } - ++level; - } + if (name != null) { + name = String(name); + if (!this._names.has(name)) { + this._names.add(name); + } + } - // 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; + this._mappings.add({ + generatedLine: generated.line, + generatedColumn: generated.column, + originalLine: original != null && original.line, + originalColumn: original != null && original.column, + source: source, + name: name + }); + }; -var supportsNullProto = (function () { - var obj = Object.create(null); - return !('__proto__' in obj); -}()); +/** + * 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); + } -function identity (s) { - return s; -} + 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; + } + } + }; /** - * 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 + * 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 String aStr + * @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. */ -function toSetString(aStr) { - if (isProtoString(aStr)) { - return '$' + aStr; - } - - return aStr; -} -exports.toSetString = supportsNullProto ? identity : toSetString; - -function fromSetString(aStr) { - if (isProtoString(aStr)) { - return aStr.slice(1); - } - - return aStr; -} -exports.fromSetString = supportsNullProto ? identity : fromSetString; - -function isProtoString(s) { - if (!s) { - return false; - } +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(); - var length = s.length; + // 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; + } + } + } - if (length < 9 /* "__proto__".length */) { - return false; - } + var source = mapping.source; + if (source != null && !newSources.has(source)) { + newSources.add(source); + } - 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 name = mapping.name; + if (name != null && !newNames.has(name)) { + newNames.add(name); + } - for (var i = length - 10; i >= 0; i--) { - if (s.charCodeAt(i) !== 36 /* '$' */) { - return false; - } - } + }, this); + this._sources = newSources; + this._names = newNames; - return true; -} + // 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); + }; /** - * Comparator between two mappings where the original positions are compared. + * A mapping can have one of the three levels of data: * - * 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. + * 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. */ -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; - } - - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp !== 0) { - return cmp; - } - - cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp !== 0) { - return cmp; - } +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.' + ); + } - return mappingA.name - mappingB.name; -} -exports.compareByOriginalPositions = compareByOriginalPositions; + 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 + })); + } + }; /** - * 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. + * Serialize the accumulated mappings in to the stream of base 64 VLQs + * specified by the source map format. */ -function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) { - var cmp = mappingA.generatedLine - mappingB.generatedLine; - if (cmp !== 0) { - return cmp; - } +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; - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp !== 0 || onlyCompareGenerated) { - return cmp; - } + var mappings = this._mappings.toArray(); + for (var i = 0, len = mappings.length; i < len; i++) { + mapping = mappings[i]; + next = '' - cmp = mappingA.source - mappingB.source; - if (cmp !== 0) { - return cmp; - } + 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 += ','; + } + } - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp !== 0) { - return cmp; - } + next += base64VLQ.encode(mapping.generatedColumn + - previousGeneratedColumn); + previousGeneratedColumn = mapping.generatedColumn; - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp !== 0) { - return cmp; - } + if (mapping.source != null) { + sourceIdx = this._sources.indexOf(mapping.source); + next += base64VLQ.encode(sourceIdx - previousSource); + previousSource = sourceIdx; - return mappingA.name - mappingB.name; -} -exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; + // lines are stored 0-based in SourceMap spec version 3 + next += base64VLQ.encode(mapping.originalLine - 1 + - previousOriginalLine); + previousOriginalLine = mapping.originalLine - 1; -function strcmp(aStr1, aStr2) { - if (aStr1 === aStr2) { - return 0; - } + next += base64VLQ.encode(mapping.originalColumn + - previousOriginalColumn); + previousOriginalColumn = mapping.originalColumn; - if (aStr1 > aStr2) { - return 1; - } + if (mapping.name != null) { + nameIdx = this._names.indexOf(mapping.name); + next += base64VLQ.encode(nameIdx - previousName); + previousName = nameIdx; + } + } - return -1; -} + result += next; + } -/** - * 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; - } + return result; + }; - cmp = mappingA.generatedColumn - mappingB.generatedColumn; - if (cmp !== 0) { - return cmp; - } +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); + }; - cmp = strcmp(mappingA.source, mappingB.source); - if (cmp !== 0) { - return cmp; - } +/** + * 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); + } - cmp = mappingA.originalLine - mappingB.originalLine; - if (cmp !== 0) { - return cmp; - } + return map; + }; - cmp = mappingA.originalColumn - mappingB.originalColumn; - if (cmp !== 0) { - return cmp; - } +/** + * Render the source map being generated to a string. + */ +SourceMapGenerator.prototype.toString = + function SourceMapGenerator_toString() { + return JSON.stringify(this.toJSON()); + }; - return strcmp(mappingA.name, mappingB.name); -} -exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; +exports.SourceMapGenerator = SourceMapGenerator; /***/ }), -/* 604 */ +/* 618 */ /***/ (function(module, exports, __webpack_require__) { /* -*- Mode: js; js-indent-level: 2; -*- */ @@ -68640,4114 +71108,3968 @@ exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflate * 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 util = __webpack_require__(603); -var has = Object.prototype.hasOwnProperty; -var hasNativeMap = typeof Map !== "undefined"; +var base64 = __webpack_require__(619); + +// 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; /** - * 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. + * 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 ArraySet() { - this._array = []; - this._set = hasNativeMap ? new Map() : Object.create(null); +function toVLQSigned(aValue) { + return aValue < 0 + ? ((-aValue) << 1) + 1 + : (aValue << 1) + 0; } /** - * Static method for creating ArraySet instances from an existing array. + * 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 */ -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; -}; +function fromVLQSigned(aValue) { + var isNegative = (aValue & 1) === 1; + var shifted = aValue >> 1; + return isNegative + ? -shifted + : shifted; +} /** - * Return how many unique items are in this ArraySet. If duplicates have been - * added, than those do not count towards the size. - * - * @returns Number + * Returns the base 64 VLQ encoded value. */ -ArraySet.prototype.size = function ArraySet_size() { - return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length; -}; +exports.encode = function base64VLQ_encode(aValue) { + var encoded = ""; + var digit; -/** - * 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; + 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); -/** - * 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); - } + return encoded; }; /** - * What is the index of the given string in the array? - * - * @param String aStr + * 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. */ -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]; +exports.decode = function base64VLQ_decode(aStr, aIndex, aOutParam) { + var strLen = aStr.length; + var result = 0; + var shift = 0; + var continuation, digit; + + do { + if (aIndex >= strLen) { + throw new Error("Expected more digits in base 64 VLQ value."); } - } - throw new Error('"' + aStr + '" is not in the set.'); -}; + digit = base64.decode(aStr.charCodeAt(aIndex++)); + if (digit === -1) { + throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1)); + } -/** - * 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]; - } - throw new Error('No element indexed by ' + aIdx); -}; + continuation = !!(digit & VLQ_CONTINUATION_BIT); + digit &= VLQ_BASE_MASK; + result = result + (digit << shift); + shift += VLQ_BASE_SHIFT; + } while (continuation); -/** - * 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(); + aOutParam.value = fromVLQSigned(result); + aOutParam.rest = aIndex; }; -exports.ArraySet = ArraySet; - /***/ }), -/* 605 */ -/***/ (function(module, exports, __webpack_require__) { +/* 619 */ +/***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ /* - * Copyright 2014 Mozilla Foundation and contributors + * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ -var util = __webpack_require__(603); - -/** - * 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; -} - -/** - * 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}; -} - -/** - * 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); - }; +var intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); /** - * Add the given source mapping. - * - * @param Object aMapping + * Encode an integer in the range of 0 to 63 to a single base 64 digit. */ -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); +exports.encode = function (number) { + if (0 <= number && number < intToCharMap.length) { + return intToCharMap[number]; } + throw new TypeError("Must be between 0 and 63: " + number); }; /** - * 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. + * Decode a single base 64 character code digit to an integer. Returns -1 on + * failure. */ -MappingList.prototype.toArray = function MappingList_toArray() { - if (!this._sorted) { - this._array.sort(util.compareByGeneratedPositionsInflated); - this._sorted = true; - } - return this._array; -}; - -exports.MappingList = MappingList; +exports.decode = function (charCode) { + var bigA = 65; // 'A' + var bigZ = 90; // 'Z' + var littleA = 97; // 'a' + var littleZ = 122; // 'z' -/***/ }), -/* 606 */ -/***/ (function(module, exports, __webpack_require__) { + var zero = 48; // '0' + var nine = 57; // '9' -/* -*- 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 plus = 43; // '+' + var slash = 47; // '/' -var util = __webpack_require__(603); -var binarySearch = __webpack_require__(607); -var ArraySet = __webpack_require__(604).ArraySet; -var base64VLQ = __webpack_require__(601); -var quickSort = __webpack_require__(608).quickSort; + var littleOffset = 26; + var numberOffset = 52; -function SourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); + // 0 - 25: ABCDEFGHIJKLMNOPQRSTUVWXYZ + if (bigA <= charCode && charCode <= bigZ) { + return (charCode - bigA); } - return sourceMap.sections != null - ? new IndexedSourceMapConsumer(sourceMap) - : new BasicSourceMapConsumer(sourceMap); -} - -SourceMapConsumer.fromSourceMap = function(aSourceMap) { - return BasicSourceMapConsumer.fromSourceMap(aSourceMap); -} - -/** - * The version of the source mapping spec that we are consuming. - */ -SourceMapConsumer.prototype._version = 3; - -// `__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. - -SourceMapConsumer.prototype.__generatedMappings = null; -Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { - get: function () { - if (!this.__generatedMappings) { - this._parseMappings(this._mappings, this.sourceRoot); - } + // 26 - 51: abcdefghijklmnopqrstuvwxyz + if (littleA <= charCode && charCode <= littleZ) { + return (charCode - littleA + littleOffset); + } - return this.__generatedMappings; + // 52 - 61: 0123456789 + if (zero <= charCode && charCode <= nine) { + return (charCode - zero + numberOffset); } -}); -SourceMapConsumer.prototype.__originalMappings = null; -Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { - get: function () { - if (!this.__originalMappings) { - this._parseMappings(this._mappings, this.sourceRoot); - } + // 62: + + if (charCode == plus) { + return 62; + } - return this.__originalMappings; + // 63: / + if (charCode == slash) { + return 63; } -}); -SourceMapConsumer.prototype._charIsMappingSeparator = - function SourceMapConsumer_charIsMappingSeparator(aStr, index) { - var c = aStr.charAt(index); - return c === ";" || c === ","; - }; + // Invalid base64 digit. + return -1; +}; -/** - * 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"); - }; -SourceMapConsumer.GENERATED_ORDER = 1; -SourceMapConsumer.ORIGINAL_ORDER = 2; +/***/ }), +/* 620 */ +/***/ (function(module, exports) { -SourceMapConsumer.GREATEST_LOWER_BOUND = 1; -SourceMapConsumer.LEAST_UPPER_BOUND = 2; +/* -*- 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 + */ /** - * Iterate over each mapping between an original source/line/column and a - * generated line/column in this source map. + * This is a helper function for getting values from parameter/options + * objects. * - * @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`. + * @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. */ -SourceMapConsumer.prototype.eachMapping = - function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { - var context = aContext || null; - var order = aOrder || SourceMapConsumer.GENERATED_ORDER; +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; - 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 urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; +var dataUrlRegexp = /^data:.+\,.+$/; - 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); +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; -/** - * 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: +function urlGenerate(aParsedUrl) { + var url = ''; + if (aParsedUrl.scheme) { + url += aParsedUrl.scheme + ':'; + } + url += '//'; + if (aParsedUrl.auth) { + url += aParsedUrl.auth + '@'; + } + if (aParsedUrl.host) { + url += aParsedUrl.host; + } + if (aParsedUrl.port) { + url += ":" + aParsedUrl.port + } + if (aParsedUrl.path) { + url += aParsedUrl.path; + } + return url; +} +exports.urlGenerate = urlGenerate; + +/** + * Normalizes a path, or the path portion of a URL: * - * - source: The filename of the original source. - * - line: The line number in the original source. - * - column: Optional. the column number in the original source. + * - Replaces consecutive slashes with one slash. + * - Removes unnecessary '.' parts. + * - Removes unnecessary '<dir>/..' parts. * - * and an array of objects is returned, each with the following properties: + * Based on code in the Node.js 'path' core module. * - * - line: The line number in the generated source, or null. - * - column: The column number in the generated source, or null. + * @param aPath The path or url to normalize. */ -SourceMapConsumer.prototype.allGeneratedPositionsFor = - function SourceMapConsumer_allGeneratedPositionsFor(aArgs) { - var line = util.getArg(aArgs, 'line'); - - // 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) - }; - - if (this.sourceRoot != null) { - needle.source = util.relative(this.sourceRoot, needle.source); +function normalize(aPath) { + var path = aPath; + var url = urlParse(aPath); + if (url) { + if (!url.path) { + return aPath; } - if (!this._sources.has(needle.source)) { - return []; + 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; + } else { + parts.splice(i, 2); + up--; + } } - needle.source = this._sources.indexOf(needle.source); + } + path = parts.join('/'); - var mappings = []; + if (path === '') { + path = isAbsolute ? '/' : '.'; + } - var index = this._findMapping(needle, - this._originalMappings, - "originalLine", - "originalColumn", - util.compareByOriginalPositions, - binarySearch.LEAST_UPPER_BOUND); - if (index >= 0) { - var mapping = this._originalMappings[index]; + if (url) { + url.path = path; + return urlGenerate(url); + } + return path; +} +exports.normalize = normalize; - if (aArgs.column === undefined) { - var originalLine = mapping.originalLine; +/** + * 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 || '/'; + } - // 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) - }); + // `join(foo, '//www.example.org')` + if (aPathUrl && !aPathUrl.scheme) { + if (aRootUrl) { + aPathUrl.scheme = aRootUrl.scheme; + } + return urlGenerate(aPathUrl); + } - mapping = this._originalMappings[++index]; - } - } else { - var originalColumn = mapping.originalColumn; + if (aPathUrl || aPath.match(dataUrlRegexp)) { + return aPath; + } - // 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) - }); + // `join('http://', 'www.example.com')` + if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { + aRootUrl.host = aPath; + return urlGenerate(aRootUrl); + } - mapping = this._originalMappings[++index]; - } - } - } + var joined = aPath.charAt(0) === '/' + ? aPath + : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); - return mappings; - }; + if (aRootUrl) { + aRootUrl.path = joined; + return urlGenerate(aRootUrl); + } + return joined; +} +exports.join = join; -exports.SourceMapConsumer = SourceMapConsumer; +exports.isAbsolute = function (aPath) { + return aPath.charAt(0) === '/' || !!aPath.match(urlRegexp); +}; /** - * 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;" - * } + * Make a path relative to a URL or another path. * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1# + * @param aRoot The root path or URL. + * @param aPath The path or URL to be made relative to aRoot. */ -function BasicSourceMapConsumer(aSourceMap) { - var sourceMap = aSourceMap; - if (typeof aSourceMap === 'string') { - sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); +function relative(aRoot, aPath) { + if (aRoot === "") { + aRoot = "."; } - 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); + aRoot = aRoot.replace(/\/$/, ''); - // 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); - } + // 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; + } - 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; - }); + // 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; + } - // 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); + ++level; + } - this.sourceRoot = sourceRoot; - this.sourcesContent = sourcesContent; - this._mappings = mappings; - this.file = file; + // 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; -BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); -BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer; +var supportsNullProto = (function () { + var obj = Object.create(null); + return !('__proto__' in obj); +}()); + +function identity (s) { + return s; +} /** - * Create a BasicSourceMapConsumer from a SourceMapGenerator. + * Because behavior goes wacky when you set `__proto__` on objects, we + * have to prefix all the strings in our set with an arbitrary character. * - * @param SourceMapGenerator aSourceMap - * The source map that will be consumed. - * @returns BasicSourceMapConsumer + * See https://github.com/mozilla/source-map/pull/31 and + * https://github.com/mozilla/source-map/issues/30 + * + * @param String aStr */ -BasicSourceMapConsumer.fromSourceMap = - function SourceMapConsumer_fromSourceMap(aSourceMap) { - var smc = Object.create(BasicSourceMapConsumer.prototype); +function toSetString(aStr) { + if (isProtoString(aStr)) { + return '$' + aStr; + } - 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; + return aStr; +} +exports.toSetString = supportsNullProto ? identity : toSetString; - // 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. +function fromSetString(aStr) { + if (isProtoString(aStr)) { + return aStr.slice(1); + } - var generatedMappings = aSourceMap._mappings.toArray().slice(); - var destGeneratedMappings = smc.__generatedMappings = []; - var destOriginalMappings = smc.__originalMappings = []; + return aStr; +} +exports.fromSetString = supportsNullProto ? identity : fromSetString; - 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; +function isProtoString(s) { + if (!s) { + return false; + } - if (srcMapping.source) { - destMapping.source = sources.indexOf(srcMapping.source); - destMapping.originalLine = srcMapping.originalLine; - destMapping.originalColumn = srcMapping.originalColumn; + var length = s.length; - if (srcMapping.name) { - destMapping.name = names.indexOf(srcMapping.name); - } + if (length < 9 /* "__proto__".length */) { + return false; + } - destOriginalMappings.push(destMapping); - } + 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; + } - destGeneratedMappings.push(destMapping); + for (var i = length - 10; i >= 0; i--) { + if (s.charCodeAt(i) !== 36 /* '$' */) { + return false; } + } - quickSort(smc.__originalMappings, util.compareByOriginalPositions); - - return smc; - }; + return true; +} /** - * The version of the source mapping spec that we are consuming. + * 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. */ -BasicSourceMapConsumer.prototype._version = 3; +function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { + var cmp = mappingA.source - mappingB.source; + if (cmp !== 0) { + return cmp; + } -/** - * 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); + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; } -}); -/** - * 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; + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0 || onlyCompareOriginal) { + return cmp; + } + + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0) { + return cmp; + } + + cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; + } + + return mappingA.name - mappingB.name; } +exports.compareByOriginalPositions = compareByOriginalPositions; /** - * 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). + * 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. */ -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; +function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) { + var cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp !== 0) { + return cmp; + } - while (index < length) { - if (aStr.charAt(index) === ';') { - generatedLine++; - index++; - previousGeneratedColumn = 0; - } - else if (aStr.charAt(index) === ',') { - index++; - } - else { - mapping = new Mapping(); - mapping.generatedLine = generatedLine; + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0 || onlyCompareGenerated) { + return cmp; + } - // 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); + cmp = mappingA.source - mappingB.source; + if (cmp !== 0) { + return cmp; + } - 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); - } + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; + } - if (segment.length === 2) { - throw new Error('Found a source, but no line and column'); - } + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0) { + return cmp; + } + + return mappingA.name - mappingB.name; +} +exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; + +function strcmp(aStr1, aStr2) { + if (aStr1 === aStr2) { + return 0; + } - if (segment.length === 3) { - throw new Error('Found a source and line, but no column'); - } + if (aStr1 > aStr2) { + return 1; + } - cachedSegments[str] = segment; - } + return -1; +} - // Generated column. - mapping.generatedColumn = previousGeneratedColumn + segment[0]; - previousGeneratedColumn = mapping.generatedColumn; +/** + * 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 (segment.length > 1) { - // Original source. - mapping.source = previousSource + segment[1]; - previousSource += segment[1]; + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp !== 0) { + return cmp; + } - // Original line. - mapping.originalLine = previousOriginalLine + segment[2]; - previousOriginalLine = mapping.originalLine; - // Lines are stored 0-based - mapping.originalLine += 1; + cmp = strcmp(mappingA.source, mappingB.source); + if (cmp !== 0) { + return cmp; + } - // Original column. - mapping.originalColumn = previousOriginalColumn + segment[3]; - previousOriginalColumn = mapping.originalColumn; + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp !== 0) { + return cmp; + } - if (segment.length > 4) { - // Original name. - mapping.name = previousName + segment[4]; - previousName += segment[4]; - } - } + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp !== 0) { + return cmp; + } - generatedMappings.push(mapping); - if (typeof mapping.originalLine === 'number') { - originalMappings.push(mapping); - } - } - } + return strcmp(mappingA.name, mappingB.name); +} +exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; - quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated); - this.__generatedMappings = generatedMappings; - quickSort(originalMappings, util.compareByOriginalPositions); - this.__originalMappings = originalMappings; - }; +/***/ }), +/* 621 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Find the mapping that best matches the hypothetical "needle" mapping that - * we are searching for in the given "haystack" of mappings. +/* -*- 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 */ -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. - - 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]); - } - return binarySearch.search(aNeedle, aMappings, aComparator, aBias); - }; +var util = __webpack_require__(620); +var has = Object.prototype.hasOwnProperty; +var hasNativeMap = typeof Map !== "undefined"; /** - * Compute the last column for each generated mapping. The last column is - * inclusive. + * 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. */ -BasicSourceMapConsumer.prototype.computeColumnSpans = - function SourceMapConsumer_computeColumnSpans() { - for (var index = 0; index < this._generatedMappings.length; ++index) { - var mapping = this._generatedMappings[index]; +function ArraySet() { + this._array = []; + this._set = hasNativeMap ? new Map() : Object.create(null); +} - // 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]; +/** + * 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; +}; - if (mapping.generatedLine === nextMapping.generatedLine) { - mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1; - continue; - } - } +/** + * 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; +}; - // The last mapping for each line spans the entire line. - mapping.lastGeneratedColumn = Infinity; +/** + * 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; } - }; + } +}; /** - * 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: + * Is the given string a member of this set? * - * - 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. + * @param String aStr */ -BasicSourceMapConsumer.prototype.originalPositionFor = - function SourceMapConsumer_originalPositionFor(aArgs) { - var needle = { - generatedLine: util.getArg(aArgs, 'line'), - generatedColumn: util.getArg(aArgs, 'column') - }; - - var index = this._findMapping( - needle, - this._generatedMappings, - "generatedLine", - "generatedColumn", - util.compareByGeneratedPositionsDeflated, - util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) - ); - - if (index >= 0) { - var mapping = this._generatedMappings[index]; +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); + } +}; - 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 - }; - } +/** + * 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]; } + } - return { - source: null, - line: null, - column: null, - name: null - }; - }; + throw new Error('"' + aStr + '" is not in the set.'); +}; /** - * Return true if we have the source content for every source in the source - * map, false otherwise. + * What is the element at the given index? + * + * @param Number aIdx */ -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; }); - }; +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 the original source content. The only argument is the url of the - * original source file. Returns null if no original source content is - * available. + * 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. */ -BasicSourceMapConsumer.prototype.sourceContentFor = - function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { - if (!this.sourcesContent) { - return null; - } +ArraySet.prototype.toArray = function ArraySet_toArray() { + return this._array.slice(); +}; - if (this.sourceRoot != null) { - aSource = util.relative(this.sourceRoot, aSource); - } +exports.ArraySet = ArraySet; - if (this._sources.has(aSource)) { - return this.sourcesContent[this._sources.indexOf(aSource)]; - } - 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)] - } +/***/ }), +/* 622 */ +/***/ (function(module, exports, __webpack_require__) { - if ((!url.path || url.path == "/") - && this._sources.has("/" + aSource)) { - return this.sourcesContent[this._sources.indexOf("/" + aSource)]; - } - } +/* -*- 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 + */ - // 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.'); - } - }; +var util = __webpack_require__(620); /** - * 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. + * Determine whether mappingB is after mappingA with respect to generated + * position. */ -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); - - var needle = { - source: source, - originalLine: util.getArg(aArgs, 'line'), - originalColumn: util.getArg(aArgs, 'column') - }; - - 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]; +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; +} - if (mapping.source === needle.source) { - return { - line: util.getArg(mapping, 'generatedLine', null), - column: util.getArg(mapping, 'generatedColumn', null), - lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) - }; - } - } +/** + * 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}; +} - return { - line: null, - column: null, - lastColumn: null - }; +/** + * 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); }; -exports.BasicSourceMapConsumer = BasicSourceMapConsumer; - /** - * 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. + * Add the given source mapping. * - * { - * 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;" - * } - * }], - * } + * @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); + } +}; + +/** + * Returns the flat, sorted array of mappings. The mappings are sorted by + * generated position. * - * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt + * 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; +}; + +exports.MappingList = MappingList; + + +/***/ }), +/* 623 */ +/***/ (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 */ -function IndexedSourceMapConsumer(aSourceMap) { + +var util = __webpack_require__(620); +var binarySearch = __webpack_require__(624); +var ArraySet = __webpack_require__(621).ArraySet; +var base64VLQ = __webpack_require__(618); +var quickSort = __webpack_require__(625).quickSort; + +function SourceMapConsumer(aSourceMap) { var sourceMap = aSourceMap; if (typeof aSourceMap === 'string') { sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); } - var version = util.getArg(sourceMap, 'version'); - var sections = util.getArg(sourceMap, 'sections'); - - if (version != this._version) { - throw new Error('Unsupported version: ' + version); - } - - this._sources = new ArraySet(); - this._names = new ArraySet(); - - 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'); - - if (offsetLine < lastOffset.line || - (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) { - throw new Error('Section offsets must be ordered and non-overlapping.'); - } - 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')) - } - }); + return sourceMap.sections != null + ? new IndexedSourceMapConsumer(sourceMap) + : new BasicSourceMapConsumer(sourceMap); } -IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); -IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer; +SourceMapConsumer.fromSourceMap = function(aSourceMap) { + return BasicSourceMapConsumer.fromSourceMap(aSourceMap); +} /** * The version of the source mapping spec that we are consuming. */ -IndexedSourceMapConsumer.prototype._version = 3; +SourceMapConsumer.prototype._version = 3; -/** - * The list of original sources. - */ -Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', { +// `__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. + +SourceMapConsumer.prototype.__generatedMappings = null; +Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { 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 (!this.__generatedMappings) { + this._parseMappings(this._mappings, this.sourceRoot); } - return sources; + + return this.__generatedMappings; } }); -/** - * 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') - }; - - // 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; - } - - return (needle.generatedColumn - - section.generatedOffset.generatedColumn); - }); - var section = this._sections[sectionIndex]; - - if (!section) { - return { - source: null, - line: null, - column: null, - name: null - }; +SourceMapConsumer.prototype.__originalMappings = null; +Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { + get: function () { + if (!this.__originalMappings) { + this._parseMappings(this._mappings, this.sourceRoot); } - 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 - }); + return this.__originalMappings; + } +}); + +SourceMapConsumer.prototype._charIsMappingSeparator = + function SourceMapConsumer_charIsMappingSeparator(aStr, index) { + var c = aStr.charAt(index); + return c === ";" || c === ","; }; /** - * Return true if we have the source content for every source in the source - * map, false otherwise. + * 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.hasContentsOfAllSources = - function IndexedSourceMapConsumer_hasContentsOfAllSources() { - return this._sections.every(function (s) { - return s.consumer.hasContentsOfAllSources(); - }); +SourceMapConsumer.prototype._parseMappings = + function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { + throw new Error("Subclasses must implement _parseMappings"); }; +SourceMapConsumer.GENERATED_ORDER = 1; +SourceMapConsumer.ORIGINAL_ORDER = 2; + +SourceMapConsumer.GREATEST_LOWER_BOUND = 1; +SourceMapConsumer.LEAST_UPPER_BOUND = 2; + /** - * 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. + * 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`. */ -IndexedSourceMapConsumer.prototype.sourceContentFor = - function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) { - for (var i = 0; i < this._sections.length; i++) { - var section = this._sections[i]; +SourceMapConsumer.prototype.eachMapping = + function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) { + var context = aContext || null; + var order = aOrder || SourceMapConsumer.GENERATED_ORDER; - 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.'); + 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); }; /** - * 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: + * 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: The column number in the original source. + * - column: Optional. the column number in the original source. * - * and an object is returned with the following properties: + * 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. */ -IndexedSourceMapConsumer.prototype.generatedPositionFor = - function IndexedSourceMapConsumer_generatedPositionFor(aArgs) { - for (var i = 0; i < this._sections.length; i++) { - var section = this._sections[i]; +SourceMapConsumer.prototype.allGeneratedPositionsFor = + function SourceMapConsumer_allGeneratedPositionsFor(aArgs) { + var line = util.getArg(aArgs, 'line'); - // 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; - } - 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; - } + // 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) + }; + + 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); - return { - line: null, - column: null - }; - }; + var mappings = []; -/** - * 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]; + var index = this._findMapping(needle, + this._originalMappings, + "originalLine", + "originalColumn", + util.compareByOriginalPositions, + binarySearch.LEAST_UPPER_BOUND); + if (index >= 0) { + var mapping = this._originalMappings[index]; - 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); + if (aArgs.column === undefined) { + var originalLine = mapping.originalLine; - var name = section.consumer._names.at(mapping.name); - this._names.add(name); - name = this._names.indexOf(name); + // 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) + }); - // 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 - }; + mapping = this._originalMappings[++index]; + } + } else { + var originalColumn = mapping.originalColumn; - this.__generatedMappings.push(adjustedMapping); - if (typeof adjustedMapping.originalLine === 'number') { - this.__originalMappings.push(adjustedMapping); + // 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) + }); + + mapping = this._originalMappings[++index]; } } } - quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated); - quickSort(this.__originalMappings, util.compareByOriginalPositions); + return mappings; }; -exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; - - -/***/ }), -/* 607 */ -/***/ (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; +exports.SourceMapConsumer = SourceMapConsumer; /** - * Recursive implementation of binary search. + * 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. * - * @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); - } - - // 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); - } - - // 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; - } - } -} - -/** - * 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. + * 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: * - * @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'. + * - 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# */ -exports.search = function search(aNeedle, aHaystack, aCompare, aBias) { - if (aHaystack.length === 0) { - return -1; +function BasicSourceMapConsumer(aSourceMap) { + var sourceMap = aSourceMap; + if (typeof aSourceMap === 'string') { + sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, '')); } - var index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, - aCompare, aBias || exports.GREATEST_LOWER_BOUND); - if (index < 0) { - return -1; - } + 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); - // 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; + // 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); } - return index; -}; - + 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; + }); -/***/ }), -/* 608 */ -/***/ (function(module, exports) { + // 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); -/* -*- 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 - */ + this.sourceRoot = sourceRoot; + this.sourcesContent = sourcesContent; + this._mappings = mappings; + this.file = file; +} -// 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`. +BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); +BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer; /** - * Swap the elements indexed by `x` and `y` in the array `ary`. + * Create a BasicSourceMapConsumer from a SourceMapGenerator. * - * @param {Array} ary - * The array. - * @param {Number} x - * The index of the first item. - * @param {Number} y - * The index of the second item. + * @param SourceMapGenerator aSourceMap + * The source map that will be consumed. + * @returns BasicSourceMapConsumer */ -function swap(ary, x, y) { - var temp = ary[x]; - ary[x] = ary[y]; - ary[y] = temp; -} +BasicSourceMapConsumer.fromSourceMap = + function SourceMapConsumer_fromSourceMap(aSourceMap) { + var smc = Object.create(BasicSourceMapConsumer.prototype); -/** - * 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))); -} + 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; -/** - * 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 - */ -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. + // 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. - 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. + var generatedMappings = aSourceMap._mappings.toArray().slice(); + var destGeneratedMappings = smc.__generatedMappings = []; + var destOriginalMappings = smc.__originalMappings = []; - // 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; + 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; - swap(ary, pivotIndex, r); - var pivot = ary[r]; + if (srcMapping.source) { + destMapping.source = sources.indexOf(srcMapping.source); + destMapping.originalLine = srcMapping.originalLine; + destMapping.originalColumn = srcMapping.originalColumn; - // 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); + if (srcMapping.name) { + destMapping.name = names.indexOf(srcMapping.name); + } + + destOriginalMappings.push(destMapping); } - } - swap(ary, i + 1, j); - var q = i + 1; + destGeneratedMappings.push(destMapping); + } - // (2) Recurse on each half. + quickSort(smc.__originalMappings, util.compareByOriginalPositions); - doQuickSort(ary, comparator, p, q - 1); - doQuickSort(ary, comparator, q + 1, r); - } -} + return smc; + }; /** - * 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. + * The version of the source mapping spec that we are consuming. */ -exports.quickSort = function (ary, comparator) { - doQuickSort(ary, comparator, 0, ary.length - 1); -}; - - -/***/ }), -/* 609 */ -/***/ (function(module, exports, __webpack_require__) { +BasicSourceMapConsumer.prototype._version = 3; -/* -*- 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 +/** + * The list of original sources. */ - -var SourceMapGenerator = __webpack_require__(600).SourceMapGenerator; -var util = __webpack_require__(603); - -// 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$$$"; +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); + } +}); /** - * 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. + * Provide the JIT with a nice shape / hidden class. */ -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); +function Mapping() { + this.generatedLine = 0; + this.generatedColumn = 0; + this.source = null; + this.originalLine = null; + this.originalColumn = null; + this.name = null; } /** - * 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. + * 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). */ -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; +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; - function getNextLine() { - return remainingLinesIndex < remainingLines.length ? - remainingLines[remainingLinesIndex++] : undefined; + while (index < length) { + if (aStr.charAt(index) === ';') { + generatedLine++; + index++; + previousGeneratedColumn = 0; } - }; - - // We need to remember the position of "remainingLines" - var lastGeneratedLine = 1, lastGeneratedColumn = 0; + else if (aStr.charAt(index) === ',') { + index++; + } + else { + mapping = new Mapping(); + mapping.generatedLine = generatedLine; - // 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; + // 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); - 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 + segment = cachedSegments[str]; + if (segment) { + index += str.length; } 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("")); - } + segment = []; + while (index < end) { + base64VLQ.decode(aStr, index, temp); + value = temp.value; + index = temp.rest; + segment.push(value); + } - // 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); + 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'); + } + + cachedSegments[str] = segment; } - node.setSourceContent(sourceFile, content); - } - }); - return node; + // Generated column. + mapping.generatedColumn = previousGeneratedColumn + segment[0]; + previousGeneratedColumn = mapping.generatedColumn; - 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)); - } - } - }; + if (segment.length > 1) { + // Original source. + mapping.source = previousSource + segment[1]; + previousSource += segment[1]; -/** - * 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); - } - 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; -}; + // Original line. + mapping.originalLine = previousOriginalLine + segment[2]; + previousOriginalLine = mapping.originalLine; + // Lines are stored 0-based + mapping.originalLine += 1; -/** - * 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; -}; + // Original column. + mapping.originalColumn = previousOriginalColumn + segment[3]; + previousOriginalColumn = mapping.originalColumn; -/** - * 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 aFn The traversal function. - */ -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 }); + if (segment.length > 4) { + // Original name. + mapping.name = previousName + segment[4]; + previousName += segment[4]; + } + } + + generatedMappings.push(mapping); + if (typeof mapping.originalLine === 'number') { + originalMappings.push(mapping); + } } } - } -}; -/** - * 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 this; -}; + quickSort(generatedMappings, util.compareByGeneratedPositionsDeflated); + this.__generatedMappings = generatedMappings; -/** - * 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); - } - else if (typeof lastChild === 'string') { - this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); - } - else { - this.children.push(''.replace(aPattern, aReplacement)); - } - return this; -}; + quickSort(originalMappings, util.compareByOriginalPositions); + this.__originalMappings = originalMappings; + }; /** - * 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 + * Find the mapping that best matches the hypothetical "needle" mapping that + * we are searching for in the given "haystack" of mappings. */ -SourceNode.prototype.setSourceContent = - function SourceNode_setSourceContent(aSourceFile, aSourceContent) { - this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; +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. + + 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]); + } + + return binarySearch.search(aNeedle, aMappings, aComparator, aBias); }; /** - * 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 aFn The traversal function. + * Compute the last column for each generated mapping. The last column is + * inclusive. */ -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); +BasicSourceMapConsumer.prototype.computeColumnSpans = + function SourceMapConsumer_computeColumnSpans() { + for (var index = 0; index < this._generatedMappings.length; ++index) { + var mapping = this._generatedMappings[index]; + + // 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; + } } - } - 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]]); + // The last mapping for each line spans the entire line. + mapping.lastGeneratedColumn = Infinity; } }; /** - * Return the string representation of this source node. Walks over the tree - * and concatenates all the various snippets together to one string. + * 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. */ -SourceNode.prototype.toString = function SourceNode_toString() { - var str = ""; - this.walk(function (chunk) { - str += chunk; - }); - return str; -}; +BasicSourceMapConsumer.prototype.originalPositionFor = + function SourceMapConsumer_originalPositionFor(aArgs) { + var needle = { + generatedLine: util.getArg(aArgs, 'line'), + generatedColumn: util.getArg(aArgs, 'column') + }; -/** - * 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 + var index = this._findMapping( + needle, + this._generatedMappings, + "generatedLine", + "generatedColumn", + util.compareByGeneratedPositionsDeflated, + util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) + ); + + if (index >= 0) { + var mapping = this._generatedMappings[index]; + + 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); + } } - }); - 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 - }); + var name = util.getArg(mapping, 'name', null); + if (name !== null) { + name = this._names.at(name); } - } else { - generated.column++; + return { + source: source, + line: util.getArg(mapping, 'originalLine', null), + column: util.getArg(mapping, 'originalColumn', null), + name: name + }; } } - }); - this.walkSourceContents(function (sourceFile, sourceContent) { - map.setSourceContent(sourceFile, sourceContent); - }); - - return { code: generated.code, map: map }; -}; - -exports.SourceNode = SourceNode; - - -/***/ }), -/* 610 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright 2014, 2015, 2016, 2017 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) - -var sourceMappingURL = __webpack_require__(611) -var resolveUrl = __webpack_require__(612) -var decodeUriComponent = __webpack_require__(613) -var urix = __webpack_require__(615) -var atob = __webpack_require__(616) + return { + source: null, + line: null, + column: null, + name: null + }; + }; +/** + * 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; }); + }; -function callbackAsync(callback, error, result) { - setImmediate(function() { callback(error, result) }) -} +/** + * 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; + } -function parseMapToJSON(string, data) { - try { - return JSON.parse(string.replace(/^\)\]\}'/, "")) - } catch (error) { - error.sourceMapData = data - throw error - } -} + if (this.sourceRoot != null) { + aSource = util.relative(this.sourceRoot, aSource); + } -function readSync(read, url, data) { - var readUrl = decodeUriComponent(url) - try { - return String(read(readUrl)) - } catch (error) { - error.sourceMapData = data - throw error - } -} + if (this._sources.has(aSource)) { + return this.sourcesContent[this._sources.indexOf(aSource)]; + } + 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)] + } + if ((!url.path || url.path == "/") + && this._sources.has("/" + aSource)) { + return this.sourcesContent[this._sources.indexOf("/" + aSource)]; + } + } -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) - } - var readUrl = decodeUriComponent(mapData.url) - read(readUrl, function(error, result) { - if (error) { - error.sourceMapData = mapData - return callback(error) + // 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; } - mapData.map = String(result) - try { - mapData.map = parseMapToJSON(mapData.map, mapData) - } catch (error) { - return callback(error) + else { + throw new Error('"' + aSource + '" is not in the SourceMap.'); } - callback(null, mapData) - }) -} + }; -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 -} +/** + * 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); -var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/ -var jsonMimeTypeRegex = /^(?:application|text)\/json$/ + var needle = { + source: source, + originalLine: util.getArg(aArgs, 'line'), + originalColumn: util.getArg(aArgs, 'column') + }; -function resolveSourceMapHelper(code, codeUrl) { - codeUrl = urix(codeUrl) + var index = this._findMapping( + needle, + this._originalMappings, + "originalLine", + "originalColumn", + util.compareByOriginalPositions, + util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND) + ); - var url = sourceMappingURL.getFrom(code) - if (!url) { - return null - } + if (index >= 0) { + var mapping = this._originalMappings[index]; - 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 + if (mapping.source === needle.source) { + return { + line: util.getArg(mapping, 'generatedLine', null), + column: util.getArg(mapping, 'generatedColumn', null), + lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null) + }; + } } - 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 + return { + line: null, + column: null, + lastColumn: null + }; + }; + +exports.BasicSourceMapConsumer = BasicSourceMapConsumer; + +/** + * 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(/^\)\]\}'/, '')); } -} - + var version = util.getArg(sourceMap, 'version'); + var sections = util.getArg(sourceMap, 'sections'); -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: [] + if (version != this._version) { + throw new Error('Unsupported version: ' + version); } - if (pending === 0) { - callbackAsync(callback, null, result) - return - } + this._sources = new ArraySet(); + this._names = new ArraySet(); - var done = function() { - pending-- - if (pending === 0) { - callback(null, result) + 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'); - 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() - }) + if (offsetLine < lastOffset.line || + (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) { + throw new Error('Section offsets must be ordered and non-overlapping.'); } - }) + 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')) + } + }); } -function resolveSourcesSync(map, mapUrl, read, options) { - var result = { - sourcesResolved: [], - sourcesContent: [] - } +IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype); +IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer; - if (!map.sources || map.sources.length === 0) { - return result - } +/** + * The version of the source mapping spec that we are consuming. + */ +IndexedSourceMapConsumer.prototype._version = 3; - 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 - } +/** + * 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; + } +}); - return result -} +/** + * 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') + }; -var endingSlash = /\/?$/ + // 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; + } -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/<source>`, not `/scripts/<source>`. Pointing to a file as source root - // does not make sense. - fullUrl = resolveUrl(mapUrl, sourceRoot.replace(endingSlash, "/"), map.sources[index]) + return (needle.generatedColumn - + section.generatedOffset.generatedColumn); + }); + var section = this._sections[sectionIndex]; + + if (!section) { + return { + source: null, + line: null, + column: null, + name: null + }; } - sourceContent = (map.sourcesContent || [])[index] - fn(fullUrl, sourceContent, index) - } -} + 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 + }); + }; +/** + * 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(); + }); + }; -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) - }) - } +/** + * 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]; - function _resolveSources(mapData) { - resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) { - if (error) { - return callback(error) + var content = section.consumer.sourceContentFor(aSource, true); + if (content) { + return content; } - mapData.sourcesResolved = result.sourcesResolved - mapData.sourcesContent = result.sourcesContent - callback(null, mapData) - }) - } -} - -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 + if (nullOnMissing) { + return null; } - } - 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 -} - - -/***/ }), -/* 611 */ -/***/ (function(module, exports, __webpack_require__) { - -var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) - -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() { + else { + throw new Error('"' + aSource + '" is not in the SourceMap.'); + } + }; - var innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/ +/** + * 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 regex = RegExp( - "(?:" + - "/\\*" + - "(?:\\s*\r?\n(?://)?)?" + - "(?:" + innerRegex.source + ")" + - "\\s*" + - "\\*/" + - "|" + - "//(?:" + innerRegex.source + ")" + - ")" + - "\\s*" - ) + // 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; + } + 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; + } + } - return { + return { + line: null, + column: null + }; + }; - regex: regex, - _innerRegex: innerRegex, +/** + * 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]; - getFrom: function(code) { - var match = code.match(regex) - return (match ? match[1] || match[2] || "" : null) - }, + 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); - existsIn: function(code) { - return regex.test(code) - }, + var name = section.consumer._names.at(mapping.name); + this._names.add(name); + name = this._names.indexOf(name); - removeFrom: function(code) { - return code.replace(regex, "") - }, + // 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 + }; - 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 + this.__generatedMappings.push(adjustedMapping); + if (typeof adjustedMapping.originalLine === 'number') { + this.__originalMappings.push(adjustedMapping); + } } } - } - -})); - -/***/ }), -/* 612 */ -/***/ (function(module, exports, __webpack_require__) { - -// Copyright 2014 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) + quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated); + quickSort(this.__originalMappings, util.compareByOriginalPositions); + }; -var url = __webpack_require__(196) +exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer; -function resolveUrl(/* ...urls */) { - return Array.prototype.reduce.call(arguments, function(resolved, nextUrl) { - return url.resolve(resolved, nextUrl) - }) -} -module.exports = resolveUrl +/***/ }), +/* 624 */ +/***/ (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 + */ -/***/ }), -/* 613 */ -/***/ (function(module, exports, __webpack_require__) { +exports.GREATEST_LOWER_BOUND = 1; +exports.LEAST_UPPER_BOUND = 2; -// Copyright 2017 Simon Lydell -// X11 (“MIT”) Licensed. (See LICENSE.) +/** + * 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); + } -var decodeUriComponent = __webpack_require__(614) + // 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); + } -function customDecodeUriComponent(string) { - // `decodeUriComponent` turns `+` into ` `, but that's not wanted. - return decodeUriComponent(string.replace(/\+/g, "%2B")) + // 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; + } + } } -module.exports = customDecodeUriComponent - +/** + * 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; + } -/***/ }), -/* 614 */ -/***/ (function(module, exports, __webpack_require__) { + var index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, + aCompare, aBias || exports.GREATEST_LOWER_BOUND); + if (index < 0) { + return -1; + } -"use strict"; + // 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; + } -var token = '%[a-f0-9]{2}'; -var singleMatcher = new RegExp(token, 'gi'); -var multiMatcher = new RegExp('(' + token + ')+', 'gi'); + return index; +}; -function decodeComponents(components, split) { - try { - // Try to decode the entire string first - return decodeURIComponent(components.join('')); - } catch (err) { - // Do nothing - } - if (components.length === 1) { - return components; - } +/***/ }), +/* 625 */ +/***/ (function(module, exports) { - split = split || 1; +/* -*- 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 + */ - // Split the array in 2 parts - var left = components.slice(0, split); - var right = components.slice(split); +// 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 Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right)); +/** + * 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 swap(ary, x, y) { + var temp = ary[x]; + ary[x] = ary[y]; + ary[y] = temp; } -function decode(input) { - try { - return decodeURIComponent(input); - } catch (err) { - var tokens = input.match(singleMatcher); - - for (var i = 1; i < tokens.length; i++) { - input = decodeComponents(tokens, i).join(''); - - tokens = input.match(singleMatcher); - } - - return input; - } +/** + * 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 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' - }; +/** + * 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 + */ +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. - 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]); + 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. - if (result !== match[0]) { - replaceMap[match[0]] = result; - } - } + // 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; - match = multiMatcher.exec(input); - } + swap(ary, pivotIndex, r); + var pivot = ary[r]; - // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else - replaceMap['%C2'] = '\uFFFD'; + // 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); + } + } - var entries = Object.keys(replaceMap); + swap(ary, i + 1, j); + var q = i + 1; - 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]); - } + // (2) Recurse on each half. - return input; + doQuickSort(ary, comparator, p, q - 1); + doQuickSort(ary, comparator, q + 1, r); + } } -module.exports = function (encodedURI) { - if (typeof encodedURI !== 'string') { - throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`'); - } - - 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); - } +/** + * 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); }; /***/ }), -/* 615 */ +/* 626 */ /***/ (function(module, exports, __webpack_require__) { -// 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 +/* -*- 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 SourceMapGenerator = __webpack_require__(617).SourceMapGenerator; +var util = __webpack_require__(620); -/***/ }), -/* 616 */ -/***/ (function(module, exports, __webpack_require__) { +// 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)/; -"use strict"; +// 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$$$"; -function atob(str) { - return Buffer.from(str, 'base64').toString('binary'); +/** + * 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. + */ +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 = atob.atob = atob; +/** + * 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. + */ +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; -/***/ }), -/* 617 */ -/***/ (function(module, exports, __webpack_require__) { + function getNextLine() { + return remainingLinesIndex < remainingLines.length ? + remainingLines[remainingLinesIndex++] : undefined; + } + }; -"use strict"; + // 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; -var fs = __webpack_require__(133); -var path = __webpack_require__(4); -var define = __webpack_require__(521); -var utils = __webpack_require__(598); + 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("")); + } -/** - * 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 <tj@vision-media.ca> - */ + // 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); + } + }); -module.exports = mixin; + 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)); + } + } + }; /** - * Mixin source map support into `compiler`. + * Add a chunk of generated JS to this source node. * - * @param {Object} `compiler` - * @api public + * @param aChunk A string snippet of generated JS code, another instance of + * SourceNode, or an array where each member is one of those things. */ - -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]); +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 + ); + } + return this; +}; /** - * Update position. + * Add a chunk of generated JS to the beginning of this source node. * - * @param {String} str + * @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; +}; -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; +/** + * 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 aFn The traversal function. + */ +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 }); + } + } + } }; /** - * Emit `str` with `position`. + * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between + * each of `this.children`. * - * @param {String} str - * @param {Object} [pos] - * @return {String} + * @param aSep The separator. */ - -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 - } - }); - - if (position.content) { - this.addContent(source, position); - } - if (position.filepath) { - this.addFile(source, position); +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); } - - this.updatePosition(str); - this.output += str; + newChildren.push(this.children[i]); + this.children = newChildren; } - return str; + return this; }; /** - * Adds a file to the source map output if it has not already been added - * @param {String} `file` - * @param {Object} `pos` + * 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. */ - -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; +SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) { + var lastChild = this.children[this.children.length - 1]; + if (lastChild[isSourceNode]) { + lastChild.replaceRight(aPattern, aReplacement); + } + else if (typeof lastChild === 'string') { + this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement); + } + else { + this.children.push(''.replace(aPattern, aReplacement)); + } + return this; }; /** - * Adds a content source to the source map output if it has not already been added - * @param {String} `source` - * @param {Object} `position` + * 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 */ - -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); -}; +SourceNode.prototype.setSourceContent = + function SourceNode_setSourceContent(aSourceFile, aSourceContent) { + this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent; + }; /** - * Applies any original source maps to the output and embeds the source file - * contents in the source map. + * 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 aFn The traversal function. */ - -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))); +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); } } - }, this); + + 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; }; /** - * Process comments, drops sourceMap comments. - * @param {Object} node + * 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); + }); -exports.comment = function(node) { - if (/^# sourceMappingURL=/.test(node.comment)) { - return this.emit('', node.position); - } - return this._comment(node); + return { code: generated.code, map: map }; }; +exports.SourceNode = SourceNode; + /***/ }), -/* 618 */ +/* 627 */ /***/ (function(module, exports, __webpack_require__) { -"use strict"; +// Copyright 2014, 2015, 2016, 2017 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) +var sourceMappingURL = __webpack_require__(628) +var resolveUrl = __webpack_require__(629) +var decodeUriComponent = __webpack_require__(630) +var urix = __webpack_require__(632) +var atob = __webpack_require__(633) -var use = __webpack_require__(590); -var util = __webpack_require__(111); -var Cache = __webpack_require__(619); -var define = __webpack_require__(521); -var debug = __webpack_require__(592)('snapdragon:parser'); -var Position = __webpack_require__(620); -var utils = __webpack_require__(598); -/** - * Create a new `Parser` with the given `input` and `options`. - * @param {String} `input` - * @param {Object} `options` - * @api public - */ -function Parser(options) { - debug('initializing', __filename); - this.options = utils.extend({source: 'string'}, options); - this.init(this.options); - use(this); +function callbackAsync(callback, error, result) { + setImmediate(function() { callback(error, result) }) } -/** - * Prototype methods - */ +function parseMapToJSON(string, data) { + try { + return JSON.parse(string.replace(/^\)\]\}'/, "")) + } catch (error) { + error.sourceMapData = data + throw error + } +} -Parser.prototype = { - constructor: Parser, +function readSync(read, url, data) { + var readUrl = decodeUriComponent(url) + try { + return String(read(readUrl)) + } catch (error) { + error.sourceMapData = data + throw error + } +} - 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'; +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) + } + 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) + }) +} - var pos = this.position(); - this.bos = pos({type: 'bos', val: ''}); +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 +} - this.ast = { - type: 'root', - errors: this.errors, - nodes: [this.bos] - }; +var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/ +var jsonMimeTypeRegex = /^(?:application|text)\/json$/ - define(this.bos, 'parent', this.ast); - this.nodes = [this.ast]; +function resolveSourceMapHelper(code, codeUrl) { + codeUrl = urix(codeUrl) - this.count = 0; - this.setCount = 0; - this.stack = []; - }, + var url = sourceMappingURL.getFrom(code) + if (!url) { + return null + } - /** - * Throw a formatted error with the cursor column and `msg`. - * @param {String} `msg` Message to use in the Error. - */ + 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 + } - 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 mapUrl = resolveUrl(codeUrl, url) + return { + sourceMappingURL: url, + url: mapUrl, + sourcesRelativeTo: mapUrl, + map: null + } +} - var message = source + ' <line:' + line + ' column:' + column + '>: ' + 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; - } - }, - /** - * 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 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: [] + } - define: function(key, val) { - define(this, key, val); - return this; - }, + if (pending === 0) { + callbackAsync(callback, null, result) + return + } - /** - * Mark position and patch `node.position`. - */ + var done = function() { + pending-- + if (pending === 0) { + callback(null, result) + } + } - position: function() { - var start = { line: this.line, column: this.column }; - var self = this; + 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() + }) + } + }) +} - return function(node) { - define(node, 'position', new Position(start, self)); - return node; - }; - }, +function resolveSourcesSync(map, mapUrl, read, options) { + var result = { + sourcesResolved: [], + sourcesContent: [] + } - /** - * Set parser `name` with the given `fn` - * @param {String} `name` - * @param {Function} `fn` - * @api public - */ + if (!map.sources || map.sources.length === 0) { + return result + } - set: function(type, fn) { - if (this.types.indexOf(type) === -1) { - this.types.push(type); + 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 + } + } } - this.parsers[type] = fn.bind(this); - return this; - }, + }) - /** - * Get parser `name` - * @param {String} `name` - * @api public - */ + return result +} - get: function(name) { - return this.parsers[name]; - }, +var endingSlash = /\/?$/ - /** - * Push a `token` onto the `type` stack. - * - * @param {String} `type` - * @return {Object} `token` - * @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/<source>`, not `/scripts/<source>`. 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) + } +} - 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 - */ - pop: function(type) { - this.sets[type] = this.sets[type] || []; - this.count--; - this.stack.pop(); - return this.sets[type].pop(); - }, +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) + }) + } - /** - * Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`. - * - * @param {String} `type` - * @return {Boolean} - * @api public - */ + 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) + }) + } +} - isInside: function(type) { - this.sets[type] = this.sets[type] || []; - return this.sets[type].length > 0; - }, +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 +} - /** - * Return true if `node` is the given `type`. - * - * ```js - * parser.isType(node, 'brace'); - * ``` - * @param {Object} `node` - * @param {String} `type` - * @return {Boolean} - * @api public - */ - isType: function(node, type) { - return node && node.type === type; - }, - /** - * Get the previous AST node - * @return {Object} - */ +module.exports = { + resolveSourceMap: resolveSourceMap, + resolveSourceMapSync: resolveSourceMapSync, + resolveSources: resolveSources, + resolveSourcesSync: resolveSourcesSync, + resolve: resolve, + resolveSync: resolveSync, + parseMapToJSON: parseMapToJSON +} - prev: function(n) { - return this.stack.length > 0 - ? utils.last(this.stack, n) - : utils.last(this.nodes, n); - }, - /** - * Update line and column based on `str`. - */ +/***/ }), +/* 628 */ +/***/ (function(module, exports, __webpack_require__) { - consume: function(len) { - this.input = this.input.substr(len); - }, +var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) - /** - * Update column based on `str`. - */ +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() { - 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 innerRegex = /[#@] sourceMappingURL=([^\s'"]*)/ - /** - * Match `regex`, return captures, and update the cursor position by `match[0]` length. - * @param {RegExp} `regex` - * @return {Object} - */ + var regex = RegExp( + "(?:" + + "/\\*" + + "(?:\\s*\r?\n(?://)?)?" + + "(?:" + innerRegex.source + ")" + + "\\s*" + + "\\*/" + + "|" + + "//(?:" + innerRegex.source + ")" + + ")" + + "\\s*" + ) - match: function(regex) { - var m = regex.exec(this.input); - if (m) { - this.updatePosition(m[0], m[0].length); - return m; - } - }, + return { - /** - * Capture `type` with the given regex. - * @param {String} `type` - * @param {RegExp} `regex` - * @return {Function} - */ + regex: regex, + _innerRegex: innerRegex, - capture: function(type, regex) { - if (typeof regex === 'function') { - return this.set.apply(this, arguments); - } + getFrom: function(code) { + var match = code.match(regex) + return (match ? match[1] || match[2] || "" : null) + }, - 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; + existsIn: function(code) { + return regex.test(code) + }, - var prev = this.prev(); - var node = pos({ - type: type, - val: m[0], - parsed: parsed, - rest: this.input - }); + removeFrom: function(code) { + return code.replace(regex, "") + }, - if (m[1]) { - node.inner = m[1]; + 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 } + } + } - define(node, 'inside', this.stack.length > 0); - define(node, 'parent', prev); - prev.nodes.push(node); - }.bind(this)); - return this; - }, - - /** - * 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 - }); +/***/ }), +/* 629 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof m[1] !== 'undefined') { - open.inner = m[1]; - } +// Copyright 2014 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) - var prev = this.prev(); - var node = pos({ - type: type, - nodes: [open] - }); +var url = __webpack_require__(197) - define(node, 'rest', this.input); - define(node, 'parsed', parsed); - define(node, 'prefix', m[1]); - define(node, 'parent', prev); - define(open, 'parent', node); +function resolveUrl(/* ...urls */) { + return Array.prototype.reduce.call(arguments, function(resolved, nextUrl) { + return url.resolve(resolved, nextUrl) + }) +} - if (typeof fn === 'function') { - fn.call(this, open, node); - } +module.exports = resolveUrl - this.push(type, node); - prev.nodes.push(node); - }); - /** - * Close - */ +/***/ }), +/* 630 */ +/***/ (function(module, exports, __webpack_require__) { - this.set(type + '.close', function() { - var pos = this.position(); - var m = this.match(closeRegex); - if (!m || !m[0]) return; +// Copyright 2017 Simon Lydell +// X11 (“MIT”) Licensed. (See LICENSE.) - var parent = this.pop(type); - var node = pos({ - type: type + '.close', - rest: this.input, - suffix: m[1], - val: m[0] - }); +var decodeUriComponent = __webpack_require__(631) - if (!this.isType(parent, type)) { - if (this.options.strict) { - throw new Error('missing opening "' + type + '"'); - } +function customDecodeUriComponent(string) { + // `decodeUriComponent` turns `+` into ` `, but that's not wanted. + return decodeUriComponent(string.replace(/\+/g, "%2B")) +} - this.setCount--; - node.escaped = true; - return node; - } +module.exports = customDecodeUriComponent - if (node.suffix === '\\') { - parent.escaped = true; - node.escaped = true; - } - parent.nodes.push(node); - define(node, 'parent', parent); - }); +/***/ }), +/* 631 */ +/***/ (function(module, exports, __webpack_require__) { - return this; - }, +"use strict"; - /** - * Capture end-of-string - */ +var token = '%[a-f0-9]{2}'; +var singleMatcher = new RegExp(token, 'gi'); +var multiMatcher = new RegExp('(' + token + ')+', 'gi'); - eos: function() { - var pos = this.position(); - if (this.input) return; - var prev = this.prev(); +function decodeComponents(components, split) { + try { + // Try to decode the entire string first + return decodeURIComponent(components.join('')); + } catch (err) { + // Do nothing + } - while (prev.type !== 'root' && !prev.visited) { - if (this.options.strict === true) { - throw new SyntaxError('invalid syntax:' + util.inspect(prev, null, 2)); - } + if (components.length === 1) { + return components; + } - if (!hasDelims(prev)) { - prev.parent.escaped = true; - prev.escaped = true; - } + split = split || 1; - visit(prev, function(node) { - if (!hasDelims(node.parent)) { - node.parent.escaped = true; - node.escaped = true; - } - }); + // Split the array in 2 parts + var left = components.slice(0, split); + var right = components.slice(split); - prev = prev.parent; - } + return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right)); +} - var tok = pos({ - type: 'eos', - val: this.append || '' - }); +function decode(input) { + try { + return decodeURIComponent(input); + } catch (err) { + var tokens = input.match(singleMatcher); - define(tok, 'parent', this.ast); - return tok; - }, + for (var i = 1; i < tokens.length; i++) { + input = decodeComponents(tokens, i).join(''); - /** - * Run parsers to advance the cursor position - */ + tokens = input.match(singleMatcher); + } - next: function() { - var parsed = this.parsed; - var len = this.types.length; - var idx = -1; - var tok; + return input; + } +} - 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; - } - } - }, +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 given string. - * @return {Array} - */ + 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]); - parse: function(input) { - if (typeof input !== 'string') { - throw new TypeError('expected a string'); - } + if (result !== match[0]) { + replaceMap[match[0]] = result; + } + } - this.init(this.options); - this.orig = input; - this.input = input; - var self = this; + match = multiMatcher.exec(input); + } - function parse() { - // check input before calling `.next()` - input = self.input; + // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else + replaceMap['%C2'] = '\uFFFD'; - // 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); - } - } + var entries = Object.keys(replaceMap); - if (self.sets.hasOwnProperty(prev.type)) { - self.currentType = prev.type; - } - } + 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 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) + '"'); - } - } + return input; +} - 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 + '"'); - } +module.exports = function (encodedURI) { + if (typeof encodedURI !== 'string') { + throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`'); + } - var eos = this.eos(); - var tok = this.prev(); - if (tok.type !== 'eos') { - this.ast.nodes.push(eos); - } + try { + encodedURI = encodedURI.replace(/\+/g, ' '); - return this.ast; - } + // Try the built in decoder first + return decodeURIComponent(encodedURI); + } catch (err) { + // Fallback to a more advanced decoder + return customDecodeURIComponent(encodedURI); + } }; -/** - * Visit `node` with the given `fn` - */ -function visit(node, fn) { - if (!node.visited) { - define(node, 'visited', true); - return node.nodes ? mapVisit(node.nodes, fn) : fn(node); - } - return node; -} +/***/ }), +/* 632 */ +/***/ (function(module, exports, __webpack_require__) { -/** - * Map visit over array of `nodes`. - */ +// 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 -function mapVisit(nodes, fn) { - var len = nodes.length; - var idx = -1; - while (++idx < len) { - visit(nodes[idx], fn); - } -} -function hasOpen(node) { - return node.nodes && node.nodes[0].type === (node.type + '.open'); -} +/***/ }), +/* 633 */ +/***/ (function(module, exports, __webpack_require__) { -function hasClose(node) { - return node.nodes && utils.last(node.nodes).type === (node.type + '.close'); -} +"use strict"; -function hasDelims(node) { - return hasOpen(node) && hasClose(node); -} -/** - * Expose `Parser` - */ +function atob(str) { + return Buffer.from(str, 'base64').toString('binary'); +} -module.exports = Parser; +module.exports = atob.atob = atob; /***/ }), -/* 619 */ +/* 634 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * map-cache <https://github.com/jonschlinkert/map-cache> - * - * Copyright (c) 2015, Jon Schlinkert. - * Licensed under the MIT License. - */ - -var hasOwn = Object.prototype.hasOwnProperty; +var fs = __webpack_require__(134); +var path = __webpack_require__(4); +var define = __webpack_require__(596); +var utils = __webpack_require__(615); /** - * Expose `MapCache` + * 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 <tj@vision-media.ca> */ -module.exports = MapCache; +module.exports = mixin; /** - * Creates a cache object to store key/value pairs. - * - * ```js - * var cache = new MapCache(); - * ``` + * Mixin source map support into `compiler`. * + * @param {Object} `compiler` * @api public */ -function MapCache(data) { - this.__data__ = data || {}; +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]); + } } /** - * Adds `value` to `key` on the cache. - * - * ```js - * cache.set('foo', 'bar'); - * ``` + * Update position. * - * @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 + * @param {String} str */ -MapCache.prototype.set = function mapSet(key, value) { - if (key !== '__proto__') { - this.__data__[key] = value; - } - return this; +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; }; /** - * Gets the cached value for `key`. - * - * ```js - * cache.get('foo'); - * //=> 'bar' - * ``` + * Emit `str` with `position`. * - * @param {String} `key` The key of the value to get. - * @returns {*} Returns the cached value. - * @api public + * @param {String} str + * @param {Object} [pos] + * @return {String} */ -MapCache.prototype.get = function mapGet(key) { - return key === '__proto__' ? undefined : this.__data__[key]; +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 + } + }); + + if (position.content) { + this.addContent(source, position); + } + if (position.filepath) { + this.addFile(source, position); + } + + this.updatePosition(str); + this.output += str; + } + return str; }; /** - * 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 + * Adds a file to the source map output if it has not already been added + * @param {String} `file` + * @param {Object} `pos` */ -MapCache.prototype.has = function mapHas(key) { - return key !== '__proto__' && hasOwn.call(this.__data__, key); +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; }; /** - * 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 + * Adds a content source to the source map output if it has not already been added + * @param {String} `source` + * @param {Object} `position` */ -MapCache.prototype.del = function mapDelete(key) { - return this.has(key) && delete this.__data__[key]; +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); }; +/** + * Applies any original source maps to the output and embeds the source file + * contents in the source map. + */ -/***/ }), -/* 620 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - +exports.applySourceMaps = function() { + Object.keys(this.files).forEach(function(file) { + var content = this.files[file]; + this.map.setSourceContent(file, content); -var define = __webpack_require__(521); + 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))); + } + } + }, this); +}; /** - * Store position for a node + * Process comments, drops sourceMap comments. + * @param {Object} node */ -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); +exports.comment = function(node) { + if (/^# sourceMappingURL=/.test(node.comment)) { + return this.emit('', node.position); + } + return this._comment(node); }; /***/ }), -/* 621 */ +/* 635 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var safe = __webpack_require__(622); -var define = __webpack_require__(628); -var extend = __webpack_require__(629); -var not = __webpack_require__(631); -var MAX_LENGTH = 1024 * 64; - -/** - * Session cache - */ - -var cache = {}; +var use = __webpack_require__(607); +var util = __webpack_require__(112); +var Cache = __webpack_require__(636); +var define = __webpack_require__(596); +var debug = __webpack_require__(609)('snapdragon:parser'); +var Position = __webpack_require__(637); +var utils = __webpack_require__(615); /** - * Create a regular expression from the given `pattern` string. - * - * @param {String|RegExp} `pattern` Pattern can be a string or regular expression. + * Create a new `Parser` with the given `input` and `options`. + * @param {String} `input` * @param {Object} `options` - * @return {RegExp} * @api public */ -module.exports = function(patterns, options) { - if (!Array.isArray(patterns)) { - return makeRe(patterns, options); - } - return makeRe(patterns.join('|'), options); -}; +function Parser(options) { + debug('initializing', __filename); + this.options = utils.extend({source: 'string'}, options); + this.init(this.options); + use(this); +} /** - * 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 + * Prototype methods */ -function makeRe(pattern, options) { - if (pattern instanceof RegExp) { - return pattern; - } - - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } +Parser.prototype = { + constructor: Parser, - if (pattern.length > MAX_LENGTH) { - throw new Error('expected pattern to be less than ' + MAX_LENGTH + ' characters'); - } + init: function(options) { + this.orig = ''; + this.input = ''; + this.parsed = ''; - var key = pattern; - // do this before shallow cloning options, it's a lot faster - if (!options || (options && options.cache !== false)) { - key = createKey(pattern, options); + this.column = 1; + this.line = 1; - if (cache.hasOwnProperty(key)) { - return cache[key]; - } - } + 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 opts = extend({}, options); - if (opts.contains === true) { - if (opts.negate === true) { - opts.strictNegate = false; - } else { - opts.strict = false; - } - } + var pos = this.position(); + this.bos = pos({type: 'bos', val: ''}); - if (opts.strict === false) { - opts.strictOpen = false; - opts.strictClose = false; - } + this.ast = { + type: 'root', + errors: this.errors, + nodes: [this.bos] + }; - var open = opts.strictOpen !== false ? '^' : ''; - var close = opts.strictClose !== false ? '$' : ''; - var flags = opts.flags || ''; - var regex; + define(this.bos, 'parent', this.ast); + this.nodes = [this.ast]; - if (opts.nocase === true && !/i/.test(flags)) { - flags += 'i'; - } + this.count = 0; + this.setCount = 0; + this.stack = []; + }, - try { - if (opts.negate || typeof opts.strictNegate === 'boolean') { - pattern = not.create(pattern, opts); - } + /** + * Throw a formatted error with the cursor column and `msg`. + * @param {String} `msg` Message to use in the Error. + */ - var str = open + '(?:' + pattern + ')' + close; - regex = new RegExp(str, flags); + 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; - if (opts.safe === true && safe(regex) === false) { - throw new Error('potentially unsafe regular expression: ' + regex.source); - } + var message = source + ' <line:' + line + ' column:' + column + '>: ' + msg; + var err = new Error(message); + err.source = source; + err.reason = msg; + err.pos = pos; - } catch (err) { - if (opts.strictErrors === true || opts.safe === true) { - err.key = key; - err.pattern = pattern; - err.originalOptions = options; - err.createdOptions = opts; + if (this.options.silent) { + this.errors.push(err); + } else { throw err; } + }, - try { - regex = new RegExp('^' + pattern.replace(/(\W)/g, '\\$1') + '$'); - } catch (err) { - regex = /.^/; //<= match nothing - } - } - - if (opts.cache !== false) { - memoize(regex, key, pattern, opts); - } - return regex; -} - -/** - * 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. - */ - -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; -} - -/** - * 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. - */ - -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; -} - -/** - * Expose `makeRe` - */ - -module.exports.makeRe = makeRe; - - -/***/ }), -/* 622 */ -/***/ (function(module, exports, __webpack_require__) { - -var parse = __webpack_require__(623); -var types = parse.types; - -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]'; -} - - -/***/ }), -/* 623 */ -/***/ (function(module, exports, __webpack_require__) { - -var util = __webpack_require__(624); -var types = __webpack_require__(625); -var sets = __webpack_require__(626); -var positions = __webpack_require__(627); - - -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 = []; - - - 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; - - // Iterate through each character in string. - while (i < l) { - c = str[i++]; - - switch (c) { - // Handle escaped characters, inclues a few sets. - case '\\': - c = str[i++]; + /** + * 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 + */ - switch (c) { - case 'b': - last.push(positions.wordBoundary()); - break; + define: function(key, val) { + define(this, key, val); + return this; + }, - case 'B': - last.push(positions.nonWordBoundary()); - break; + /** + * Mark position and patch `node.position`. + */ - case 'w': - last.push(sets.words()); - break; + position: function() { + var start = { line: this.line, column: this.column }; + var self = this; - case 'W': - last.push(sets.notWords()); - break; + return function(node) { + define(node, 'position', new Position(start, self)); + return node; + }; + }, - case 'd': - last.push(sets.ints()); - break; + /** + * Set parser `name` with the given `fn` + * @param {String} `name` + * @param {Function} `fn` + * @api public + */ - case 'D': - last.push(sets.notInts()); - break; + set: function(type, fn) { + if (this.types.indexOf(type) === -1) { + this.types.push(type); + } + this.parsers[type] = fn.bind(this); + return this; + }, - case 's': - last.push(sets.whitespace()); - break; + /** + * Get parser `name` + * @param {String} `name` + * @api public + */ - case 'S': - last.push(sets.notWhitespace()); - break; + get: function(name) { + return this.parsers[name]; + }, - 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) }); + /** + * Push a `token` onto the `type` stack. + * + * @param {String} `type` + * @return {Object} `token` + * @api public + */ - // Escaped character. - } else { - last.push({ type: types.CHAR, value: c.charCodeAt(0) }); - } - } + push: function(type, token) { + this.sets[type] = this.sets[type] || []; + this.count++; + this.stack.push(token); + return this.sets[type].push(token); + }, - break; + /** + * Pop a token off of the `type` stack + * @param {String} `type` + * @returns {Object} Returns a token + * @api public + */ + pop: function(type) { + this.sets[type] = this.sets[type] || []; + this.count--; + this.stack.pop(); + return this.sets[type].pop(); + }, - // Positionals. - case '^': - last.push(positions.begin()); - break; + /** + * Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`. + * + * @param {String} `type` + * @return {Boolean} + * @api public + */ - case '$': - last.push(positions.end()); - break; + isInside: function(type) { + this.sets[type] = this.sets[type] || []; + return this.sets[type].length > 0; + }, + /** + * Return true if `node` is the given `type`. + * + * ```js + * parser.isType(node, 'brace'); + * ``` + * @param {Object} `node` + * @param {String} `type` + * @return {Boolean} + * @api public + */ - // Handle custom sets. - case '[': - // Check if this class is 'anti' i.e. [^abc]. - var not; - if (str[i] === '^') { - not = true; - i++; - } else { - not = false; - } + isType: function(node, type) { + return node && node.type === type; + }, - // Get all the characters in class. - var classTokens = util.tokenizeClass(str.slice(i), regexpStr); + /** + * Get the previous AST node + * @return {Object} + */ - // Increase index by length of class. - i += classTokens[1]; - last.push({ - type: types.SET, - set: classTokens[0], - not: not, - }); + prev: function(n) { + return this.stack.length > 0 + ? utils.last(this.stack, n) + : utils.last(this.nodes, n); + }, - break; + /** + * Update line and column based on `str`. + */ + consume: function(len) { + this.input = this.input.substr(len); + }, - // Class of any character except \n. - case '.': - last.push(sets.anyChar()); - break; + /** + * Update column based on `str`. + */ + 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); + }, - // Push group onto stack. - case '(': - // Create group. - var group = { - type: types.GROUP, - stack: [], - remember: true, - }; + /** + * Match `regex`, return captures, and update the cursor position by `match[0]` length. + * @param {RegExp} `regex` + * @return {Object} + */ - c = str[i]; + match: function(regex) { + var m = regex.exec(this.input); + if (m) { + this.updatePosition(m[0], m[0].length); + return m; + } + }, - // If if this is a special kind of group. - if (c === '?') { - c = str[i + 1]; - i += 2; + /** + * Capture `type` with the given regex. + * @param {String} `type` + * @param {RegExp} `regex` + * @return {Function} + */ - // Match if followed by. - if (c === '=') { - group.followedBy = true; + capture: function(type, regex) { + if (typeof regex === 'function') { + return this.set.apply(this, arguments); + } - // Match if not followed by. - } else if (c === '!') { - group.notFollowedBy = true; + 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; - } else if (c !== ':') { - util.error(regexpStr, - 'Invalid group, character \'' + c + - '\' after \'?\' at column ' + (i - 1)); - } + var prev = this.prev(); + var node = pos({ + type: type, + val: m[0], + parsed: parsed, + rest: this.input + }); - group.remember = false; - } + if (m[1]) { + node.inner = m[1]; + } - // Insert subgroup into current group stack. - last.push(group); + define(node, 'inside', this.stack.length > 0); + define(node, 'parent', prev); + prev.nodes.push(node); + }.bind(this)); + return this; + }, - // Remember the current group for when the group closes. - groupStack.push(lastGroup); + /** + * Create a parser with open and close for parens, + * brackets or braces + */ - // Make this new group the current group. - lastGroup = group; - last = group.stack; - break; + capturePair: function(type, openRegex, closeRegex, fn) { + this.sets[type] = this.sets[type] || []; + /** + * Open + */ - // Pop group out of stack. - case ')': - if (groupStack.length === 0) { - util.error(regexpStr, 'Unmatched ) at column ' + (i - 1)); - } - lastGroup = groupStack.pop(); + this.set(type + '.open', function() { + var parsed = this.parsed; + var pos = this.position(); + var m = this.match(openRegex); + if (!m || !m[0]) return; - // 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; + var val = m[0]; + this.setCount++; + this.specialChars = true; + var open = pos({ + type: type + '.open', + val: val, + rest: this.input + }); + if (typeof m[1] !== 'undefined') { + open.inner = m[1]; + } - // 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; - } + var prev = this.prev(); + var node = pos({ + type: type, + nodes: [open] + }); - // Create a new stack and add to options for rest of clause. - var stack = []; - lastGroup.options.push(stack); - last = stack; - break; + define(node, 'rest', this.input); + define(node, 'parsed', parsed); + define(node, 'prefix', m[1]); + define(node, 'parent', prev); + define(open, 'parent', node); + if (typeof fn === 'function') { + fn.call(this, open, node); + } - // 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; + this.push(type, node); + prev.nodes.push(node); + }); - last.push({ - type: types.REPETITION, - min: min, - max: max, - value: last.pop(), - }); - } else { - last.push({ - type: types.CHAR, - value: 123, - }); - } - break; + /** + * Close + */ - case '?': - if (last.length === 0) { - repeatErr(i); - } - last.push({ - type: types.REPETITION, - min: 0, - max: 1, - value: last.pop(), - }); - break; + this.set(type + '.close', function() { + var pos = this.position(); + var m = this.match(closeRegex); + if (!m || !m[0]) return; - case '+': - if (last.length === 0) { - repeatErr(i); - } - last.push({ - type: types.REPETITION, - min: 1, - max: Infinity, - value: last.pop(), - }); - break; + var parent = this.pop(type); + var node = pos({ + type: type + '.close', + rest: this.input, + suffix: m[1], + val: m[0] + }); - case '*': - if (last.length === 0) { - repeatErr(i); + if (!this.isType(parent, type)) { + if (this.options.strict) { + throw new Error('missing opening "' + type + '"'); } - 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), - }); - } + this.setCount--; + node.escaped = true; + return node; + } - } + if (node.suffix === '\\') { + parent.escaped = true; + node.escaped = true; + } - // Check if any groups have not been closed. - if (groupStack.length !== 0) { - util.error(regexpStr, 'Unterminated group'); - } + parent.nodes.push(node); + define(node, 'parent', parent); + }); - return start; -}; + return this; + }, -module.exports.types = types; + /** + * Capture end-of-string + */ + eos: function() { + var pos = this.position(); + if (this.input) return; + var prev = this.prev(); -/***/ }), -/* 624 */ -/***/ (function(module, exports, __webpack_require__) { + while (prev.type !== 'root' && !prev.visited) { + if (this.options.strict === true) { + throw new SyntaxError('invalid syntax:' + util.inspect(prev, null, 2)); + } -var types = __webpack_require__(625); -var sets = __webpack_require__(626); + if (!hasDelims(prev)) { + prev.parent.escaped = true; + prev.escaped = true; + } + visit(prev, function(node) { + if (!hasDelims(node.parent)) { + node.parent.escaped = true; + node.escaped = true; + } + }); -// All of these are private and only used by randexp. -// It's assumed that they will always be called with the correct input. + prev = prev.parent; + } -var CTRL = '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^ ?'; -var SLSH = { '0': 0, 't': 9, 'n': 10, 'v': 11, 'f': 12, 'r': 13 }; + var tok = pos({ + type: 'eos', + val: this.append || '' + }); -/** - * 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; - } + define(tok, 'parent', this.ast); + return tok; + }, - var code = b ? 8 : - a16 ? parseInt(a16, 16) : - b16 ? parseInt(b16, 16) : - c8 ? parseInt(c8, 8) : - dctrl ? CTRL.indexOf(dctrl) : - SLSH[eslsh]; + /** + * Run parsers to advance the cursor position + */ - var c = String.fromCharCode(code); + next: function() { + var parsed = this.parsed; + var len = this.types.length; + var idx = -1; + var tok; - // Escape special regex characters. - if (/[\[\]{}\^$.|?*+()]/.test(c)) { - c = '\\' + c; + 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; + } } + }, - return c; - }); + /** + * Parse the given string. + * @return {Array} + */ - return str; -}; + parse: function(input) { + if (typeof input !== 'string') { + throw new TypeError('expected a string'); + } + this.init(this.options); + this.orig = input; + this.input = input; + var self = this; -/** - * turns class into tokens - * reads str until it encounters a ] not preceeded by a \ - * - * @param {String} str - * @param {String} regexpStr - * @return {Array.<Array.<Object>, Number>} - */ -exports.tokenizeClass = function(str, regexpStr) { - /* jshint maxlen: false */ - var tokens = []; - var regexp = /\\(?:(w)|(d)|(s)|(W)|(D)|(S))|((?:(?:\\)(.)|([^\]\\]))-(?:\\)?([^\]]))|(\])|(?:\\)?(.)/g; - var rs, c; + function parse() { + // check input before calling `.next()` + input = self.input; + // 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); + } + } - while ((rs = regexp.exec(str)) != null) { - if (rs[1]) { - tokens.push(sets.words()); + if (self.sets.hasOwnProperty(prev.type)) { + self.currentType = prev.type; + } + } - } else if (rs[2]) { - tokens.push(sets.ints()); + // 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) + '"'); + } + } - } else if (rs[3]) { - tokens.push(sets.whitespace()); + 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 + '"'); + } - } else if (rs[4]) { - tokens.push(sets.notWords()); + var eos = this.eos(); + var tok = this.prev(); + if (tok.type !== 'eos') { + this.ast.nodes.push(eos); + } - } else if (rs[5]) { - tokens.push(sets.notInts()); + return this.ast; + } +}; - } else if (rs[6]) { - tokens.push(sets.notWhitespace()); +/** + * Visit `node` with the given `fn` + */ - } else if (rs[7]) { - tokens.push({ - type: types.RANGE, - from: (rs[8] || rs[9]).charCodeAt(0), - to: rs[10].charCodeAt(0), - }); +function visit(node, fn) { + if (!node.visited) { + define(node, 'visited', true); + return node.nodes ? mapVisit(node.nodes, fn) : fn(node); + } + return node; +} - } else if (c = rs[12]) { - tokens.push({ - type: types.CHAR, - value: c.charCodeAt(0), - }); +/** + * Map visit over array of `nodes`. + */ - } else { - return [tokens, regexp.lastIndex]; - } +function mapVisit(nodes, fn) { + var len = nodes.length; + var idx = -1; + while (++idx < len) { + visit(nodes[idx], fn); } +} - exports.error(regexpStr, 'Unterminated character class'); -}; +function hasOpen(node) { + return node.nodes && node.nodes[0].type === (node.type + '.open'); +} + +function hasClose(node) { + return node.nodes && utils.last(node.nodes).type === (node.type + '.close'); +} +function hasDelims(node) { + return hasOpen(node) && hasClose(node); +} /** - * Shortcut to throw errors. - * - * @param {String} regexp - * @param {String} msg + * Expose `Parser` */ -exports.error = function(regexp, msg) { - throw new SyntaxError('Invalid regular expression: /' + regexp + '/: ' + msg); -}; - - -/***/ }), -/* 625 */ -/***/ (function(module, exports) { -module.exports = { - ROOT : 0, - GROUP : 1, - POSITION : 2, - SET : 3, - RANGE : 4, - REPETITION : 5, - REFERENCE : 6, - CHAR : 7, -}; +module.exports = Parser; /***/ }), -/* 626 */ +/* 636 */ /***/ (function(module, exports, __webpack_require__) { -var types = __webpack_require__(625); +"use strict"; +/*! + * map-cache <https://github.com/jonschlinkert/map-cache> + * + * Copyright (c) 2015, Jon Schlinkert. + * Licensed under the MIT License. + */ -var INTS = function() { - return [{ type: types.RANGE , from: 48, to: 57 }]; -}; -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()); -}; -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 hasOwn = Object.prototype.hasOwnProperty; -var NOTANYCHAR = function() { - return [ - { type: types.CHAR, value: 10 }, - { type: types.CHAR, value: 13 }, - { type: types.CHAR, value: 8232 }, - { type: types.CHAR, value: 8233 }, - ]; -}; +/** + * Expose `MapCache` + */ -// Predefined class objects. -exports.words = function() { - return { type: types.SET, set: WORDS(), not: false }; -}; +module.exports = MapCache; -exports.notWords = function() { - return { type: types.SET, set: WORDS(), not: true }; -}; +/** + * Creates a cache object to store key/value pairs. + * + * ```js + * var cache = new MapCache(); + * ``` + * + * @api public + */ -exports.ints = function() { - return { type: types.SET, set: INTS(), not: false }; -}; +function MapCache(data) { + this.__data__ = data || {}; +} -exports.notInts = function() { - return { type: types.SET, set: INTS(), not: true }; -}; +/** + * 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 + */ -exports.whitespace = function() { - return { type: types.SET, set: WHITESPACE(), not: false }; +MapCache.prototype.set = function mapSet(key, value) { + if (key !== '__proto__') { + this.__data__[key] = value; + } + return this; }; -exports.notWhitespace = function() { - return { type: types.SET, set: WHITESPACE(), not: true }; -}; +/** + * 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 + */ -exports.anyChar = function() { - return { type: types.SET, set: NOTANYCHAR(), not: true }; +MapCache.prototype.get = function mapGet(key) { + return key === '__proto__' ? undefined : this.__data__[key]; }; +/** + * 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 + */ -/***/ }), -/* 627 */ -/***/ (function(module, exports, __webpack_require__) { - -var types = __webpack_require__(625); - -exports.wordBoundary = function() { - return { type: types.POSITION, value: 'b' }; -}; - -exports.nonWordBoundary = function() { - return { type: types.POSITION, value: 'B' }; +MapCache.prototype.has = function mapHas(key) { + return key !== '__proto__' && hasOwn.call(this.__data__, key); }; -exports.begin = function() { - return { type: types.POSITION, value: '^' }; -}; +/** + * 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 + */ -exports.end = function() { - return { type: types.POSITION, value: '$' }; +MapCache.prototype.del = function mapDelete(key) { + return this.has(key) && delete this.__data__[key]; }; /***/ }), -/* 628 */ +/* 637 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -/*! - * define-property <https://github.com/jonschlinkert/define-property> - * - * Copyright (c) 2015-2018, Jon Schlinkert. - * Released under the MIT License. - */ - - - -var isobject = __webpack_require__(539); -var isDescriptor = __webpack_require__(551); -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; - } +var define = __webpack_require__(596); - define(obj, key, { - configurable: true, - enumerable: false, - writable: true, - value: val - }); +/** + * Store position for a node + */ - return obj; +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); }; /***/ }), -/* 629 */ +/* 638 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(630); -var assignSymbols = __webpack_require__(540); +var isExtendable = __webpack_require__(639); +var assignSymbols = __webpack_require__(544); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -72807,7 +75129,7 @@ function isEnum(obj, key) { /***/ }), -/* 630 */ +/* 639 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -72820,7 +75142,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(538); +var isPlainObject = __webpack_require__(543); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -72828,93 +75150,14 @@ module.exports = function isExtendable(val) { /***/ }), -/* 631 */ -/***/ (function(module, exports, __webpack_require__) { - -"use strict"; - - -var extend = __webpack_require__(629); -var safe = __webpack_require__(622); - -/** - * 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 - */ - -function toRegex(pattern, options) { - return new RegExp(toRegex.create(pattern, options)); -} - -/** - * 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` - * @return {String} - * @api public - */ - -toRegex.create = function(pattern, options) { - if (typeof pattern !== 'string') { - throw new TypeError('expected a string'); - } - - var opts = extend({}, options); - if (opts.contains === true) { - opts.strictNegate = false; - } - - var open = opts.strictOpen !== false ? '^' : ''; - var close = opts.strictClose !== false ? '$' : ''; - var endChar = opts.endChar ? opts.endChar : '+'; - var str = pattern; - - if (opts.strictNegate === false) { - str = '(?:(?!(?:' + pattern + ')).)' + endChar; - } else { - str = '(?:(?!^(?:' + pattern + ')$).)' + endChar; - } - - var res = open + str + close; - if (opts.safe === true && safe(res) === false) { - throw new Error('potentially unsafe regular expression: ' + res); - } - - return res; -}; - -/** - * Expose `toRegex` - */ - -module.exports = toRegex; - - -/***/ }), -/* 632 */ +/* 640 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var nanomatch = __webpack_require__(633); -var extglob = __webpack_require__(648); +var nanomatch = __webpack_require__(641); +var extglob = __webpack_require__(656); module.exports = function(snapdragon) { var compilers = snapdragon.compiler.compilers; @@ -72991,7 +75234,7 @@ function escapeExtglobs(compiler) { /***/ }), -/* 633 */ +/* 641 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73001,18 +75244,18 @@ function escapeExtglobs(compiler) { * Module dependencies */ -var util = __webpack_require__(111); -var toRegex = __webpack_require__(520); -var extend = __webpack_require__(634); +var util = __webpack_require__(112); +var toRegex = __webpack_require__(526); +var extend = __webpack_require__(642); /** * Local dependencies */ -var compilers = __webpack_require__(636); -var parsers = __webpack_require__(637); -var cache = __webpack_require__(640); -var utils = __webpack_require__(642); +var compilers = __webpack_require__(644); +var parsers = __webpack_require__(645); +var cache = __webpack_require__(648); +var utils = __webpack_require__(650); var MAX_LENGTH = 1024 * 64; /** @@ -73836,14 +76079,14 @@ module.exports = nanomatch; /***/ }), -/* 634 */ +/* 642 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var isExtendable = __webpack_require__(635); -var assignSymbols = __webpack_require__(540); +var isExtendable = __webpack_require__(643); +var assignSymbols = __webpack_require__(544); module.exports = Object.assign || function(obj/*, objects*/) { if (obj === null || typeof obj === 'undefined') { @@ -73903,7 +76146,7 @@ function isEnum(obj, key) { /***/ }), -/* 635 */ +/* 643 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -73916,7 +76159,7 @@ function isEnum(obj, key) { -var isPlainObject = __webpack_require__(538); +var isPlainObject = __webpack_require__(543); module.exports = function isExtendable(val) { return isPlainObject(val) || typeof val === 'function' || Array.isArray(val); @@ -73924,7 +76167,7 @@ module.exports = function isExtendable(val) { /***/ }), -/* 636 */ +/* 644 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74270,15 +76513,15 @@ module.exports = function(nanomatch, options) { /***/ }), -/* 637 */ +/* 645 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regexNot = __webpack_require__(531); -var toRegex = __webpack_require__(520); -var isOdd = __webpack_require__(638); +var regexNot = __webpack_require__(545); +var toRegex = __webpack_require__(526); +var isOdd = __webpack_require__(646); /** * Characters to use in negation regex (we want to "not" match @@ -74664,7 +76907,7 @@ module.exports.not = NOT_REGEX; /***/ }), -/* 638 */ +/* 646 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74677,7 +76920,7 @@ module.exports.not = NOT_REGEX; -var isNumber = __webpack_require__(639); +var isNumber = __webpack_require__(647); module.exports = function isOdd(i) { if (!isNumber(i)) { @@ -74691,7 +76934,7 @@ module.exports = function isOdd(i) { /***/ }), -/* 639 */ +/* 647 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74719,14 +76962,14 @@ module.exports = function isNumber(num) { /***/ }), -/* 640 */ +/* 648 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(641))(); +module.exports = new (__webpack_require__(649))(); /***/ }), -/* 641 */ +/* 649 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74739,7 +76982,7 @@ module.exports = new (__webpack_require__(641))(); -var MapCache = __webpack_require__(619); +var MapCache = __webpack_require__(636); /** * Create a new `FragmentCache` with an optional object to use for `caches`. @@ -74861,7 +77104,7 @@ exports = module.exports = FragmentCache; /***/ }), -/* 642 */ +/* 650 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -74874,14 +77117,14 @@ var path = __webpack_require__(4); * Module dependencies */ -var isWindows = __webpack_require__(643)(); -var Snapdragon = __webpack_require__(559); -utils.define = __webpack_require__(644); -utils.diff = __webpack_require__(645); -utils.extend = __webpack_require__(634); -utils.pick = __webpack_require__(646); -utils.typeOf = __webpack_require__(647); -utils.unique = __webpack_require__(532); +var isWindows = __webpack_require__(651)(); +var Snapdragon = __webpack_require__(569); +utils.define = __webpack_require__(652); +utils.diff = __webpack_require__(653); +utils.extend = __webpack_require__(642); +utils.pick = __webpack_require__(654); +utils.typeOf = __webpack_require__(655); +utils.unique = __webpack_require__(548); /** * Returns true if the given value is effectively an empty string @@ -75247,7 +77490,7 @@ utils.unixify = function(options) { /***/ }), -/* 643 */ +/* 651 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*! @@ -75275,7 +77518,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ /***/ }), -/* 644 */ +/* 652 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75288,8 +77531,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_ -var isobject = __webpack_require__(539); -var isDescriptor = __webpack_require__(551); +var isobject = __webpack_require__(534); +var isDescriptor = __webpack_require__(535); var define = (typeof Reflect !== 'undefined' && Reflect.defineProperty) ? Reflect.defineProperty : Object.defineProperty; @@ -75320,7 +77563,7 @@ module.exports = function defineProperty(obj, key, val) { /***/ }), -/* 645 */ +/* 653 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75374,7 +77617,7 @@ function diffArray(one, two) { /***/ }), -/* 646 */ +/* 654 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75387,7 +77630,7 @@ function diffArray(one, two) { -var isObject = __webpack_require__(539); +var isObject = __webpack_require__(534); module.exports = function pick(obj, keys) { if (!isObject(obj) && typeof obj !== 'function') { @@ -75416,7 +77659,7 @@ module.exports = function pick(obj, keys) { /***/ }), -/* 647 */ +/* 655 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -75551,7 +77794,7 @@ function isBuffer(val) { /***/ }), -/* 648 */ +/* 656 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -75561,18 +77804,18 @@ function isBuffer(val) { * Module dependencies */ -var extend = __webpack_require__(529); -var unique = __webpack_require__(532); -var toRegex = __webpack_require__(520); +var extend = __webpack_require__(549); +var unique = __webpack_require__(548); +var toRegex = __webpack_require__(526); /** * Local dependencies */ -var compilers = __webpack_require__(649); -var parsers = __webpack_require__(660); -var Extglob = __webpack_require__(663); -var utils = __webpack_require__(662); +var compilers = __webpack_require__(657); +var parsers = __webpack_require__(668); +var Extglob = __webpack_require__(671); +var utils = __webpack_require__(670); var MAX_LENGTH = 1024 * 64; /** @@ -75889,13 +78132,13 @@ module.exports = extglob; /***/ }), -/* 649 */ +/* 657 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(650); +var brackets = __webpack_require__(658); /** * Extglob compilers @@ -76065,7 +78308,7 @@ module.exports = function(extglob) { /***/ }), -/* 650 */ +/* 658 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76075,17 +78318,17 @@ module.exports = function(extglob) { * Local dependencies */ -var compilers = __webpack_require__(651); -var parsers = __webpack_require__(653); +var compilers = __webpack_require__(659); +var parsers = __webpack_require__(661); /** * Module dependencies */ -var debug = __webpack_require__(655)('expand-brackets'); -var extend = __webpack_require__(529); -var Snapdragon = __webpack_require__(559); -var toRegex = __webpack_require__(520); +var debug = __webpack_require__(663)('expand-brackets'); +var extend = __webpack_require__(549); +var Snapdragon = __webpack_require__(569); +var toRegex = __webpack_require__(526); /** * Parses the given POSIX character class `pattern` and returns a @@ -76283,13 +78526,13 @@ module.exports = brackets; /***/ }), -/* 651 */ +/* 659 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var posix = __webpack_require__(652); +var posix = __webpack_require__(660); module.exports = function(brackets) { brackets.compiler @@ -76377,7 +78620,7 @@ module.exports = function(brackets) { /***/ }), -/* 652 */ +/* 660 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -76406,14 +78649,14 @@ module.exports = { /***/ }), -/* 653 */ +/* 661 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var utils = __webpack_require__(654); -var define = __webpack_require__(521); +var utils = __webpack_require__(662); +var define = __webpack_require__(596); /** * Text regex @@ -76632,14 +78875,14 @@ module.exports.TEXT_REGEX = TEXT_REGEX; /***/ }), -/* 654 */ +/* 662 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var toRegex = __webpack_require__(520); -var regexNot = __webpack_require__(531); +var toRegex = __webpack_require__(526); +var regexNot = __webpack_require__(545); var cached; /** @@ -76673,7 +78916,7 @@ exports.createRegex = function(pattern, include) { /***/ }), -/* 655 */ +/* 663 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -76682,14 +78925,14 @@ exports.createRegex = function(pattern, include) { */ if (typeof process !== 'undefined' && process.type === 'renderer') { - module.exports = __webpack_require__(656); + module.exports = __webpack_require__(664); } else { - module.exports = __webpack_require__(659); + module.exports = __webpack_require__(667); } /***/ }), -/* 656 */ +/* 664 */ /***/ (function(module, exports, __webpack_require__) { /** @@ -76698,7 +78941,7 @@ if (typeof process !== 'undefined' && process.type === 'renderer') { * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(657); +exports = module.exports = __webpack_require__(665); exports.log = log; exports.formatArgs = formatArgs; exports.save = save; @@ -76880,7 +79123,7 @@ function localstorage() { /***/ }), -/* 657 */ +/* 665 */ /***/ (function(module, exports, __webpack_require__) { @@ -76896,7 +79139,7 @@ exports.coerce = coerce; exports.disable = disable; exports.enable = enable; exports.enabled = enabled; -exports.humanize = __webpack_require__(658); +exports.humanize = __webpack_require__(666); /** * The currently active debug mode names, and names to skip. @@ -77088,7 +79331,7 @@ function coerce(val) { /***/ }), -/* 658 */ +/* 666 */ /***/ (function(module, exports) { /** @@ -77246,15 +79489,15 @@ function plural(ms, n, name) { /***/ }), -/* 659 */ +/* 667 */ /***/ (function(module, exports, __webpack_require__) { /** * Module dependencies. */ -var tty = __webpack_require__(121); -var util = __webpack_require__(111); +var tty = __webpack_require__(122); +var util = __webpack_require__(112); /** * This is the Node.js implementation of `debug()`. @@ -77262,7 +79505,7 @@ var util = __webpack_require__(111); * Expose `debug()` as the module. */ -exports = module.exports = __webpack_require__(657); +exports = module.exports = __webpack_require__(665); exports.init = init; exports.log = log; exports.formatArgs = formatArgs; @@ -77434,14 +79677,14 @@ function createWritableStdioStream (fd) { break; case 'FILE': - var fs = __webpack_require__(133); + var fs = __webpack_require__(134); stream = new fs.SyncWriteStream(fd, { autoClose: false }); stream._type = 'fs'; break; case 'PIPE': case 'TCP': - var net = __webpack_require__(597); + var net = __webpack_require__(614); stream = new net.Socket({ fd: fd, readable: false, @@ -77500,15 +79743,15 @@ exports.enable(load()); /***/ }), -/* 660 */ +/* 668 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var brackets = __webpack_require__(650); -var define = __webpack_require__(661); -var utils = __webpack_require__(662); +var brackets = __webpack_require__(658); +var define = __webpack_require__(669); +var utils = __webpack_require__(670); /** * Characters to use in text regex (we want to "not" match @@ -77663,7 +79906,7 @@ module.exports = parsers; /***/ }), -/* 661 */ +/* 669 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77676,7 +79919,7 @@ module.exports = parsers; -var isDescriptor = __webpack_require__(551); +var isDescriptor = __webpack_require__(535); module.exports = function defineProperty(obj, prop, val) { if (typeof obj !== 'object' && typeof obj !== 'function') { @@ -77701,14 +79944,14 @@ module.exports = function defineProperty(obj, prop, val) { /***/ }), -/* 662 */ +/* 670 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var regex = __webpack_require__(531); -var Cache = __webpack_require__(641); +var regex = __webpack_require__(545); +var Cache = __webpack_require__(649); /** * Utils @@ -77777,7 +80020,7 @@ utils.createRegex = function(str) { /***/ }), -/* 663 */ +/* 671 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77787,16 +80030,16 @@ utils.createRegex = function(str) { * Module dependencies */ -var Snapdragon = __webpack_require__(559); -var define = __webpack_require__(661); -var extend = __webpack_require__(529); +var Snapdragon = __webpack_require__(569); +var define = __webpack_require__(669); +var extend = __webpack_require__(549); /** * Local dependencies */ -var compilers = __webpack_require__(649); -var parsers = __webpack_require__(660); +var compilers = __webpack_require__(657); +var parsers = __webpack_require__(668); /** * Customize Snapdragon parser and renderer @@ -77862,16 +80105,16 @@ module.exports = Extglob; /***/ }), -/* 664 */ +/* 672 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -var extglob = __webpack_require__(648); -var nanomatch = __webpack_require__(633); -var regexNot = __webpack_require__(531); -var toRegex = __webpack_require__(621); +var extglob = __webpack_require__(656); +var nanomatch = __webpack_require__(641); +var regexNot = __webpack_require__(545); +var toRegex = __webpack_require__(526); var not; /** @@ -77952,14 +80195,14 @@ function textRegex(pattern) { /***/ }), -/* 665 */ +/* 673 */ /***/ (function(module, exports, __webpack_require__) { -module.exports = new (__webpack_require__(641))(); +module.exports = new (__webpack_require__(649))(); /***/ }), -/* 666 */ +/* 674 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -77972,13 +80215,13 @@ var path = __webpack_require__(4); * Module dependencies */ -var Snapdragon = __webpack_require__(559); -utils.define = __webpack_require__(628); -utils.diff = __webpack_require__(645); -utils.extend = __webpack_require__(629); -utils.pick = __webpack_require__(646); -utils.typeOf = __webpack_require__(667); -utils.unique = __webpack_require__(532); +var Snapdragon = __webpack_require__(569); +utils.define = __webpack_require__(675); +utils.diff = __webpack_require__(653); +utils.extend = __webpack_require__(638); +utils.pick = __webpack_require__(654); +utils.typeOf = __webpack_require__(676); +utils.unique = __webpack_require__(548); /** * Returns true if the platform is windows, or `path.sep` is `\\`. @@ -78275,7 +80518,52 @@ utils.unixify = function(options) { /***/ }), -/* 667 */ +/* 675 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; +/*! + * define-property <https://github.com/jonschlinkert/define-property> + * + * Copyright (c) 2015-2018, Jon Schlinkert. + * Released under the MIT License. + */ + + + +var isobject = __webpack_require__(534); +var isDescriptor = __webpack_require__(535); +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 + }); + + return obj; +}; + + +/***/ }), +/* 676 */ /***/ (function(module, exports) { var toString = Object.prototype.toString; @@ -78410,7 +80698,7 @@ function isBuffer(val) { /***/ }), -/* 668 */ +/* 677 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78429,9 +80717,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(669); -var reader_1 = __webpack_require__(682); -var fs_stream_1 = __webpack_require__(686); +var readdir = __webpack_require__(678); +var reader_1 = __webpack_require__(691); +var fs_stream_1 = __webpack_require__(695); var ReaderAsync = /** @class */ (function (_super) { __extends(ReaderAsync, _super); function ReaderAsync() { @@ -78492,15 +80780,15 @@ exports.default = ReaderAsync; /***/ }), -/* 669 */ +/* 678 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const readdirSync = __webpack_require__(670); -const readdirAsync = __webpack_require__(678); -const readdirStream = __webpack_require__(681); +const readdirSync = __webpack_require__(679); +const readdirAsync = __webpack_require__(687); +const readdirStream = __webpack_require__(690); module.exports = exports = readdirAsyncPath; exports.readdir = exports.readdirAsync = exports.async = readdirAsyncPath; @@ -78584,7 +80872,7 @@ function readdirStreamStat (dir, options) { /***/ }), -/* 670 */ +/* 679 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -78592,11 +80880,11 @@ function readdirStreamStat (dir, options) { module.exports = readdirSync; -const DirectoryReader = __webpack_require__(671); +const DirectoryReader = __webpack_require__(680); let syncFacade = { - fs: __webpack_require__(676), - forEach: __webpack_require__(677), + fs: __webpack_require__(685), + forEach: __webpack_require__(686), sync: true }; @@ -78625,18 +80913,18 @@ function readdirSync (dir, options, internalOptions) { /***/ }), -/* 671 */ +/* 680 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const Readable = __webpack_require__(137).Readable; -const EventEmitter = __webpack_require__(155).EventEmitter; +const Readable = __webpack_require__(138).Readable; +const EventEmitter = __webpack_require__(156).EventEmitter; const path = __webpack_require__(4); -const normalizeOptions = __webpack_require__(672); -const stat = __webpack_require__(674); -const call = __webpack_require__(675); +const normalizeOptions = __webpack_require__(681); +const stat = __webpack_require__(683); +const call = __webpack_require__(684); /** * Asynchronously reads the contents of a directory and streams the results @@ -79012,14 +81300,14 @@ module.exports = DirectoryReader; /***/ }), -/* 672 */ +/* 681 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const globToRegExp = __webpack_require__(673); +const globToRegExp = __webpack_require__(682); module.exports = normalizeOptions; @@ -79196,7 +81484,7 @@ function normalizeOptions (options, internalOptions) { /***/ }), -/* 673 */ +/* 682 */ /***/ (function(module, exports) { module.exports = function (glob, opts) { @@ -79333,13 +81621,13 @@ module.exports = function (glob, opts) { /***/ }), -/* 674 */ +/* 683 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const call = __webpack_require__(675); +const call = __webpack_require__(684); module.exports = stat; @@ -79414,7 +81702,7 @@ function symlinkStat (fs, path, lstats, callback) { /***/ }), -/* 675 */ +/* 684 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79475,14 +81763,14 @@ function callOnce (fn) { /***/ }), -/* 676 */ +/* 685 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); -const call = __webpack_require__(675); +const fs = __webpack_require__(134); +const call = __webpack_require__(684); /** * A facade around {@link fs.readdirSync} that allows it to be called @@ -79546,7 +81834,7 @@ exports.lstat = function (path, callback) { /***/ }), -/* 677 */ +/* 686 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79575,7 +81863,7 @@ function syncForEach (array, iterator, done) { /***/ }), -/* 678 */ +/* 687 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79583,12 +81871,12 @@ function syncForEach (array, iterator, done) { module.exports = readdirAsync; -const maybe = __webpack_require__(679); -const DirectoryReader = __webpack_require__(671); +const maybe = __webpack_require__(688); +const DirectoryReader = __webpack_require__(680); let asyncFacade = { - fs: __webpack_require__(133), - forEach: __webpack_require__(680), + fs: __webpack_require__(134), + forEach: __webpack_require__(689), async: true }; @@ -79630,7 +81918,7 @@ function readdirAsync (dir, options, callback, internalOptions) { /***/ }), -/* 679 */ +/* 688 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79657,7 +81945,7 @@ module.exports = function maybe (cb, promise) { /***/ }), -/* 680 */ +/* 689 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79693,7 +81981,7 @@ function asyncForEach (array, iterator, done) { /***/ }), -/* 681 */ +/* 690 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79701,11 +81989,11 @@ function asyncForEach (array, iterator, done) { module.exports = readdirStream; -const DirectoryReader = __webpack_require__(671); +const DirectoryReader = __webpack_require__(680); let streamFacade = { - fs: __webpack_require__(133), - forEach: __webpack_require__(680), + fs: __webpack_require__(134), + forEach: __webpack_require__(689), async: true }; @@ -79725,16 +82013,16 @@ function readdirStream (dir, options, internalOptions) { /***/ }), -/* 682 */ +/* 691 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var path = __webpack_require__(4); -var deep_1 = __webpack_require__(683); -var entry_1 = __webpack_require__(685); -var pathUtil = __webpack_require__(684); +var deep_1 = __webpack_require__(692); +var entry_1 = __webpack_require__(694); +var pathUtil = __webpack_require__(693); var Reader = /** @class */ (function () { function Reader(options) { this.options = options; @@ -79800,14 +82088,14 @@ exports.default = Reader; /***/ }), -/* 683 */ +/* 692 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(684); -var patternUtils = __webpack_require__(513); +var pathUtils = __webpack_require__(693); +var patternUtils = __webpack_require__(520); var DeepFilter = /** @class */ (function () { function DeepFilter(options, micromatchOptions) { this.options = options; @@ -79890,7 +82178,7 @@ exports.default = DeepFilter; /***/ }), -/* 684 */ +/* 693 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -79921,14 +82209,14 @@ exports.makeAbsolute = makeAbsolute; /***/ }), -/* 685 */ +/* 694 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var pathUtils = __webpack_require__(684); -var patternUtils = __webpack_require__(513); +var pathUtils = __webpack_require__(693); +var patternUtils = __webpack_require__(520); var EntryFilter = /** @class */ (function () { function EntryFilter(options, micromatchOptions) { this.options = options; @@ -80013,7 +82301,7 @@ exports.default = EntryFilter; /***/ }), -/* 686 */ +/* 695 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80032,9 +82320,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(137); -var fsStat = __webpack_require__(687); -var fs_1 = __webpack_require__(691); +var stream = __webpack_require__(138); +var fsStat = __webpack_require__(696); +var fs_1 = __webpack_require__(700); var FileSystemStream = /** @class */ (function (_super) { __extends(FileSystemStream, _super); function FileSystemStream() { @@ -80084,14 +82372,14 @@ exports.default = FileSystemStream; /***/ }), -/* 687 */ +/* 696 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const optionsManager = __webpack_require__(688); -const statProvider = __webpack_require__(690); +const optionsManager = __webpack_require__(697); +const statProvider = __webpack_require__(699); /** * Asynchronous API. */ @@ -80122,13 +82410,13 @@ exports.statSync = statSync; /***/ }), -/* 688 */ +/* 697 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fsAdapter = __webpack_require__(689); +const fsAdapter = __webpack_require__(698); function prepare(opts) { const options = Object.assign({ fs: fsAdapter.getFileSystemAdapter(opts ? opts.fs : undefined), @@ -80141,13 +82429,13 @@ exports.prepare = prepare; /***/ }), -/* 689 */ +/* 698 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = __webpack_require__(133); +const fs = __webpack_require__(134); exports.FILE_SYSTEM_ADAPTER = { lstat: fs.lstat, stat: fs.stat, @@ -80164,7 +82452,7 @@ exports.getFileSystemAdapter = getFileSystemAdapter; /***/ }), -/* 690 */ +/* 699 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80216,7 +82504,7 @@ exports.isFollowedSymlink = isFollowedSymlink; /***/ }), -/* 691 */ +/* 700 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80247,7 +82535,7 @@ exports.default = FileSystem; /***/ }), -/* 692 */ +/* 701 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80266,10 +82554,10 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var stream = __webpack_require__(137); -var readdir = __webpack_require__(669); -var reader_1 = __webpack_require__(682); -var fs_stream_1 = __webpack_require__(686); +var stream = __webpack_require__(138); +var readdir = __webpack_require__(678); +var reader_1 = __webpack_require__(691); +var fs_stream_1 = __webpack_require__(695); var TransformStream = /** @class */ (function (_super) { __extends(TransformStream, _super); function TransformStream(reader) { @@ -80337,7 +82625,7 @@ exports.default = ReaderStream; /***/ }), -/* 693 */ +/* 702 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80356,9 +82644,9 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var readdir = __webpack_require__(669); -var reader_1 = __webpack_require__(682); -var fs_sync_1 = __webpack_require__(694); +var readdir = __webpack_require__(678); +var reader_1 = __webpack_require__(691); +var fs_sync_1 = __webpack_require__(703); var ReaderSync = /** @class */ (function (_super) { __extends(ReaderSync, _super); function ReaderSync() { @@ -80418,7 +82706,7 @@ exports.default = ReaderSync; /***/ }), -/* 694 */ +/* 703 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80437,8 +82725,8 @@ var __extends = (this && this.__extends) || (function () { }; })(); Object.defineProperty(exports, "__esModule", { value: true }); -var fsStat = __webpack_require__(687); -var fs_1 = __webpack_require__(691); +var fsStat = __webpack_require__(696); +var fs_1 = __webpack_require__(700); var FileSystemSync = /** @class */ (function (_super) { __extends(FileSystemSync, _super); function FileSystemSync() { @@ -80484,7 +82772,7 @@ exports.default = FileSystemSync; /***/ }), -/* 695 */ +/* 704 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80500,13 +82788,13 @@ exports.flatten = flatten; /***/ }), -/* 696 */ +/* 705 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -var merge2 = __webpack_require__(283); +var merge2 = __webpack_require__(284); /** * Merge multiple streams and propagate their errors into one stream in parallel. */ @@ -80521,13 +82809,13 @@ exports.merge = merge; /***/ }), -/* 697 */ +/* 706 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const pathType = __webpack_require__(698); +const pathType = __webpack_require__(707); const getExtensions = extensions => extensions.length > 1 ? `{${extensions.join(',')}}` : extensions[0]; @@ -80593,13 +82881,13 @@ module.exports.sync = (input, opts) => { /***/ }), -/* 698 */ +/* 707 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); -const pify = __webpack_require__(699); +const fs = __webpack_require__(134); +const pify = __webpack_require__(708); function type(fn, fn2, fp) { if (typeof fp !== 'string') { @@ -80642,7 +82930,7 @@ exports.symlinkSync = typeSync.bind(null, 'lstatSync', 'isSymbolicLink'); /***/ }), -/* 699 */ +/* 708 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -80733,17 +83021,17 @@ module.exports = (obj, opts) => { /***/ }), -/* 700 */ +/* 709 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); +const fs = __webpack_require__(134); const path = __webpack_require__(4); -const fastGlob = __webpack_require__(509); -const gitIgnore = __webpack_require__(701); -const pify = __webpack_require__(702); -const slash = __webpack_require__(703); +const fastGlob = __webpack_require__(516); +const gitIgnore = __webpack_require__(710); +const pify = __webpack_require__(711); +const slash = __webpack_require__(712); const DEFAULT_IGNORE = [ '**/node_modules/**', @@ -80841,7 +83129,7 @@ module.exports.sync = options => { /***/ }), -/* 701 */ +/* 710 */ /***/ (function(module, exports) { // A simple implementation of make-array @@ -81310,7 +83598,7 @@ module.exports = options => new IgnoreBase(options) /***/ }), -/* 702 */ +/* 711 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81385,7 +83673,7 @@ module.exports = (input, options) => { /***/ }), -/* 703 */ +/* 712 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81403,71 +83691,17 @@ module.exports = input => { /***/ }), -/* 704 */ -/***/ (function(module, exports, __webpack_require__) { - -/*! - * is-glob <https://github.com/jonschlinkert/is-glob> - * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. - */ - -var isExtglob = __webpack_require__(294); -var chars = { '{': '}', '(': ')', '[': ']'}; -var strictRegex = /\\(.)|(^!|\*|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; -var relaxedRegex = /\\(.)|(^!|[*?{}()[\]]|\(\?)/; - -module.exports = function isGlob(str, options) { - if (typeof str !== 'string' || str === '') { - return false; - } - - if (isExtglob(str)) { - return true; - } - - var regex = strictRegex; - var match; - - // optionally relax regex - if (options && options.strict === false) { - regex = relaxedRegex; - } - - while ((match = regex.exec(str))) { - if (match[2]) return true; - var idx = match.index + match[0].length; - - // if an open bracket/brace/paren is escaped, - // set the index to the next closing character - var open = match[1]; - var close = open ? chars[open] : null; - if (open && close) { - var n = str.indexOf(close, idx); - if (n !== -1) { - idx = n + 1; - } - } - - str = str.slice(idx); - } - return false; -}; - - -/***/ }), -/* 705 */ +/* 713 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; const path = __webpack_require__(4); -const {constants: fsConstants} = __webpack_require__(133); -const pEvent = __webpack_require__(706); -const CpFileError = __webpack_require__(709); -const fs = __webpack_require__(713); -const ProgressEmitter = __webpack_require__(716); +const {constants: fsConstants} = __webpack_require__(134); +const pEvent = __webpack_require__(714); +const CpFileError = __webpack_require__(717); +const fs = __webpack_require__(719); +const ProgressEmitter = __webpack_require__(722); const cpFileAsync = async (source, destination, options, progressEmitter) => { let readError; @@ -81581,12 +83815,12 @@ module.exports.sync = (source, destination, options) => { /***/ }), -/* 706 */ +/* 714 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pTimeout = __webpack_require__(707); +const pTimeout = __webpack_require__(715); const symbolAsyncIterator = Symbol.asyncIterator || '@@asyncIterator'; @@ -81877,12 +84111,12 @@ module.exports.iterator = (emitter, event, options) => { /***/ }), -/* 707 */ +/* 715 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const pFinally = __webpack_require__(708); +const pFinally = __webpack_require__(716); class TimeoutError extends Error { constructor(message) { @@ -81928,7 +84162,7 @@ module.exports.TimeoutError = TimeoutError; /***/ }), -/* 708 */ +/* 716 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -81950,12 +84184,12 @@ module.exports = (promise, onFinally) => { /***/ }), -/* 709 */ +/* 717 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(710); +const NestedError = __webpack_require__(718); class CpFileError extends NestedError { constructor(message, nested) { @@ -81969,15 +84203,17 @@ module.exports = CpFileError; /***/ }), -/* 710 */ +/* 718 */ /***/ (function(module, exports, __webpack_require__) { -var inherits = __webpack_require__(711); +var inherits = __webpack_require__(112).inherits; var NestedError = function (message, nested) { this.nested = nested; - if (typeof message !== 'undefined') { + if (message instanceof Error) { + nested = message; + } else if (typeof message !== 'undefined') { Object.defineProperty(this, 'message', { value: message, writable: true, @@ -82023,58 +84259,16 @@ module.exports = NestedError; /***/ }), -/* 711 */ -/***/ (function(module, exports, __webpack_require__) { - -try { - var util = __webpack_require__(111); - if (typeof util.inherits !== 'function') throw ''; - module.exports = util.inherits; -} catch (e) { - module.exports = __webpack_require__(712); -} - - -/***/ }), -/* 712 */ -/***/ (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 - } -} - - -/***/ }), -/* 713 */ +/* 719 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const {promisify} = __webpack_require__(111); -const fs = __webpack_require__(132); -const makeDir = __webpack_require__(714); -const pEvent = __webpack_require__(706); -const CpFileError = __webpack_require__(709); +const {promisify} = __webpack_require__(112); +const fs = __webpack_require__(133); +const makeDir = __webpack_require__(720); +const pEvent = __webpack_require__(714); +const CpFileError = __webpack_require__(717); const stat = promisify(fs.stat); const lstat = promisify(fs.lstat); @@ -82171,15 +84365,15 @@ exports.copyFileSync = (source, destination, flags) => { /***/ }), -/* 714 */ +/* 720 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const fs = __webpack_require__(133); +const fs = __webpack_require__(134); const path = __webpack_require__(4); -const {promisify} = __webpack_require__(111); -const semver = __webpack_require__(715); +const {promisify} = __webpack_require__(112); +const semver = __webpack_require__(721); const useNativeRecursiveOption = semver.satisfies(process.version, '>=10.12.0'); @@ -82334,7 +84528,7 @@ module.exports.sync = (input, options) => { /***/ }), -/* 715 */ +/* 721 */ /***/ (function(module, exports) { exports = module.exports = SemVer @@ -83936,12 +86130,12 @@ function coerce (version, options) { /***/ }), -/* 716 */ +/* 722 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const EventEmitter = __webpack_require__(155); +const EventEmitter = __webpack_require__(156); const written = new WeakMap(); @@ -83977,7 +86171,7 @@ module.exports = ProgressEmitter; /***/ }), -/* 717 */ +/* 723 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; @@ -84023,12 +86217,12 @@ exports.default = module.exports; /***/ }), -/* 718 */ +/* 724 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; -const NestedError = __webpack_require__(719); +const NestedError = __webpack_require__(718); class CpyError extends NestedError { constructor(message, nested) { @@ -84041,61 +86235,5 @@ class CpyError extends NestedError { module.exports = CpyError; -/***/ }), -/* 719 */ -/***/ (function(module, exports, __webpack_require__) { - -var inherits = __webpack_require__(111).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; - - /***/ }) /******/ ]); \ No newline at end of file diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 2db3eede16e253..e733c7fda5d5a0 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -477,17 +477,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -558,7 +548,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -568,7 +558,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -597,7 +587,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -663,17 +653,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -744,7 +724,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -754,7 +734,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -845,17 +825,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -926,7 +896,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -937,7 +907,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -966,7 +936,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -1031,17 +1001,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -1112,7 +1072,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -1123,7 +1083,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -2109,17 +2069,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2190,7 +2140,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2200,7 +2150,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -2229,7 +2179,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -2295,17 +2245,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2376,7 +2316,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2386,7 +2326,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -2477,17 +2417,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2558,7 +2488,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2569,7 +2499,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -2598,7 +2528,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -2663,17 +2593,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2744,7 +2664,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -2755,7 +2675,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -3295,17 +3215,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3376,7 +3286,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3386,7 +3296,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -3415,7 +3325,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -3481,17 +3391,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3562,7 +3462,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3572,7 +3472,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -3663,17 +3563,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3744,7 +3634,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3755,7 +3645,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -3784,7 +3674,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -3849,17 +3739,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3930,7 +3810,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -3941,7 +3821,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -4038,17 +3918,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4119,7 +3989,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4129,7 +3999,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -4158,7 +4028,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -4224,17 +4094,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": "", - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4305,7 +4165,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4315,7 +4175,7 @@ exports[`Header renders 1`] = ` "thrownError": null, }, ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -4406,17 +4266,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4487,7 +4337,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4498,7 +4348,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, @@ -4527,7 +4377,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 1, - "outerValue": [Circular], + "outerValue": undefined, "parent": CombineLatestSubscriber { "_parentOrParents": Subscriber { "_parentOrParents": null, @@ -4592,17 +4442,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": BehaviorSubject { - "_isScalar": false, - "_value": undefined, - "closed": false, - "hasError": false, - "isStopped": false, - "observers": Array [ - [Circular], - ], - "thrownError": null, - }, + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4673,7 +4513,7 @@ exports[`Header renders 1`] = ` "index": 1, "isStopped": false, "outerIndex": 0, - "outerValue": [Circular], + "outerValue": undefined, "parent": [Circular], "syncErrorThrowable": false, "syncErrorThrown": false, @@ -4684,7 +4524,7 @@ exports[`Header renders 1`] = ` }, [Circular], ], - "resultSelector": null, + "resultSelector": undefined, "syncErrorThrowable": true, "syncErrorThrown": false, "syncErrorValue": null, diff --git a/yarn.lock b/yarn.lock index c4af49d12c8cb6..fabd775570bb58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,29 +63,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.7.5", "@babel/core@^7.9.0": - version "7.11.1" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.1.tgz#2c55b604e73a40dc21b0e52650b11c65cf276643" - integrity sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.0" - "@babel/helper-module-transforms" "^7.11.0" - "@babel/helpers" "^7.10.4" - "@babel/parser" "^7.11.1" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.11.0" - "@babel/types" "^7.11.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/core@^7.11.6": +"@babel/core@^7.1.0", "@babel/core@^7.11.6", "@babel/core@^7.7.5", "@babel/core@^7.9.0": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651" integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg== @@ -107,16 +85,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.10.5", "@babel/generator@^7.11.0", "@babel/generator@^7.9.6": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.0.tgz#4b90c78d8c12825024568cbe83ee6c9af193585c" - integrity sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ== - dependencies: - "@babel/types" "^7.11.0" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.11.5", "@babel/generator@^7.11.6": +"@babel/generator@^7.10.5", "@babel/generator@^7.11.5", "@babel/generator@^7.11.6", "@babel/generator@^7.9.6": version "7.11.6" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620" integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA== @@ -351,12 +320,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.10.5", "@babel/parser@^7.11.0", "@babel/parser@^7.11.1", "@babel/parser@^7.11.2", "@babel/parser@^7.2.0", "@babel/parser@^7.7.5", "@babel/parser@^7.9.6": - version "7.11.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.3.tgz#9e1eae46738bcd08e23e867bab43e7b95299a8f9" - integrity sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA== - -"@babel/parser@^7.11.5": +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.10.5", "@babel/parser@^7.11.2", "@babel/parser@^7.11.5", "@babel/parser@^7.2.0", "@babel/parser@^7.9.6": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037" integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q== @@ -1117,7 +1081,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.10.4", "@babel/template@^7.3.3", "@babel/template@^7.7.4": +"@babel/template@^7.10.4", "@babel/template@^7.3.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== @@ -1126,22 +1090,7 @@ "@babel/parser" "^7.10.4" "@babel/types" "^7.10.4" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.10.4", "@babel/traverse@^7.10.5", "@babel/traverse@^7.11.0", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.4": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.0.tgz#9b996ce1b98f53f7c3e4175115605d56ed07dd24" - integrity sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.11.0" - "@babel/helper-function-name" "^7.10.4" - "@babel/helper-split-export-declaration" "^7.11.0" - "@babel/parser" "^7.11.0" - "@babel/types" "^7.11.0" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/traverse@^7.11.5": +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.10.4", "@babel/traverse@^7.10.5", "@babel/traverse@^7.11.5", "@babel/traverse@^7.4.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3" integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ== @@ -1156,16 +1105,7 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.5": - version "7.11.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.0.tgz#2ae6bf1ba9ae8c3c43824e5861269871b206e90d" - integrity sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA== - dependencies: - "@babel/helper-validator-identifier" "^7.10.4" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@babel/types@^7.11.5": +"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.9.5": version "7.11.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d" integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q== @@ -1442,17 +1382,7 @@ resolved "https://registry.yarnpkg.com/@elastic/ui-ace/-/ui-ace-0.2.3.tgz#5281aed47a79b7216c55542b0675e435692f20cd" integrity sha512-Nti5s2dplBPhSKRwJxG9JXTMOev4jVOWcnTJD1TOkJr1MUBYKVZcNcJtIVMSvahWGmP0B/UfO9q9lyRqdivkvQ== -"@emotion/cache@^10.0.17", "@emotion/cache@^10.0.9": - version "10.0.19" - resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.19.tgz#d258d94d9c707dcadaf1558def968b86bb87ad71" - integrity sha512-BoiLlk4vEsGBg2dAqGSJu0vJl/PgVtCYLBFJaEO8RmQzPugXewQCXZJNXTDFaRlfCs0W+quesayav4fvaif5WQ== - dependencies: - "@emotion/sheet" "0.9.3" - "@emotion/stylis" "0.8.4" - "@emotion/utils" "0.11.2" - "@emotion/weak-memoize" "0.2.4" - -"@emotion/cache@^10.0.27": +"@emotion/cache@^10.0.27", "@emotion/cache@^10.0.9": version "10.0.29" resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ== @@ -1462,7 +1392,7 @@ "@emotion/utils" "0.11.3" "@emotion/weak-memoize" "0.2.5" -"@emotion/core@^10.0.20": +"@emotion/core@^10.0.20", "@emotion/core@^10.0.9": version "10.0.28" resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.28.tgz#bb65af7262a234593a9e952c041d0f1c9b9bef3d" integrity sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA== @@ -1474,28 +1404,7 @@ "@emotion/sheet" "0.9.4" "@emotion/utils" "0.11.3" -"@emotion/core@^10.0.9": - version "10.0.22" - resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.22.tgz#2ac7bcf9b99a1979ab5b0a876fbf37ab0688b177" - integrity sha512-7eoP6KQVUyOjAkE6y4fdlxbZRA4ILs7dqkkm6oZUJmihtHv0UBq98VgPirq9T8F9K2gKu0J/au/TpKryKMinaA== - dependencies: - "@babel/runtime" "^7.5.5" - "@emotion/cache" "^10.0.17" - "@emotion/css" "^10.0.22" - "@emotion/serialize" "^0.11.12" - "@emotion/sheet" "0.9.3" - "@emotion/utils" "0.11.2" - -"@emotion/css@^10.0.22", "@emotion/css@^10.0.9": - version "10.0.22" - resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.22.tgz#37b1abb6826759fe8ac0af0ac0034d27de6d1793" - integrity sha512-8phfa5mC/OadBTmGpMpwykIVH0gFCbUoO684LUkyixPq4F1Wwri7fK5Xlm8lURNBrd2TuvTbPUGxFsGxF9UacA== - dependencies: - "@emotion/serialize" "^0.11.12" - "@emotion/utils" "0.11.2" - babel-plugin-emotion "^10.0.22" - -"@emotion/css@^10.0.27": +"@emotion/css@^10.0.27", "@emotion/css@^10.0.9": version "10.0.27" resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c" integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw== @@ -1504,11 +1413,6 @@ "@emotion/utils" "0.11.3" babel-plugin-emotion "^10.0.27" -"@emotion/hash@0.7.3": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.7.3.tgz#a166882c81c0c6040975dd30df24fae8549bd96f" - integrity sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw== - "@emotion/hash@0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" @@ -1521,27 +1425,11 @@ dependencies: "@emotion/memoize" "0.7.4" -"@emotion/memoize@0.7.3": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.3.tgz#5b6b1c11d6a6dddf1f2fc996f74cf3b219644d78" - integrity sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow== - "@emotion/memoize@0.7.4": version "0.7.4" resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb" integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw== -"@emotion/serialize@^0.11.12", "@emotion/serialize@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.14.tgz#56a6d8d04d837cc5b0126788b2134c51353c6488" - integrity sha512-6hTsySIuQTbDbv00AnUO6O6Xafdwo5GswRlMZ5hHqiFx+4pZ7uGWXUQFW46Kc2taGhP89uXMXn/lWQkdyTosPA== - dependencies: - "@emotion/hash" "0.7.3" - "@emotion/memoize" "0.7.3" - "@emotion/unitless" "0.7.4" - "@emotion/utils" "0.11.2" - csstype "^2.5.7" - "@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": version "0.11.16" resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad" @@ -1553,11 +1441,6 @@ "@emotion/utils" "0.11.3" csstype "^2.5.7" -"@emotion/sheet@0.9.3": - version "0.9.3" - resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.3.tgz#689f135ecf87d3c650ed0c4f5ddcbe579883564a" - integrity sha512-c3Q6V7Df7jfwSq5AzQWbXHa5soeE4F5cbqi40xn0CzXxWW9/6Mxq48WJEtqfWzbZtW9odZdnRAkwCQwN12ob4A== - "@emotion/sheet@0.9.4": version "0.9.4" resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" @@ -1581,41 +1464,21 @@ "@emotion/styled-base" "^10.0.27" babel-plugin-emotion "^10.0.27" -"@emotion/stylis@0.8.4", "@emotion/stylis@^0.8.4": - version "0.8.4" - resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.4.tgz#6c51afdf1dd0d73666ba09d2eb6c25c220d6fe4c" - integrity sha512-TLmkCVm8f8gH0oLv+HWKiu7e8xmBIaokhxcEKPh1m8pXiV/akCiq50FvYgOwY42rjejck8nsdQxZlXZ7pmyBUQ== - -"@emotion/stylis@0.8.5": +"@emotion/stylis@0.8.5", "@emotion/stylis@^0.8.4": version "0.8.5" resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== -"@emotion/unitless@0.7.4", "@emotion/unitless@^0.7.4": - version "0.7.4" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.4.tgz#a87b4b04e5ae14a88d48ebef15015f6b7d1f5677" - integrity sha512-kBa+cDHOR9jpRJ+kcGMsysrls0leukrm68DmFQoMIWQcXdr2cZvyvypWuGYT7U+9kAExUE7+T7r6G3C3A6L8MQ== - -"@emotion/unitless@0.7.5": +"@emotion/unitless@0.7.5", "@emotion/unitless@^0.7.4": version "0.7.5" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@emotion/utils@0.11.2": - version "0.11.2" - resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.2.tgz#713056bfdffb396b0a14f1c8f18e7b4d0d200183" - integrity sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA== - "@emotion/utils@0.11.3": version "0.11.3" resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== -"@emotion/weak-memoize@0.2.4": - version "0.2.4" - resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.4.tgz#622a72bebd1e3f48d921563b4b60a762295a81fc" - integrity sha512-6PYY5DVdAY1ifaQW6XYTnOMihmBVT27elqSjEoodchsGjzYlEsTQMcEhSud99kVawatyTZRTiVkJ/c6lwbQ7nA== - "@emotion/weak-memoize@0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" @@ -2490,7 +2353,7 @@ supports-color "^5.4.0" tslib "^1" -"@oclif/command@1.5.19": +"@oclif/command@1.5.19", "@oclif/command@^1.5.13", "@oclif/command@^1.5.3": version "1.5.19" resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.5.19.tgz#13f472450eb83bd6c6871a164c03eadb5e1a07ed" integrity sha512-6+iaCMh/JXJaB2QWikqvGE9//wLEVYYwZd5sud8aLoLKog1Q75naZh2vlGVtg5Mq/NqpqGQvdIjJb3Bm+64AUQ== @@ -2502,16 +2365,6 @@ debug "^4.1.1" semver "^5.6.0" -"@oclif/command@^1.5.13", "@oclif/command@^1.5.3": - version "1.5.14" - resolved "https://registry.yarnpkg.com/@oclif/command/-/command-1.5.14.tgz#cdf6013134abfb5630783f27e78436096ce3ed88" - integrity sha512-R9gat0yUjamDVd4trvFgjlx2XYuMz1nM0uEBFP6nR1zOQAwJ3E1zqYbsVlCMWiUDGjU1hYmudmK7IjAxDUoqDw== - dependencies: - "@oclif/errors" "^1.2.2" - "@oclif/parser" "^3.7.3" - debug "^4.1.1" - semver "^5.6.0" - "@oclif/config@^1": version "1.13.0" resolved "https://registry.yarnpkg.com/@oclif/config/-/config-1.13.0.tgz#fc2bd82a9cb30a73faf7d2aa5ae937c719492bd1" @@ -2536,15 +2389,6 @@ resolved "https://registry.yarnpkg.com/@oclif/linewrap/-/linewrap-1.0.0.tgz#aedcb64b479d4db7be24196384897b5000901d91" integrity sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw== -"@oclif/parser@^3.7.3": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.8.1.tgz#3c842ef4a8ce3d1f5bcfa5938d3fbebf1c6a0618" - integrity sha512-0OavFuLj6FBTdZDD6DXdNqH4qdLFLQD/PKK1OvNZhUd4/5v/lp6Ftzilwmirf549naNHq0u15uk1YCBvym5tNQ== - dependencies: - "@oclif/linewrap" "^1.0.0" - chalk "^2.4.2" - tslib "^1.9.3" - "@oclif/parser@^3.8.3": version "3.8.4" resolved "https://registry.yarnpkg.com/@oclif/parser/-/parser-3.8.4.tgz#1a90fc770a42792e574fb896325618aebbe8c9e4" @@ -2854,27 +2698,13 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-2.1.0.tgz#6ad4ca610f696098e92954ab431ff83bea0ce13f" integrity sha512-lXKXfypKo644k4Da4yXkPCrwcvn6SlUW2X2zFbuflKHNjf0w9htru01bo26uMhleMXsDmnZ12eJLdrAZa9MANg== -"@sinonjs/commons@^1": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" - integrity sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg== - dependencies: - type-detect "4.0.8" - -"@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0": +"@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0", "@sinonjs/commons@^1.7.0": version "1.7.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" integrity sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw== dependencies: type-detect "4.0.8" -"@sinonjs/commons@^1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1" - integrity sha512-Debi3Baff1Qu1Unc3mjJ96MgpbwTn43S1+9yJ0llWygPwDNu2aaWBD6yc9y/Z8XDRNhx7U+u2UDg2OGQXkclUQ== - dependencies: - type-detect "4.0.8" - "@sinonjs/fake-timers@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" @@ -3721,16 +3551,11 @@ "@turf/helpers" "6.x" "@turf/invariant" "6.x" -"@turf/helpers@6.0.1": +"@turf/helpers@6.0.1", "@turf/helpers@6.x": version "6.0.1" resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.0.1.tgz#625112616159e519033dc5d24c094ccbce7a457f" integrity sha512-EQtcbwiNbkBnvxvlxcTcrwTzaq2eR4fnDVlzx/hsw5tYxDiMJfuL9goy1rDCmXxcyDnk8gCyZapYfEQWYLl1pQ== -"@turf/helpers@6.x": - version "6.1.4" - resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.1.4.tgz#d6fd7ebe6782dd9c87dca5559bda5c48ae4c3836" - integrity sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g== - "@turf/invariant@6.x": version "6.1.2" resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-6.1.2.tgz#6013ed6219f9ac2edada9b31e1dfa5918eb0a2f7" @@ -3794,7 +3619,7 @@ resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.4.tgz#bfd5b0d0d1ba13e351dff65b6e52783b816826c8" integrity sha512-WiZhq3SVJHFRgRYLXvpf65XnV6ipVHhnNaNvE8yCimejrGglkg38kEj0JcizqwSHxmPSjcTlig/6JouxLGEhGw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.10": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.10", "@types/babel__core@^7.1.7": version "7.1.10" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40" integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw== @@ -3805,17 +3630,6 @@ "@types/babel__template" "*" "@types/babel__traverse" "*" -"@types/babel__core@^7.1.7": - version "7.1.7" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" - integrity sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - "@types/babel__generator@*": version "7.0.2" resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.0.2.tgz#d2112a6b21fad600d7674274293c85dce0cb47fc" @@ -3831,14 +3645,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.6.tgz#328dd1a8fc4cfe3c8458be9477b219ea158fd7b2" - integrity sha512-XYVgHF2sQ0YblLRMLNPB3CkFMewzFmlDsH/TneZFHUXDlABQgh88uOxuez7ZcXxayLFrqLwtDH1t+FmlFwNZxw== - dependencies: - "@babel/types" "^7.3.0" - -"@types/babel__traverse@^7.0.6": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.7.tgz#2496e9ff56196cc1429c72034e07eab6121b6f3f" integrity sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw== @@ -3941,12 +3748,7 @@ dependencies: "@types/color-name" "*" -"@types/color-name@*": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.0.tgz#926f76f7e66f49cc59ad880bb15b030abbf0b66d" - integrity sha512-gZ/Rb+MFXF0pXSEQxdRoPMm5jeO3TycjOdvbpbcpHX/B+n9AqaHFe5q6Ga9CsZ7ir/UgIWPfrBzUzn3F19VH/w== - -"@types/color-name@^1.1.1": +"@types/color-name@*", "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== @@ -4017,12 +3819,7 @@ resolved "https://registry.yarnpkg.com/@types/d3-time-format/-/d3-time-format-2.1.1.tgz#dd2c79ec4575f1355484ab6b10407824668eba42" integrity sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g== -"@types/d3-time@*": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.8.tgz#6c083127b330b3c2fc65cd0f3a6e9cbd9607b28c" - integrity sha512-/UCphyyw97YAq4zKsuXH33R3UNB4jDSza0fLvMubWr/ONh9IePi1NbgFP222blhiCe724ebJs8U87+aDuAq/jA== - -"@types/d3-time@^1.0.10": +"@types/d3-time@*", "@types/d3-time@^1.0.10": version "1.0.10" resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.10.tgz#d338c7feac93a98a32aac875d1100f92c7b61f4f" integrity sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw== @@ -4088,11 +3885,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== -"@types/events@*": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86" - integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA== - "@types/expect@^1.20.4": version "1.20.4" resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" @@ -4163,25 +3955,7 @@ "@types/glob" "*" "@types/node" "*" -"@types/glob@*": - version "5.0.35" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-5.0.35.tgz#1ae151c802cece940443b5ac246925c85189f32a" - integrity sha512-wc+VveszMLyMWFvXLkloixT4n0harUIVZjnpzztaZ0nKLuul7Z32iMt2fUFGAaZ4y1XWjFRMtCI5ewvyh4aIeg== - dependencies: - "@types/events" "*" - "@types/minimatch" "*" - "@types/node" "*" - -"@types/glob@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" - integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== - dependencies: - "@types/events" "*" - "@types/minimatch" "*" - "@types/node" "*" - -"@types/glob@^7.1.2": +"@types/glob@*", "@types/glob@^7.1.1", "@types/glob@^7.1.2": version "7.1.3" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== @@ -4285,12 +4059,7 @@ resolved "https://registry.yarnpkg.com/@types/he/-/he-1.1.1.tgz#19e14033c4ee8f1a702c74dcc6182664839ac2b7" integrity sha512-jpzrsR1ns0n3kyWt92QfOUQhIuJGQ9+QGa7M62rO6toe98woQjnsnzjdMtsQXCdvjjmqjS2ZBCC7xKw0cdzU+Q== -"@types/history@*": - version "4.7.2" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.2.tgz#0e670ea254d559241b6eeb3894f8754991e73220" - integrity sha512-ui3WwXmjTaY73fOQ3/m3nnajU/Orhi6cEu5rzX+BrAAJxa3eITXZ5ch9suPqtM03OWhAHhPSyBGCN4UKoxO20Q== - -"@types/history@^4.7.3": +"@types/history@*", "@types/history@^4.7.3": version "4.7.3" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.3.tgz#856c99cdc1551d22c22b18b5402719affec9839a" integrity sha512-cS5owqtwzLN5kY+l+KgKdRJ/Cee8tlmQoGQuIE9tWnSmS3JMKzmxo2HIAk2wODMifGwO20d62xZQLYz+RLfXmw== @@ -4415,18 +4184,18 @@ dependencies: "@types/jest" "*" -"@types/jest@*", "@types/jest@^25.1.1": - version "25.2.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf" - integrity sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw== +"@types/jest@*", "@types/jest@^26.0.14": + version "26.0.14" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.14.tgz#078695f8f65cb55c5a98450d65083b2b73e5a3f3" + integrity sha512-Hz5q8Vu0D288x3iWXePSn53W7hAjP0H7EQ6QvDO9c7t46mR0lNOLlfuwQ+JkVxuhygHzlzPX+0jKdA3ZgSh+Vg== dependencies: jest-diff "^25.2.1" pretty-format "^25.2.1" -"@types/jest@^26.0.14": - version "26.0.14" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.14.tgz#078695f8f65cb55c5a98450d65083b2b73e5a3f3" - integrity sha512-Hz5q8Vu0D288x3iWXePSn53W7hAjP0H7EQ6QvDO9c7t46mR0lNOLlfuwQ+JkVxuhygHzlzPX+0jKdA3ZgSh+Vg== +"@types/jest@^25.1.1": + version "25.2.3" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf" + integrity sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw== dependencies: jest-diff "^25.2.1" pretty-format "^25.2.1" @@ -4858,15 +4627,7 @@ resolved "https://registry.yarnpkg.com/@types/rbush/-/rbush-3.0.0.tgz#b6887d99b159e87ae23cd14eceff34f139842aa6" integrity sha512-W3ue/GYWXBOpkRm0VSoifrP3HV0Ni47aVJWvXyWMcbtpBy/l/K/smBRiJ+fI8f7shXRjZBiux+iJzYbh7VmcZg== -"@types/reach__router@^1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@types/reach__router/-/reach__router-1.2.6.tgz#b14cf1adbd1a365d204bbf6605cd9dd7b8816c87" - integrity sha512-Oh5DAVr/L2svBvubw6QEFpXGu295Y406BPs4i9t1n2pp7M+q3pmCmhzb9oZV5wncR41KCD3NHl1Yhi7uKnTPsA== - dependencies: - "@types/history" "*" - "@types/react" "*" - -"@types/reach__router@^1.3.5": +"@types/reach__router@^1.2.6", "@types/reach__router@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/reach__router/-/reach__router-1.3.5.tgz#14e1e981cccd3a5e50dc9e969a72de0b9d472f6d" integrity sha512-h0NbqXN/tJuBY/xggZSej1SKQEstbHO7J/omt1tYoFGmj3YXOodZKbbqD4mNDh7zvEGYd7YFrac1LTtAr3xsYQ== @@ -5160,26 +4921,14 @@ "@types/superagent" "*" "@types/supertest" "*" -"@types/supertest@*": +"@types/supertest@*", "@types/supertest@^2.0.5": version "2.0.8" resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.8.tgz#23801236e2b85204ed771a8e7c40febba7da2bda" integrity sha512-wcax7/ip4XSSJRLbNzEIUVy2xjcBIZZAuSd2vtltQfRK7kxhx5WMHbLHkYdxN3wuQCrwpYrg86/9byDjPXoGMA== dependencies: "@types/superagent" "*" -"@types/supertest@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@types/supertest/-/supertest-2.0.5.tgz#18d082a667eaed22759be98f4923e0061ae70c62" - integrity sha512-orl732spRnz4+Bqwk1OnXkJUX2YhiGvMUyfn8VTGXqMZSk6jrMhBq7IuoSXMyBrpsCV5eSddPiC3S0iIafYxsQ== - dependencies: - "@types/superagent" "*" - -"@types/tapable@*": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370" - integrity sha512-78AdXtlhpCHT0K3EytMpn4JNxaf5tbqbLcbIRoQIHzpTIyjpxLQKRoxU55ujBXAtg3Nl2h/XWvfDa9dsMOd0pQ== - -"@types/tapable@^1.0.5", "@types/tapable@^1.0.6": +"@types/tapable@*", "@types/tapable@^1.0.5", "@types/tapable@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74" integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA== @@ -5204,14 +4953,7 @@ resolved "https://registry.yarnpkg.com/@types/tempy/-/tempy-0.2.0.tgz#8b7a93f6912aef25cc0b8d8a80ff974151478685" integrity sha512-YaX74QljqR45Xu7dd22wMvzTS+ItUiSyDl9XJl6WTgYNE09r2TF+mV2FDjWRM5Sdzf9C9dXRTUdz9J5SoEYxXg== -"@types/testing-library__jest-dom@^5.9.1": - version "5.9.2" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.2.tgz#59e4771a1cf87d51e89a5cc8195cd3b647cba322" - integrity sha512-K7nUSpH/5i8i0NagTJ+uFUDRueDlnMNhJtMjMwTGPPSqyImbWC/hgKPDCKt6Phu2iMJg2kWqlax+Ucj2DKMwpA== - dependencies: - "@types/jest" "*" - -"@types/testing-library__jest-dom@^5.9.3": +"@types/testing-library__jest-dom@^5.9.1", "@types/testing-library__jest-dom@^5.9.3": version "5.9.3" resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.3.tgz#574039e210140a536c6ec891063289fb742a75eb" integrity sha512-5YxiCFA2vk0cxq2LIxYgHBpFlnJvMH9bkUIVNin+1GXT+LZgVOgXBeEyyo2ZrGXMO/KWe1ZV3p7Kb6LJAvJasw== @@ -5232,16 +4974,11 @@ dependencies: "@types/node" "*" -"@types/tinycolor2@^1.4.0": +"@types/tinycolor2@^1.4.0", "@types/tinycolor2@^1.4.1": version "1.4.2" resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf" integrity sha512-PeHg/AtdW6aaIO2a+98Xj7rWY4KC1E6yOy7AFknJQ7VXUGNrMlyxDFxJo7HqLtjQms/ZhhQX52mLVW/EX3JGOw== -"@types/tinycolor2@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.1.tgz#2f5670c9d1d6e558897a810ed284b44918fc1253" - integrity sha512-25L/RL5tqZkquKXVHM1fM2bd23qjfbcPpAZ2N/H05Y45g3UEi+Hw8CbDV28shKY8gH1SHiLpZSxPI1lacqdpGg== - "@types/tough-cookie@*": version "2.3.5" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d" @@ -5311,14 +5048,7 @@ "@types/node" "*" "@types/vinyl" "*" -"@types/vinyl@*": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.3.tgz#80a6ce362ab5b32a0c98e860748a31bce9bff0de" - integrity sha512-hrT6xg16CWSmndZqOTJ6BGIn2abKyTw0B58bI+7ioUoj3Sma6u8ftZ1DTI2yCaJamOVGLOnQWiPH3a74+EaqTA== - dependencies: - "@types/node" "*" - -"@types/vinyl@^2.0.4": +"@types/vinyl@*", "@types/vinyl@^2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.4.tgz#9a7a8071c8d14d3a95d41ebe7135babe4ad5995a" integrity sha512-2o6a2ixaVI2EbwBPg1QYLGQoHK56p/8X/sGfKbFC8N6sY9lfjsMf/GprtkQkSya0D4uRiutRZ2BWj7k3JvLsAQ== @@ -5356,19 +5086,7 @@ "@types/source-list-map" "*" source-map "^0.6.1" -"@types/webpack@*", "@types/webpack@^4.4.31", "@types/webpack@^4.41.3": - version "4.41.3" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.3.tgz#30c2251db1d69a45bbffd79c0577dd9baf50e7ba" - integrity sha512-dH+BZ6pHBZFrXpnif0YU/PbmUq3lQrvRPnqkxsciSIzvG/DE+Vm/Wrjn56T7V3+B5ryQa5fw0oGnHL8tk4ll6w== - dependencies: - "@types/anymatch" "*" - "@types/node" "*" - "@types/tapable" "*" - "@types/uglify-js" "*" - "@types/webpack-sources" "*" - source-map "^0.6.0" - -"@types/webpack@^4.41.8": +"@types/webpack@*", "@types/webpack@^4.4.31", "@types/webpack@^4.41.3", "@types/webpack@^4.41.8": version "4.41.21" resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.21.tgz#cc685b332c33f153bb2f5fc1fa3ac8adeb592dee" integrity sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA== @@ -5546,15 +5264,6 @@ "@typescript-eslint/types" "4.3.0" eslint-visitor-keys "^2.0.0" -"@webassemblyjs/ast@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" - integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== - dependencies: - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" - "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -5564,43 +5273,21 @@ "@webassemblyjs/helper-wasm-bytecode" "1.9.0" "@webassemblyjs/wast-parser" "1.9.0" -"@webassemblyjs/floating-point-hex-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" - integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== - "@webassemblyjs/floating-point-hex-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== -"@webassemblyjs/helper-api-error@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" - integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== - "@webassemblyjs/helper-api-error@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== -"@webassemblyjs/helper-buffer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" - integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== - "@webassemblyjs/helper-buffer@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== -"@webassemblyjs/helper-code-frame@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" - integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== - dependencies: - "@webassemblyjs/wast-printer" "1.8.5" - "@webassemblyjs/helper-code-frame@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" @@ -5608,24 +5295,11 @@ dependencies: "@webassemblyjs/wast-printer" "1.9.0" -"@webassemblyjs/helper-fsm@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" - integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== - "@webassemblyjs/helper-fsm@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== -"@webassemblyjs/helper-module-context@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" - integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== - dependencies: - "@webassemblyjs/ast" "1.8.5" - mamacro "^0.0.3" - "@webassemblyjs/helper-module-context@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" @@ -5633,26 +5307,11 @@ dependencies: "@webassemblyjs/ast" "1.9.0" -"@webassemblyjs/helper-wasm-bytecode@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" - integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== - "@webassemblyjs/helper-wasm-bytecode@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== -"@webassemblyjs/helper-wasm-section@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" - integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/helper-wasm-section@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" @@ -5663,13 +5322,6 @@ "@webassemblyjs/helper-wasm-bytecode" "1.9.0" "@webassemblyjs/wasm-gen" "1.9.0" -"@webassemblyjs/ieee754@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" - integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== - dependencies: - "@xtuc/ieee754" "^1.2.0" - "@webassemblyjs/ieee754@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" @@ -5677,13 +5329,6 @@ dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" - integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== - dependencies: - "@xtuc/long" "4.2.2" - "@webassemblyjs/leb128@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" @@ -5691,30 +5336,11 @@ dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" - integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== - "@webassemblyjs/utf8@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== -"@webassemblyjs/wasm-edit@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" - integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/helper-wasm-section" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-opt" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - "@webassemblyjs/wast-printer" "1.8.5" - "@webassemblyjs/wasm-edit@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" @@ -5729,17 +5355,6 @@ "@webassemblyjs/wasm-parser" "1.9.0" "@webassemblyjs/wast-printer" "1.9.0" -"@webassemblyjs/wasm-gen@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" - integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - "@webassemblyjs/wasm-gen@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" @@ -5751,16 +5366,6 @@ "@webassemblyjs/leb128" "1.9.0" "@webassemblyjs/utf8" "1.9.0" -"@webassemblyjs/wasm-opt@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" - integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - "@webassemblyjs/wasm-opt@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" @@ -5771,18 +5376,6 @@ "@webassemblyjs/wasm-gen" "1.9.0" "@webassemblyjs/wasm-parser" "1.9.0" -"@webassemblyjs/wasm-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" - integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - "@webassemblyjs/wasm-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" @@ -5795,18 +5388,6 @@ "@webassemblyjs/leb128" "1.9.0" "@webassemblyjs/utf8" "1.9.0" -"@webassemblyjs/wast-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" - integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/floating-point-hex-parser" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-code-frame" "1.8.5" - "@webassemblyjs/helper-fsm" "1.8.5" - "@xtuc/long" "4.2.2" - "@webassemblyjs/wast-parser@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" @@ -5819,15 +5400,6 @@ "@webassemblyjs/helper-fsm" "1.9.0" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" - integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" - "@xtuc/long" "4.2.2" - "@webassemblyjs/wast-printer@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" @@ -5909,12 +5481,7 @@ abab@^1.0.4: resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" integrity sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4= -abab@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" - integrity sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w== - -abab@^2.0.3: +abab@^2.0.0, abab@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== @@ -5944,15 +5511,7 @@ accept@3.0.2, accept@3.x.x: boom "7.x.x" hoek "5.x.x" -accepts@~1.3.4, accepts@~1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -accepts@~1.3.7: +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== @@ -6002,12 +5561,7 @@ acorn-walk@^6.0.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" integrity sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw== -acorn-walk@^7.0.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" - integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== - -acorn-walk@^7.1.1: +acorn-walk@^7.0.0, acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== @@ -6022,36 +5576,21 @@ acorn@^3.0.4: resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= -acorn@^6.0.1, acorn@^6.0.4, acorn@^6.2.1, acorn@^6.4.1: +acorn@^6.0.1, acorn@^6.0.4, acorn@^6.4.1: version "6.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.0.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" - integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== - -acorn@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" - integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== - -acorn@^7.1.1: +acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1: version "7.4.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== -address@1.1.2: +address@1.1.2, address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" integrity sha512-aT6camzM4xEA54YVJYSqxz1kv4IHnQZRtThJJHhUMRExaU5spC7jX5ugSwTaTgJliIgs4VhZOk7htClvQ/LmRA== -address@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" - integrity sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg== - adm-zip@0.4.16: version "0.4.16" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" @@ -6062,10 +5601,10 @@ after-all-results@^2.0.0: resolved "https://registry.yarnpkg.com/after-all-results/-/after-all-results-2.0.0.tgz#6ac2fc202b500f88da8f4f5530cfa100f4c6a2d0" integrity sha1-asL8ICtQD4jaj09VMM+hAPTGotA= -agent-base@4: - version "4.2.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce" - integrity sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg== +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== dependencies: es6-promisify "^5.0.0" @@ -6081,20 +5620,6 @@ agent-base@6: dependencies: debug "4" -agent-base@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" - integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== - dependencies: - es6-promisify "^5.0.0" - -agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - agentkeepalive@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.4.1.tgz#aa95aebc3a749bca5ed53e3880a09f5235b48f0c" @@ -6175,12 +5700,7 @@ ajv-keywords@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" integrity sha1-MU3QpLM2j609/NxU7eYXG4htrzw= -ajv-keywords@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" - integrity sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo= - -ajv-keywords@^3.4.1: +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== @@ -6290,7 +5810,7 @@ ansi-align@^3.0.0: dependencies: string-width "^3.0.0" -ansi-colors@3.2.3: +ansi-colors@3.2.3, ansi-colors@^3.0.0: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== @@ -6302,22 +5822,12 @@ ansi-colors@^1.0.1: dependencies: ansi-wrap "^0.1.0" -ansi-colors@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.1.tgz#9638047e4213f3428a11944a7d4b31cba0a3ff95" - integrity sha512-Xt+zb6nqgvV9SWAVp0EG3lRsHcbq5DDgqjPPz6pwgtj6RKz65zGXMNa82oJfOSBA/to6GmRP7Dr+6o+kbApTzQ== - -ansi-escapes@^1.0.0, ansi-escapes@^1.1.0: +ansi-escapes@^1.1.0: version "1.4.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= -ansi-escapes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" - integrity sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ== - -ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: +ansi-escapes@^3.0.0, ansi-escapes@^3.1.0, ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== @@ -6363,12 +5873,7 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" - integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== - -ansi-regex@^4.1.0: +ansi-regex@^4.0.0, ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -6388,20 +5893,13 @@ ansi-styles@^2.2.1: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= -ansi-styles@^3.0.0, ansi-styles@^3.2.1: +ansi-styles@^3.0.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" -ansi-styles@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88" - integrity sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug== - dependencies: - color-convert "^1.9.0" - ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" @@ -6882,15 +6380,7 @@ array-from@^2.1.1: resolved "https://registry.yarnpkg.com/array-from/-/array-from-2.1.1.tgz#cfe9d8c26628b9dc5aecc62a9f5d8f1f352c1195" integrity sha1-z+nYwmYoudxa7MYqn12PHzUsEZU= -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" - integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - -array-includes@^3.1.1: +array-includes@^3.0.3, array-includes@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== @@ -6966,16 +6456,7 @@ array.prototype.flat@^1.2.1, array.prototype.flat@^1.2.3: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -array.prototype.flatmap@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.1.tgz#3103cd4826ef90019c9b0a4839b2535fa6faf4e9" - integrity sha512-i18e2APdsiezkcqDyZor78Pbfjfds3S94dG6dgIV2ZASJaUf1N0dz2tGdrmwrmlZuNUgxH+wz6Z0zYVH2c5xzQ== - dependencies: - define-properties "^1.1.2" - es-abstract "^1.10.0" - function-bind "^1.1.1" - -array.prototype.flatmap@^1.2.3: +array.prototype.flatmap@^1.2.1, array.prototype.flatmap@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.3.tgz#1c13f84a178566042dd63de4414440db9222e443" integrity sha512-OOEk+lkePcg+ODXIpvuU9PAryCikCJyo7GlDG1upleEpQRx6mzL9puEBkozQ5iAx20KV0l3DbyQwqciJtqe5Pg== @@ -7208,7 +6689,7 @@ autobind-decorator@^1.3.4: resolved "https://registry.yarnpkg.com/autobind-decorator/-/autobind-decorator-1.4.3.tgz#4c96ffa77b10622ede24f110f5dbbf56691417d1" integrity sha1-TJb/p3sQYi7eJPEQ9du/VmkUF9E= -autoprefixer@^9.7.2: +autoprefixer@^9.7.2, autoprefixer@^9.7.4: version "9.8.5" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.5.tgz#2c225de229ddafe1d1424c02791d0c3e10ccccaa" integrity sha512-C2p5KkumJlsTHoNv9w31NrBRgXhf6eCMteJuHZi2xhkgC+5Vm40MEtCKPhc0qdgAOhox0YPy1SQHTAky05UoKg== @@ -7221,19 +6702,6 @@ autoprefixer@^9.7.2: postcss "^7.0.32" postcss-value-parser "^4.1.0" -autoprefixer@^9.7.4: - version "9.7.4" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.7.4.tgz#f8bf3e06707d047f0641d87aee8cfb174b2a5378" - integrity sha512-g0Ya30YrMBAEZk60lp+qfX5YQllG+S5W3GYCFvyHTvhOki0AEQJLPEcIuGRsqVwLi8FvXPVtwTGhfr38hVpm0g== - dependencies: - browserslist "^4.8.3" - caniuse-lite "^1.0.30001020" - chalk "^2.4.2" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.26" - postcss-value-parser "^4.0.2" - available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" @@ -7432,26 +6900,10 @@ babel-plugin-emotion@^10.0.20, babel-plugin-emotion@^10.0.27: find-root "^1.1.0" source-map "^0.5.7" -babel-plugin-emotion@^10.0.22: - version "10.0.23" - resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.0.23.tgz#040d40bf61dcab6d31dd6043d10e180240b8515b" - integrity sha512-1JiCyXU0t5S2xCbItejCduLGGcKmF3POT0Ujbexog2MI4IlRcIn/kWjkYwCUZlxpON0O5FC635yPl/3slr7cKQ== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@emotion/hash" "0.7.3" - "@emotion/memoize" "0.7.3" - "@emotion/serialize" "^0.11.14" - babel-plugin-macros "^2.0.0" - babel-plugin-syntax-jsx "^6.18.0" - convert-source-map "^1.5.0" - escape-string-regexp "^1.0.5" - find-root "^1.1.0" - source-map "^0.5.7" - -babel-plugin-extract-import-names@1.6.16: - version "1.6.16" - resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.16.tgz#b964004e794bdd62534c525db67d9e890d5cc079" - integrity sha512-Da6Ra0sbA/1Iavli8LdMbTjyrsOPaxMm4lrKl8VJN4sJI5F64qy2EpLj3+5INLvNPfW4ddwpStbfP3Rf3jIgcw== +babel-plugin-extract-import-names@1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.16.tgz#b964004e794bdd62534c525db67d9e890d5cc079" + integrity sha512-Da6Ra0sbA/1Iavli8LdMbTjyrsOPaxMm4lrKl8VJN4sJI5F64qy2EpLj3+5INLvNPfW4ddwpStbfP3Rf3jIgcw== dependencies: "@babel/helper-plugin-utils" "7.10.4" @@ -7476,15 +6928,7 @@ babel-plugin-jest-hoist@^26.2.0: "@types/babel__core" "^7.0.0" "@types/babel__traverse" "^7.0.6" -babel-plugin-macros@^2.0.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.5.0.tgz#01f4d3b50ed567a67b80a30b9da066e94f4097b6" - integrity sha512-BWw0lD0kVZAXRD3Od1kMrdmfudqzDzYv2qrN3l2ISR1HVp1EgLKfbOrYV9xmY5k3qx3RIu5uPAUZZZHpo0o5Iw== - dependencies: - cosmiconfig "^5.0.5" - resolve "^1.8.1" - -babel-plugin-macros@^2.8.0: +babel-plugin-macros@^2.0.0, babel-plugin-macros@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== @@ -7847,12 +7291,7 @@ base64-js@0.0.8: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-0.0.8.tgz#1101e9544f4a76b1bc3b26d452ca96d7a35e7978" integrity sha1-EQHpVE9KdrG8OybUUsqW16NeeXg= -base64-js@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.1.tgz#a91947da1f4a516ea38e5b4ec0ec3773675e0886" - integrity sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw== - -base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: +base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.3.0, base64-js@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== @@ -7882,7 +7321,7 @@ basic-auth@^2.0.1: dependencies: safe-buffer "5.1.2" -batch-processor@^1.0.0: +batch-processor@1.0.0, batch-processor@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/batch-processor/-/batch-processor-1.0.0.tgz#75c95c32b748e0850d10c2b168f6bdbe9891ace8" integrity sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg= @@ -8001,26 +7440,16 @@ bluebird-retry@^0.11.0: resolved "https://registry.yarnpkg.com/bluebird-retry/-/bluebird-retry-0.11.0.tgz#1289ab22cbbc3a02587baad35595351dd0c1c047" integrity sha1-EomrIsu8OgJYe6rTVZU1HdDBwEc= -bluebird@3.5.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5: +bluebird@3.5.5: version "3.5.5" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.5.tgz#a8d0afd73251effbbd5fe384a77d73003c17a71f" integrity sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w== -bluebird@3.7.2, bluebird@^3.7.1, bluebird@^3.7.2: +bluebird@3.7.2, bluebird@^3.3.1, bluebird@^3.3.5, bluebird@^3.5.0, bluebird@^3.5.1, bluebird@^3.5.5, bluebird@^3.7.1, bluebird@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bluebird@^3.3.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" - integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA== - -bluebird@^3.3.5: - version "3.5.3" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" - integrity sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw== - bmp-js@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/bmp-js/-/bmp-js-0.1.0.tgz#e05a63f796a6c1ff25f4771ec7adadc148c07233" @@ -8031,22 +7460,6 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.18.3: - version "1.18.3" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" - integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= - dependencies: - bytes "3.0.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "~1.6.3" - iconv-lite "0.4.23" - on-finished "~2.3.0" - qs "6.5.2" - raw-body "2.3.3" - type-is "~1.6.16" - body-parser@1.19.0, body-parser@^1.18.1, body-parser@^1.18.3: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -8220,11 +7633,6 @@ brotli@^1.2.0: dependencies: base64-js "^1.1.2" -browser-process-hrtime@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz#425d68a58d3447f02a04aa894187fce8af8b7b8e" - integrity sha1-Ql1opY00R/AqBKqJQYf86K+Le44= - browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -8319,7 +7727,7 @@ browserslist@4.10.0: node-releases "^1.1.52" pkg-up "^3.1.0" -browserslist@^4.12.0: +browserslist@^4.12.0, browserslist@^4.8.3: version "4.12.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.12.0.tgz#06c6d5715a1ede6c51fc39ff67fd647f740b656d" integrity sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg== @@ -8329,15 +7737,6 @@ browserslist@^4.12.0: node-releases "^1.1.53" pkg-up "^2.0.0" -browserslist@^4.8.3: - version "4.8.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.8.5.tgz#691af4e327ac877b25e7a3f7ee869c4ef36cdea3" - integrity sha512-4LMHuicxkabIB+n9874jZX/az1IaZ5a+EUuvD7KFOu9x/Bd5YHyO0DIz2ls/Kl8g0ItS4X/ilEgf4T1Br0lgSg== - dependencies: - caniuse-lite "^1.0.30001022" - electron-to-chromium "^1.3.338" - node-releases "^1.1.46" - bser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -8402,11 +7801,6 @@ buffer@^5.1.0, buffer@^5.2.0, buffer@^5.5.0: base64-js "^1.0.2" ieee754 "^1.1.4" -builtin-modules@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" @@ -8482,30 +7876,7 @@ cacache@^13.0.1: ssri "^7.0.0" unique-filename "^1.1.1" -cacache@^15.0.3, cacache@^15.0.4: - version "15.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.4.tgz#b2c23cf4ac4f5ead004fb15a0efb0a20340741f1" - integrity sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw== - dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^5.1.1" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.0" - tar "^6.0.2" - unique-filename "^1.1.1" - -cacache@^15.0.5: +cacache@^15.0.3, cacache@^15.0.4, cacache@^15.0.5: version "15.0.5" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== @@ -8727,21 +8098,11 @@ camelize@^1.0.0: resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= -caniuse-lite@^1.0.30001020, caniuse-lite@^1.0.30001022, caniuse-lite@^1.0.30001043: - version "1.0.30001094" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001094.tgz#0b11d02e1cdc201348dbd8e3e57bd9b6ce82b175" - integrity sha512-ufHZNtMaDEuRBpTbqD93tIQnngmJ+oBknjvr0IbFympSdtFpAUFmNv4mVKbb53qltxFx0nK3iy32S9AqkLzUNA== - -caniuse-lite@^1.0.30001035: +caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001043, caniuse-lite@^1.0.30001097: version "1.0.30001114" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001114.tgz#2e88119afb332ead5eaa330e332e951b1c4bfea9" integrity sha512-ml/zTsfNBM+T1+mjglWRPgVsu2L76GAaADKX5f4t0pbhttEp0WMawJsHDYlFkVZkoA+89uvBRrVrEE4oqenzXQ== -caniuse-lite@^1.0.30001097: - version "1.0.30001107" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001107.tgz#809360df7a5b3458f627aa46b0f6ed6d5239da9a" - integrity sha512-86rCH+G8onCmdN4VZzJet5uPELII59cUzDphko3thQFgAQG1RNa+sVLDoALIhRYmflo5iSIzWY3vu1XTWtNMQQ== - canvas@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.6.1.tgz#0d087dd4d60f5a5a9efa202757270abea8bef89e" @@ -8821,7 +8182,7 @@ chai@3.5.0: deep-eql "^0.1.3" type-detect "^1.0.0" -chalk@2.4.2, chalk@^2.3.2, chalk@^2.4.2, chalk@~2.4.1: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2, chalk@~2.4.1: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -8841,24 +8202,6 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" - integrity sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796" - integrity sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g== - dependencies: - ansi-styles "^3.2.0" - escape-string-regexp "^1.0.5" - supports-color "^5.2.0" - chalk@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" @@ -9066,7 +8409,7 @@ chokidar@^2.0.0, chokidar@^2.0.4, chokidar@^2.1.2, chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.2.2, chokidar@^3.3.0, chokidar@^3.4.0, chokidar@^3.4.1, chokidar@^3.4.2: +chokidar@^3.2.2, chokidar@^3.3.0, chokidar@^3.4.1, chokidar@^3.4.2: version "3.4.2" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.2.tgz#38dc8e658dec3809741eb3ef7bb0a47fe424232d" integrity sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A== @@ -9164,24 +8507,12 @@ class-utils@^0.3.5: lazy-cache "^2.0.2" static-extend "^0.1.1" -classnames@2.2.6, classnames@^2.2.5, classnames@^2.2.6: +classnames@2.2.6, classnames@2.x, classnames@^2.2.5, classnames@^2.2.6: version "2.2.6" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== -classnames@2.x: - version "2.2.5" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" - integrity sha1-+zgB1FNGdknvNgPH1hoCvRKb3m0= - -clean-css@4.2.x: - version "4.2.1" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" - integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== - dependencies: - source-map "~0.6.0" - -clean-css@^4.2.3: +clean-css@4.2.x, clean-css@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== @@ -9242,17 +8573,7 @@ cli-list@^0.2.0: resolved "https://registry.yarnpkg.com/cli-list/-/cli-list-0.2.0.tgz#7e673ee0dd39a611a486476e53f3c6b3941cb582" integrity sha1-fmc+4N05phGkhkduU/PGs5QctYI= -cli-spinners@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c" - integrity sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw= - -cli-spinners@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.1.0.tgz#22c34b4d51f573240885b201efda4e4ec9fff3c7" - integrity sha512-8B00fJOEh1HPrx4fo5eW16XmE1PcL1tGpGrxy63CXGP9nHdPBN63X75hA1zhvQuhVztJWLqV58Roj2qlNM7cAA== - -cli-spinners@^2.2.0, cli-spinners@^2.4.0: +cli-spinners@^2.0.0, cli-spinners@^2.2.0, cli-spinners@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.4.0.tgz#c6256db216b878cfba4720e719cec7cf72685d7f" integrity sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA== @@ -9484,16 +8805,11 @@ coffeescript@~1.10.0: resolved "https://registry.yarnpkg.com/coffeescript/-/coffeescript-1.10.0.tgz#e7aa8301917ef621b35d8a39f348dcdd1db7e33e" integrity sha1-56qDAZF+9iGzXYo580jc3R234z4= -collapse-white-space@^1.0.0: +collapse-white-space@^1.0.0, collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== -collapse-white-space@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.3.tgz#4b906f670e5a963a87b76b0e1689643341b6023c" - integrity sha1-S5BvZw5aljqHt2sOFolkM0G2Ajw= - collect-v8-coverage@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz#150ee634ac3650b71d9c985eb7f608942334feb1" @@ -9516,14 +8832,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.8.2: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" - integrity sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ== - dependencies: - color-name "^1.1.1" - -color-convert@^1.9.0, color-convert@^1.9.1: +color-convert@^1.8.2, color-convert@^1.9.0, color-convert@^1.9.1: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -9547,20 +8856,12 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0, color-name@^1.1.1, color-name@~1.1.4: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.4.0: - version "1.5.2" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.2.tgz#26e45814bc3c9a7cbd6751648a41434514a773a9" - integrity sha1-JuRYFLw8mny9Z1FkikFDRRSnc6k= - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color-string@^1.5.2: +color-string@^1.4.0, color-string@^1.5.2: version "1.5.3" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== @@ -9639,42 +8940,25 @@ combokeys@^3.0.1: resolved "https://registry.yarnpkg.com/combokeys/-/combokeys-3.0.1.tgz#fc8ca5c3f5f2d2b03a458544cb88b14ab5f53f86" integrity sha512-5nAfaLZ3oO3kA+/xdoL7t197UJTz2WWidyH3BBeU6hqHtvyFERICd0y3DQFrQkJFTKBrtUDck/xCLLoFpnjaCw== -comma-separated-tokens@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.5.tgz#b13793131d9ea2d2431cf5b507ddec258f0ce0db" - integrity sha512-Cg90/fcK93n0ecgYTAz1jaA3zvnQ0ExlmKY1rdbyHqAx6BHxwoJc+J7HDu0iuQ7ixEs1qaa+WyQ6oeuBpYP1iA== - dependencies: - trim "0.0.1" - -comma-separated-tokens@^1.0.1: +comma-separated-tokens@^1.0.0, comma-separated-tokens@^1.0.1: version "1.0.8" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -commander@2, commander@2.19.0, commander@^2.11.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== +commander@2, commander@^2.11.0, commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1, commander@^2.8.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commander@2.17.x, commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== -commander@^2.13.0, commander@^2.15.1, commander@^2.16.0, commander@^2.19.0: - version "2.20.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" - integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== - -commander@^2.20.0, commander@^2.7.1: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@^2.8.1: - version "2.18.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.18.0.tgz#2bf063ddee7c7891176981a2cc798e5754bc6970" - integrity sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ== +commander@2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" + integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== commander@^3.0.2: version "3.0.2" @@ -9757,7 +9041,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@^1.6.2: +concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@^1.6.2, concat-stream@~1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -9767,15 +9051,6 @@ concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^ readable-stream "^2.2.2" typedarray "^0.0.6" -concat-stream@^1.4.7, concat-stream@~1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7" - integrity sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc= - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - concat-stream@~1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.5.2.tgz#708978624d856af41a5a741defdd261da752c266" @@ -9806,15 +9081,7 @@ conf@^1.3.1: pkg-up "^2.0.0" write-file-atomic "^2.3.0" -config-chain@^1.1.11: - version "1.1.11" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" - integrity sha1-q6CXR9++TD5w52am5BWG4YWfxvI= - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -config-chain@~1.1.8: +config-chain@^1.1.11, config-chain@~1.1.8: version "1.1.12" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== @@ -9836,19 +9103,7 @@ configstore@^1.0.0: write-file-atomic "^1.1.2" xdg-basedir "^2.0.0" -configstore@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" - integrity sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw== - dependencies: - dot-prop "^4.1.0" - graceful-fs "^4.1.2" - make-dir "^1.0.0" - unique-string "^1.0.0" - write-file-atomic "^2.0.0" - xdg-basedir "^3.0.0" - -configstore@^3.1.2: +configstore@^3.0.0, configstore@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.2.tgz#c6f25defaeef26df12dd33414b001fe81a543f8f" integrity sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw== @@ -9932,11 +9187,6 @@ contains-path@^0.1.0: resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= - content-disposition@0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" @@ -9978,11 +9228,6 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= - cookie@0.4.0, cookie@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" @@ -10018,14 +9263,7 @@ copy-props@^2.0.1: each-props "^1.3.0" is-plain-object "^2.0.1" -copy-to-clipboard@^3.0.8: - version "3.0.8" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz#f4e82f4a8830dce4666b7eb8ded0c9bcc313aba9" - integrity sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw== - dependencies: - toggle-selection "^1.0.3" - -copy-to-clipboard@^3.2.0: +copy-to-clipboard@^3.0.8, copy-to-clipboard@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz#d2724a3ccbfed89706fac8a894872c979ac74467" integrity sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w== @@ -10057,16 +9295,11 @@ core-js-compat@^3.6.2: browserslist "^4.8.3" semver "7.0.0" -core-js-pure@^3.0.0: +core-js-pure@^3.0.0, core-js-pure@^3.0.1: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813" integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA== -core-js-pure@^3.0.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.2.1.tgz#879a23699cff46175bfd2d09158b5c50645a3c45" - integrity sha512-+qpvnYrsi/JDeQTArB7NnNc2VoMYLE1YSkziCDHgjexC2KH7OFiGhLUd3urxfyWmNjSwSW7NYXPWHMhuIJx9Ow== - core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" @@ -10077,12 +9310,7 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.3, core-js@^2.6.5, core-js@^2.6.9: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.1, core-js@^3.0.4: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" - integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== - -core-js@^3.6.5: +core-js@^3.0.1, core-js@^3.0.4, core-js@^3.6.5: version "3.6.5" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== @@ -10110,16 +9338,6 @@ cosmiconfig@^4.0.0: parse-json "^4.0.0" require-from-string "^2.0.1" -cosmiconfig@^5.0.5: - version "5.0.7" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.7.tgz#39826b292ee0d78eda137dfa3173bd1c21a43b04" - integrity sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.9.0" - parse-json "^4.0.0" - cosmiconfig@^5.2.1: version "5.2.1" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" @@ -10386,25 +9604,7 @@ css-in-js-utils@^2.0.0: hyphenate-style-name "^1.0.2" isobject "^3.0.1" -css-loader@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.4.2.tgz#d3fdb3358b43f233b78501c5ed7b1c6da6133202" - integrity sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA== - dependencies: - camelcase "^5.3.1" - cssesc "^3.0.0" - icss-utils "^4.1.1" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.23" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.2" - postcss-modules-scope "^2.1.1" - postcss-modules-values "^3.0.0" - postcss-value-parser "^4.0.2" - schema-utils "^2.6.0" - -css-loader@^3.5.3: +css-loader@^3.4.2, css-loader@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== @@ -10457,7 +9657,7 @@ css-to-react-native@^3.0.0: css-color-keywords "^1.0.0" postcss-value-parser "^4.0.2" -css-tree@1.0.0-alpha.37: +css-tree@1.0.0-alpha.37, css-tree@^1.0.0-alpha.28: version "1.0.0-alpha.37" resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== @@ -10465,14 +9665,6 @@ css-tree@1.0.0-alpha.37: mdn-data "2.0.4" source-map "^0.6.1" -css-tree@^1.0.0-alpha.28: - version "1.0.0-alpha.39" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.39.tgz#2bff3ffe1bb3f776cf7eefd91ee5cba77a149eeb" - integrity sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA== - dependencies: - mdn-data "2.0.6" - source-map "^0.6.1" - css-what@2.1, css-what@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" @@ -10642,12 +9834,12 @@ cytoscape@^3.10.0: heap "^0.2.6" lodash.debounce "^4.0.8" -d3-array@1, d3-array@1.2.4, d3-array@^1.1.1, d3-array@^1.2.0, d3-array@^1.2.4: +d3-array@1, "d3-array@1 - 2", d3-array@1.2.4, d3-array@^1.1.1, d3-array@^1.2.0, d3-array@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f" integrity sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== -"d3-array@1 - 2", d3-array@>=2.5, d3-array@^2.3.0, d3-array@^2.7.1: +d3-array@>=2.5, d3-array@^2.3.0, d3-array@^2.7.1: version "2.8.0" resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.8.0.tgz#f76e10ad47f1f4f75f33db5fc322eb9ffde5ef23" integrity sha512-6V272gsOeg7+9pTW1jSYOR1QE37g95I3my1hBmY+vOUNHRrk9yt4OTz/gK7PMkVAVDrYYq4mq3grTiZ8iJdNIw== @@ -10664,12 +9856,12 @@ d3-collection@1, d3-collection@^1.0.3, d3-collection@^1.0.7: resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e" integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A== -d3-color@1, d3-color@^1.0.3, d3-color@^1.4.0: +d3-color@1, "d3-color@1 - 2", d3-color@^1.0.3, d3-color@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a" integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q== -"d3-color@1 - 2", d3-color@^2.0.0: +d3-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== @@ -10688,16 +9880,11 @@ d3-delaunay@^5.3.0: dependencies: delaunator "4" -d3-dispatch@1, d3-dispatch@^1.0.3: +d3-dispatch@1, "d3-dispatch@1 - 2", d3-dispatch@^1.0.3: version "1.0.6" resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58" integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA== -"d3-dispatch@1 - 2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-2.0.0.tgz#8a18e16f76dd3fcaef42163c97b926aa9b55e7cf" - integrity sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA== - d3-dsv@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-2.0.0.tgz#b37b194b6df42da513a120d913ad1be22b5fe7c5" @@ -10721,12 +9908,12 @@ d3-force@^2.1.1: d3-quadtree "1 - 2" d3-timer "1 - 2" -d3-format@1, d3-format@^1.2.0: +d3-format@1, "d3-format@1 - 2", d3-format@^1.2.0: version "1.4.4" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.4.tgz#356925f28d0fd7c7983bfad593726fce46844030" integrity sha512-TWks25e7t8/cqctxCmxpUuzZN11QxIA7YrMbram94zMQ0PXjE4LVIMe/f6a4+xxL8HQ3OsAFULOINQi1pE62Aw== -"d3-format@1 - 2", d3-format@^2.0.0: +d3-format@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767" integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA== @@ -10765,14 +9952,14 @@ d3-hierarchy@^2.0.0: resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz#dab88a58ca3e7a1bc6cab390e89667fcc6d20218" integrity sha512-SwIdqM3HxQX2214EG9GTjgmCc/mbSx4mQBn+DuEETubhOw6/U3fmnji4uCVrmzOydMHSO1nZle5gh6HB/wdOzw== -d3-interpolate@1, d3-interpolate@^1.1.4: +d3-interpolate@1, "d3-interpolate@1.2.0 - 2", d3-interpolate@^1.1.4: version "1.4.0" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987" integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA== dependencies: d3-color "1" -"d3-interpolate@1.2.0 - 2", d3-interpolate@^2.0.1: +d3-interpolate@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== @@ -11095,7 +10282,7 @@ deep-eql@^0.1.3: dependencies: type-detect "0.1.1" -deep-equal@^1.0.0, deep-equal@^1.1.1: +deep-equal@^1.0.0, deep-equal@^1.0.1, deep-equal@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== @@ -11107,11 +10294,6 @@ deep-equal@^1.0.0, deep-equal@^1.1.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-equal@^1.0.1, deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - deep-equal@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0" @@ -11132,6 +10314,11 @@ deep-equal@^2.0.3: which-collection "^1.0.1" which-typed-array "^1.1.2" +deep-equal@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + deep-extend@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" @@ -11557,16 +10744,11 @@ diagnostics@^1.1.1: enabled "1.0.x" kuler "1.0.x" -diff-match-patch@^1.0.0: +diff-match-patch@^1.0.0, diff-match-patch@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.5.tgz#abb584d5f10cd1196dfc55aa03701592ae3f7b37" integrity sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw== -diff-match-patch@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1" - integrity sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg== - diff-sequences@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" @@ -11687,15 +10869,7 @@ dom-converter@~0.2: dependencies: utila "~0.4" -dom-helpers@^5.0.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.3.tgz#7233248eb3a2d1f74aafca31e52c5299cc8ce821" - integrity sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw== - dependencies: - "@babel/runtime" "^7.6.3" - csstype "^2.6.7" - -dom-helpers@^5.0.1: +dom-helpers@^5.0.0, dom-helpers@^5.0.1: version "5.1.4" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b" integrity sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A== @@ -11830,12 +11004,7 @@ dotenv@^6.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064" integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w== -dotenv@^8.0.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.1.0.tgz#d811e178652bfb8a1e593c6dd704ec7e90d85ea2" - integrity sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA== - -dotenv@^8.1.0: +dotenv@^8.0.0, dotenv@^8.1.0: version "8.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== @@ -11884,7 +11053,7 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= -duplexify@^3.2.0, duplexify@^3.4.2, duplexify@^3.5.3, duplexify@^3.6.0: +duplexify@^3.2.0, duplexify@^3.4.2, duplexify@^3.5.3: version "3.7.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== @@ -11931,13 +11100,6 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -ecdsa-sig-formatter@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3" - integrity sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM= - dependencies: - safe-buffer "^5.0.1" - ecdsa-sig-formatter@1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" @@ -12026,16 +11188,7 @@ elastic-apm-node@^3.7.0: traceparent "^1.0.0" unicode-byte-truncate "^1.0.0" -elasticsearch@^16.4.0: - version "16.5.0" - resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-16.5.0.tgz#619a48040be25d345fdddf09fa6042a88c3974d6" - integrity sha512-9YbmU2AtM/kQdmp96EI5nu2bjxowdarV6IsKmcS+jQowJ3mhG98J1DCVOtEKuFvsnNaLyKD3aPbCAmb72+WX3w== - dependencies: - agentkeepalive "^3.4.1" - chalk "^1.0.0" - lodash "^4.17.10" - -elasticsearch@^16.7.0: +elasticsearch@^16.4.0, elasticsearch@^16.7.0: version "16.7.0" resolved "https://registry.yarnpkg.com/elasticsearch/-/elasticsearch-16.7.0.tgz#9055e3f586934d8de5fd407b04050e9d54173333" integrity sha512-du+//TbjCFEkaG0jNcAC95Fp4B6/X5shnCRIXALFL+M4U5iT3YL5ZVUPNf1NgR7dy/sc8Dvw2Ob6IUJKB7FrCw== @@ -12044,32 +11197,22 @@ elasticsearch@^16.7.0: chalk "^1.0.0" lodash "^4.17.10" -electron-to-chromium@^1.3.338: - version "1.3.340" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.340.tgz#5d4fe78e984d4211194cf5a52e08069543da146f" - integrity sha512-hRFBAglhcj5iVYH+o8QU0+XId1WGoc0VGowJB1cuJAt3exHGrivZvWeAO5BRgBZqwZtwxjm8a5MQeGoT/Su3ww== - -electron-to-chromium@^1.3.378: +electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.413: version "1.3.533" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.533.tgz#d7e5ca4d57e9bc99af87efbe13e7be5dde729b0f" integrity sha512-YqAL+NXOzjBnpY+dcOKDlZybJDCOzgsq4koW3fvyty/ldTmsb4QazZpOWmVvZ2m0t5jbBf7L0lIGU3BUipwG+A== -electron-to-chromium@^1.3.413: - version "1.3.465" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.465.tgz#d692e5c383317570c2bd82092a24a0308c6ccf29" - integrity sha512-K/lUeT3NLAsJ5SHRDhK3/zd0tw7OUllYD8w+fTOXm6ljCPsp2qq+vMzxpLo8u1M27ZjZAjRbsA6rirvne2nAMQ== - elegant-spinner@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= element-resize-detector@^1.1.12: - version "1.1.14" - resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.1.14.tgz#af064a0a618a820ad570a95c5eec5b77be0128c1" - integrity sha1-rwZKCmGKggrVcKlcXuxbd74BKME= + version "1.2.1" + resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.2.1.tgz#b0305194447a4863155e58f13323a0aef30851d1" + integrity sha512-BdFsPepnQr9fznNPF9nF4vQ457U/ZJXQDSNF1zBe7yaga8v9AdZf3/NElYxFdUh7SitSGt040QygiTo6dtatIw== dependencies: - batch-processor "^1.0.0" + batch-processor "1.0.0" element-resize-detector@^1.1.15: version "1.1.15" @@ -12168,7 +11311,7 @@ endent@^2.0.1: fast-json-parse "^1.0.3" objectorarray "^1.0.4" -enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: +enhanced-resolve@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" integrity sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng== @@ -12177,7 +11320,7 @@ enhanced-resolve@4.1.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.4.0" tapable "^1.0.0" -enhanced-resolve@^4.3.0: +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0, enhanced-resolve@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" integrity sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ== @@ -12324,14 +11467,7 @@ error-stack-parser@^1.3.5: dependencies: stackframe "^0.3.1" -error-stack-parser@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.4.tgz#a757397dc5d9de973ac9a5d7d4e8ade7cfae9101" - integrity sha512-fZ0KkoxSjLFmhW5lHbUT3tLwy3nX1qEzMYo8koY1vrsAco53CMT1djnBSeC/wUjTEZRhZl9iRw7PaMaxfJ4wzQ== - dependencies: - stackframe "^1.1.0" - -error-stack-parser@^2.0.6: +error-stack-parser@^2.0.4, error-stack-parser@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.6.tgz#5a99a707bd7a4c58a797902d48d82803ede6aad8" integrity sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ== @@ -12346,24 +11482,7 @@ error@^7.0.0, error@^7.0.2: string-template "~0.2.1" xtend "~4.0.0" -es-abstract@^1.10.0, es-abstract@^1.13.0, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.5.1, es-abstract@^1.7.0, es-abstract@^1.9.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.0.tgz#f42a517d0036a5591dbb2c463591dc8bb50309b1" - integrity sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.1.5" - is-regex "^1.0.5" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimleft "^2.1.1" - string.prototype.trimright "^2.1.1" - -es-abstract@^1.17.0, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5: +es-abstract@^1.13.0, es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5, es-abstract@^1.4.3, es-abstract@^1.5.0, es-abstract@^1.9.0: version "1.17.6" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a" integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw== @@ -12380,23 +11499,6 @@ es-abstract@^1.17.0, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17 string.prototype.trimend "^1.0.1" string.prototype.trimstart "^1.0.1" -es-abstract@^1.17.0-next.1: - version "1.17.4" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" - integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.1.5" - is-regex "^1.0.5" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimleft "^2.1.1" - string.prototype.trimright "^2.1.1" - es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" @@ -12424,15 +11526,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.40" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.40.tgz#ab3d2179b943008c5e9ef241beb25ef41424c774" - integrity sha512-S9Fh3oya5OOvYSNGvPZJ+vyrs6VYpe1IXPowVe3N1OhaiwVaGlwfn3Zf5P5klYcWOA0toIwYQW8XEv/QqhdHvQ== - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - -es5-ext@^0.10.45, es5-ext@~0.10.2, es5-ext@~0.10.46: +es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.45, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: version "0.10.46" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572" integrity sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw== @@ -12477,12 +11571,7 @@ es6-promise-pool@^2.5.0: resolved "https://registry.yarnpkg.com/es6-promise-pool/-/es6-promise-pool-2.5.0.tgz#147c612b36b47f105027f9d2bf54a598a99d9ccb" integrity sha1-FHxhKza0fxBQJ/nSv1SlmKmdnMs= -es6-promise@^4.0.3: - version "4.2.4" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" - integrity sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ== - -es6-promise@^4.2.5: +es6-promise@^4.0.3, es6-promise@^4.2.5: version "4.2.6" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== @@ -12561,31 +11650,7 @@ escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escodegen@^1.11.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" - integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escodegen@^1.11.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.0.tgz#f763daf840af172bb3a2b6dd7219c0e17f7ff541" - integrity sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg== - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -escodegen@^1.12.0, escodegen@^1.14.1: +escodegen@^1.11.0, escodegen@^1.11.1, escodegen@^1.12.0, escodegen@^1.14.1, escodegen@^1.8.0: version "1.14.3" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== @@ -12597,18 +11662,6 @@ escodegen@^1.12.0, escodegen@^1.14.1: optionalDependencies: source-map "~0.6.1" -escodegen@^1.8.0: - version "1.11.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" - integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== - dependencies: - esprima "^3.1.3" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - escodegen@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.2.0.tgz#09de7967791cc958b7f89a2ddb6d23451af327e1" @@ -12862,12 +11915,7 @@ eslint-utils@^2.0.0: dependencies: eslint-visitor-keys "^1.1.0" -eslint-visitor-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" - integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== - -eslint-visitor-keys@^1.1.0: +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== @@ -12976,11 +12024,6 @@ espree@^6.1.2: acorn-jsx "^5.1.0" eslint-visitor-keys "^1.1.0" -esprima@^3.1.3, esprima@~3.1.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -12991,6 +12034,11 @@ esprima@~1.0.4: resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= +esprima@~3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= + esquery@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" @@ -13153,7 +12201,7 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^4.0.0: +execa@^4.0.0, execa@^4.0.2: version "4.0.3" resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.3.tgz#0a34dabbad6d66100bd6f2c576c8669403f317f2" integrity sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A== @@ -13168,21 +12216,6 @@ execa@^4.0.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.2.tgz#ad87fb7b2d9d564f70d2b62d511bee41d5cbb240" - integrity sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - execall@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73" @@ -13249,18 +12282,6 @@ expect@^24.8.0, expect@^24.9.0: jest-message-util "^24.9.0" jest-regex-util "^24.9.0" -expect@^26.4.0: - version "26.4.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-26.4.0.tgz#34a0aae523343b0931ff1cf0aa972dfe40edfab4" - integrity sha512-dbYDJhFcqQsamlos6nEwAMe+ahdckJBk5fmw1DYGLQGabGSlUuT+Fm2jHYw5119zG3uIhP+lCQbjJhFEdZMJtg== - dependencies: - "@jest/types" "^26.3.0" - ansi-styles "^4.0.0" - jest-get-type "^26.3.0" - jest-matcher-utils "^26.4.0" - jest-message-util "^26.3.0" - jest-regex-util "^26.0.0" - expect@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/expect/-/expect-26.4.2.tgz#36db120928a5a2d7d9736643032de32f24e1b2a1" @@ -13283,43 +12304,7 @@ expose-loader@^0.7.5: resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.5.tgz#e29ea2d9aeeed3254a3faa1b35f502db9f9c3f6f" integrity sha512-iPowgKUZkTPX5PznYsmifVj9Bob0w2wTHVkt/eYNPSzyebkUgIedmskf/kcfEIWpiWjg3JRjnW+a17XypySMuw== -express@^4.16.3: - version "4.16.4" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" - integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== - dependencies: - accepts "~1.3.5" - array-flatten "1.1.1" - body-parser "1.18.3" - content-disposition "0.5.2" - content-type "~1.0.4" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.1.1" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.2" - path-to-regexp "0.1.7" - proxy-addr "~2.0.4" - qs "6.5.2" - range-parser "~1.2.0" - safe-buffer "5.1.2" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" - utils-merge "1.0.1" - vary "~1.1.2" - -express@^4.17.0, express@^4.17.1: +express@^4.16.3, express@^4.17.0, express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -13451,16 +12436,11 @@ extract-zip@^2.0.0, extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -extsprintf@1.3.0: +extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - faker@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/faker/-/faker-1.1.0.tgz#230738ebd37edad9de4a421de12922bd8206a872" @@ -13524,7 +12504,7 @@ fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@~2.1.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.4, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -13568,7 +12548,7 @@ fastq@^1.6.0: dependencies: reusify "^1.0.0" -fault@^1.0.0, fault@^1.0.2: +fault@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== @@ -13616,11 +12596,6 @@ fd-slicer@1.1.0, fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fecha@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" - integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== - fecha@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" @@ -13801,19 +12776,6 @@ filter-obj@^1.1.0: resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b" integrity sha1-mzERErxsYSehbgFsbF1/GeCAXFs= -finalhandler@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" - integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.4.0" - unpipe "~1.0.0" - finalhandler@1.1.2, finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -13989,7 +12951,7 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= -flush-write-stream@^1.0.0: +flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" integrity sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw== @@ -13997,14 +12959,6 @@ flush-write-stream@^1.0.0: inherits "^2.0.1" readable-stream "^2.0.4" -flush-write-stream@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" - integrity sha1-yBuQ2HRnZvGmCaRoCZRsRd2K5Bc= - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.4" - fmin@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/fmin/-/fmin-0.0.2.tgz#59bbb40d43ffdc1c94cd00a568c41f95f1973017" @@ -14047,20 +13001,13 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -follow-redirects@1.9.0: +follow-redirects@1.9.0, follow-redirects@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f" integrity sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A== dependencies: debug "^3.0.0" -follow-redirects@^1.0.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" - integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== - dependencies: - debug "^3.2.6" - font-awesome@4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" @@ -14083,14 +13030,7 @@ fontkit@^1.8.0: unicode-properties "^1.2.2" unicode-trie "^0.3.0" -for-each@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4" - integrity sha1-LEBFC5NI6X8oEyJZO6lnBLmr1NQ= - dependencies: - is-function "~1.0.0" - -for-each@^0.3.3, for-each@~0.3.3: +for-each@^0.3.2, for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== @@ -14176,16 +13116,7 @@ form-data-to-object@^0.2.0: resolved "https://registry.yarnpkg.com/form-data-to-object/-/form-data-to-object-0.2.0.tgz#f7a8e68ddd910a1100a65e25ac6a484143ff8168" integrity sha1-96jmjd2RChEApl4lrGpIQUP/gWg= -form-data@^2.3.1, form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -form-data@^2.5.0: +form-data@^2.3.1, form-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.0.tgz#094ec359dc4b55e7d62e0db4acd76e89fe874d37" integrity sha512-WXieX3G/8side6VIqx44ablyULoGruSde5PNTxoUyo5CeyAMX6nVWUd0rgist/EuX655cjhUhTo1Fo3tRYqbcA== @@ -14203,6 +13134,15 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + format@^0.2.0: version "0.2.2" resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" @@ -14882,15 +13822,7 @@ global-tunnel-ng@^2.5.3: npm-conf "^1.1.3" tunnel "^0.0.6" -global@^4.3.1, global@^4.3.2, global@~4.3.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" - integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= - dependencies: - min-document "^2.19.0" - process "~0.5.1" - -global@^4.4.0: +global@^4.3.1, global@^4.3.2, global@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== @@ -14898,12 +13830,15 @@ global@^4.4.0: min-document "^2.19.0" process "^0.11.10" -globals@^11.1.0: - version "11.7.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" - integrity sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg== +global@~4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" + integrity sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8= + dependencies: + min-document "^2.19.0" + process "~0.5.1" -globals@^11.12.0: +globals@^11.1.0, globals@^11.12.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== @@ -16061,21 +14996,16 @@ heavy@6.x.x: hoek "5.x.x" joi "13.x.x" -highlight.js@9.15.10, highlight.js@~9.15.0, highlight.js@~9.15.1: +highlight.js@9.15.10, highlight.js@^9.12.0, highlight.js@~9.15.0, highlight.js@~9.15.1: version "9.15.10" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.15.10.tgz#7b18ed75c90348c045eef9ed08ca1319a2219ad2" integrity sha512-RoV7OkQm0T3os3Dd2VHLNMoaoDVx77Wygln3n9l5YV172XonWG6rgQD3XnF/BuFFZw9A0TJgmMSO8FEWQgvcXw== -highlight.js@^9.12.0, highlight.js@~9.12.0: +highlight.js@~9.12.0: version "9.12.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e" integrity sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4= -highlight.js@~10.1.0: - version "10.1.2" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.1.2.tgz#c20db951ba1c22c055010648dfffd7b2a968e00c" - integrity sha512-Q39v/Mn5mfBlMff9r+zzA+gWxRsCRKwEMvYTiisLr/XUiFI/4puWt0Ojdko3R3JCNWGdOWaA5g/Yxqa23kC5AA== - history-extra@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/history-extra/-/history-extra-5.0.1.tgz#95a2e59dda526c4241d0ae1b124a77a5e4675ce8" @@ -16299,17 +15229,7 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= -http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-errors@1.7.2: +http-errors@1.7.2, http-errors@~1.7.0, http-errors@~1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== @@ -16320,16 +15240,15 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@~1.7.0, http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= dependencies: depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" http-headers@^3.0.2: version "3.0.2" @@ -16343,11 +15262,6 @@ http-https@^1.0.0: resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" integrity sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs= -http-parser-js@>=0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.2.tgz#da2e31d237b393aae72ace43882dd7e270a8ff77" - integrity sha512-opCO9ASqg5Wy2FNo7A0sxy71yGbbkJJXLdgMK04Tcypw9jr2MgWbyubb0+WdmDmGnFflO7fRbqbaihh/ENDlRQ== - http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -16405,15 +15319,7 @@ https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz#51552970fa04d723e04c56d04178c3f92592bbc0" - integrity sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ== - dependencies: - agent-base "^4.1.0" - debug "^3.1.0" - -https-proxy-agent@^2.2.4: +https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== @@ -16477,40 +15383,14 @@ iconv-lite@0.4, iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.4.19: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - integrity sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ== - -iconv-lite@0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" - integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.0.tgz#59cdde0a2a297cc2aeb0c6445a195ee89f127550" - integrity sha512-NnEhI9hIEKHOzJ4f697DMz9IQEXr/MMJ5w64vN2/4Ai+wRnvV7SBrL0KLoRlwaKVghOc7LQ5YkPLuX146b6Ydw== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.5.1: +iconv-lite@^0.5.0, iconv-lite@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.5.1.tgz#b2425d3c7b18f7219f2ca663d103bddb91718d64" integrity sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q== dependencies: safer-buffer ">= 2.1.2 < 3" -icss-utils@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.0.0.tgz#d52cf4bcdcfa1c45c2dbefb4ffdf6b00ef608098" - integrity sha512-bA/xGiwWM17qjllIs9X/y0EjsB7e0AV08F3OL8UPsoNkNRibIuu8f1eKTnQ8QO1DteKKTxTUAn+IEWUToIwGOA== - dependencies: - postcss "^7.0.5" - -icss-utils@^4.1.1: +icss-utils@^4.0.0, icss-utils@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== @@ -16598,16 +15478,11 @@ immediate@~3.0.5: resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= -immer@1.10.0: +immer@1.10.0, immer@^1.5.0: version "1.10.0" resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== -immer@^1.5.0: - version "1.12.1" - resolved "https://registry.yarnpkg.com/immer/-/immer-1.12.1.tgz#40c6e5b292c00560836c2993bda3a24379d466f5" - integrity sha512-3fmKM6ovaqDt0CdC9daXpNi5x/YCYS3i4cwLdTVkhJdk5jrDXoPs7lCm3IqM3yhfSnz4tjjxbRG2CziQ7m8ztg== - import-cwd@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" @@ -16623,15 +15498,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" - integrity sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-fresh@^3.1.0: +import-fresh@^3.0.0, import-fresh@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -16707,11 +15574,6 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - inert@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/inert/-/inert-5.1.0.tgz#e9f130dc3047ccd9ffaa64b157b4c1114611035d" @@ -16742,7 +15604,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -16752,7 +15614,7 @@ inherits@2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= -inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: +inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= @@ -16782,7 +15644,7 @@ inline-style@^2.0.0: dependencies: dashify "^0.1.0" -inquirer@6.2.2: +inquirer@6.2.2, inquirer@^6.0.0: version "6.2.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.2.tgz#46941176f65c9eb20804627149b743a218f25406" integrity sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA== @@ -16878,25 +15740,6 @@ inquirer@^5.0.0: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^6.0.0: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - inquirer@^7.0.0, inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -17000,20 +15843,13 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@2.2.4, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.3, invariant@^2.2.4: +invariant@2.2.4, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" -invariant@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" - integrity sha1-nh9WrArNtr8wMwbzOL47IErmA2A= - dependencies: - loose-envify "^1.0.0" - invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" @@ -17039,11 +15875,6 @@ ip@^1.1.0, ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" - integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= - ipaddr.js@1.9.0, ipaddr.js@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" @@ -17090,16 +15921,11 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-alphabetical@1.0.4: +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== -is-alphabetical@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.1.tgz#c77079cc91d4efac775be1034bf2d243f95e6f08" - integrity sha1-x3B5zJHU76x3W+EDS/LSQ/lebwg= - is-alphanumerical@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.1.tgz#dfb4aa4d1085e33bdb61c2dee9c80e9c6c19f53b" @@ -17157,19 +15983,7 @@ is-buffer@^2.0.0, is-buffer@^2.0.2, is-buffer@~2.0.3: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - integrity sha1-VAVy0096wxGfj3bDDLwbHgN6/74= - dependencies: - builtin-modules "^1.0.0" - -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== - -is-callable@^1.2.0: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== @@ -17202,22 +16016,12 @@ is-data-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY= - -is-date-object@^1.0.2: +is-date-object@^1.0.1, is-date-object@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== -is-decimal@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.1.tgz#f5fb6a94996ad9e8e3761fbfbd091f1fca8c4e82" - integrity sha1-9ftqlJlq2ejjdh+/vQkfH8qMToI= - -is-decimal@^1.0.2: +is-decimal@^1.0.0, is-decimal@^1.0.2: version "1.0.4" resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== @@ -17309,12 +16113,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-function@^1.0.1, is-function@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" - integrity sha1-Es+5i2W1fdPRk6MSH19uL0N2ArU= - -is-function@^1.0.2: +is-function@^1.0.1, is-function@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== @@ -17329,7 +16128,7 @@ is-generator-function@^1.0.7: resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== -is-glob@4.0.0, is-glob@^4.0.0: +is-glob@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" integrity sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A= @@ -17350,7 +16149,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -17509,12 +16308,7 @@ is-path-cwd@^1.0.0: resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= -is-path-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.1.0.tgz#2e0c7e463ff5b7a0eb60852d851a6809347a124c" - integrity sha512-Sc5j3/YnM8tDeyCsVeKlm/0p95075DyLmDEIkSgQ7mXkrOX+uTCtmQFm0CYzVyJwcCCmO3k8qfJt17SxQwB5Zw== - -is-path-cwd@^2.2.0: +is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== @@ -17601,21 +16395,7 @@ is-redirect@^1.0.0: resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" integrity sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ= -is-regex@^1.0.4, is-regex@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" - integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== - dependencies: - has "^1.0.3" - -is-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" - integrity sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw== - dependencies: - has-symbols "^1.0.1" - -is-regex@^1.1.1: +is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== @@ -17708,14 +16488,7 @@ is-supported-regexp-flag@^1.0.0: resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.0.tgz#8b520c85fae7a253382d4b02652e045576e13bb8" integrity sha1-i1IMhfrnolM4LUsCZS4EVXbhO7g= -is-symbol@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38" - integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw== - dependencies: - has-symbols "^1.0.0" - -is-symbol@^1.0.3: +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== @@ -17808,12 +16581,7 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -is-wsl@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" - integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== - -is-wsl@^2.2.0: +is-wsl@^2.1.1, is-wsl@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== @@ -17941,20 +16709,7 @@ istanbul-lib-instrument@^1.7.3: istanbul-lib-coverage "^1.2.1" semver "^5.3.0" -istanbul-lib-instrument@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" - integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== - dependencies: - "@babel/core" "^7.7.5" - "@babel/parser" "^7.7.5" - "@babel/template" "^7.7.4" - "@babel/traverse" "^7.7.4" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-instrument@^4.0.3: +istanbul-lib-instrument@^4.0.0, istanbul-lib-instrument@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== @@ -18159,16 +16914,6 @@ jest-diff@^25.2.1: jest-get-type "^25.2.6" pretty-format "^25.5.0" -jest-diff@^26.4.0: - version "26.4.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.4.0.tgz#d073a0a11952b5bd9f1ff39bb9ad24304a0c55f7" - integrity sha512-wwC38HlOW+iTq6j5tkj/ZamHn6/nrdcEOc/fKaVILNtN2NLWGdkfRaHWwfNYr5ehaLvuoG2LfCZIcWByVj0gjg== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.3.0" - jest-get-type "^26.3.0" - pretty-format "^26.4.0" - jest-diff@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.4.2.tgz#a1b7b303bcc534aabdb3bd4a7caf594ac059f5aa" @@ -18309,16 +17054,6 @@ jest-matcher-utils@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-matcher-utils@^26.4.0: - version "26.4.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.4.0.tgz#2bce9a939e008b894faf1bd4b5bb58facd00c252" - integrity sha512-u+xdCdq+F262DH+PutJKXLGr2H5P3DImdJCir51PGSfi3TtbLQ5tbzKaN8BkXbiTIU6ayuAYBWTlU1nyckVdzA== - dependencies: - chalk "^4.0.0" - jest-diff "^26.4.0" - jest-get-type "^26.3.0" - pretty-format "^26.4.0" - jest-matcher-utils@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.4.2.tgz#fa81f3693f7cb67e5fc1537317525ef3b85f4b06" @@ -18372,12 +17107,7 @@ jest-mock@^26.3.0: "@jest/types" "^26.3.0" "@types/node" "*" -jest-pnp-resolver@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" - integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== - -jest-pnp-resolver@^1.2.2: +jest-pnp-resolver@^1.2.1, jest-pnp-resolver@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== @@ -18516,28 +17246,7 @@ jest-snapshot@^24.1.0: pretty-format "^24.9.0" semver "^6.2.0" -jest-snapshot@^26.3.0: - version "26.4.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.4.0.tgz#efd42eef09bcb33e9a3eb98e229f2368c73c9235" - integrity sha512-vFGmNGWHMBomrlOpheTMoqihymovuH3GqfmaEIWoPpsxUXyxT3IlbxI5I4m2vg0uv3HUJYg5JoGrkgMzVsAwCg== - dependencies: - "@babel/types" "^7.0.0" - "@jest/types" "^26.3.0" - "@types/prettier" "^2.0.0" - chalk "^4.0.0" - expect "^26.4.0" - graceful-fs "^4.2.4" - jest-diff "^26.4.0" - jest-get-type "^26.3.0" - jest-haste-map "^26.3.0" - jest-matcher-utils "^26.4.0" - jest-message-util "^26.3.0" - jest-resolve "^26.4.0" - natural-compare "^1.4.0" - pretty-format "^26.4.0" - semver "^7.3.2" - -jest-snapshot@^26.4.2: +jest-snapshot@^26.3.0, jest-snapshot@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.4.2.tgz#87d3ac2f2bd87ea8003602fbebd8fcb9e94104f6" integrity sha512-N6Uub8FccKlf5SBFnL2Ri/xofbaA68Cc3MGjP/NuwgnsvWh+9hLIR/DhrxbSiKXMY9vUW5dI6EW1eHaDHqe9sg== @@ -18738,12 +17447,12 @@ js-string-escape@^1.0.1: resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" integrity sha1-4mJbrbwNZ8dTPp7cEGjFh65BN+8= -js-tokens@^3.0.0, js-tokens@^3.0.2: +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -18858,12 +17567,7 @@ json-buffer@3.0.1: resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" - integrity sha512-xyQpxeWWMKyJps9CuGJYeng6ssI5bpqS9ltQpdVQ90t4ql6NdnxFKh95JcRt2cun/DjMVNrdjniLPuMA69xmCw== - -json-parse-better-errors@^1.0.2: +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -18932,20 +17636,13 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.1: +json5@^2.1.1, json5@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== dependencies: minimist "^1.2.5" -json5@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.2.tgz#43ef1f0af9835dd624751a6b7fa48874fb2d608e" - integrity sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== - dependencies: - minimist "^1.2.5" - jsondiffpatch@0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/jsondiffpatch/-/jsondiffpatch-0.4.1.tgz#9fb085036767f03534ebd46dcd841df6070c5773" @@ -19007,22 +17704,7 @@ jsonpointer@^4.0.0: resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" integrity sha1-T9kss04OnbPInIYi7PUfm5eMbLk= -jsonwebtoken@^8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz#056c90eee9a65ed6e6c72ddb0a1d325109aaf643" - integrity sha512-oge/hvlmeJCH+iIz1DwcO7vKPkNGJHhgkspk8OH3VKlw+mbi42WtD4ig1+VXRln765vxptAv+xT26Fd3cteqag== - dependencies: - jws "^3.1.5" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - -jsonwebtoken@^8.5.1: +jsonwebtoken@^8.3.0, jsonwebtoken@^8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== @@ -19058,15 +17740,7 @@ jsts@^1.6.2: resolved "https://registry.yarnpkg.com/jsts/-/jsts-1.6.2.tgz#c0efc885edae06ae84f78cbf2a0110ba929c5925" integrity sha512-JNfDQk/fo5MeXx4xefvCyHZD22/DHowHr5K07FdgCJ81MEqn02HsDV5FQvYTz60ZIOv/+hhGbsVzXX5cuDWWlA== -jsx-ast-utils@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb" - integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ== - dependencies: - array-includes "^3.0.3" - object.assign "^4.1.0" - -jsx-ast-utils@^2.4.1: +jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.4.1.tgz#1114a4c1209481db06c690c2b4f488cc665f657e" integrity sha512-z1xSldJ6imESSzOjd3NNkieVJKRlKYSOtMG8SFyCj2FIrvSaSuli/WjpBkEzCBoR9bYYYFgqJw61Xhu7Lcgk+w== @@ -19109,15 +17783,6 @@ just-reduce-object@^1.0.3: resolved "https://registry.yarnpkg.com/just-reduce-object/-/just-reduce-object-1.1.0.tgz#d29d172264f8511c74462de30d72d5838b6967e6" integrity sha512-nGyg7N9FEZsyrGQNilkyVLxKPsf96iel5v0DrozQ19ML+96HntyS/53bOP68iK/kZUGvsL3FKygV8nQYYhgTFw== -jwa@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.6.tgz#87240e76c9808dbde18783cf2264ef4929ee50e6" - integrity sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.10" - safe-buffer "^5.0.1" - jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -19127,14 +17792,6 @@ jwa@^1.4.1: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" -jws@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.5.tgz#80d12d05b293d1e841e7cb8b4e69e561adcf834f" - integrity sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ== - dependencies: - jwa "^1.1.5" - safe-buffer "^5.0.1" - jws@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" @@ -19163,20 +17820,13 @@ keymirror@0.1.1: resolved "https://registry.yarnpkg.com/keymirror/-/keymirror-0.1.1.tgz#918889ea13f8d0a42e7c557250eee713adc95c35" integrity sha1-kYiJ6hP40KQufFVyUO7nE63JXDU= -keyv@3.0.0: +keyv@3.0.0, keyv@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== dependencies: json-buffer "3.0.0" -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - keyv@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.0.0.tgz#2d1dab694926b2d427e4c74804a10850be44c12f" @@ -19484,30 +18134,6 @@ listr-update-renderer@0.5.0, listr-update-renderer@^0.5.0: log-update "^2.3.0" strip-ansi "^3.0.1" -listr-update-renderer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7" - integrity sha1-NE2YDaLKLosUW6MFkI8yrj9MyKc= - dependencies: - chalk "^1.1.3" - cli-truncate "^0.2.1" - elegant-spinner "^1.0.1" - figures "^1.7.0" - indent-string "^3.0.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - strip-ansi "^3.0.1" - -listr-verbose-renderer@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35" - integrity sha1-ggb0z21S3cWCfl/RSYng6WWTOjU= - dependencies: - chalk "^1.1.3" - cli-cursor "^1.0.2" - date-fns "^1.27.2" - figures "^1.7.0" - listr-verbose-renderer@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db" @@ -19518,7 +18144,7 @@ listr-verbose-renderer@^0.5.0: date-fns "^1.27.2" figures "^2.0.0" -listr@0.14.3, listr@^0.14.3: +listr@0.14.3, listr@^0.14.1, listr@^0.14.3: version "0.14.3" resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586" integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA== @@ -19533,28 +18159,6 @@ listr@0.14.3, listr@^0.14.3: p-map "^2.0.0" rxjs "^6.3.3" -listr@^0.14.1: - version "0.14.1" - resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.1.tgz#8a7afa4a7135cee4c921d128e0b7dfc6e522d43d" - integrity sha512-MSMUUVN1f8aRnPi4034RkOqdiUlpYW+FqwFE3aL0uYNPRavkt2S2SsSpDDofn8BDpqv2RNnsdOcCHWsChcq77A== - dependencies: - "@samverschueren/stream-to-observable" "^0.3.0" - cli-truncate "^0.2.1" - figures "^1.7.0" - indent-string "^2.1.0" - is-observable "^1.1.0" - is-promise "^2.1.0" - is-stream "^1.1.0" - listr-silent-renderer "^1.1.1" - listr-update-renderer "^0.4.0" - listr-verbose-renderer "^0.4.0" - log-symbols "^1.0.2" - log-update "^1.0.2" - ora "^0.2.3" - p-map "^1.1.1" - rxjs "^6.1.0" - strip-ansi "^3.0.1" - livereload-js@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.4.0.tgz#447c31cf1ea9ab52fc20db615c5ddf678f78009c" @@ -19975,7 +18579,7 @@ lodash.uniq@4.5.0, lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5: +lodash@4.17.11, lodash@4.17.15, lodash@>4.17.4, lodash@^4, lodash@^4.0.0, lodash@^4.0.1, lodash@^4.10.0, lodash@^4.11.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.2, lodash@^4.17.20, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.10, lodash@~4.17.15, lodash@~4.17.5: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -20018,33 +18622,14 @@ log-symbols@^4.0.0: log-update@2.3.0, log-update@^2.3.0: version "2.3.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" - integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= - dependencies: - ansi-escapes "^3.0.0" - cli-cursor "^2.0.0" - wrap-ansi "^3.0.1" - -log-update@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1" - integrity sha1-GZKfZMQJPS0ucHWh2tivWcKWuNE= - dependencies: - ansi-escapes "^1.0.0" - cli-cursor "^1.0.2" - -logform@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.1.2.tgz#957155ebeb67a13164069825ce67ddb5bb2dd360" - integrity sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ== - dependencies: - colors "^1.2.1" - fast-safe-stringify "^2.0.4" - fecha "^2.3.3" - ms "^2.1.1" - triple-beam "^1.3.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" + integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg= + dependencies: + ansi-escapes "^3.0.0" + cli-cursor "^2.0.0" + wrap-ansi "^3.0.1" -logform@^2.2.0: +logform@^2.1.1, logform@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/logform/-/logform-2.2.0.tgz#40f036d19161fc76b68ab50fdc7fe495544492f2" integrity sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg== @@ -20090,20 +18675,13 @@ longest@^1.0.1: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== dependencies: js-tokens "^3.0.0 || ^4.0.0" -loose-envify@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" - integrity sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg= - dependencies: - js-tokens "^3.0.0" - loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -20146,7 +18724,7 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lowlight@1.12.1: +lowlight@1.12.1, lowlight@^1.2.0: version "1.12.1" resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.12.1.tgz#014acf8dd73a370e02ff1cc61debcde3bb1681eb" integrity sha512-OqaVxMGIESnawn+TU/QMV5BJLbUghUfjDWPAtFqDYDmDtr4FnB+op8xM+pR7nKlauHNUHXGt0VgWatFB8voS5w== @@ -20154,14 +18732,6 @@ lowlight@1.12.1: fault "^1.0.2" highlight.js "~9.15.0" -lowlight@^1.2.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.14.0.tgz#83ebc143fec0f9e6c0d3deffe01be129ce56b108" - integrity sha512-N2E7zTM7r1CwbzwspPxJvmjAbxljCPThTFawEX2Z7+P3NGrrvY54u8kyU16IY4qWfoVIxY8SYCS8jTkuG7TqYA== - dependencies: - fault "^1.0.0" - highlight.js "~10.1.0" - lowlight@~1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.9.1.tgz#ed7c3dffc36f8c1f263735c0fe0c907847c11250" @@ -20169,15 +18739,7 @@ lowlight@~1.9.1: dependencies: highlight.js "~9.12.0" -lru-cache@4.1.x, lru-cache@^4.0.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.2.tgz#45234b2e6e2f2b33da125624c4664929a0224c3f" - integrity sha512-wgeVXhrDwAWnIF/yZARsFnMBtdFXOg1b8RIrhilp+0iDYN4mdQcNZElDZ0e4B64BhaxeQ5zN7PMyvu7we1kPeQ== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^4.0.1, lru-cache@^4.1.5: +lru-cache@4.1.x, lru-cache@^4.0.0, lru-cache@^4.0.1, lru-cache@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== @@ -20282,11 +18844,6 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" - integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== - map-age-cleaner@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz#098fb15538fd3dbe461f12745b0ca8568d4e3f74" @@ -20478,11 +19035,6 @@ mdn-data@2.0.4: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== -mdn-data@2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.6.tgz#852dc60fcaa5daa2e8cf6c9189c440ed3e042978" - integrity sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA== - mdurl@^1.0.0, mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -20552,16 +19104,11 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^1.1.0" -"memoize-one@>=3.1.1 <6", memoize-one@^5.1.1: +"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0, memoize-one@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0" integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA== -memoize-one@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.1.tgz#35a709ffb6e5f0cb79f9679a96f09ec3a35addfa" - integrity sha512-S3plzyksLOSF4pkf1Xlb7mA8ZRKZlgp3ebg7rULbfwPT8Ww7uZz5CbLgRKaR92GeXpsNiFbfCRWf/uOrCYIbRg== - memoizee@0.4.X: version "0.4.14" resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.14.tgz#07a00f204699f9a95c2d9e77218271c7cd610d57" @@ -20742,59 +19289,18 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": - version "1.40.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== - -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== - -mime-db@1.44.0: +mime-db@1.44.0, mime-db@1.x.x, "mime-db@>= 1.40.0 < 2": version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== -mime-db@1.x.x, mime-db@~1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" - integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.19: - version "2.1.21" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" - integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== - dependencies: - mime-db "~1.37.0" - -mime-types@^2.1.25: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== - dependencies: - mime-db "1.43.0" - -mime-types@^2.1.26: +mime-types@^2.1.12, mime-types@^2.1.25, mime-types@^2.1.26, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== dependencies: mime-db "1.44.0" -mime-types@~2.1.24: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== - dependencies: - mime-db "1.40.0" - -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== - mime@1.6.0, mime@^1.2.11, mime@^1.3.4, mime@^1.4.1: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -20820,12 +19326,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -mimic-response@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.0.0.tgz#996a51c60adf12cb8a87d7fb8ef24c2f3d5ebb46" - integrity sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ== - -mimic-response@^2.1.0: +mimic-response@^2.0.0, mimic-response@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== @@ -21001,7 +19502,7 @@ mkdirp@0.5.3: dependencies: minimist "^1.2.5" -mkdirp@0.5.4, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.4, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.4, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== @@ -21013,13 +19514,6 @@ mkdirp@^0.3.5: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" integrity sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc= -mkdirp@^0.5.3: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -21347,12 +19841,7 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -nan@^2.12.1, nan@^2.13.2: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== - -nan@^2.14.0, nan@^2.14.1: +nan@^2.12.1, nan@^2.13.2, nan@^2.14.0, nan@^2.14.1: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw== @@ -21426,27 +19915,12 @@ needle@^2.2.1: iconv-lite "^0.4.4" sax "^1.2.4" -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.5.0: - version "2.5.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.2.tgz#489105ce7bc54e709d736b195f82135048c50fcc" - integrity sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw== - -neo-async@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" - integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== - -neo-async@^2.6.1: +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== @@ -21458,14 +19932,7 @@ nested-error-stacks@^1.0.0: dependencies: inherits "~2.0.1" -nested-error-stacks@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.0.0.tgz#98b2ffaefb4610fa3936f1e71435d30700de2840" - integrity sha1-mLL/rvtGEPo5NvHnFDXTBwDeKEA= - dependencies: - inherits "~2.0.1" - -nested-error-stacks@^2.1.0: +nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== @@ -21644,36 +20111,7 @@ node-jose@1.1.0: node-forge "^0.7.6" uuid "^3.3.2" -"node-libs-browser@^1.0.0 || ^2.0.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.0.tgz#c72f60d9d46de08a940dedbb25f3ffa2f9bbaa77" - integrity sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.0" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "0.0.4" - -node-libs-browser@^2.2.1: +"node-libs-browser@^1.0.0 || ^2.0.0", node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== @@ -21742,23 +20180,11 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-releases@^1.1.46: - version "1.1.47" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.47.tgz#c59ef739a1fd7ecbd9f0b7cf5b7871e8a8b591e4" - integrity sha512-k4xjVPx5FpwBUj0Gw7uvFOTF4Ep8Hok1I6qjwL3pLfwe7Y0REQSAqOwwv9TWBCUtMHxcXfY4PgRLRozcChvTcA== - dependencies: - semver "^6.3.0" - -node-releases@^1.1.52: +node-releases@^1.1.52, node-releases@^1.1.53: version "1.1.60" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== -node-releases@^1.1.53: - version "1.1.58" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.58.tgz#8ee20eef30fa60e52755fcc0942def5a734fe935" - integrity sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg== - node-sass@^4.13.1: version "4.13.1" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.13.1.tgz#9db5689696bb2eec2c32b98bfea4c7a2e992d0a3" @@ -21852,17 +20278,7 @@ nopt@~1.0.10: dependencies: abbrev "1" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - integrity sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw== - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-package-data@^2.5.0: +normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -22141,12 +20557,7 @@ object-inspect@~1.6.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== -object-is@^1.0.1, object-is@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" - integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== - -object-is@^1.1.2: +object-is@^1.0.1, object-is@^1.0.2, object-is@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== @@ -22198,17 +20609,7 @@ object.defaults@^1.0.0, object.defaults@^1.1.0: for-own "^1.0.0" isobject "^3.0.0" -object.entries@^1.0.4, object.entries@^1.1.0, object.entries@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.1.tgz#ee1cf04153de02bb093fec33683900f57ce5399b" - integrity sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.0-next.1" - function-bind "^1.1.1" - has "^1.0.3" - -object.entries@^1.1.2: +object.entries@^1.0.4, object.entries@^1.1.0, object.entries@^1.1.1, object.entries@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA== @@ -22228,14 +20629,6 @@ object.entries@^1.1.2: has "^1.0.3" object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - -object.getownpropertydescriptors@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg== @@ -22288,12 +20681,7 @@ oboe@^2.1.4: dependencies: http-https "^1.0.0" -obuf@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" - integrity sha1-EEEktsYCxnlogaBCVB0220OlJk4= - -obuf@^1.1.2: +obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== @@ -22303,16 +20691,11 @@ octokit-pagination-methods@^1.1.0: resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ== -omggif@^1.0.10: +omggif@^1.0.10, omggif@^1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.10.tgz#ddaaf90d4a42f532e9e7cb3a95ecdd47f17c7b19" integrity sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw== -omggif@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/omggif/-/omggif-1.0.9.tgz#dcb7024dacd50c52b4d303f04802c91c057c765f" - integrity sha1-3LcCTazVDFK00wPwSALJHAV8dl8= - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -22388,14 +20771,7 @@ opentracing@^0.14.3: resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.4.tgz#a113408ea740da3a90fde5b3b0011a375c2e4268" integrity sha512-nNnZDkUNExBwEpb7LZaeMeQgvrlO8l4bgY/LvGNZCR0xG/dGWqHqjKrAmR5GUoYo0FIz38kxasvA1aevxWs2CA== -opn@^5.3.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" - integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw== - dependencies: - is-wsl "^1.1.0" - -opn@^5.5.0: +opn@^5.3.0, opn@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== @@ -22421,19 +20797,7 @@ optional-js@^2.0.0: resolved "https://registry.yarnpkg.com/optional-js/-/optional-js-2.1.1.tgz#c2dc519ad119648510b4d241dbb60b1167c36a46" integrity sha512-mUS4bDngcD5kKzzRUd1HVQkr9Lzzby3fSrrPR9wOHhQiyYo+hDS5NVli5YQzGjQRQ15k5Sno4xH9pfykJdeEUA== -optionator@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -optionator@^0.8.3: +optionator@^0.8.1, optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -22445,16 +20809,6 @@ optionator@^0.8.3: type-check "~0.3.2" word-wrap "~1.2.3" -ora@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4" - integrity sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q= - dependencies: - chalk "^1.1.1" - cli-cursor "^1.0.2" - cli-spinners "^0.1.2" - object-assign "^4.0.1" - ora@^3.0.0: version "3.4.0" resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" @@ -22696,11 +21050,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - integrity sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA== - p-map@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" @@ -22824,16 +21173,11 @@ pako@^0.2.5: resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= -pako@^1.0.5, pako@~1.0.2: +pako@^1.0.5, pako@~1.0.2, pako@~1.0.5: version "1.0.10" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== -pako@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" - integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg== - papaparse@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.2.0.tgz#97976a1b135c46612773029153dc64995caa3b7b" @@ -22906,19 +21250,7 @@ parse-color@^1.0.0: dependencies: color-convert "~0.5.0" -parse-entities@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.1.tgz#8112d88471319f27abae4d64964b122fe4e1b890" - integrity sha1-gRLYhHExnyerrk1klksSL+ThuJA= - dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" - -parse-entities@^1.1.2: +parse-entities@^1.1.0, parse-entities@^1.1.2: version "1.2.1" resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.1.tgz#2c761ced065ba7dc68148580b5a225e4918cdd69" integrity sha512-NBWYLQm1KSoDKk7GAHyioLTvCZ5QjdH/ASBBQTD3iLiAWJXS5bg1jEWI8nIJ+vgVvsceBVBcDGRWSo0KVQBvvg== @@ -23048,12 +21380,7 @@ parse5@^6.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= - -parseurl@~1.3.3: +parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -23095,11 +21422,6 @@ password-prompt@^1.0.7: ansi-escapes "^3.1.0" cross-spawn "^6.0.5" -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= - path-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" @@ -23456,12 +21778,7 @@ png-js@^1.0.0: resolved "https://registry.yarnpkg.com/png-js/-/png-js-1.0.0.tgz#e5484f1e8156996e383aceebb3789fd75df1874d" integrity sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g== -pngjs@^3.0.0: - version "3.3.1" - resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.1.tgz#8e14e6679ee7424b544334c3b2d21cea6d8c209a" - integrity sha512-ggXCTsqHRIsGMkHlCEhbHhUmNTA2r1lpkE0NL4Q9S8spkXbm4vE9TVmPso2AGYn90Gltdz8W5CyzhcIGg2Gejg== - -pngjs@^3.3.3, pngjs@^3.4.0: +pngjs@^3.0.0, pngjs@^3.3.3, pngjs@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" integrity sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w== @@ -23493,12 +21810,7 @@ polished@^3.4.4: dependencies: "@babel/runtime" "^7.9.2" -popper.js@^1.14.4: - version "1.14.7" - resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.14.7.tgz#e31ec06cfac6a97a53280c3e55e4e0c860e7738e" - integrity sha512-4q1hNvoUre/8srWsH7hnoSJ5xVmIL4qgz+s4qf2TnJIMyZFUFMGH+9vE7mXynAlHSZ/NdTmmow86muD0myUkVQ== - -popper.js@^1.14.7: +popper.js@^1.14.4, popper.js@^1.14.7: version "1.15.0" resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2" integrity sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA== @@ -23559,14 +21871,6 @@ postcss-modules-local-by-default@^3.0.2: postcss-selector-parser "^6.0.2" postcss-value-parser "^4.0.0" -postcss-modules-scope@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz#33d4fc946602eb5e9355c4165d68a10727689dba" - integrity sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - postcss-modules-scope@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" @@ -23599,17 +21903,7 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-value-parser@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d" - integrity sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ== - -postcss-value-parser@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" - integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== - -postcss-value-parser@^4.1.0: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== @@ -23623,7 +21917,7 @@ postcss-values-parser@^1.5.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.2, postcss@^7.0.23, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: +postcss@^7.0.0, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.2, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: version "7.0.32" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== @@ -23726,17 +22020,7 @@ pretty-format@^25.2.1, pretty-format@^25.5.0: ansi-styles "^4.0.0" react-is "^16.12.0" -pretty-format@^26.4.0: - version "26.4.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.0.tgz#c08073f531429e9e5024049446f42ecc9f933a3b" - integrity sha512-mEEwwpCseqrUtuMbrJG4b824877pM5xald3AkilJ47Po2YLr97/siejYQHqj2oDQBeJNbu+Q0qUuekJ8F0NAPg== - dependencies: - "@jest/types" "^26.3.0" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^16.12.0" - -pretty-format@^26.4.2: +pretty-format@^26.4.0, pretty-format@^26.4.2: version "26.4.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.4.2.tgz#d081d032b398e801e2012af2df1214ef75a81237" integrity sha512-zK6Gd8zDsEiVydOCGLkoBoZuqv8VTiHyAbKznXe/gaph/DAeZOmit9yMfgIz5adIgAMMs5XfoYSwAX3jcCO1tA== @@ -23777,21 +22061,16 @@ private@^0.1.8, private@~0.1.5: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -process-nextick-args@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - integrity sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw== +process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - process-on-spawn@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" @@ -23814,12 +22093,7 @@ progress@^1.1.8: resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74= -progress@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" - integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8= - -progress@^2.0.1: +progress@^2.0.0, progress@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== @@ -23901,20 +22175,13 @@ proper-lockfile@^3.2.0: retry "^0.12.0" signal-exit "^3.0.2" -property-information@^5.0.0, property-information@^5.2.0, property-information@^5.3.0: +property-information@^5.0.0, property-information@^5.0.1, property-information@^5.2.0, property-information@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.5.0.tgz#4dc075d493061a82e2b7d096f406e076ed859943" integrity sha512-RgEbCx2HLa1chNgvChcx+rrCWD0ctBmGSE0M7lVm1yyv4UbvbrWoXp/BkVLZefzjrRBGW8/Js6uh/BnlHXFyjA== dependencies: xtend "^4.0.0" -property-information@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.0.1.tgz#c3b09f4f5750b1634c0b24205adbf78f18bdf94f" - integrity sha512-nAtBDVeSwFM3Ot/YxT7s4NqZmqXI7lLzf46BThvotEtYf2uk2yH0ACYuWQkJ7gxKs49PPtKVY0UlDGkyN9aJlw== - dependencies: - xtend "^4.0.1" - proto-list@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" @@ -23930,14 +22197,6 @@ protocols@^1.1.0, protocols@^1.4.0: resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.7.tgz#95f788a4f0e979b291ffefcf5636ad113d037d32" integrity sha512-Fx65lf9/YDn3hUX08XUc0J8rSux36rEsyiv21ZGUC1mOyeM3lTRpZLcrm8aAolzS4itwVfm7TAPyxC2E5zd6xg== -proxy-addr@~2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" - integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.8.0" - proxy-addr@~2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" @@ -23970,11 +22229,6 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= -psl@^1.1.24: - version "1.1.29" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" - integrity sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ== - psl@^1.1.28: version "1.4.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" @@ -24017,7 +22271,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@1.3.x: +pumpify@1.3.x, pumpify@^1.3.3, pumpify@^1.3.5: version "1.3.6" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.3.6.tgz#00d40e5ded0a3bf1e0788b1c0cf426a42882ab64" integrity sha512-BurGAcvezsINL5US9T9wGHHcLNrG6MCp//ECtxron3vcR+Rfx5Anqq7HbZXNJvFQli8FGVsWCAvywEJFV5Hx/Q== @@ -24026,24 +22280,6 @@ pumpify@1.3.x: inherits "^2.0.3" pump "^2.0.0" -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -pumpify@^1.3.5: - version "1.4.0" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb" - integrity sha512-2kmNR9ry+Pf45opRVirpNuIFotsxUGLaYqxIwuR77AYrYRMuFCz9eryHBS52L360O+NcR383CL4QYlMKPq4zYA== - dependencies: - duplexify "^3.5.3" - inherits "^2.0.3" - pump "^2.0.0" - punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -24054,7 +22290,7 @@ punycode@2.x.x, punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -punycode@^1.2.4, punycode@^1.4.1: +punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= @@ -24101,20 +22337,15 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.5.2, qs@^6.4.0, qs@^6.5.1, qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -qs@6.7.0: +qs@6.7.0, qs@^6.4.0, qs@^6.5.1, qs@^6.6.0: version "6.7.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -qs@^6.6.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.8.0.tgz#87b763f0d37ca54200334cd57bb2ef8f68a1d081" - integrity sha512-tPSkj8y92PfZVbinY1n84i1Qdx75lZjMQYx9WZhnkofyxzw2r7Ho39G3/aEvSUdebxpnnM4LZJCtvE/Aq3+s9w== +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== query-string@^4.1.0: version "4.3.4" @@ -24183,12 +22414,7 @@ quote-stream@^1.0.1: minimist "^1.1.3" through2 "^2.0.0" -raf-schd@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.0.tgz#9855756c5045ff4ed4516e14a47719387c3c907b" - integrity sha512-m7zq0JkIrECzw9mO5Zcq6jN4KayE34yoIS9hJoiZNXyOAT06PPA8PrR+WtJIeFW09YjUfNkMMN9lrmAt6BURCA== - -raf-schd@^4.0.2: +raf-schd@^4.0.0, raf-schd@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0" integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ== @@ -24248,21 +22474,11 @@ randomfill@^1.0.3: randombytes "^2.0.5" safe-buffer "^5.1.0" -range-parser@^1.2.1, range-parser@~1.2.0, range-parser@~1.2.1: +range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" - integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== - dependencies: - bytes "3.0.0" - http-errors "1.6.3" - iconv-lite "0.4.23" - unpipe "1.0.0" - raw-body@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" @@ -24387,18 +22603,7 @@ react-clientside-effect@^1.2.2: dependencies: "@babel/runtime" "^7.0.0" -react-color@^2.13.8: - version "2.14.1" - resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.14.1.tgz#db8ad4f45d81e74896fc2e1c99508927c6d084e0" - integrity sha512-ssv2ArSZdhTbIs29hyfw8JW+s3G4BCx/ILkwCajWZzrcx/2ZQfRpsaLVt38LAPbxe50LLszlmGtRerA14JzzRw== - dependencies: - lodash "^4.0.1" - material-colors "^1.2.1" - prop-types "^15.5.10" - reactcss "^1.2.0" - tinycolor2 "^1.4.1" - -react-color@^2.17.0: +react-color@^2.13.8, react-color@^2.17.0: version "2.17.0" resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.17.0.tgz#e14b8a11f4e89163f65a34c8b43faf93f7f02aaa" integrity sha512-kJfE5tSaFe6GzalXOHksVjqwCPAsTl+nzS9/BWfP7j3EXbQ4IiLAF9sZGNzk3uq7HfofGYgjmcUgh0JP7xAQ0w== @@ -24450,16 +22655,7 @@ react-dev-utils@^10.0.0: strip-ansi "6.0.0" text-table "0.2.0" -react-docgen-typescript-loader@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/react-docgen-typescript-loader/-/react-docgen-typescript-loader-3.1.1.tgz#c1992538524fb9e45246d6c1314ddcfbf26e9d08" - integrity sha512-h8xfQIiEI4Z1oZewZhi9oohiWMS5Ek19LmgrvoL77Y/5d3tzu6fE3QHqhzYzdPnTaCfMzF7JMDUaydJiLbsDKg== - dependencies: - "@webpack-contrib/schema-utils" "^1.0.0-beta.0" - loader-utils "^1.2.3" - react-docgen-typescript "^1.12.3" - -react-docgen-typescript-loader@^3.7.2: +react-docgen-typescript-loader@^3.1.1, react-docgen-typescript-loader@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/react-docgen-typescript-loader/-/react-docgen-typescript-loader-3.7.2.tgz#45cb2305652c0602767242a8700ad1ebd66bbbbd" integrity sha512-fNzUayyUGzSyoOl7E89VaPKJk9dpvdSgyXg81cUkwy0u+NBvkzQG3FC5WBIlXda0k/iaxS+PWi+OC+tUiGxzPA== @@ -24480,11 +22676,6 @@ react-docgen-typescript-plugin@^0.5.2: react-docgen-typescript-loader "^3.7.2" tslib "^2.0.0" -react-docgen-typescript@^1.12.3: - version "1.12.3" - resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.12.3.tgz#fe62a5ce82e93573e316366e53adfe8273121c70" - integrity sha512-s1XswWs4ykNdWKsPyfM4qptV5dT8nnjnVi2IcjoS/vGlRNYrc0TkW0scVOrinHZ+ndKhPqA4iVNrdwhxZBzJcg== - react-docgen-typescript@^1.15.0, react-docgen-typescript@^1.20.1: version "1.20.2" resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.20.2.tgz#78f4a14f18a4e236e31051961c75583133752d46" @@ -25070,19 +23261,7 @@ read-installed@~4.0.3: optionalDependencies: graceful-fs "^4.1.2" -read-package-json@^2.0.0: - version "2.0.13" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.13.tgz#2e82ebd9f613baa6d2ebe3aa72cefe3f68e41f4a" - integrity sha512-/1dZ7TRZvGrYqE0UAfN6qQb5GYBsNcqS1C0tNK601CFOJmtHI7NIGXwetEPU/OtoFHZL3hDxm4rolFFVE9Bnmg== - dependencies: - glob "^7.1.1" - json-parse-better-errors "^1.0.1" - normalize-package-data "^2.0.0" - slash "^1.0.0" - optionalDependencies: - graceful-fs "^4.1.2" - -read-package-json@^2.0.10: +read-package-json@^2.0.0, read-package-json@^2.0.10: version "2.1.1" resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.1.1.tgz#16aa66c59e7d4dad6288f179dd9295fd59bb98f1" integrity sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A== @@ -25164,7 +23343,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.3, readable-stream@~2.3.6: +"readable-stream@1 || 2", "readable-stream@2 || 3", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.7, readable-stream@~2.3.3, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -25187,7 +23366,7 @@ readable-stream@1.0, "readable-stream@>=1.0.33-1 <1.1.0-0": isarray "0.0.1" string_decoder "~0.10.x" -"readable-stream@2 || 3", readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: +readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -25455,17 +23634,7 @@ regenerator-runtime@^0.12.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== -regenerator-runtime@^0.13.1: - version "0.13.2" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" - integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== - -regenerator-runtime@^0.13.3: - version "0.13.3" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5" - integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== - -regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.1, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: version "0.13.5" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== @@ -25478,14 +23647,7 @@ regenerator-transform@^0.14.2: "@babel/runtime" "^7.8.4" private "^0.1.8" -regex-not@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.0.tgz#42f83e39771622df826b02af176525d6a5f157f9" - integrity sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k= - dependencies: - extend-shallow "^2.0.1" - -regex-not@^1.0.2: +regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== @@ -25493,14 +23655,7 @@ regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz#6b30724e306a27833eeb171b66ac8890ba37e41c" - integrity sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA== - dependencies: - define-properties "^1.1.2" - -regexp.prototype.flags@^1.3.0: +regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== @@ -25852,13 +24007,6 @@ request-promise-core@1.1.2: dependencies: lodash "^4.17.11" -request-promise-core@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" - integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ== - dependencies: - lodash "^4.17.15" - request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -25866,16 +24014,7 @@ request-promise-core@1.1.4: dependencies: lodash "^4.17.19" -request-promise-native@^1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" - integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== - dependencies: - request-promise-core "1.1.3" - stealthy-require "^1.1.1" - tough-cookie "^2.3.3" - -request-promise-native@^1.0.8: +request-promise-native@^1.0.5, request-promise-native@^1.0.8: version "1.0.9" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28" integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g== @@ -25935,16 +24074,7 @@ require-from-string@^2.0.1: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-in-the-middle@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.0.2.tgz#ce3593007a61583b39ccdcd2c167a2a326c670b2" - integrity sha512-l2r6F9i6t5xp4OE9cw/daB/ooQKHZOOW1AYPADhEvk/Tj/THJDS8gePp76Zyuht6Cj57a0KL+eHK5Dyv7wZnKA== - dependencies: - debug "^4.1.1" - module-details-from-path "^1.0.3" - resolve "^1.12.0" - -require-in-the-middle@^5.0.3: +require-in-the-middle@^5.0.2, require-in-the-middle@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.0.3.tgz#ef8bfd771760db573bc86d1341d8ae411a04c600" integrity sha512-p/ICV8uMlqC4tjOYabLMxAWCIKa0YUQgZZ6KDM0xgXJNgdGQ1WmL2A07TwmrZw+wi6ITUFKzH5v3n+ENEyXVkA== @@ -26012,12 +24142,7 @@ reselect@^4.0.0: resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7" integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA== -resize-observer-polyfill@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.0.tgz#660ff1d9712a2382baa2cad450a4716209f9ca69" - integrity sha512-M2AelyJDVR/oLnToJLtuDJRBBWUGUvvGigj1411hXhAdyFWqMaqHp7TixW3FpiLuVaikIcR1QL+zqoJoZlOgpg== - -resize-observer-polyfill@^1.5.1: +resize-observer-polyfill@^1.5.0, resize-observer-polyfill@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== @@ -26205,14 +24330,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.0, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.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.6.3, rimraf@~2.6.2: +rimraf@2, rimraf@2.6.3, rimraf@^2.2.0, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -26226,6 +24344,13 @@ rimraf@3.0.0: 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@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -26304,14 +24429,7 @@ run-async@^0.1.0: dependencies: once "^1.3.0" -run-async@^2.0.0, run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= - dependencies: - is-promise "^2.1.0" - -run-async@^2.4.0: +run-async@^2.0.0, run-async@^2.2.0, run-async@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.0.tgz#e59054a5b86876cfae07f431d18cbaddc594f1e8" integrity sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg== @@ -26364,34 +24482,13 @@ rxjs@^5.5.2: dependencies: symbol-observable "1.0.1" -rxjs@^6.1.0, rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1: - version "6.5.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.3.tgz#510e26317f4db91a7eb1de77d9dd9ba0a4899a3a" - integrity sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA== - dependencies: - tslib "^1.9.0" - -rxjs@^6.5.3: +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.1, rxjs@^6.5.3, rxjs@^6.5.5, rxjs@^6.6.0: version "6.6.2" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== dependencies: tslib "^1.9.0" -rxjs@^6.5.5: - version "6.5.5" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" - integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== - dependencies: - tslib "^1.9.0" - -rxjs@^6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84" - integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg== - dependencies: - tslib "^1.9.0" - safe-buffer@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -26402,16 +24499,6 @@ safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, s resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-buffer@~5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" - integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== - safe-json-parse@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-1.0.1.tgz#3e76723e38dfdda13c9b1d29a1e07ffee4b30b57" @@ -26559,7 +24646,7 @@ schema-utils@^0.4.5: ajv "^6.1.0" ajv-keywords "^3.1.0" -schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.4, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0: +schema-utils@^2.0.0, schema-utils@^2.0.1, schema-utils@^2.5.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== @@ -26673,7 +24760,7 @@ semver-truncate@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" integrity sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto= -"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -26688,55 +24775,21 @@ semver@7.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== -semver@7.3.2, semver@^7.3.2: +semver@7.3.2, semver@^7.1.3, semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -semver@^5.5.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.1.tgz#7dfdd8814bdb7cabc7be0fb1d734cfb66c940477" - integrity sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw== - -semver@^6.0.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@~6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.2, semver@^6.2.0, semver@^6.3.0, semver@~6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" - integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ== - -semver@^7.1.3: - version "7.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.0.tgz#91f7c70ec944a63e5dc7a74cde2da375d8e0853c" - integrity sha512-uyvgU/igkrMgNHwLgXvlpD9jEADbJhB0+JXSywoO47JgJ6c16iau9F9cjtc/E5o0PoqRYTiTIAPRKaYe84z6eQ== - semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= -send@0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" - integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== - dependencies: - debug "2.6.9" - depd "~1.1.2" - destroy "~1.0.4" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.4.0" - send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -26802,16 +24855,6 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.13.2: - version "1.13.2" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" - integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.2" - serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" @@ -27210,7 +25253,7 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@0.1.32: +source-map@0.1.32, source-map@~0.1.30: version "0.1.32" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" integrity sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY= @@ -27244,13 +25287,6 @@ source-map@^0.7.3: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== -source-map@~0.1.30: - version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= - dependencies: - amdefine ">=0.0.4" - sourcemap-codec@^1.4.1: version "1.4.6" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9" @@ -27400,12 +25436,7 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -sprintf-js@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" - integrity sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw= - -sprintf-js@~1.0.2: +sprintf-js@^1.0.3, sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= @@ -27491,12 +25522,7 @@ stackframe@^0.3.1: resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-0.3.1.tgz#33aa84f1177a5548c8935533cbfeb3420975f5a4" integrity sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ= -stackframe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.0.tgz#e3fc2eb912259479c9822f7d1f1ff365bd5cbc83" - integrity sha512-Vx6W1Yvy+AM1R/ckVwcHQHV147pTPBKWCRLrXMuPrFVfvBUc3os7PR1QLIWCMhPpRg5eX9ojzbQIMLGBwyLjqg== - -stackframe@^1.1.1: +stackframe@^1.1.0, stackframe@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.1.tgz#ffef0a3318b1b60c3b58564989aca5660729ec71" integrity sha512-0PlYhdKh6AfFxRyK/v+6/k+/mMfyiEBbTM5L94D0ZytQnJ166wuwoTYLHFWGbs2dpA8Rgq763KGWmN1EQEYHRQ== @@ -27593,11 +25619,6 @@ stats-lite@^2.2.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= -statuses@~1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== - stdout-stream@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" @@ -27801,22 +25822,6 @@ string.prototype.trimend@^1.0.1: define-properties "^1.1.3" es-abstract "^1.17.5" -string.prototype.trimleft@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string.prototype.trimright@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - string.prototype.trimstart@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" @@ -27830,14 +25835,7 @@ string_decoder@0.10, string_decoder@~0.10.x: resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= -string_decoder@^1.0.0, string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: +string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== @@ -27864,7 +25862,14 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -strip-ansi@*, strip-ansi@6.0.0, strip-ansi@^6.0.0: +strip-ansi@*, strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@6.0.0, strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== @@ -27892,13 +25897,6 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - strip-ansi@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" @@ -27963,12 +25961,7 @@ strip-json-comments@2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== - -strip-json-comments@^3.1.1: +strip-json-comments@^3.0.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -27994,15 +25987,7 @@ style-it@^2.1.3: dependencies: react-lib-adler32 "^1.0.3" -style-loader@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.1.3.tgz#9e826e69c683c4d9bf9db924f85e9abb30d5e200" - integrity sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw== - dependencies: - loader-utils "^1.2.3" - schema-utils "^2.6.4" - -style-loader@^1.2.1: +style-loader@^1.1.3, style-loader@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.2.1.tgz#c5cbbfbf1170d076cfdd86e0109c5bba114baa1a" integrity sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg== @@ -28155,21 +26140,7 @@ supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-co dependencies: has-flag "^3.0.0" -supports-color@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" - integrity sha512-0aP01LLIskjKs3lq52EC0aGBAJhLq7B2Rd8HC/DR/PtNNpcLilNmHC12O+hu0usQpo7wtHNRqtrhBwtDb0+dNg== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.0.0.tgz#f2392c50ab35bb3cae7beebf24d254a19f880c06" - integrity sha512-WRt32iTpYEZWYOpcetGm0NPeSvaebccx7hhS/5M6sAiqnhedtFCHFxkjzZlJvFNCPowiKSFGiZk5USQDFy83vQ== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== @@ -28256,12 +26227,7 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.4, symbol-observable@^1.1.0, sy resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -symbol-tree@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" - integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= - -symbol-tree@^3.2.4: +symbol-tree@^3.2.2, symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== @@ -28273,16 +26239,11 @@ symbol.prototype.description@^1.0.0: dependencies: has-symbols "^1.0.0" -tabbable@1.1.3: +tabbable@1.1.3, tabbable@^1.0.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.3.tgz#0e4ee376f3631e42d7977a074dbd2b3827843081" integrity sha512-nOWwx35/JuDI4ONuF0ZTo6lYvI0fY0tZCH1ErzY2EXfu4az50ZyiUX8X073FLiZtmWUVlkRnuXsehjJgCw9tYg== -tabbable@^1.0.3: - version "1.1.2" - resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-1.1.2.tgz#b171680aea6e0a3e9281ff23532e2e5de11c0d94" - integrity sha512-77oqsKEPrxIwgRcXUwipkj9W5ItO97L6eUT1Ar7vh+El16Zm4M6V+YU1cbipHEa6q0Yjw8O3Hoh8oRgatV5s7A== - tabbable@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-3.1.2.tgz#f2d16cccd01f400e38635c7181adfe0ad965a4a2" @@ -28335,12 +26296,7 @@ tapable@^0.1.8: resolved "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" integrity sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q= -tapable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.0.tgz#0d076a172e3d9ba088fd2272b2668fb8d194b78c" - integrity sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA== - -tapable@^1.1.3: +tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== @@ -28565,16 +26521,7 @@ terser-webpack-plugin@^3.0.0: terser "^4.8.0" webpack-sources "^1.4.3" -terser@^4.1.2, terser@^4.6.12: - version "4.7.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.7.0.tgz#15852cf1a08e3256a80428e865a2fa893ffba006" - integrity sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -terser@^4.6.3, terser@^4.8.0: +terser@^4.1.2, terser@^4.6.12, terser@^4.6.3, terser@^4.8.0: version "4.8.0" resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== @@ -28635,12 +26582,12 @@ through2-filter@^2.0.0: through2 "~2.0.0" xtend "~4.0.0" -through2@2.X, through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" - integrity sha1-AARWmzfHx0ujnEPzzteNGtlBQL4= +through2@2.X, through2@^2.0.0, through2@^2.0.3, through2@~2.0.0, through2@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== dependencies: - readable-stream "^2.1.5" + readable-stream "~2.3.6" xtend "~4.0.1" through2@^0.6.3: @@ -28658,14 +26605,6 @@ through2@^3.0.1: dependencies: readable-stream "2 || 3" -through2@~2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8, through@~2.3.4, through@~2.3.6, through@~2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -28726,12 +26665,7 @@ tiny-inflate@^1.0.0, tiny-inflate@^1.0.2: resolved "https://registry.yarnpkg.com/tiny-inflate/-/tiny-inflate-1.0.3.tgz#122715494913a1805166aaf7c93467933eea26c4" integrity sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw== -tiny-invariant@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.4.tgz#346b5415fd93cb696b0c4e8a96697ff590f92463" - integrity sha512-lMhRd/djQJ3MoaHEBrw8e2/uM4rs9YMNk0iOr8rHQ0QdbM7D4l0gFl3szKdeixrlyfm9Zqi4dxHCM2qVG8ND5g== - -tiny-invariant@^1.0.6: +tiny-invariant@^1.0.2, tiny-invariant@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73" integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA== @@ -28748,12 +26682,7 @@ tiny-lr@^1.1.1: object-assign "^4.1.0" qs "^6.4.0" -tiny-warning@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.2.tgz#1dfae771ee1a04396bdfde27a3adcebc6b648b28" - integrity sha512-rru86D9CpQRLvsFG5XFdy0KdLAvjdQDyZCsRcuu60WtzFylDM3eAWSxEVz5kzL2Gp544XiUvPbVKtOA/txLi9Q== - -tiny-warning@^1.0.3: +tiny-warning@^1.0.0, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -28894,16 +26823,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -to-regex@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae" - integrity sha1-FTWL7kosg712N3uh3ASdDxiDeq4= - dependencies: - define-property "^0.2.5" - extend-shallow "^2.0.1" - regex-not "^1.0.0" - -to-regex@^3.0.2: +to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== @@ -28934,7 +26854,7 @@ to-through@^2.0.0: dependencies: through2 "^2.0.3" -toggle-selection@^1.0.3, toggle-selection@^1.0.6: +toggle-selection@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= @@ -28972,15 +26892,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^2.0.0, tough-cookie@^2.3.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" - integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== - dependencies: - psl "^1.1.24" - punycode "^1.4.1" - -tough-cookie@^2.5.0, tough-cookie@~2.5.0: +tough-cookie@^2.0.0, tough-cookie@^2.3.3, tough-cookie@^2.5.0, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -29164,12 +27076,7 @@ tslib@^1, tslib@^1.0.0, tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== -tslib@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.0.tgz#18d13fc2dce04051e20f074cc8387fd8089ce4f3" - integrity sha512-lTqkx847PI7xEDYJntxZH89L2/aXInsyF2luSafe/+0fHOMjlBNXdH6th7f70qxLDhul7KZK0zC8V5ZIyHl0/g== - -tslib@~2.0.1: +tslib@^2.0.0, tslib@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== @@ -29269,7 +27176,7 @@ type-fest@^0.8.0, type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-is@~1.6.16, type-is@~1.6.17, type-is@~1.6.18: +type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -29613,11 +27520,6 @@ unist-util-generated@^1.0.0: resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.5.tgz#1e903e68467931ebfaea386dae9ea253628acd42" integrity sha512-1TC+NxQa4N9pNdayCYA1EGUOCAO0Le3fVp7Jzns6lnua/mYgwHo0tz5WUAfrdpNch1RZLHc61VZ1SDgrtNXLSw== -unist-util-is@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.1.tgz#0c312629e3f960c66e931e812d3d80e77010947b" - integrity sha1-DDEmKeP5YMZukx6BLT2A53AQlHs= - unist-util-is@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-3.0.0.tgz#d9e84381c2468e82629e4a5be9d7d05a2dd324cd" @@ -29695,20 +27597,13 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0, unist-util-visit@^2.0.2: unist-util-is "^4.0.0" unist-util-visit-parents "^3.0.0" -unist-util-visit@^1.0.0: +unist-util-visit@^1.0.0, unist-util-visit@^1.1.0, unist-util-visit@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.1.tgz#4724aaa8486e6ee6e26d7ff3c8685960d560b1e3" integrity sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw== dependencies: unist-util-visit-parents "^2.0.0" -unist-util-visit@^1.1.0, unist-util-visit@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.3.0.tgz#41ca7c82981fd1ce6c762aac397fc24e35711444" - integrity sha512-9ntYcxPFtl44gnwXrQKZ5bMqXMY0ZHzUpqMFiU4zcc8mmf/jzYm8GhYgezuUlX4cJIM1zIDYaO6fG/fI+L6iiQ== - dependencies: - unist-util-is "^2.1.1" - universal-user-agent@^2.0.0, universal-user-agent@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-2.0.3.tgz#9f6f09f9cc33de867bb720d84c08069b14937c6c" @@ -29798,12 +27693,7 @@ unzip-response@^2.0.1: resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" integrity sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c= -upath@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" - integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== - -upath@^1.1.1: +upath@^1.1.0, upath@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== @@ -30020,7 +27910,7 @@ util-extend@^1.0.1: resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" integrity sha1-p8IW0mdUUWljeztu3GypEZ4v+T8= -util.promisify@1.0.0, util.promisify@~1.0.0: +util.promisify@1.0.0, util.promisify@^1.0.0, util.promisify@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== @@ -30028,16 +27918,6 @@ util.promisify@1.0.0, util.promisify@~1.0.0: define-properties "^1.1.2" object.getownpropertydescriptors "^2.0.3" -util.promisify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - util@0.10.3, util@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -30097,16 +27977,11 @@ uuid@^8.0.0, uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== -v8-compile-cache@2.0.3: +v8-compile-cache@2.0.3, v8-compile-cache@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== -v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" - integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== - v8-to-istanbul@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz#0608f5b49a481458625edb058488607f25498ba5" @@ -30303,18 +28178,7 @@ vega-lite@^4.16.8: vega-util "~1.15.3" yargs "~16.0.3" -vega-loader@^4.3.2, vega-loader@^4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.3.3.tgz#1432dabae4dd7ab344f84c5ae6e250234e9c1005" - integrity sha512-ZcAMi6C7yfbA3gpxDnFe3PvsP/jcDwUjgPIpZ2IYsaQS+JijZAj5g3i4mpQCao0Atc+C/g7htg0Ir3twFLPjkQ== - dependencies: - d3-dsv "^2.0.0" - node-fetch "^2.6.1" - topojson-client "^3.1.0" - vega-format "^1.0.4" - vega-util "^1.15.2" - -vega-loader@~4.4.0: +vega-loader@^4.3.2, vega-loader@^4.3.3, vega-loader@~4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/vega-loader/-/vega-loader-4.4.0.tgz#fc515b7368c46b2be8df1fcf3c35c696c13c453d" integrity sha512-e5enQECdau7rJob0NFB5pGumh3RaaSWWm90+boxMy3ay2b4Ki/3XIvo+C4F1Lx04qSxvQF7tO2LJcklRm6nqRA== @@ -30444,16 +28308,16 @@ vega-typings@~0.19.0: dependencies: vega-util "^1.15.2" -vega-util@^1.15.2, vega-util@~1.15.3: - version "1.15.3" - resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.15.3.tgz#b42b4fb11f32fbb57fb5cd116d4d3e1827d177aa" - integrity sha512-NCbfCPMVgdP4geLrFtCDN9PTEXrgZgJBBLvpyos7HGv2xSe9bGjDCysv6qcueHrc1myEeCQzrHDFaShny6wXDg== - -vega-util@^1.16.0, vega-util@~1.16.0: +vega-util@^1.15.2, vega-util@^1.16.0, vega-util@~1.16.0: version "1.16.0" resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.16.0.tgz#77405d8df0a94944d106bdc36015f0d43aa2caa3" integrity sha512-6mmz6mI+oU4zDMeKjgvE2Fjz0Oh6zo6WGATcvCfxH2gXBzhBHmy5d25uW5Zjnkc6QBXSWPLV9Xa6SiqMsrsKog== +vega-util@~1.15.3: + version "1.15.3" + resolved "https://registry.yarnpkg.com/vega-util/-/vega-util-1.15.3.tgz#b42b4fb11f32fbb57fb5cd116d4d3e1827d177aa" + integrity sha512-NCbfCPMVgdP4geLrFtCDN9PTEXrgZgJBBLvpyos7HGv2xSe9bGjDCysv6qcueHrc1myEeCQzrHDFaShny6wXDg== + vega-view-transforms@~4.5.8: version "4.5.8" resolved "https://registry.yarnpkg.com/vega-view-transforms/-/vega-view-transforms-4.5.8.tgz#c8dc42c3c7d4aa725d40b8775180c9f23bc98f4e" @@ -30558,7 +28422,7 @@ vfile-location@^3.0.0: resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.0.1.tgz#d78677c3546de0f7cd977544c367266764d31bb3" integrity sha512-yYBO06eeN/Ki6Kh1QAkgzYpWT1d3Qln+ZCtSbJqFExPl1S3y2qqotJQXoh6qEvl/jDlgpUJolBn3PItVnnZRqQ== -vfile-message@*: +vfile-message@*, vfile-message@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== @@ -30573,14 +28437,6 @@ vfile-message@^1.0.0: dependencies: unist-util-stringify-position "^1.1.1" -vfile-message@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.0.tgz#750bbb86fe545988a67e899b329bbcabb73edef6" - integrity sha512-YS6qg6UpBfIeiO+6XlhPOuJaoLvt1Y9g2cmlwqhBOOU0XRV8j5RLeoz72t6PWLvNXq3EBG1fQ05wNPrUoz0deQ== - dependencies: - "@types/unist" "^2.0.2" - unist-util-stringify-position "^1.1.1" - vfile-reporter@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-5.1.1.tgz#419688c7e9dcaf65ba81bfdb0ad443e9e0248e09" @@ -30613,18 +28469,7 @@ vfile@^2.0.0: unist-util-stringify-position "^1.0.0" vfile-message "^1.0.0" -vfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.0.0.tgz#ebf3b48af9fcde524d5e08d5f75812058a5f78ad" - integrity sha512-WMNeHy5djSl895BqE86D7WqA0Ie5fAIeGCa7V1EqiXyJg5LaGch2SUaZueok5abYQGH6mXEAsZ45jkoILIOlyA== - dependencies: - "@types/unist" "^2.0.2" - is-buffer "^2.0.0" - replace-ext "1.0.0" - unist-util-stringify-position "^2.0.0" - vfile-message "^2.0.0" - -vfile@^4.2.0: +vfile@^4.0.0, vfile@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.0.tgz#26c78ac92eb70816b01d4565e003b7e65a2a0e01" integrity sha512-a/alcwCvtuc8OX92rqqo7PflxiCgXRFjdyoGVuYV+qbgCb0GgZJRvIgCD4+U/Kl1yhaRsaTwksF88xbPyGsgpw== @@ -30728,13 +28573,6 @@ vision@^5.3.3: items "2.x.x" joi "13.x.x" -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= - dependencies: - indexof "0.0.1" - vm-browserify@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.0.tgz#bd76d6a23323e2ca8ffa12028dc04559c75f9019" @@ -30780,14 +28618,7 @@ vt-pbf@^3.1.1: "@mapbox/vector-tile" "^1.3.1" pbf "^3.0.5" -w3c-hr-time@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" - integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= - dependencies: - browser-process-hrtime "^0.1.2" - -w3c-hr-time@^1.0.2: +w3c-hr-time@^1.0.1, w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== @@ -30810,14 +28641,7 @@ w3c-xmlserializer@^2.0.0: dependencies: xml-name-validator "^3.0.0" -walk@2.3.x: - version "2.3.9" - resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.9.tgz#31b4db6678f2ae01c39ea9fb8725a9031e558a7b" - integrity sha1-MbTbZnjyrgHDnqn7hyWpAx5Vins= - dependencies: - foreachasync "^3.0.0" - -walk@^2.3.14: +walk@2.3.x, walk@^2.3.14: version "2.3.14" resolved "https://registry.yarnpkg.com/walk/-/walk-2.3.14.tgz#60ec8631cfd23276ae1e7363ce11d626452e1ef3" integrity sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg== @@ -30850,18 +28674,7 @@ watchpack-chokidar2@^2.0.0: dependencies: chokidar "^2.1.8" -watchpack@^1.6.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.2.tgz#c02e4d4d49913c3e7e122c3325365af9d331e9aa" - integrity sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.0" - watchpack-chokidar2 "^2.0.0" - -watchpack@^1.7.4: +watchpack@^1.6.0, watchpack@^1.7.4: version "1.7.4" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.4.tgz#6e9da53b3c80bb2d6508188f5b200410866cd30b" integrity sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg== @@ -30872,14 +28685,7 @@ watchpack@^1.7.4: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.0" -wbuf@^1.1.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe" - integrity sha1-1pe5nx9ZUS3ydRvkJ2nBWAtYAf4= - dependencies: - minimalistic-assert "^1.0.0" - -wbuf@^1.7.3: +wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== @@ -30898,16 +28704,11 @@ weak-lru-cache@^0.2.0: resolved "https://registry.yarnpkg.com/weak-lru-cache/-/weak-lru-cache-0.2.0.tgz#447379ccff6dfda1b7a9566c9ef168260be859d1" integrity sha512-M1l5CzKvM7maa7tCbtL0NW6sOnp8gqup853+9Aq7GL0XNWKNnFOkeE3v3Z5X2IeMzedPwQyPbi4RlFvD6rxs7A== -web-namespaces@^1.0.0: +web-namespaces@^1.0.0, web-namespaces@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== -web-namespaces@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.2.tgz#c8dc267ab639505276bae19e129dbd6ae72b22b4" - integrity sha512-II+n2ms4mPxK+RnIxRPOw3zwF2jRscdJIUE9BfkKHm4FYEg9+biIoTMnaZF5MpemE3T+VhMLrhbyD4ilkPCSbg== - webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -31040,36 +28841,7 @@ webpack-virtual-modules@^0.2.2: dependencies: debug "^3.0.0" -webpack@^4.41.5: - version "4.41.5" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c" - integrity sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw== - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.2.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.1.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.1" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.6.0" - webpack-sources "^1.4.1" - -webpack@^4.43.0: +webpack@^4.41.5, webpack@^4.43.0: version "4.44.1" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.1.tgz#17e69fff9f321b8f117d1fda714edfc0b939cc21" integrity sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ== @@ -31098,22 +28870,13 @@ webpack@^4.43.0: watchpack "^1.7.4" webpack-sources "^1.4.1" -websocket-driver@0.6.5: +websocket-driver@0.6.5, websocket-driver@>=0.5.1: version "0.6.5" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= dependencies: websocket-extensions ">=0.1.1" -websocket-driver@>=0.5.1: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - websocket-extensions@>=0.1.1: version "0.1.4" resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" @@ -31132,14 +28895,7 @@ wgs84@0.0.0: resolved "https://registry.yarnpkg.com/wgs84/-/wgs84-0.0.0.tgz#34fdc555917b6e57cf2a282ed043710c049cdc76" integrity sha1-NP3FVZF7blfPKigu0ENxDASc3HY= -whatwg-encoding@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz#57c235bc8657e914d24e1a397d3c82daee0a6ba3" - integrity sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw== - dependencies: - iconv-lite "0.4.19" - -whatwg-encoding@^1.0.5: +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== @@ -31245,28 +29001,14 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -wide-align@1.1.3: +wide-align@1.1.3, wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== dependencies: string-width "^1.0.2 || 2" -wide-align@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" - integrity sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w== - dependencies: - string-width "^1.0.2" - -widest-line@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" - integrity sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM= - dependencies: - string-width "^2.1.1" - -widest-line@^2.0.1: +widest-line@^2.0.0, widest-line@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.1.tgz#7438764730ec7ef4381ce4df82fb98a53142a3fc" integrity sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA== @@ -31304,15 +29046,7 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" -winston-transport@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.3.0.tgz#df68c0c202482c448d9b47313c07304c2d7c2c66" - integrity sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A== - dependencies: - readable-stream "^2.3.6" - triple-beam "^1.2.0" - -winston-transport@^4.4.0: +winston-transport@^4.3.0, winston-transport@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.4.0.tgz#17af518daa690d5b2ecccaa7acf7b20ca7925e59" integrity sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw== @@ -31320,7 +29054,7 @@ winston-transport@^4.4.0: readable-stream "^2.3.7" triple-beam "^1.2.0" -winston@3.2.1, winston@^3.0.0, winston@^3.2.1: +winston@3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/winston/-/winston-3.2.1.tgz#63061377976c73584028be2490a1846055f77f07" integrity sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw== @@ -31335,7 +29069,7 @@ winston@3.2.1, winston@^3.0.0, winston@^3.2.1: triple-beam "^1.3.0" winston-transport "^4.3.0" -winston@^3.3.3: +winston@^3.0.0, winston@^3.2.1, winston@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/winston/-/winston-3.3.3.tgz#ae6172042cafb29786afa3d09c8ff833ab7c9170" integrity sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw== @@ -31360,7 +29094,7 @@ wordwrap@0.0.2: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= -wordwrap@^1.0.0, wordwrap@~1.0.0: +wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= @@ -31453,16 +29187,7 @@ write-file-atomic@^1.1.2: imurmurhash "^0.1.4" slide "^1.1.5" -write-file-atomic@^2.0.0, write-file-atomic@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" - integrity sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - -write-file-atomic@^2.4.2: +write-file-atomic@^2.0.0, write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" integrity sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== @@ -31516,14 +29241,7 @@ write@^0.2.1: dependencies: mkdirp "^0.5.1" -ws@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.0.tgz#13806d9913b2a5f3cbb9ba47b563c002cbc7c526" - integrity sha512-deZYUNlt2O4buFCa3t5bKLf8A7FPP/TVjwOeVNpw818Ma5nk4MLXls2eoEGS39o8119QIYxTrTDoPQ5B/gTD6w== - dependencies: - async-limiter "~1.0.0" - -ws@^6.1.2, ws@^6.2.1: +ws@^6.1.0, ws@^6.1.2, ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== @@ -31590,7 +29308,7 @@ xml-parse-from-string@^1.0.0: resolved "https://registry.yarnpkg.com/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz#a9029e929d3dbcded169f3c6e28238d95a5d5a28" integrity sha1-qQKekp09vN7RafPG4oI42VpdWig= -xml2js@^0.4.22: +xml2js@^0.4.22, xml2js@^0.4.5: version "0.4.22" resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.22.tgz#4fa2d846ec803237de86f30aa9b5f70b6600de02" integrity sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw== @@ -31599,14 +29317,6 @@ xml2js@^0.4.22: util.promisify "~1.0.0" xmlbuilder "~11.0.0" -xml2js@^0.4.5: - version "0.4.19" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" - integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== - dependencies: - sax ">=0.6.0" - xmlbuilder "~9.0.1" - xml@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" @@ -31622,11 +29332,6 @@ xmlbuilder@~11.0.0: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== -xmlbuilder@~9.0.1: - version "9.0.4" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.4.tgz#519cb4ca686d005a8420d3496f3f0caeecca580f" - integrity sha1-UZy0ymhtAFqEINNJbz8MruzKWA8= - xmlchars@^2.1.1, xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" @@ -31649,16 +29354,11 @@ xregexp@4.2.4: dependencies: "@babel/runtime-corejs2" "^7.2.0" -"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.2: +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68= - y18n@^3.2.0, y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" @@ -31679,21 +29379,11 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yallist@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" - integrity sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k= - -yallist@^3.0.2: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yallist@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" - integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== - yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" @@ -32022,17 +29712,10 @@ z-schema@~3.18.3: optionalDependencies: commander "^2.7.1" -zen-observable-ts@^0.8.10: - version "0.8.10" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.10.tgz#18e2ce1c89fe026e9621fd83cc05168228fce829" - integrity sha512-5vqMtRggU/2GhePC9OU4sYEWOdvmayp2k3gjPf4F0mXwB3CSbbNznfDUvDJx9O2ZTa1EIXdJhPchQveFKwNXPQ== - dependencies: - zen-observable "^0.8.0" - -zen-observable-ts@^0.8.18: - version "0.8.18" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.18.tgz#ade44b1060cc4a800627856ec10b9c67f5f639c8" - integrity sha512-q7d05s75Rn1j39U5Oapg3HI2wzriVwERVo4N7uFGpIYuHB9ff02P/E92P9B8T7QVC93jCMHpbXH7X0eVR5LA7A== +zen-observable-ts@^0.8.10, zen-observable-ts@^0.8.18: + version "0.8.21" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz#85d0031fbbde1eba3cd07d3ba90da241215f421d" + integrity sha512-Yj3yXweRc8LdRMrCC8nIc4kkjWecPAUVh0TI0OUrWXx6aX790vLcDlWca6I4vsyCGH3LpWxq0dJRcMOFoVqmeg== dependencies: tslib "^1.9.3" zen-observable "^0.8.0" From 6da7dc8695f8f4805146b98ba8971d47f0d69d37 Mon Sep 17 00:00:00 2001 From: Frank Hassanabad <frank.hassanabad@elastic.co> Date: Sat, 3 Oct 2020 09:08:02 -0600 Subject: [PATCH 121/128] [Security Solutions][Detection Engine] Updates the edit rules page to only have what is selected for editing (#79233) ## Summary Before when you would edit rules you get all the rules as disabled but you cannot switch between them in edit mode as it's already a rule you created: <img width="1063" alt="Screen Shot 2020-10-01 at 5 06 18 PM" src="https://user-images.githubusercontent.com/1151048/94872518-0bdaba00-040a-11eb-8b7d-3b3a59980e99.png"> After, now we remove those cards and only show the card of the rule type you're editing: <img width="1074" alt="Screen Shot 2020-10-02 at 6 29 48 PM" src="https://user-images.githubusercontent.com/1151048/94978954-50835580-04dd-11eb-9e08-8e473fc216bf.png"> Changes the card's icon placement and text. Before: <img width="1098" alt="Screen Shot 2020-10-02 at 9 27 44 AM" src="https://user-images.githubusercontent.com/1151048/94979008-9dffc280-04dd-11eb-8bce-88cd49b063a8.png"> After: <img width="1190" alt="Screen Shot 2020-10-02 at 6 31 01 PM" src="https://user-images.githubusercontent.com/1151048/94979005-95a78780-04dd-11eb-92ec-e81bc3ce436e.png"> Fixes the Schedule/Actions and weirdness with CSS. Before: <img width="588" alt="Screen Shot 2020-10-02 at 5 42 09 PM" src="https://user-images.githubusercontent.com/1151048/94979030-c5568f80-04dd-11eb-9c91-683d75dc37da.png"> <img width="516" alt="Screen Shot 2020-10-02 at 5 42 05 PM" src="https://user-images.githubusercontent.com/1151048/94979035-c8ea1680-04dd-11eb-9049-120c9d676b1a.png"> After: <img width="1099" alt="Screen Shot 2020-10-02 at 5 42 51 PM" src="https://user-images.githubusercontent.com/1151048/94979038-d0112480-04dd-11eb-8a2a-8eb314023669.png"> <img width="1067" alt="Screen Shot 2020-10-02 at 5 42 57 PM" src="https://user-images.githubusercontent.com/1151048/94979040-d30c1500-04dd-11eb-85ab-a09e4def6adb.png"> ### 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 - [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --- .../__snapshots__/index.test.tsx.snap | 30 -- .../components/wrapper_page/index.test.tsx | 38 --- .../common/components/wrapper_page/index.tsx | 23 +- .../rules/select_rule_type/index.test.tsx | 220 ++++++++++++- .../rules/select_rule_type/index.tsx | 170 +++++----- .../rules/step_define_rule/index.tsx | 2 +- .../detection_engine/rules/create/index.tsx | 306 +++++++++--------- .../detection_engine/rules/edit/index.tsx | 131 ++++---- .../pages/detection_engine/rules/helpers.tsx | 7 + 9 files changed, 550 insertions(+), 377 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/components/wrapper_page/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/wrapper_page/__snapshots__/index.test.tsx.snap index 5ade447994a1ec..89ed2f45a6bf1f 100644 --- a/x-pack/plugins/security_solution/public/common/components/wrapper_page/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/wrapper_page/__snapshots__/index.test.tsx.snap @@ -7,33 +7,3 @@ exports[`WrapperPage it renders 1`] = ` </p> </Memo(WrapperPageComponent)> `; - -exports[`WrapperPage restrict width custom max width when restrictWidth is number 1`] = ` -<Memo(WrapperPageComponent) - restrictWidth={600} -> - <p> - Test page - </p> -</Memo(WrapperPageComponent)> -`; - -exports[`WrapperPage restrict width custom max width when restrictWidth is string 1`] = ` -<Memo(WrapperPageComponent) - restrictWidth="600px" -> - <p> - Test page - </p> -</Memo(WrapperPageComponent)> -`; - -exports[`WrapperPage restrict width default max width when restrictWidth is true 1`] = ` -<Memo(WrapperPageComponent) - restrictWidth={true} -> - <p> - Test page - </p> -</Memo(WrapperPageComponent)> -`; diff --git a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.test.tsx index 23cae15004e39f..e87faf1b5c82c6 100644 --- a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.test.tsx @@ -22,42 +22,4 @@ describe('WrapperPage', () => { expect(wrapper.find('Memo(WrapperPageComponent)')).toMatchSnapshot(); }); - - describe('restrict width', () => { - test('default max width when restrictWidth is true', () => { - const wrapper = shallow( - <TestProviders> - <WrapperPage restrictWidth> - <p>{'Test page'}</p> - </WrapperPage> - </TestProviders> - ); - - expect(wrapper.find('Memo(WrapperPageComponent)')).toMatchSnapshot(); - }); - - test('custom max width when restrictWidth is number', () => { - const wrapper = shallow( - <TestProviders> - <WrapperPage restrictWidth={600}> - <p>{'Test page'}</p> - </WrapperPage> - </TestProviders> - ); - - expect(wrapper.find('Memo(WrapperPageComponent)')).toMatchSnapshot(); - }); - - test('custom max width when restrictWidth is string', () => { - const wrapper = shallow( - <TestProviders> - <WrapperPage restrictWidth="600px"> - <p>{'Test page'}</p> - </WrapperPage> - </TestProviders> - ); - - expect(wrapper.find('Memo(WrapperPageComponent)')).toMatchSnapshot(); - }); - }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx index 0908c887d25f6e..8eff52dae89f36 100644 --- a/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/wrapper_page/index.tsx @@ -16,16 +16,6 @@ import { AppGlobalStyle } from '../page/index'; const Wrapper = styled.div` padding: ${(props) => `${props.theme.eui.paddingSizes.l}`}; - &.siemWrapperPage--restrictWidthDefault, - &.siemWrapperPage--restrictWidthCustom { - box-sizing: content-box; - margin: 0 auto; - } - - &.siemWrapperPage--restrictWidthDefault { - max-width: 1000px; - } - &.siemWrapperPage--fullHeight { height: 100%; display: flex; @@ -58,7 +48,6 @@ interface WrapperPageProps { const WrapperPageComponent: React.FC<WrapperPageProps & CommonProps> = ({ children, className, - restrictWidth, style, noPadding, noTimeline, @@ -74,20 +63,10 @@ const WrapperPageComponent: React.FC<WrapperPageProps & CommonProps> = ({ 'siemWrapperPage--noPadding': noPadding, 'siemWrapperPage--withTimeline': !noTimeline, 'siemWrapperPage--fullHeight': globalFullScreen, - 'siemWrapperPage--restrictWidthDefault': - restrictWidth && typeof restrictWidth === 'boolean' && restrictWidth === true, - 'siemWrapperPage--restrictWidthCustom': restrictWidth && typeof restrictWidth !== 'boolean', }); - let customStyle: WrapperPageProps['style']; - - if (restrictWidth && typeof restrictWidth !== 'boolean') { - const value = typeof restrictWidth === 'number' ? `${restrictWidth}px` : restrictWidth; - customStyle = { ...style, maxWidth: value }; - } - return ( - <Wrapper className={classes} style={customStyle || style} {...otherProps}> + <Wrapper className={classes} style={style} {...otherProps}> {children} <AppGlobalStyle /> </Wrapper> diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx index 87401408c3cc18..b2c22af3a75f9e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.test.tsx @@ -5,21 +5,235 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import { SelectRuleType } from './index'; -import { useFormFieldMock } from '../../../../common/mock'; +import { TestProviders, useFormFieldMock } from '../../../../common/mock'; jest.mock('../../../../common/lib/kibana'); describe('SelectRuleType', () => { + // I do this to avoid the messy warning from happening + // Warning: React does not recognize the `isVisible` prop on a DOM element. + beforeEach(() => { + jest.spyOn(console, 'error').mockImplementation(jest.fn()); + }); + + afterEach(() => { + jest.spyOn(console, 'error').mockRestore(); + }); + it('renders correctly', () => { const Component = () => { const field = useFormFieldMock(); - return <SelectRuleType field={field} />; + return ( + <SelectRuleType + field={field} + describedByIds={[]} + isUpdateView={false} + hasValidLicense={true} + isMlAdmin={true} + /> + ); }; const wrapper = shallow(<Component />); expect(wrapper.dive().find('[data-test-subj="selectRuleType"]')).toHaveLength(1); }); + + describe('update mode vs. non-update mode', () => { + it('renders all the cards when not in update mode', () => { + const field = useFormFieldMock<unknown>({ value: 'query' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={false} + hasValidLicense={true} + isMlAdmin={true} + /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeTruthy(); + }); + + it('renders only the card selected when in update mode of "eql"', () => { + const field = useFormFieldMock<unknown>({ value: 'eql' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={true} + hasValidLicense={true} + isMlAdmin={true} + /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "machine_learning', () => { + const field = useFormFieldMock<unknown>({ value: 'machine_learning' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={true} + hasValidLicense={true} + isMlAdmin={true} + /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "query', () => { + const field = useFormFieldMock<unknown>({ value: 'query' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={true} + hasValidLicense={true} + isMlAdmin={true} + /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "threshold"', () => { + const field = useFormFieldMock<unknown>({ value: 'threshold' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={true} + hasValidLicense={true} + isMlAdmin={true} + /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeTruthy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeFalsy(); + }); + + it('renders only the card selected when in update mode of "threat_match', () => { + const field = useFormFieldMock<unknown>({ value: 'threat_match' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={true} + hasValidLicense={true} + isMlAdmin={true} + /> + </TestProviders> + ); + expect(wrapper.find('[data-test-subj="customRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="machineLearningRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="thresholdRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="eqlRuleType"]').exists()).toBeFalsy(); + expect(wrapper.find('[data-test-subj="threatMatchRuleType"]').exists()).toBeTruthy(); + }); + }); + + describe('permissions', () => { + it('renders machine learning as disabled if "hasValidLicense" is false and it is not selected', () => { + const field = useFormFieldMock<unknown>({ value: 'query' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={false} + hasValidLicense={false} + isMlAdmin={true} + /> + </TestProviders> + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(true); + }); + + it('renders machine learning as not disabled if "hasValidLicense" is false and it is selected', () => { + const field = useFormFieldMock<unknown>({ value: 'machine_learning' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={false} + hasValidLicense={false} + isMlAdmin={true} + /> + </TestProviders> + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(false); + }); + + it('renders machine learning as disabled if "isMlAdmin" is false and it is not selected', () => { + const field = useFormFieldMock<unknown>({ value: 'query' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={false} + hasValidLicense={true} + isMlAdmin={false} + /> + </TestProviders> + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(true); + }); + + it('renders machine learning as not disabled if "isMlAdmin" is false and it is selected', () => { + const field = useFormFieldMock<unknown>({ value: 'machine_learning' }); + const wrapper = mount( + <TestProviders> + <SelectRuleType + describedByIds={[]} + field={field} + isUpdateView={false} + hasValidLicense={true} + isMlAdmin={false} + /> + </TestProviders> + ); + expect( + wrapper.find('[data-test-subj="machineLearningRuleType"]').first().prop('isDisabled') + ).toEqual(false); + }); + }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx index 4b96b8a0ad7bd1..c365982db9cd8c 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/select_rule_type/index.tsx @@ -22,19 +22,19 @@ import { MlCardDescription } from './ml_card_description'; import EqlSearchIcon from './eql_search_icon.svg'; interface SelectRuleTypeProps { - describedByIds?: string[]; + describedByIds: string[]; field: FieldHook; - hasValidLicense?: boolean; - isMlAdmin?: boolean; - isReadOnly?: boolean; + hasValidLicense: boolean; + isMlAdmin: boolean; + isUpdateView: boolean; } export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({ describedByIds = [], field, - isReadOnly = false, - hasValidLicense = false, - isMlAdmin = false, + isUpdateView, + hasValidLicense, + isMlAdmin, }) => { const ruleType = field.value as Type; const setType = useCallback( @@ -48,54 +48,54 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({ const setQuery = useCallback(() => setType('query'), [setType]); const setThreshold = useCallback(() => setType('threshold'), [setType]); const setThreatMatch = useCallback(() => setType('threat_match'), [setType]); - const mlCardDisabled = isReadOnly || !hasValidLicense || !isMlAdmin; const licensingUrl = useKibana().services.application.getUrlForApp('kibana', { path: '#/management/stack/license_management', }); const eqlSelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setEql, isSelected: isEqlRule(ruleType), + isVisible: !isUpdateView || isEqlRule(ruleType), }), - [isReadOnly, ruleType, setEql] + [ruleType, setEql, isUpdateView] ); const querySelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setQuery, isSelected: isQueryRule(ruleType), + isVisible: !isUpdateView || isQueryRule(ruleType), }), - [isReadOnly, ruleType, setQuery] + [ruleType, setQuery, isUpdateView] ); const mlSelectableConfig = useMemo( () => ({ - isDisabled: mlCardDisabled, + isDisabled: !hasValidLicense || !isMlAdmin, onClick: setMl, isSelected: isMlRule(ruleType), + isVisible: !isUpdateView || isMlRule(ruleType), }), - [mlCardDisabled, ruleType, setMl] + [ruleType, setMl, isUpdateView, hasValidLicense, isMlAdmin] ); const thresholdSelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setThreshold, isSelected: isThresholdRule(ruleType), + isVisible: !isUpdateView || isThresholdRule(ruleType), }), - [isReadOnly, ruleType, setThreshold] + [ruleType, setThreshold, isUpdateView] ); const threatMatchSelectableConfig = useMemo( () => ({ - isDisabled: isReadOnly, onClick: setThreatMatch, isSelected: isThreatMatchRule(ruleType), + isVisible: !isUpdateView || isThreatMatchRule(ruleType), }), - [isReadOnly, ruleType, setThreatMatch] + [ruleType, setThreatMatch, isUpdateView] ); return ( @@ -105,63 +105,83 @@ export const SelectRuleType: React.FC<SelectRuleTypeProps> = ({ describedByIds={describedByIds} label={field.label} > - <EuiFlexGrid columns={4}> - <EuiFlexItem> - <EuiCard - data-test-subj="customRuleType" - title={i18n.QUERY_TYPE_TITLE} - description={i18n.QUERY_TYPE_DESCRIPTION} - icon={<EuiIcon size="l" type="search" />} - isDisabled={querySelectableConfig.isDisabled && !querySelectableConfig.isSelected} - selectable={querySelectableConfig} - /> - </EuiFlexItem> - <EuiFlexItem> - <EuiCard - data-test-subj="machineLearningRuleType" - title={i18n.ML_TYPE_TITLE} - description={ - <MlCardDescription subscriptionUrl={licensingUrl} hasValidLicense={hasValidLicense} /> - } - icon={<EuiIcon size="l" type="machineLearningApp" />} - isDisabled={mlSelectableConfig.isDisabled && !mlSelectableConfig.isSelected} - selectable={mlSelectableConfig} - /> - </EuiFlexItem> - <EuiFlexItem> - <EuiCard - data-test-subj="thresholdRuleType" - title={i18n.THRESHOLD_TYPE_TITLE} - description={i18n.THRESHOLD_TYPE_DESCRIPTION} - icon={<EuiIcon size="l" type="indexFlush" />} - isDisabled={ - thresholdSelectableConfig.isDisabled && !thresholdSelectableConfig.isSelected - } - selectable={thresholdSelectableConfig} - /> - </EuiFlexItem> - <EuiFlexItem> - <EuiCard - data-test-subj="eqlRuleType" - title={i18n.EQL_TYPE_TITLE} - description={i18n.EQL_TYPE_DESCRIPTION} - icon={<EuiIcon size="l" type={EqlSearchIcon} />} - isDisabled={eqlSelectableConfig.isDisabled && !eqlSelectableConfig.isSelected} - selectable={eqlSelectableConfig} - /> - </EuiFlexItem> - <EuiFlexItem> - <EuiCard - data-test-subj="threatMatchRuleType" - title={i18n.THREAT_MATCH_TYPE_TITLE} - description={i18n.THREAT_MATCH_TYPE_DESCRIPTION} - icon={<EuiIcon size="l" type="list" />} - isDisabled={ - threatMatchSelectableConfig.isDisabled && !threatMatchSelectableConfig.isSelected - } - selectable={threatMatchSelectableConfig} - /> - </EuiFlexItem> + <EuiFlexGrid columns={3}> + {querySelectableConfig.isVisible && ( + <EuiFlexItem> + <EuiCard + data-test-subj="customRuleType" + title={i18n.QUERY_TYPE_TITLE} + titleSize="xs" + description={i18n.QUERY_TYPE_DESCRIPTION} + icon={<EuiIcon size="xl" type="search" />} + selectable={querySelectableConfig} + layout="horizontal" + textAlign="left" + /> + </EuiFlexItem> + )} + {mlSelectableConfig.isVisible && ( + <EuiFlexItem> + <EuiCard + data-test-subj="machineLearningRuleType" + title={i18n.ML_TYPE_TITLE} + titleSize="xs" + description={ + <MlCardDescription + subscriptionUrl={licensingUrl} + hasValidLicense={hasValidLicense} + /> + } + icon={<EuiIcon size="l" type="machineLearningApp" />} + isDisabled={mlSelectableConfig.isDisabled && !mlSelectableConfig.isSelected} + selectable={mlSelectableConfig} + layout="horizontal" + textAlign="left" + /> + </EuiFlexItem> + )} + {thresholdSelectableConfig.isVisible && ( + <EuiFlexItem> + <EuiCard + data-test-subj="thresholdRuleType" + title={i18n.THRESHOLD_TYPE_TITLE} + titleSize="xs" + description={i18n.THRESHOLD_TYPE_DESCRIPTION} + icon={<EuiIcon size="l" type="indexFlush" />} + selectable={thresholdSelectableConfig} + layout="horizontal" + textAlign="left" + /> + </EuiFlexItem> + )} + {eqlSelectableConfig.isVisible && ( + <EuiFlexItem> + <EuiCard + data-test-subj="eqlRuleType" + title={i18n.EQL_TYPE_TITLE} + titleSize="xs" + description={i18n.EQL_TYPE_DESCRIPTION} + icon={<EuiIcon size="l" type={EqlSearchIcon} />} + selectable={eqlSelectableConfig} + layout="horizontal" + textAlign="left" + /> + </EuiFlexItem> + )} + {threatMatchSelectableConfig.isVisible && ( + <EuiFlexItem> + <EuiCard + data-test-subj="threatMatchRuleType" + title={i18n.THREAT_MATCH_TYPE_TITLE} + titleSize="xs" + description={i18n.THREAT_MATCH_TYPE_DESCRIPTION} + icon={<EuiIcon size="l" type="list" />} + selectable={threatMatchSelectableConfig} + layout="horizontal" + textAlign="left" + /> + </EuiFlexItem> + )} </EuiFlexGrid> </EuiFormRow> ); 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 9bd0e4fb4da5de..d13635bfd1b508 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 @@ -236,7 +236,7 @@ const StepDefineRuleComponent: FC<StepDefineRuleProps> = ({ component={SelectRuleType} componentProps={{ describedByIds: ['detectionEngineStepDefineRuleType'], - isReadOnly: isUpdateView, + isUpdateView, hasValidLicense: hasMlLicense(mlCapabilities), isMlAdmin: hasMlAdminPermissions(mlCapabilities), }} 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 542b7b1b84c3cb..9d54879ee74953 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 @@ -4,7 +4,14 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButtonEmpty, EuiAccordion, EuiHorizontalRule, EuiPanel, EuiSpacer } from '@elastic/eui'; +import { + EuiButtonEmpty, + EuiAccordion, + EuiHorizontalRule, + EuiPanel, + EuiSpacer, + EuiFlexGroup, +} from '@elastic/eui'; import React, { useCallback, useRef, useState, useMemo } from 'react'; import { useHistory } from 'react-router-dom'; import styled, { StyledComponent } from 'styled-components'; @@ -28,7 +35,12 @@ import { StepScheduleRule } from '../../../../components/rules/step_schedule_rul import { StepRuleActions } from '../../../../components/rules/step_rule_actions'; import { DetectionEngineHeaderPage } from '../../../../components/detection_engine_header_page'; import * as RuleI18n from '../translations'; -import { redirectToDetections, getActionMessageParams, userHasNoPermissions } from '../helpers'; +import { + redirectToDetections, + getActionMessageParams, + userHasNoPermissions, + MaxWidthEuiFlexItem, +} from '../helpers'; import { RuleStep, RuleStepsFormData, RuleStepsFormHooks } from '../types'; import { formatRule, stepIsValid } from './helpers'; import * as i18n from './translations'; @@ -273,151 +285,155 @@ const CreateRulePageComponent: React.FC = () => { return ( <> - <WrapperPage restrictWidth> - <DetectionEngineHeaderPage - backOptions={{ - href: getRulesUrl(), - text: i18n.BACK_TO_RULES, - pageId: SecurityPageName.detections, - }} - border - isLoading={isLoading || loading} - title={i18n.PAGE_TITLE} - /> - <MyEuiPanel zindex={4}> - <StepDefineRuleAccordion - initialIsOpen={true} - id={RuleStep.defineRule} - buttonContent={defineRuleButton} - paddingSize="xs" - ref={defineRuleRef} - onToggle={handleAccordionToggle.bind(null, RuleStep.defineRule)} - extraAction={ - stepsData.current[RuleStep.defineRule].isValid && ( - <EuiButtonEmpty - data-test-subj="edit-define-rule" - iconType="pencil" - size="xs" - onClick={() => editStep(RuleStep.defineRule)} - > - {i18n.EDIT_RULE} - </EuiButtonEmpty> - ) - } - > - <EuiHorizontalRule margin="m" /> - <StepDefineRule - addPadding={true} - defaultValues={stepsData.current[RuleStep.defineRule].data} - isReadOnlyView={activeStep !== RuleStep.defineRule} - isLoading={isLoading || loading} - setForm={setFormHook} - onSubmit={() => submitStep(RuleStep.defineRule)} - descriptionColumns="singleSplit" - /> - </StepDefineRuleAccordion> - </MyEuiPanel> - <EuiSpacer size="l" /> - <MyEuiPanel zindex={3}> - <EuiAccordion - initialIsOpen={false} - id={RuleStep.aboutRule} - buttonContent={aboutRuleButton} - paddingSize="xs" - ref={aboutRuleRef} - onToggle={handleAccordionToggle.bind(null, RuleStep.aboutRule)} - extraAction={ - stepsData.current[RuleStep.aboutRule].isValid && ( - <EuiButtonEmpty - data-test-subj="edit-about-rule" - iconType="pencil" - size="xs" - onClick={() => editStep(RuleStep.aboutRule)} - > - {i18n.EDIT_RULE} - </EuiButtonEmpty> - ) - } - > - <EuiHorizontalRule margin="m" /> - <StepAboutRule - addPadding={true} - defaultValues={stepsData.current[RuleStep.aboutRule].data} - defineRuleData={stepsData.current[RuleStep.defineRule].data} - descriptionColumns="singleSplit" - isReadOnlyView={activeStep !== RuleStep.aboutRule} - isLoading={isLoading || loading} - setForm={setFormHook} - onSubmit={() => submitStep(RuleStep.aboutRule)} - /> - </EuiAccordion> - </MyEuiPanel> - <EuiSpacer size="l" /> - <MyEuiPanel zindex={2}> - <EuiAccordion - initialIsOpen={false} - id={RuleStep.scheduleRule} - buttonContent={scheduleRuleButton} - paddingSize="xs" - ref={scheduleRuleRef} - onToggle={handleAccordionToggle.bind(null, RuleStep.scheduleRule)} - extraAction={ - stepsData.current[RuleStep.scheduleRule].isValid && ( - <EuiButtonEmpty - iconType="pencil" - size="xs" - onClick={() => editStep(RuleStep.scheduleRule)} - > - {i18n.EDIT_RULE} - </EuiButtonEmpty> - ) - } - > - <EuiHorizontalRule margin="m" /> - <StepScheduleRule - addPadding={true} - defaultValues={stepsData.current[RuleStep.scheduleRule].data} - descriptionColumns="singleSplit" - isReadOnlyView={activeStep !== RuleStep.scheduleRule} - isLoading={isLoading || loading} - setForm={setFormHook} - onSubmit={() => submitStep(RuleStep.scheduleRule)} - /> - </EuiAccordion> - </MyEuiPanel> - <EuiSpacer size="l" /> - <MyEuiPanel zindex={1}> - <EuiAccordion - initialIsOpen={false} - id={RuleStep.ruleActions} - buttonContent={ruleActionsButton} - paddingSize="xs" - ref={ruleActionsRef} - onToggle={handleAccordionToggle.bind(null, RuleStep.ruleActions)} - extraAction={ - stepsData.current[RuleStep.ruleActions].isValid && ( - <EuiButtonEmpty - iconType="pencil" - size="xs" - onClick={() => editStep(RuleStep.ruleActions)} - > - {i18n.EDIT_RULE} - </EuiButtonEmpty> - ) - } - > - <EuiHorizontalRule margin="m" /> - <StepRuleActions - addPadding={true} - defaultValues={stepsData.current[RuleStep.ruleActions].data} - isReadOnlyView={activeStep !== RuleStep.ruleActions} + <WrapperPage> + <EuiFlexGroup direction="row" justifyContent="spaceAround"> + <MaxWidthEuiFlexItem> + <DetectionEngineHeaderPage + backOptions={{ + href: getRulesUrl(), + text: i18n.BACK_TO_RULES, + pageId: SecurityPageName.detections, + }} + border isLoading={isLoading || loading} - setForm={setFormHook} - onSubmit={() => submitStep(RuleStep.ruleActions)} - actionMessageParams={actionMessageParams} + title={i18n.PAGE_TITLE} /> - </EuiAccordion> - </MyEuiPanel> + <MyEuiPanel zindex={4}> + <StepDefineRuleAccordion + initialIsOpen={true} + id={RuleStep.defineRule} + buttonContent={defineRuleButton} + paddingSize="xs" + ref={defineRuleRef} + onToggle={handleAccordionToggle.bind(null, RuleStep.defineRule)} + extraAction={ + stepsData.current[RuleStep.defineRule].isValid && ( + <EuiButtonEmpty + data-test-subj="edit-define-rule" + iconType="pencil" + size="xs" + onClick={() => editStep(RuleStep.defineRule)} + > + {i18n.EDIT_RULE} + </EuiButtonEmpty> + ) + } + > + <EuiHorizontalRule margin="m" /> + <StepDefineRule + addPadding={true} + defaultValues={stepsData.current[RuleStep.defineRule].data} + isReadOnlyView={activeStep !== RuleStep.defineRule} + isLoading={isLoading || loading} + setForm={setFormHook} + onSubmit={() => submitStep(RuleStep.defineRule)} + descriptionColumns="singleSplit" + /> + </StepDefineRuleAccordion> + </MyEuiPanel> + <EuiSpacer size="l" /> + <MyEuiPanel zindex={3}> + <EuiAccordion + initialIsOpen={false} + id={RuleStep.aboutRule} + buttonContent={aboutRuleButton} + paddingSize="xs" + ref={aboutRuleRef} + onToggle={handleAccordionToggle.bind(null, RuleStep.aboutRule)} + extraAction={ + stepsData.current[RuleStep.aboutRule].isValid && ( + <EuiButtonEmpty + data-test-subj="edit-about-rule" + iconType="pencil" + size="xs" + onClick={() => editStep(RuleStep.aboutRule)} + > + {i18n.EDIT_RULE} + </EuiButtonEmpty> + ) + } + > + <EuiHorizontalRule margin="m" /> + <StepAboutRule + addPadding={true} + defaultValues={stepsData.current[RuleStep.aboutRule].data} + defineRuleData={stepsData.current[RuleStep.defineRule].data} + descriptionColumns="singleSplit" + isReadOnlyView={activeStep !== RuleStep.aboutRule} + isLoading={isLoading || loading} + setForm={setFormHook} + onSubmit={() => submitStep(RuleStep.aboutRule)} + /> + </EuiAccordion> + </MyEuiPanel> + <EuiSpacer size="l" /> + <MyEuiPanel zindex={2}> + <EuiAccordion + initialIsOpen={false} + id={RuleStep.scheduleRule} + buttonContent={scheduleRuleButton} + paddingSize="xs" + ref={scheduleRuleRef} + onToggle={handleAccordionToggle.bind(null, RuleStep.scheduleRule)} + extraAction={ + stepsData.current[RuleStep.scheduleRule].isValid && ( + <EuiButtonEmpty + iconType="pencil" + size="xs" + onClick={() => editStep(RuleStep.scheduleRule)} + > + {i18n.EDIT_RULE} + </EuiButtonEmpty> + ) + } + > + <EuiHorizontalRule margin="m" /> + <StepScheduleRule + addPadding={true} + defaultValues={stepsData.current[RuleStep.scheduleRule].data} + descriptionColumns="singleSplit" + isReadOnlyView={activeStep !== RuleStep.scheduleRule} + isLoading={isLoading || loading} + setForm={setFormHook} + onSubmit={() => submitStep(RuleStep.scheduleRule)} + /> + </EuiAccordion> + </MyEuiPanel> + <EuiSpacer size="l" /> + <MyEuiPanel zindex={1}> + <EuiAccordion + initialIsOpen={false} + id={RuleStep.ruleActions} + buttonContent={ruleActionsButton} + paddingSize="xs" + ref={ruleActionsRef} + onToggle={handleAccordionToggle.bind(null, RuleStep.ruleActions)} + extraAction={ + stepsData.current[RuleStep.ruleActions].isValid && ( + <EuiButtonEmpty + iconType="pencil" + size="xs" + onClick={() => editStep(RuleStep.ruleActions)} + > + {i18n.EDIT_RULE} + </EuiButtonEmpty> + ) + } + > + <EuiHorizontalRule margin="m" /> + <StepRuleActions + addPadding={true} + defaultValues={stepsData.current[RuleStep.ruleActions].data} + isReadOnlyView={activeStep !== RuleStep.ruleActions} + isLoading={isLoading || loading} + setForm={setFormHook} + onSubmit={() => submitStep(RuleStep.ruleActions)} + actionMessageParams={actionMessageParams} + /> + </EuiAccordion> + </MyEuiPanel> + </MaxWidthEuiFlexItem> + </EuiFlexGroup> </WrapperPage> <SpyRoute pageName={SecurityPageName.detections} /> diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx index e2772af72da06b..fa74565be660f7 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/edit/index.tsx @@ -47,6 +47,7 @@ import { redirectToDetections, getActionMessageParams, userHasNoPermissions, + MaxWidthEuiFlexItem, } from '../helpers'; import * as ruleI18n from '../translations'; import { RuleStep, RuleStepsFormHooks, RuleStepsFormData, RuleStepsData } from '../types'; @@ -332,75 +333,79 @@ const EditRulePageComponent: FC = () => { return ( <> - <WrapperPage restrictWidth> - <DetectionEngineHeaderPage - backOptions={{ - href: getRuleDetailsUrl(ruleId ?? ''), - text: `${i18n.BACK_TO} ${rule?.name ?? ''}`, - pageId: SecurityPageName.detections, - }} - isLoading={isLoading} - title={i18n.PAGE_TITLE} - /> - {invalidSteps.length > 0 && ( - <EuiCallOut title={i18n.SORRY_ERRORS} color="danger" iconType="alert"> - <FormattedMessage - id="xpack.securitySolution.detectionEngine.rule.editRule.errorMsgDescription" - defaultMessage="You have an invalid input in {countError, plural, one {this tab} other {these tabs}}: {tabHasError}" - values={{ - countError: invalidSteps.length, - tabHasError: invalidSteps - .map((t) => { - if (t === RuleStep.aboutRule) { - return ruleI18n.ABOUT; - } else if (t === RuleStep.defineRule) { - return ruleI18n.DEFINITION; - } else if (t === RuleStep.scheduleRule) { - return ruleI18n.SCHEDULE; - } else if (t === RuleStep.ruleActions) { - return ruleI18n.RULE_ACTIONS; - } - return t; - }) - .join(', '), + <WrapperPage> + <EuiFlexGroup direction="row" justifyContent="spaceAround"> + <MaxWidthEuiFlexItem> + <DetectionEngineHeaderPage + backOptions={{ + href: getRuleDetailsUrl(ruleId ?? ''), + text: `${i18n.BACK_TO} ${rule?.name ?? ''}`, + pageId: SecurityPageName.detections, }} + isLoading={isLoading} + title={i18n.PAGE_TITLE} /> - </EuiCallOut> - )} - - <EuiTabbedContent - initialSelectedTab={tabs[0]} - selectedTab={tabs.find((t) => t.id === activeStep)} - onTabClick={onTabClick} - tabs={tabs} - /> + {invalidSteps.length > 0 && ( + <EuiCallOut title={i18n.SORRY_ERRORS} color="danger" iconType="alert"> + <FormattedMessage + id="xpack.securitySolution.detectionEngine.rule.editRule.errorMsgDescription" + defaultMessage="You have an invalid input in {countError, plural, one {this tab} other {these tabs}}: {tabHasError}" + values={{ + countError: invalidSteps.length, + tabHasError: invalidSteps + .map((t) => { + if (t === RuleStep.aboutRule) { + return ruleI18n.ABOUT; + } else if (t === RuleStep.defineRule) { + return ruleI18n.DEFINITION; + } else if (t === RuleStep.scheduleRule) { + return ruleI18n.SCHEDULE; + } else if (t === RuleStep.ruleActions) { + return ruleI18n.RULE_ACTIONS; + } + return t; + }) + .join(', '), + }} + /> + </EuiCallOut> + )} - <EuiSpacer /> + <EuiTabbedContent + initialSelectedTab={tabs[0]} + selectedTab={tabs.find((t) => t.id === activeStep)} + onTabClick={onTabClick} + tabs={tabs} + /> - <EuiFlexGroup - alignItems="center" - gutterSize="s" - justifyContent="flexEnd" - responsive={false} - > - <EuiFlexItem grow={false}> - <EuiButton iconType="cross" onClick={goToDetailsRule}> - {i18n.CANCEL} - </EuiButton> - </EuiFlexItem> + <EuiSpacer /> - <EuiFlexItem grow={false}> - <EuiButton - data-test-subj="ruleEditSubmitButton" - fill - onClick={onSubmit} - iconType="save" - isLoading={isLoading} - isDisabled={loading} + <EuiFlexGroup + alignItems="center" + gutterSize="s" + justifyContent="flexEnd" + responsive={false} > - {i18n.SAVE_CHANGES} - </EuiButton> - </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiButton iconType="cross" onClick={goToDetailsRule}> + {i18n.CANCEL} + </EuiButton> + </EuiFlexItem> + + <EuiFlexItem grow={false}> + <EuiButton + data-test-subj="ruleEditSubmitButton" + fill + onClick={onSubmit} + iconType="save" + isLoading={isLoading} + isDisabled={loading} + > + {i18n.SAVE_CHANGES} + </EuiButton> + </EuiFlexItem> + </EuiFlexGroup> + </MaxWidthEuiFlexItem> </EuiFlexGroup> </WrapperPage> diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx index 456bf8419a1f71..4dbcffbc807ec1 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/helpers.tsx @@ -9,6 +9,8 @@ import moment from 'moment'; import memoizeOne from 'memoize-one'; import { useLocation } from 'react-router-dom'; +import styled from 'styled-components'; +import { EuiFlexItem } from '@elastic/eui'; import { ActionVariable } from '../../../../../../triggers_actions_ui/public'; import { RuleAlertAction } from '../../../../../common/detection_engine/types'; import { assertUnreachable } from '../../../../../common/utility_types'; @@ -377,3 +379,8 @@ export const getActionMessageParams = memoizeOne((ruleType: Type | undefined): A // typed as null not undefined as the initial state for this value is null. export const userHasNoPermissions = (canUserCRUD: boolean | null): boolean => canUserCRUD != null ? !canUserCRUD : false; + +export const MaxWidthEuiFlexItem = styled(EuiFlexItem)` + max-width: 1000px; + overflow: hidden; +`; From 513ebb50f9b6d68598fc6e85a75d5efca0c77096 Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Sat, 3 Oct 2020 17:50:23 +0100 Subject: [PATCH 122/128] [Security Solution] Untitled Timeline created when first action is to add note (#78988) * init tests * Untitled Timeline created * remove console * fix from server side * set timeline status to draft if created by saving notes * add unit test Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com> --- .../lib/timeline/pick_saved_timeline.test.ts | 239 ++++++++++++++++++ .../lib/timeline/pick_saved_timeline.ts | 2 +- 2 files changed, 240 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts diff --git a/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts new file mode 100644 index 00000000000000..92d2a9c5a3077a --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.test.ts @@ -0,0 +1,239 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { AuthenticatedUser } from '../../../../security/common/model'; + +import { TimelineStatus, TimelineType } from '../../../common/types/timeline'; + +import { pickSavedTimeline } from './pick_saved_timeline'; + +describe('pickSavedTimeline', () => { + const mockDateNow = new Date('2020-04-03T23:00:00.000Z').valueOf(); + const getMockSavedTimeline = () => ({ + savedObjectId: '7af80430-03f4-11eb-9d9d-ffba20fabba8', + version: 'WzQ0ODgsMV0=', + created: 1601563413330, + createdBy: 'Elastic', + updated: 1601563454756, + updatedBy: 'Elastic', + dateRange: { start: '2020-09-30T14:42:30.094Z', end: '2020-10-01T14:42:30.094Z' }, + columns: [ + { columnHeaderType: 'not-filtered', id: '@timestamp' }, + { columnHeaderType: 'not-filtered', id: 'message' }, + { columnHeaderType: 'not-filtered', id: 'event.category' }, + { columnHeaderType: 'not-filtered', id: 'event.action' }, + { columnHeaderType: 'not-filtered', id: 'host.name' }, + { columnHeaderType: 'not-filtered', id: 'source.ip' }, + { columnHeaderType: 'not-filtered', id: 'destination.ip' }, + { columnHeaderType: 'not-filtered', id: 'user.name' }, + ], + indexNames: [ + 'auditbeat-*', + 'endgame-*', + 'filebeat-*', + 'logs-*', + 'packetbeat-*', + 'winlogbeat-*', + '.siem-signals-angelachuang-default', + ], + description: 'hhh', + templateTimelineVersion: null, + eventType: 'all', + filters: [], + sort: { sortDirection: 'desc', columnId: '@timestamp' }, + title: 'title', + kqlMode: 'filter', + timelineType: TimelineType.default, + savedQueryId: null, + kqlQuery: { filterQuery: null }, + dataProviders: [], + templateTimelineId: null, + eventNotes: [], + globalNotes: [ + { + noteId: '7ba7a520-03f4-11eb-9d9d-ffba20fabba8', + version: 'WzQ0ODEsMV0=', + note: '789', + timelineId: '7af80430-03f4-11eb-9d9d-ffba20fabba8', + created: 1601563414477, + createdBy: 'Elastic', + updated: 1601563414477, + updatedBy: 'Elastic', + }, + ], + pinnedEventIds: [], + }); + + beforeAll(() => { + Date = (jest.fn(() => ({ + valueOf: jest.fn().mockReturnValue(mockDateNow), + })) as unknown) as DateConstructor; + }); + + afterAll(() => { + ((Date as unknown) as jest.Mock).mockRestore(); + }); + + describe('Set create / update time correctly ', () => { + test('Creating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.created).toEqual(mockDateNow); + expect(result.updated).toEqual(mockDateNow); + }); + + test('Updating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.created).toEqual(savedTimeline.created); + expect(result.updated).toEqual(mockDateNow); + }); + }); + + describe('Set userInfo correctly ', () => { + test('Creating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.createdBy).toEqual(userInfo.username); + expect(result.updatedBy).toEqual(userInfo.username); + }); + + test('Updating a timeline', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.createdBy).toEqual(savedTimeline.createdBy); + expect(result.updatedBy).toEqual(userInfo.username); + }); + }); + + describe('Set status correctly ', () => { + test('Creating a timeline with title', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Creating a timeline without title', () => { + const savedTimeline = { ...getMockSavedTimeline(), title: null }; + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.draft); + }); + + test('Updating a timeline with a new title', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating a timeline without title', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating an immutable timeline with a new title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.immutable }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.immutable); + }); + + test('Creating a draft timeline with title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Creating a draft timeline without title', () => { + const savedTimeline = { + ...getMockSavedTimeline(), + title: null, + status: TimelineStatus.draft, + }; + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.draft); + }); + + test('Updating an untitled draft timeline with a title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating a draft timeline with a new title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + + test('Updating a draft timeline without title', () => { + const savedTimeline = { ...getMockSavedTimeline(), status: TimelineStatus.draft }; + const timelineId = savedTimeline.savedObjectId; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.status).toEqual(TimelineStatus.active); + }); + }); + + test('Set timelineType correctly ', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.timelineType).toEqual(TimelineType.default); + expect(result.status).toEqual(TimelineStatus.active); + expect(result.templateTimelineId).toBeNull(); + expect(result.templateTimelineVersion).toBeNull(); + }); + + test('Set excludedRowRendererIds correctly ', () => { + const savedTimeline = getMockSavedTimeline(); + const timelineId = null; + const userInfo = { username: 'elastic' } as AuthenticatedUser; + const result = pickSavedTimeline(timelineId, savedTimeline, userInfo); + + expect(result.excludedRowRendererIds).toEqual([]); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts index d3d7783dc93859..6c664f55eccf86 100644 --- a/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts +++ b/x-pack/plugins/security_solution/server/lib/timeline/pick_saved_timeline.ts @@ -27,7 +27,7 @@ export const pickSavedTimeline = ( savedTimeline.updatedBy = userInfo?.username ?? UNAUTHENTICATED_USER; } - if (savedTimeline.status === TimelineStatus.draft) { + if (savedTimeline.status === TimelineStatus.draft || savedTimeline.status == null) { savedTimeline.status = !isEmpty(savedTimeline.title) ? TimelineStatus.active : TimelineStatus.draft; From e859fe7c65655ff7108b045793bf27d6e8502a70 Mon Sep 17 00:00:00 2001 From: Paul Tavares <56442535+paul-tavares@users.noreply.github.com> Date: Sat, 3 Oct 2020 19:24:39 -0400 Subject: [PATCH 123/128] Fix agentPolicyUpdateEventHandler() to use app context soClient for creation of actions (#79341) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../server/services/agent_policy_update.ts | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts b/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts index ff20e25e5bf0d9..4ddb3cfb6a6a52 100644 --- a/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts +++ b/x-pack/plugins/ingest_manager/server/services/agent_policy_update.ts @@ -4,11 +4,27 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract } from 'src/core/server'; +import { KibanaRequest, SavedObjectsClientContract } from 'src/core/server'; import { generateEnrollmentAPIKey, deleteEnrollmentApiKeyForAgentPolicyId } from './api_keys'; import { unenrollForAgentPolicyId } from './agents'; import { outputService } from './output'; import { agentPolicyService } from './agent_policy'; +import { appContextService } from './app_context'; + +const fakeRequest = ({ + headers: {}, + getBasePath: () => '', + path: '/', + route: { settings: {} }, + url: { + href: '/', + }, + raw: { + req: { + url: '/', + }, + }, +} as unknown) as KibanaRequest; export async function agentPolicyUpdateEventHandler( soClient: SavedObjectsClientContract, @@ -17,20 +33,25 @@ export async function agentPolicyUpdateEventHandler( ) { const adminUser = await outputService.getAdminUser(soClient); const outputId = await outputService.getDefaultOutputId(soClient); + // If no admin user and no default output fleet is not enabled just skip this hook if (!adminUser || !outputId) { return; } + // `soClient` from ingest `appContextService` is used to create policy change actions + // to ensure encrypted SOs are handled correctly + const internalSoClient = appContextService.getInternalUserSOClient(fakeRequest); + if (action === 'created') { await generateEnrollmentAPIKey(soClient, { agentPolicyId, }); - await agentPolicyService.createFleetPolicyChangeAction(soClient, agentPolicyId); + await agentPolicyService.createFleetPolicyChangeAction(internalSoClient, agentPolicyId); } if (action === 'updated') { - await agentPolicyService.createFleetPolicyChangeAction(soClient, agentPolicyId); + await agentPolicyService.createFleetPolicyChangeAction(internalSoClient, agentPolicyId); } if (action === 'deleted') { From acedb350333242727e6ef6712480c7a31aa14707 Mon Sep 17 00:00:00 2001 From: Brian Seeders <seeders@gmail.com> Date: Sat, 3 Oct 2020 21:07:45 -0400 Subject: [PATCH 124/128] skip flaky suite (#77278) --- .../security_solution_endpoint/apps/endpoint/endpoint_list.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts index e81851238882cf..dd28752bf29b44 100644 --- a/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts +++ b/x-pack/test/security_solution_endpoint/apps/endpoint/endpoint_list.ts @@ -64,7 +64,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ], ]; - describe('endpoint list', function () { + // Failing: See https://github.com/elastic/kibana/issues/77278 + describe.skip('endpoint list', function () { this.tags('ciGroup7'); const sleep = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms)); From 5cc945f9e8a9e979771ef5bebc3c03aa2e3e0cb4 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov <restrry@gmail.com> Date: Sun, 4 Oct 2020 22:10:19 +0300 Subject: [PATCH 125/128] load js-yaml lazily (#79092) --- .../public/components/navigation/connected_link.tsx | 4 ++-- .../public/lib/__tests__/config_blocks.test.ts | 12 ++++++------ .../public/lib/configuration_blocks.ts | 11 ++++++----- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/beats_management/public/components/navigation/connected_link.tsx b/x-pack/plugins/beats_management/public/components/navigation/connected_link.tsx index ebac34afa016b9..57fb6d4fc33bba 100644 --- a/x-pack/plugins/beats_management/public/components/navigation/connected_link.tsx +++ b/x-pack/plugins/beats_management/public/components/navigation/connected_link.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import _ from 'lodash'; +import { get } from 'lodash'; import React from 'react'; import { EuiLink } from '@elastic/eui'; @@ -31,7 +31,7 @@ export const ConnectedLinkComponent = ({ } // Shorthand for pathname - const pathname = path || _.get(props.to, 'pathname') || location.pathname; + const pathname = path || get(props.to, 'pathname') || location.pathname; return ( <Link diff --git a/x-pack/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts b/x-pack/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts index 6776fb0b6f5add..179df6c19e21df 100644 --- a/x-pack/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts +++ b/x-pack/plugins/beats_management/public/lib/__tests__/config_blocks.test.ts @@ -20,7 +20,7 @@ describe('Tags Client Domain Lib', () => { }); it('should use helper function to convert users yaml in tag to config object', async () => { - const convertedBlocks = lib.userConfigsToJson([ + const convertedBlocks = await lib.userConfigsToJson([ { id: 'foo', tag: 'basic', @@ -42,7 +42,7 @@ describe('Tags Client Domain Lib', () => { }); it('should use helper function to convert user config to json with undefined `other`', async () => { - const convertedTag = lib.userConfigsToJson([ + const convertedTag = await lib.userConfigsToJson([ { id: 'foo', tag: 'basic', @@ -61,7 +61,7 @@ describe('Tags Client Domain Lib', () => { }); it('should use helper function to convert users yaml in tag to config object, where empty other leads to no other fields saved', async () => { - const convertedTag = lib.userConfigsToJson([ + const convertedTag = await lib.userConfigsToJson([ { id: 'foo', tag: 'basic', @@ -83,7 +83,7 @@ describe('Tags Client Domain Lib', () => { }); it('should convert tokenized fields to JSON', async () => { - const convertedTag = lib.userConfigsToJson([ + const convertedTag = await lib.userConfigsToJson([ { id: 'foo', tag: 'basic', @@ -106,7 +106,7 @@ describe('Tags Client Domain Lib', () => { }); it('should use helper function to convert config object to users yaml', async () => { - const convertedTag = lib.jsonConfigToUserYaml([ + const convertedTag = await lib.jsonConfigToUserYaml([ { id: 'foo', tag: 'basic', @@ -127,7 +127,7 @@ describe('Tags Client Domain Lib', () => { }); it('should use helper function to convert config object to users yaml with empty `other`', async () => { - const convertedTag = lib.jsonConfigToUserYaml([ + const convertedTag = await lib.jsonConfigToUserYaml([ { id: 'foo', tag: 'basic', diff --git a/x-pack/plugins/beats_management/public/lib/configuration_blocks.ts b/x-pack/plugins/beats_management/public/lib/configuration_blocks.ts index 09c079ea129e67..d84bd21381c3ee 100644 --- a/x-pack/plugins/beats_management/public/lib/configuration_blocks.ts +++ b/x-pack/plugins/beats_management/public/lib/configuration_blocks.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import yaml from 'js-yaml'; import { set } from '@elastic/safer-lodash-set'; import { get, has, omit } from 'lodash'; import { ConfigBlockSchema, ConfigurationBlock } from '../../common/domain_types'; @@ -19,16 +18,17 @@ export class ConfigBlocksLib { ) {} public upsert = async (blocks: ConfigurationBlock[]) => { - return await this.adapter.upsert(this.userConfigsToJson(blocks)); + return await this.adapter.upsert(await this.userConfigsToJson(blocks)); }; public getForTags = async (tagIds: string[], page: number) => { const result = await this.adapter.getForTags(tagIds, page); - result.list = this.jsonConfigToUserYaml(result.list); + result.list = await this.jsonConfigToUserYaml(result.list); return result; }; - public jsonConfigToUserYaml(blocks: ConfigurationBlock[]): ConfigurationBlock[] { + public async jsonConfigToUserYaml(blocks: ConfigurationBlock[]): Promise<ConfigurationBlock[]> { + const yaml = await import('js-yaml'); // configuration_blocks yaml, JS cant read YAML so we parse it into JS, // because beats flattens all fields, and we need more structure. // we take tagConfigs, grab the config that applies here, render what we can into @@ -73,7 +73,8 @@ export class ConfigBlocksLib { }); } - public userConfigsToJson(blocks: ConfigurationBlock[]): ConfigurationBlock[] { + public async userConfigsToJson(blocks: ConfigurationBlock[]): Promise<ConfigurationBlock[]> { + const yaml = await import('js-yaml'); // configurations is the JS representation of the config yaml, // so here we take that JS and convert it into a YAML string. // we do so while also flattening "other" into the flat yaml beats expect From 1d9a2a44925288be79279160965845be09870b4e Mon Sep 17 00:00:00 2001 From: Yuliia Naumenko <jo.naumenko@gmail.com> Date: Sun, 4 Oct 2020 15:32:05 -0700 Subject: [PATCH 126/128] [Alerting UI] Display a banner to users when some alerts have failures, added alert statuses column and filters (#79038) * Added ui for alert failures banner * Added UI for alerts statuses * Adjusted form * Added banned on the details page * Fixed failing intern. check and type checks * Added unit test for displaying alert error banner * Fixed type check * Fixed due to comments * Changes due to comments * Fixed due to comments * Fixed text on banners * Added i18n translations --- .../public/application/lib/alert_api.ts | 17 +- .../components/alert_details.test.tsx | 33 +++ .../components/alert_details.tsx | 35 +++ .../components/alert_status_filter.tsx | 105 ++++++++ .../components/alerts_list.test.tsx | 79 +++++- .../alerts_list/components/alerts_list.tsx | 242 ++++++++++++++++-- .../sections/alerts_list/translations.ts | 85 ++++++ .../triggers_actions_ui/public/types.ts | 2 +- 8 files changed, 557 insertions(+), 41 deletions(-) create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx create mode 100644 x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts index d5711a3e8c919a..6dfba82bdf5c97 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts @@ -11,13 +11,7 @@ import { fold } from 'fp-ts/lib/Either'; import { pick } from 'lodash'; import { alertStateSchema, AlertingFrameworkHealth } from '../../../../alerts/common'; import { BASE_ALERT_API_PATH } from '../constants'; -import { - Alert, - AlertType, - AlertWithoutId, - AlertTaskState, - AlertInstanceSummary, -} from '../../types'; +import { Alert, AlertType, AlertUpdates, AlertTaskState, AlertInstanceSummary } from '../../types'; export async function loadAlertTypes({ http }: { http: HttpSetup }): Promise<AlertType[]> { return await http.get(`${BASE_ALERT_API_PATH}/list_alert_types`); @@ -70,12 +64,14 @@ export async function loadAlerts({ searchText, typesFilter, actionTypesFilter, + alertStatusesFilter, }: { http: HttpSetup; page: { index: number; size: number }; searchText?: string; typesFilter?: string[]; actionTypesFilter?: string[]; + alertStatusesFilter?: string[]; }): Promise<{ page: number; perPage: number; @@ -97,6 +93,9 @@ export async function loadAlerts({ ].join('') ); } + if (alertStatusesFilter && alertStatusesFilter.length) { + filters.push(`alert.attributes.executionStatus.status:(${alertStatusesFilter.join(' or ')})`); + } return await http.get(`${BASE_ALERT_API_PATH}/_find`, { query: { page: page.index + 1, @@ -137,7 +136,7 @@ export async function createAlert({ }: { http: HttpSetup; alert: Omit< - AlertWithoutId, + AlertUpdates, 'createdBy' | 'updatedBy' | 'muteAll' | 'mutedInstanceIds' | 'executionStatus' >; }): Promise<Alert> { @@ -152,7 +151,7 @@ export async function updateAlert({ id, }: { http: HttpSetup; - alert: Pick<AlertWithoutId, 'throttle' | 'name' | 'tags' | 'schedule' | 'params' | 'actions'>; + alert: Pick<AlertUpdates, 'throttle' | 'name' | 'tags' | 'schedule' | 'params' | 'actions'>; id: string; }): Promise<Alert> { return await http.put(`${BASE_ALERT_API_PATH}/alert/${id}`, { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx index 5c9969221cfc35..51c3e030f44eb1 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.test.tsx @@ -15,6 +15,7 @@ import { EuiSwitch, EuiBetaBadge, EuiButtonEmpty, + EuiText, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ViewInApp } from './view_in_app'; @@ -142,6 +143,38 @@ describe('alert_details', () => { ).toBeTruthy(); }); + it('renders the alert error banner with error message, when alert status is an error', () => { + const alert = mockAlert({ + executionStatus: { + status: 'error', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: { + reason: 'unknown', + message: 'test', + }, + }, + }); + const alertType = { + id: '.noop', + name: 'No Op', + actionGroups: [{ id: 'default', name: 'Default' }], + actionVariables: { context: [], state: [], params: [] }, + defaultActionGroupId: 'default', + producer: ALERTS_FEATURE_ID, + authorizedConsumers, + }; + + expect( + shallow( + <AlertDetails alert={alert} alertType={alertType} actionTypes={[]} {...mockAlertApis} /> + ).containsMatchingElement( + <EuiText size="s" color="danger" data-test-subj="alertErrorMessageText"> + {'test'} + </EuiText> + ) + ).toBeTruthy(); + }); + describe('actions', () => { it('renders an alert action', () => { const alert = mockAlert({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx index 6ee7915e2be71f..42a25b399ddd35 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alert_details/components/alert_details.tsx @@ -24,6 +24,7 @@ import { EuiSpacer, EuiBetaBadge, EuiButtonEmpty, + EuiButton, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; @@ -42,6 +43,7 @@ import { PLUGIN } from '../../../constants/plugin'; import { AlertEdit } from '../../alert_form'; import { AlertsContextProvider } from '../../../context/alerts_context'; import { routeToAlertDetails } from '../../../constants'; +import { alertsErrorReasonTranslationsMapping } from '../../alerts_list/translations'; type AlertDetailsProps = { alert: Alert; @@ -105,11 +107,20 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ const [isEnabled, setIsEnabled] = useState<boolean>(alert.enabled); const [isMuted, setIsMuted] = useState<boolean>(alert.muteAll); const [editFlyoutVisible, setEditFlyoutVisibility] = useState<boolean>(false); + const [dissmissAlertErrors, setDissmissAlertErrors] = useState<boolean>(false); const setAlert = async () => { history.push(routeToAlertDetails.replace(`:alertId`, alert.id)); }; + const getAlertStatusErrorReasonText = () => { + if (alert.executionStatus.error && alert.executionStatus.error.reason) { + return alertsErrorReasonTranslationsMapping[alert.executionStatus.error.reason]; + } else { + return alertsErrorReasonTranslationsMapping.unknown; + } + }; + return ( <EuiPage> <EuiPageBody> @@ -275,6 +286,30 @@ export const AlertDetails: React.FunctionComponent<AlertDetailsProps> = ({ </EuiFlexGroup> </EuiFlexItem> </EuiFlexGroup> + {!dissmissAlertErrors && alert.executionStatus.status === 'error' ? ( + <EuiFlexGroup> + <EuiFlexItem> + <EuiCallOut + color="danger" + data-test-subj="alertErrorBanner" + size="s" + title={getAlertStatusErrorReasonText()} + iconType="alert" + > + <EuiText size="s" color="danger" data-test-subj="alertErrorMessageText"> + {alert.executionStatus.error?.message} + </EuiText> + <EuiSpacer size="s" /> + <EuiButton color="danger" onClick={() => setDissmissAlertErrors(true)}> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertDetails.dismissButtonTitle" + defaultMessage="Dismiss" + /> + </EuiButton> + </EuiCallOut> + </EuiFlexItem> + </EuiFlexGroup> + ) : null} <EuiFlexGroup> <EuiFlexItem> {alert.enabled ? ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx new file mode 100644 index 00000000000000..87e7a82cd8f233 --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alert_status_filter.tsx @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect, useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiFilterGroup, + EuiPopover, + EuiFilterButton, + EuiFilterSelectItem, + EuiHealth, +} from '@elastic/eui'; +import { + AlertExecutionStatuses, + AlertExecutionStatusValues, +} from '../../../../../../alerts/common'; +import { alertsStatusesTranslationsMapping } from '../translations'; + +interface AlertStatusFilterProps { + selectedStatuses: string[]; + onChange?: (selectedAlertStatusesIds: string[]) => void; +} + +export const AlertStatusFilter: React.FunctionComponent<AlertStatusFilterProps> = ({ + selectedStatuses, + onChange, +}: AlertStatusFilterProps) => { + const [selectedValues, setSelectedValues] = useState<string[]>(selectedStatuses); + const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false); + + useEffect(() => { + if (onChange) { + onChange(selectedValues); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedValues]); + + useEffect(() => { + setSelectedValues(selectedStatuses); + }, [selectedStatuses]); + + return ( + <EuiFilterGroup> + <EuiPopover + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + button={ + <EuiFilterButton + iconType="arrowDown" + hasActiveFilters={selectedValues.length > 0} + numActiveFilters={selectedValues.length} + numFilters={selectedValues.length} + onClick={() => setIsPopoverOpen(!isPopoverOpen)} + > + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.alertStatusFilterLabel" + defaultMessage="Status" + /> + </EuiFilterButton> + } + > + <div className="euiFilterSelect__items"> + {[...AlertExecutionStatusValues].sort().map((item: AlertExecutionStatuses) => { + const healthColor = getHealthColor(item); + return ( + <EuiFilterSelectItem + key={item} + style={{ textTransform: 'capitalize' }} + onClick={() => { + const isPreviouslyChecked = selectedValues.includes(item); + if (isPreviouslyChecked) { + setSelectedValues(selectedValues.filter((val) => val !== item)); + } else { + setSelectedValues(selectedValues.concat(item)); + } + }} + checked={selectedValues.includes(item) ? 'on' : undefined} + > + <EuiHealth color={healthColor}>{alertsStatusesTranslationsMapping[item]}</EuiHealth> + </EuiFilterSelectItem> + ); + })} + </div> + </EuiPopover> + </EuiFilterGroup> + ); +}; + +export function getHealthColor(status: AlertExecutionStatuses) { + switch (status) { + case 'active': + return 'primary'; + case 'error': + return 'danger'; + case 'ok': + return 'subdued'; + case 'pending': + return 'success'; + default: + return 'warning'; + } +} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx index ada881cb93d1ef..86b9afd9565f87 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.test.tsx @@ -150,7 +150,7 @@ describe('alerts_list component with items', () => { loadAlerts.mockResolvedValue({ page: 1, perPage: 10000, - total: 2, + total: 4, data: [ { id: '1', @@ -168,10 +168,59 @@ describe('alerts_list component with items', () => { throttle: '1m', muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'active', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, }, { id: '2', - name: 'test alert 2', + name: 'test alert ok', + tags: ['tag1'], + enabled: true, + alertTypeId: 'test_alert_type', + schedule: { interval: '5d' }, + actions: [], + params: { name: 'test alert type name' }, + scheduledTaskId: null, + createdBy: null, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'ok', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, + }, + { + id: '3', + name: 'test alert pending', + tags: ['tag1'], + enabled: true, + alertTypeId: 'test_alert_type', + schedule: { interval: '5d' }, + actions: [], + params: { name: 'test alert type name' }, + scheduledTaskId: null, + createdBy: null, + updatedBy: null, + apiKeyOwner: null, + throttle: '1m', + muteAll: false, + mutedInstanceIds: [], + executionStatus: { + status: 'pending', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, + }, + { + id: '4', + name: 'test alert error', tags: ['tag1'], enabled: true, alertTypeId: 'test_alert_type', @@ -185,6 +234,14 @@ describe('alerts_list component with items', () => { throttle: '1m', muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'error', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: { + reason: 'unknown', + message: 'test', + }, + }, }, ], }); @@ -245,7 +302,13 @@ describe('alerts_list component with items', () => { it('renders table of alerts', async () => { await setup(); expect(wrapper.find('EuiBasicTable')).toHaveLength(1); - expect(wrapper.find('EuiTableRow')).toHaveLength(2); + expect(wrapper.find('EuiTableRow')).toHaveLength(4); + expect(wrapper.find('[data-test-subj="alertsTableCell-status"]').length).toBeGreaterThan(0); + expect(wrapper.find('[data-test-subj="alertStatus-active"]').length).toBeGreaterThan(0); + expect(wrapper.find('[data-test-subj="alertStatus-error"]').length).toBeGreaterThan(0); + expect(wrapper.find('[data-test-subj="alertStatus-ok"]').length).toBeGreaterThan(0); + expect(wrapper.find('[data-test-subj="alertStatus-pending"]').length).toBeGreaterThan(0); + expect(wrapper.find('[data-test-subj="alertStatus-unknown"]').length).toBe(0); }); }); @@ -351,6 +414,11 @@ describe('alerts_list with show only capability', () => { throttle: '1m', muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'active', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, }, { id: '2', @@ -368,6 +436,11 @@ describe('alerts_list with show only capability', () => { throttle: '1m', muteAll: false, mutedInstanceIds: [], + executionStatus: { + status: 'active', + lastExecutionDate: new Date('2020-08-20T19:23:38Z'), + error: null, + }, }, ], }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx index 07e0fd7ae19e55..95082bc6ca99f8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/components/alerts_list.tsx @@ -4,6 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ +/* eslint-disable react-hooks/exhaustive-deps */ + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React, { useEffect, useState, Fragment } from 'react'; @@ -19,6 +21,10 @@ import { EuiLink, EuiLoadingSpinner, EuiEmptyPrompt, + EuiCallOut, + EuiButtonEmpty, + EuiHealth, + EuiText, } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; @@ -32,14 +38,20 @@ import { AlertQuickEditButtonsWithApi as AlertQuickEditButtons } from '../../com import { CollapsedItemActionsWithApi as CollapsedItemActions } from './collapsed_item_actions'; import { TypeFilter } from './type_filter'; import { ActionTypeFilter } from './action_type_filter'; +import { AlertStatusFilter, getHealthColor } from './alert_status_filter'; import { loadAlerts, loadAlertTypes, deleteAlerts } from '../../../lib/alert_api'; import { loadActionTypes } from '../../../lib/action_connector_api'; import { hasExecuteActionsCapability } from '../../../lib/capabilities'; import { routeToAlertDetails, DEFAULT_SEARCH_PAGE_SIZE } from '../../../constants'; import { DeleteModalConfirmation } from '../../../components/delete_modal_confirmation'; import { EmptyPrompt } from '../../../components/prompts/empty_prompt'; -import { ALERTS_FEATURE_ID } from '../../../../../../alerts/common'; +import { + AlertExecutionStatus, + AlertExecutionStatusValues, + ALERTS_FEATURE_ID, +} from '../../../../../../alerts/common'; import { hasAllPrivilege } from '../../../lib/capabilities'; +import { alertsStatusesTranslationsMapping } from '../translations'; const ENTER_KEY = 13; @@ -77,7 +89,19 @@ export const AlertsList: React.FunctionComponent = () => { const [inputText, setInputText] = useState<string | undefined>(); const [typesFilter, setTypesFilter] = useState<string[]>([]); const [actionTypesFilter, setActionTypesFilter] = useState<string[]>([]); + const [alertStatusesFilter, setAlertStatusesFilter] = useState<string[]>([]); const [alertFlyoutVisible, setAlertFlyoutVisibility] = useState<boolean>(false); + const [dissmissAlertErrors, setDissmissAlertErrors] = useState<boolean>(false); + const [alertsStatusesTotal, setAlertsStatusesTotal] = useState<Record<string, number>>( + AlertExecutionStatusValues.reduce( + (prev: Record<string, number>, status: string) => + ({ + ...prev, + [status]: 0, + } as Record<string, number>), + {} + ) + ); const [alertTypesState, setAlertTypesState] = useState<AlertTypeState>({ isLoading: false, isInitialized: false, @@ -92,13 +116,14 @@ export const AlertsList: React.FunctionComponent = () => { useEffect(() => { loadAlertsData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [alertTypesState, page, searchText]); - - useEffect(() => { - loadAlertsData(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [JSON.stringify(typesFilter), JSON.stringify(actionTypesFilter)]); + }, [ + alertTypesState, + page, + searchText, + JSON.stringify(typesFilter), + JSON.stringify(actionTypesFilter), + JSON.stringify(alertStatusesFilter), + ]); useEffect(() => { (async () => { @@ -120,7 +145,6 @@ export const AlertsList: React.FunctionComponent = () => { setAlertTypesState({ ...alertTypesState, isLoading: false }); } })(); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { @@ -137,7 +161,6 @@ export const AlertsList: React.FunctionComponent = () => { }); } })(); - // eslint-disable-next-line react-hooks/exhaustive-deps }, []); async function loadAlertsData() { @@ -151,7 +174,9 @@ export const AlertsList: React.FunctionComponent = () => { searchText, typesFilter, actionTypesFilter, + alertStatusesFilter, }); + await loadAlertsTotalStatuses(); setAlertsState({ isLoading: false, data: alertsResponse.data, @@ -175,7 +200,52 @@ export const AlertsList: React.FunctionComponent = () => { } } + async function loadAlertsTotalStatuses() { + let alertsStatuses = {}; + try { + AlertExecutionStatusValues.forEach(async (status: string) => { + const alertsTotalResponse = await loadAlerts({ + http, + page: { index: 0, size: 0 }, + searchText, + typesFilter, + actionTypesFilter, + alertStatusesFilter: [status], + }); + setAlertsStatusesTotal({ ...alertsStatuses, [status]: alertsTotalResponse.total }); + alertsStatuses = { ...alertsStatuses, [status]: alertsTotalResponse.total }; + }); + } catch (e) { + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.unableToLoadAlertsStatusesInfoMessage', + { + defaultMessage: 'Unable to load alert statuses info', + } + ), + }); + } + } + const alertsTableColumns = [ + { + field: 'executionStatus', + name: i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertsListTable.columns.statusTitle', + { defaultMessage: 'Status' } + ), + sortable: false, + truncateText: false, + 'data-test-subj': 'alertsTableCell-status', + render: (executionStatus: AlertExecutionStatus) => { + const healthColor = getHealthColor(executionStatus.status); + return ( + <EuiHealth data-test-subj={`alertStatus-${executionStatus.status}`} color={healthColor}> + {alertsStatusesTranslationsMapping[executionStatus.status]} + </EuiHealth> + ); + }, + }, { field: 'name', name: i18n.translate( @@ -280,24 +350,13 @@ export const AlertsList: React.FunctionComponent = () => { actionTypes={actionTypes} onChange={(ids: string[]) => setActionTypesFilter(ids)} />, + <AlertStatusFilter + key="alert-status-filter" + selectedStatuses={alertStatusesFilter} + onChange={(ids: string[]) => setAlertStatusesFilter(ids)} + />, ]; - if (authorizedToCreateAnyAlerts) { - toolsRight.push( - <EuiButton - key="create-alert" - data-test-subj="createAlertButton" - fill - onClick={() => setAlertFlyoutVisibility(true)} - > - <FormattedMessage - id="xpack.triggersActionsUI.sections.alertsList.addActionButtonLabel" - defaultMessage="Create alert" - /> - </EuiButton> - ); - } - const authorizedToModifySelectedAlerts = selectedIds.length ? filterAlertsById(alertsState.data, selectedIds).every((selectedAlert) => hasAllPrivilege(selectedAlert, alertTypesState.data.get(selectedAlert.alertTypeId)) @@ -326,6 +385,21 @@ export const AlertsList: React.FunctionComponent = () => { </BulkOperationPopover> </EuiFlexItem> )} + {authorizedToCreateAnyAlerts ? ( + <EuiFlexItem grow={false}> + <EuiButton + key="create-alert" + data-test-subj="createAlertButton" + fill + onClick={() => setAlertFlyoutVisibility(true)} + > + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.addActionButtonLabel" + defaultMessage="Create alert" + /> + </EuiButton> + </EuiFlexItem> + ) : null} <EuiFlexItem> <EuiFieldText fullWidth @@ -353,7 +427,118 @@ export const AlertsList: React.FunctionComponent = () => { </EuiFlexGroup> </EuiFlexItem> </EuiFlexGroup> - + <EuiSpacer size="m" /> + {!dissmissAlertErrors && alertsStatusesTotal.error > 0 ? ( + <EuiFlexGroup> + <EuiFlexItem> + <EuiCallOut + color="danger" + size="s" + title={ + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.attentionBannerTitle" + defaultMessage="Error found in {totalStausesError} {totalStausesError, plural, one {{singleTitle}} other {# {multipleTitle}}}." + values={{ + totalStausesError: alertsStatusesTotal.error, + singleTitle: 'alert', + multipleTitle: 'alerts', + }} + /> + } + iconType="alert" + > + <EuiButton + type="primary" + size="s" + color="danger" + onClick={() => setAlertStatusesFilter(['error'])} + > + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.viewBunnerButtonLabel" + defaultMessage="View" + /> + </EuiButton> + <EuiButtonEmpty color="danger" onClick={() => setDissmissAlertErrors(true)}> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.dismissBunnerButtonLabel" + defaultMessage="Dismiss" + /> + </EuiButtonEmpty> + </EuiCallOut> + </EuiFlexItem> + </EuiFlexGroup> + ) : null} + <EuiSpacer size="m" /> + <EuiFlexGroup> + <EuiFlexItem grow={false}> + <EuiText size="s" color="subdued"> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.totalItemsCountDescription" + defaultMessage="Showing: {pageSize} of {totalItemCount} alerts." + values={{ + totalItemCount: alertsState.totalItemCount, + pageSize: alertsState.data.length, + }} + /> + </EuiText> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiHealth color="primary"> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.totalStausesActiveDescription" + defaultMessage="Active: {totalStausesActive}" + data-test-subj="totalStausesActive" + values={{ + totalStausesActive: alertsStatusesTotal.active, + }} + /> + </EuiHealth> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiHealth color="danger"> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.totalStausesErrorDescription" + data-test-subj="totalStausesError" + defaultMessage="Error: {totalStausesError}" + values={{ totalStausesError: alertsStatusesTotal.error }} + /> + </EuiHealth> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiHealth color="subdued"> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.totalStausesOkDescription" + data-test-subj="totalStausesOk" + defaultMessage="Ok: {totalStausesOk}" + values={{ totalStausesOk: alertsStatusesTotal.ok }} + /> + </EuiHealth> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiHealth color="success"> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.totalStausesPendingDescription" + data-test-subj="totalStausesPending" + defaultMessage="Pending: {totalStausesPending}" + values={{ + totalStausesPending: alertsStatusesTotal.pending, + }} + /> + </EuiHealth> + </EuiFlexItem> + <EuiFlexItem grow={false}> + <EuiHealth color="warning"> + <FormattedMessage + id="xpack.triggersActionsUI.sections.alertsList.totalStausesUnknownDescription" + data-test-subj="totalStausesUnknown" + defaultMessage="Unknown: {totalStausesUnknown}" + values={{ + totalStausesUnknown: alertsStatusesTotal.unknown, + }} + /> + </EuiHealth> + </EuiFlexItem> + </EuiFlexGroup> {/* Large to remain consistent with ActionsList table spacing */} <EuiSpacer size="l" /> @@ -402,7 +587,8 @@ export const AlertsList: React.FunctionComponent = () => { const isFilterApplied = !( isEmpty(searchText) && isEmpty(typesFilter) && - isEmpty(actionTypesFilter) + isEmpty(actionTypesFilter) && + isEmpty(alertStatusesFilter) ); return ( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts new file mode 100644 index 00000000000000..dbcf2d6854af5e --- /dev/null +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_list/translations.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const ALERT_STATUS_OK = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertStatusOk', + { + defaultMessage: 'Ok', + } +); + +export const ALERT_STATUS_ACTIVE = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertStatusActive', + { + defaultMessage: 'Active', + } +); + +export const ALERT_STATUS_ERROR = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertStatusError', + { + defaultMessage: 'Error', + } +); + +export const ALERT_STATUS_PENDING = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertStatusPending', + { + defaultMessage: 'Pending', + } +); + +export const ALERT_STATUS_UNKNOWN = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertStatusUnknown', + { + defaultMessage: 'Unknown', + } +); + +export const alertsStatusesTranslationsMapping = { + ok: ALERT_STATUS_OK, + active: ALERT_STATUS_ACTIVE, + error: ALERT_STATUS_ERROR, + pending: ALERT_STATUS_PENDING, + unknown: ALERT_STATUS_UNKNOWN, +}; + +export const ALERT_ERROR_UNKNOWN_REASON = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertErrorReasonUnknown', + { + defaultMessage: 'An error occurred for unknown reasons.', + } +); + +export const ALERT_ERROR_READING_REASON = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertErrorReasonReading', + { + defaultMessage: 'An error occurred when reading the alert.', + } +); + +export const ALERT_ERROR_DECRYPTING_REASON = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertErrorReasonDecrypting', + { + defaultMessage: 'An error occurred when decrypting the alert.', + } +); + +export const ALERT_ERROR_EXECUTION_REASON = i18n.translate( + 'xpack.triggersActionsUI.sections.alertsList.alertErrorReasonRunning', + { + defaultMessage: 'An error occurred when running the alert.', + } +); + +export const alertsErrorReasonTranslationsMapping = { + read: ALERT_ERROR_READING_REASON, + decrypt: ALERT_ERROR_DECRYPTING_REASON, + execute: ALERT_ERROR_EXECUTION_REASON, + unknown: ALERT_ERROR_UNKNOWN_REASON, +}; diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index c551746fdec0c0..148facdee248d0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -147,7 +147,7 @@ export interface AlertType { export type SanitizedAlertType = Omit<AlertType, 'apiKey'>; -export type AlertWithoutId = Omit<Alert, 'id'>; +export type AlertUpdates = Omit<Alert, 'id' | 'executionStatus'>; export interface AlertTableItem extends Alert { alertType: AlertType['name']; From caa5da26f07bdce5dbc8c8f8928d28336af09640 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Sun, 4 Oct 2020 23:28:49 -0400 Subject: [PATCH 127/128] Add support for sharing saved objects to all spaces (#76132) --- docs/api/saved-objects/bulk_create.asciidoc | 4 + docs/api/saved-objects/create.asciidoc | 4 + docs/api/saved-objects/delete.asciidoc | 8 + ...n-core-public.savedobjectsclient.delete.md | 2 +- ...a-plugin-core-public.savedobjectsclient.md | 2 +- ...ore-server.savedobjectsbulkcreateobject.md | 1 + ...savedobjectsbulkcreateobject.namespaces.md | 15 + ...n-core-server.savedobjectscreateoptions.md | 1 + ...er.savedobjectscreateoptions.namespaces.md | 15 + ...-server.savedobjectsdeleteoptions.force.md | 13 + ...n-core-server.savedobjectsdeleteoptions.md | 1 + src/core/public/public.api.md | 3 +- .../saved_objects_client.test.ts | 4 +- .../saved_objects/saved_objects_client.ts | 18 +- .../saved_objects/routes/bulk_create.ts | 1 + .../server/saved_objects/routes/create.ts | 5 +- .../server/saved_objects/routes/delete.ts | 6 +- .../routes/integration_tests/delete.test.ts | 15 +- .../service/lib/repository.test.js | 232 +++++++++++-- .../saved_objects/service/lib/repository.ts | 127 ++++--- .../lib/search_dsl/query_params.test.ts | 4 +- .../service/lib/search_dsl/query_params.ts | 7 +- .../server/saved_objects/service/lib/utils.ts | 1 + .../service/saved_objects_client.ts | 16 + src/core/server/server.api.md | 3 + .../saved_objects_table.test.tsx | 6 +- .../objects_table/saved_objects_table.tsx | 2 +- x-pack/plugins/security/common/constants.ts | 10 + .../__snapshots__/saved_object.test.ts.snap | 25 -- .../actions/saved_object.test.ts | 8 +- .../authorization/actions/saved_object.ts | 2 +- .../check_saved_objects_privileges.test.ts | 47 +++ .../check_saved_objects_privileges.ts | 41 ++- .../feature_privilege_builder/saved_object.ts | 9 +- .../privileges/privileges.test.ts | 35 ++ ...ecure_saved_objects_client_wrapper.test.ts | 135 +++++--- .../secure_saved_objects_client_wrapper.ts | 55 +-- x-pack/plugins/spaces/common/constants.ts | 10 + .../public/lib/documentation_links.test.ts | 25 ++ .../spaces/public/lib/documentation_links.ts | 19 + x-pack/plugins/spaces/public/lib/index.ts | 7 + x-pack/plugins/spaces/public/plugin.tsx | 5 +- .../components/no_spaces_available.tsx | 47 +++ .../components/selectable_spaces_control.tsx | 160 +++++++-- .../components/share_mode_control.scss | 3 + .../components/share_mode_control.tsx | 150 ++++++++ .../components/share_to_space_flyout.test.tsx | 49 ++- .../components/share_to_space_flyout.tsx | 158 +++++---- .../components/share_to_space_form.tsx | 63 ++-- .../share_saved_objects_to_space_action.tsx | 7 +- ...are_saved_objects_to_space_column.test.tsx | 206 +++++++++++ .../share_saved_objects_to_space_column.tsx | 117 ++++--- ...are_saved_objects_to_space_service.test.ts | 3 +- .../share_saved_objects_to_space_service.ts | 17 +- .../share_saved_objects_to_space/types.ts | 2 +- .../spaces_manager/spaces_manager.mock.ts | 1 + .../spaces_manager/spaces_manager.test.ts | 20 ++ .../public/spaces_manager/spaces_manager.ts | 6 + .../lib/spaces_client/spaces_client.mock.ts | 1 - .../lib/spaces_client/spaces_client.test.ts | 125 ------- .../server/lib/spaces_client/spaces_client.ts | 15 - x-pack/plugins/spaces/server/plugin.ts | 1 + .../__fixtures__/create_mock_so_service.ts | 73 ++-- .../routes/api/external/copy_to_space.test.ts | 4 +- .../server/routes/api/external/delete.test.ts | 1 + .../server/routes/api/external/get.test.ts | 1 + .../routes/api/external/get_all.test.ts | 1 + .../server/routes/api/external/index.ts | 8 +- .../server/routes/api/external/post.test.ts | 1 + .../server/routes/api/external/put.test.ts | 1 + .../routes/api/external/share_add_spaces.ts | 62 ---- .../api/external/share_remove_spaces.ts | 62 ---- .../api/external/share_to_space.test.ts | 325 ++++++++++++++++++ .../routes/api/external/share_to_space.ts | 100 ++++++ .../spaces_saved_objects_client.ts | 3 +- .../copy_saved_objects_to_space_page.ts | 6 +- .../saved_objects/spaces/data.json | 17 + .../common/lib/saved_object_test_cases.ts | 7 +- .../common/lib/saved_object_test_utils.ts | 7 +- .../common/lib/spaces.ts | 2 + .../common/suites/bulk_create.ts | 31 +- .../common/suites/create.ts | 40 ++- .../common/suites/delete.ts | 20 +- .../common/suites/export.ts | 19 +- .../common/suites/find.ts | 7 +- .../security_and_spaces/apis/bulk_create.ts | 50 ++- .../security_and_spaces/apis/bulk_get.ts | 1 + .../security_and_spaces/apis/bulk_update.ts | 2 + .../security_and_spaces/apis/create.ts | 30 +- .../security_and_spaces/apis/delete.ts | 12 +- .../security_and_spaces/apis/get.ts | 1 + .../security_and_spaces/apis/import.ts | 1 + .../apis/resolve_import_errors.ts | 1 + .../security_and_spaces/apis/update.ts | 1 + .../security_only/apis/bulk_create.ts | 3 + .../security_only/apis/bulk_get.ts | 1 + .../security_only/apis/bulk_update.ts | 2 + .../security_only/apis/create.ts | 3 + .../security_only/apis/delete.ts | 9 +- .../security_only/apis/get.ts | 1 + .../security_only/apis/import.ts | 1 + .../apis/resolve_import_errors.ts | 1 + .../security_only/apis/update.ts | 1 + .../spaces_only/apis/bulk_create.ts | 20 +- .../spaces_only/apis/bulk_get.ts | 1 + .../spaces_only/apis/bulk_update.ts | 2 + .../spaces_only/apis/create.ts | 3 + .../spaces_only/apis/delete.ts | 12 +- .../spaces_only/apis/get.ts | 1 + .../spaces_only/apis/import.ts | 1 + .../spaces_only/apis/resolve_import_errors.ts | 1 + .../spaces_only/apis/update.ts | 1 + .../saved_objects/spaces/data.json | 19 +- .../common/lib/saved_object_test_cases.ts | 6 +- .../common/suites/copy_to_space.ts | 2 +- .../suites/resolve_copy_to_space_conflicts.ts | 2 +- .../common/suites/share_add.ts | 21 +- .../common/suites/share_remove.ts | 16 +- .../security_and_spaces/apis/share_add.ts | 47 ++- .../security_and_spaces/apis/share_remove.ts | 14 +- .../spaces_only/apis/share_add.ts | 33 +- .../spaces_only/apis/share_remove.ts | 18 +- 122 files changed, 2371 insertions(+), 868 deletions(-) create mode 100644 docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md create mode 100644 docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.force.md delete mode 100644 x-pack/plugins/security/server/authorization/actions/__snapshots__/saved_object.test.ts.snap create mode 100644 x-pack/plugins/spaces/public/lib/documentation_links.test.ts create mode 100644 x-pack/plugins/spaces/public/lib/documentation_links.ts create mode 100644 x-pack/plugins/spaces/public/lib/index.ts create mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.tsx create mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.scss create mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx create mode 100644 x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.test.tsx delete mode 100644 x-pack/plugins/spaces/server/routes/api/external/share_add_spaces.ts delete mode 100644 x-pack/plugins/spaces/server/routes/api/external/share_remove_spaces.ts create mode 100644 x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts create mode 100644 x-pack/plugins/spaces/server/routes/api/external/share_to_space.ts diff --git a/docs/api/saved-objects/bulk_create.asciidoc b/docs/api/saved-objects/bulk_create.asciidoc index 4f572b49ee5ff6..5149cef3d30c6c 100644 --- a/docs/api/saved-objects/bulk_create.asciidoc +++ b/docs/api/saved-objects/bulk_create.asciidoc @@ -41,6 +41,10 @@ experimental[] Create multiple {kib} saved objects. `references`:: (Optional, array) Objects with `name`, `id`, and `type` properties that describe the other saved objects in the referenced object. To refer to the other saved object, use `name` in the attributes. Never use `id` to refer to the other saved object. `id` can be automatically updated during migrations, import, or export. +`namespaces`:: + (Optional, string array) Identifiers for the <<xpack-spaces,spaces>> in which this object should be created. If this is not provided, the + object will be created in the current space. + `version`:: (Optional, number) Specifies the version. diff --git a/docs/api/saved-objects/create.asciidoc b/docs/api/saved-objects/create.asciidoc index e6f3301bfea2b6..c8cd9c8bfca277 100644 --- a/docs/api/saved-objects/create.asciidoc +++ b/docs/api/saved-objects/create.asciidoc @@ -46,6 +46,10 @@ any data that you send to the API is properly formed. `references`:: (Optional, array) Objects with `name`, `id`, and `type` properties that describe the other saved objects that this object references. Use `name` in attributes to refer to the other saved object, but never the `id`, which can update automatically during migrations or import/export. +`namespaces`:: + (Optional, string array) Identifiers for the <<xpack-spaces,spaces>> in which this object should be created. If this is not provided, the + object will be created in the current space. + [[saved-objects-api-create-request-codes]] ==== Response code diff --git a/docs/api/saved-objects/delete.asciidoc b/docs/api/saved-objects/delete.asciidoc index af587b0e7af10b..9c342cb4d843e9 100644 --- a/docs/api/saved-objects/delete.asciidoc +++ b/docs/api/saved-objects/delete.asciidoc @@ -27,6 +27,14 @@ WARNING: Once you delete a saved object, _it cannot be recovered_. `id`:: (Required, string) The object ID that you want to remove. +[[saved-objects-api-delete-query-params]] +==== Query parameters + +`force`:: + (Optional, boolean) When true, forces an object to be deleted if it exists in multiple namespaces. ++ +TIP: Use this if you attempted to delete an object and received an HTTP 400 error with the following message: _"Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway"_ + [[saved-objects-api-delete-response-codes]] ==== Response code diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.delete.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.delete.md index 3b5f5630e80600..3a5dcb51e2c42a 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.delete.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.delete.md @@ -9,5 +9,5 @@ Deletes an object <b>Signature:</b> ```typescript -delete: (type: string, id: string) => ReturnType<SavedObjectsApi['delete']>; +delete: (type: string, id: string, options?: SavedObjectsDeleteOptions | undefined) => ReturnType<SavedObjectsApi['delete']>; ``` diff --git a/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.md b/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.md index 904b9cce09d4e8..6e53b169b8bed3 100644 --- a/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.md +++ b/docs/development/core/public/kibana-plugin-core-public.savedobjectsclient.md @@ -23,7 +23,7 @@ The constructor for this class is marked as internal. Third-party code should no | [bulkCreate](./kibana-plugin-core-public.savedobjectsclient.bulkcreate.md) | | <code>(objects?: SavedObjectsBulkCreateObject[], options?: SavedObjectsBulkCreateOptions) => Promise<SavedObjectsBatchResponse<unknown>></code> | Creates multiple documents at once | | [bulkGet](./kibana-plugin-core-public.savedobjectsclient.bulkget.md) | | <code>(objects?: Array<{</code><br/><code> id: string;</code><br/><code> type: string;</code><br/><code> }>) => Promise<SavedObjectsBatchResponse<unknown>></code> | Returns an array of objects by id | | [create](./kibana-plugin-core-public.savedobjectsclient.create.md) | | <code><T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>></code> | Persists an object | -| [delete](./kibana-plugin-core-public.savedobjectsclient.delete.md) | | <code>(type: string, id: string) => ReturnType<SavedObjectsApi['delete']></code> | Deletes an object | +| [delete](./kibana-plugin-core-public.savedobjectsclient.delete.md) | | <code>(type: string, id: string, options?: SavedObjectsDeleteOptions | undefined) => ReturnType<SavedObjectsApi['delete']></code> | Deletes an object | | [find](./kibana-plugin-core-public.savedobjectsclient.find.md) | | <code><T = unknown>(options: SavedObjectsFindOptions) => Promise<SavedObjectsFindResponsePublic<T>></code> | Search for objects | | [get](./kibana-plugin-core-public.savedobjectsclient.get.md) | | <code><T = unknown>(type: string, id: string) => Promise<SimpleSavedObject<T>></code> | Fetches a single object | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md index 019d30570ab36b..aabbfeeff75af4 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.md @@ -18,6 +18,7 @@ export interface SavedObjectsBulkCreateObject<T = unknown> | [attributes](./kibana-plugin-core-server.savedobjectsbulkcreateobject.attributes.md) | <code>T</code> | | | [id](./kibana-plugin-core-server.savedobjectsbulkcreateobject.id.md) | <code>string</code> | | | [migrationVersion](./kibana-plugin-core-server.savedobjectsbulkcreateobject.migrationversion.md) | <code>SavedObjectsMigrationVersion</code> | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | +| [namespaces](./kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md) | <code>string[]</code> | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md)<!-- -->.<!-- -->Note: this can only be used for multi-namespace object types. | | [originId](./kibana-plugin-core-server.savedobjectsbulkcreateobject.originid.md) | <code>string</code> | Optional ID of the original saved object, if this object's <code>id</code> was regenerated | | [references](./kibana-plugin-core-server.savedobjectsbulkcreateobject.references.md) | <code>SavedObjectReference[]</code> | | | [type](./kibana-plugin-core-server.savedobjectsbulkcreateobject.type.md) | <code>string</code> | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md new file mode 100644 index 00000000000000..7db1c53c67b527 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md @@ -0,0 +1,15 @@ +<!-- Do not edit this file. It is automatically generated by API Documenter. --> + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsBulkCreateObject](./kibana-plugin-core-server.savedobjectsbulkcreateobject.md) > [namespaces](./kibana-plugin-core-server.savedobjectsbulkcreateobject.namespaces.md) + +## SavedObjectsBulkCreateObject.namespaces property + +Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md)<!-- -->. + +Note: this can only be used for multi-namespace object types. + +<b>Signature:</b> + +```typescript +namespaces?: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md index d936829443753f..63aebf6c5e7918 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.md @@ -17,6 +17,7 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions | --- | --- | --- | | [id](./kibana-plugin-core-server.savedobjectscreateoptions.id.md) | <code>string</code> | (not recommended) Specify an id for the document | | [migrationVersion](./kibana-plugin-core-server.savedobjectscreateoptions.migrationversion.md) | <code>SavedObjectsMigrationVersion</code> | Information about the migrations that have been applied to this SavedObject. When Kibana starts up, KibanaMigrator detects outdated documents and migrates them based on this value. For each migration that has been applied, the plugin's name is used as a key and the latest migration version as the value. | +| [namespaces](./kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md) | <code>string[]</code> | Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md)<!-- -->.<!-- -->Note: this can only be used for multi-namespace object types. | | [originId](./kibana-plugin-core-server.savedobjectscreateoptions.originid.md) | <code>string</code> | Optional ID of the original saved object, if this object's <code>id</code> was regenerated | | [overwrite](./kibana-plugin-core-server.savedobjectscreateoptions.overwrite.md) | <code>boolean</code> | Overwrite existing documents (defaults to false) | | [references](./kibana-plugin-core-server.savedobjectscreateoptions.references.md) | <code>SavedObjectReference[]</code> | | diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md new file mode 100644 index 00000000000000..67804999dfd442 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md @@ -0,0 +1,15 @@ +<!-- Do not edit this file. It is automatically generated by API Documenter. --> + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md) > [namespaces](./kibana-plugin-core-server.savedobjectscreateoptions.namespaces.md) + +## SavedObjectsCreateOptions.namespaces property + +Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in [SavedObjectsCreateOptions](./kibana-plugin-core-server.savedobjectscreateoptions.md)<!-- -->. + +Note: this can only be used for multi-namespace object types. + +<b>Signature:</b> + +```typescript +namespaces?: string[]; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.force.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.force.md new file mode 100644 index 00000000000000..f869d1f863a9f6 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.force.md @@ -0,0 +1,13 @@ +<!-- Do not edit this file. It is automatically generated by API Documenter. --> + +[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [SavedObjectsDeleteOptions](./kibana-plugin-core-server.savedobjectsdeleteoptions.md) > [force](./kibana-plugin-core-server.savedobjectsdeleteoptions.force.md) + +## SavedObjectsDeleteOptions.force property + +Force deletion of an object that exists in multiple namespaces + +<b>Signature:</b> + +```typescript +force?: boolean; +``` diff --git a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.md b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.md index 760c30edcdfb52..245819e44d37d6 100644 --- a/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.md +++ b/docs/development/core/server/kibana-plugin-core-server.savedobjectsdeleteoptions.md @@ -15,5 +15,6 @@ export interface SavedObjectsDeleteOptions extends SavedObjectsBaseOptions | Property | Type | Description | | --- | --- | --- | +| [force](./kibana-plugin-core-server.savedobjectsdeleteoptions.force.md) | <code>boolean</code> | Force deletion of an object that exists in multiple namespaces | | [refresh](./kibana-plugin-core-server.savedobjectsdeleteoptions.refresh.md) | <code>MutatingOperationRefreshSetting</code> | The Elasticsearch Refresh setting for this operation | diff --git a/src/core/public/public.api.md b/src/core/public/public.api.md index 08491dc76cd275..f52d5b6fbd6a5d 100644 --- a/src/core/public/public.api.md +++ b/src/core/public/public.api.md @@ -1030,8 +1030,9 @@ export class SavedObjectsClient { }>) => Promise<SavedObjectsBatchResponse<unknown>>; bulkUpdate<T = unknown>(objects?: SavedObjectsBulkUpdateObject[]): Promise<SavedObjectsBatchResponse<unknown>>; create: <T = unknown>(type: string, attributes: T, options?: SavedObjectsCreateOptions) => Promise<SimpleSavedObject<T>>; + // Warning: (ae-forgotten-export) The symbol "SavedObjectsDeleteOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "SavedObjectsClientContract" needs to be exported by the entry point index.d.ts - delete: (type: string, id: string) => ReturnType<SavedObjectsClientContract_2['delete']>; + delete: (type: string, id: string, options?: SavedObjectsDeleteOptions | undefined) => ReturnType<SavedObjectsClientContract_2['delete']>; // Warning: (ae-forgotten-export) The symbol "SavedObjectsFindOptions" needs to be exported by the entry point index.d.ts find: <T = unknown>(options: SavedObjectsFindOptions_2) => Promise<SavedObjectsFindResponsePublic<T>>; get: <T = unknown>(type: string, id: string) => Promise<SimpleSavedObject<T>>; diff --git a/src/core/public/saved_objects/saved_objects_client.test.ts b/src/core/public/saved_objects/saved_objects_client.test.ts index 20824af38af0f6..fab651379ea6af 100644 --- a/src/core/public/saved_objects/saved_objects_client.test.ts +++ b/src/core/public/saved_objects/saved_objects_client.test.ts @@ -132,7 +132,9 @@ describe('SavedObjectsClient', () => { Object { "body": undefined, "method": "DELETE", - "query": undefined, + "query": Object { + "force": false, + }, }, ] `); diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 6a10eb44d9ca49..beed3e6fe0a18f 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -96,6 +96,12 @@ export interface SavedObjectsBatchResponse<T = unknown> { savedObjects: Array<SimpleSavedObject<T>>; } +/** @public */ +export interface SavedObjectsDeleteOptions { + /** Force deletion of an object that exists in multiple namespaces */ + force?: boolean; +} + /** * Return type of the Saved Objects `find()` method. * @@ -261,12 +267,20 @@ export class SavedObjectsClient { * @param id * @returns */ - public delete = (type: string, id: string): ReturnType<SavedObjectsApi['delete']> => { + public delete = ( + type: string, + id: string, + options?: SavedObjectsDeleteOptions + ): ReturnType<SavedObjectsApi['delete']> => { if (!type || !id) { return Promise.reject(new Error('requires type and id')); } - return this.savedObjectsFetch(this.getPath([type, id]), { method: 'DELETE' }); + const query = { + force: !!options?.force, + }; + + return this.savedObjectsFetch(this.getPath([type, id]), { method: 'DELETE', query }); }; /** diff --git a/src/core/server/saved_objects/routes/bulk_create.ts b/src/core/server/saved_objects/routes/bulk_create.ts index af1a7bd2af9b76..0f925d61ead98b 100644 --- a/src/core/server/saved_objects/routes/bulk_create.ts +++ b/src/core/server/saved_objects/routes/bulk_create.ts @@ -44,6 +44,7 @@ export const registerBulkCreateRoute = (router: IRouter) => { }) ) ), + namespaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })), }) ), }, diff --git a/src/core/server/saved_objects/routes/create.ts b/src/core/server/saved_objects/routes/create.ts index 6cf906a3b2895d..191dbfaa0dbf1d 100644 --- a/src/core/server/saved_objects/routes/create.ts +++ b/src/core/server/saved_objects/routes/create.ts @@ -44,15 +44,16 @@ export const registerCreateRoute = (router: IRouter) => { }) ) ), + namespaces: schema.maybe(schema.arrayOf(schema.string(), { minSize: 1 })), }), }, }, router.handleLegacyErrors(async (context, req, res) => { const { type, id } = req.params; const { overwrite } = req.query; - const { attributes, migrationVersion, references } = req.body; + const { attributes, migrationVersion, references, namespaces } = req.body; - const options = { id, overwrite, migrationVersion, references }; + const options = { id, overwrite, migrationVersion, references, namespaces }; const result = await context.core.savedObjects.client.create(type, attributes, options); return res.ok({ body: result }); }) diff --git a/src/core/server/saved_objects/routes/delete.ts b/src/core/server/saved_objects/routes/delete.ts index d1194553362122..d99397d2a050c6 100644 --- a/src/core/server/saved_objects/routes/delete.ts +++ b/src/core/server/saved_objects/routes/delete.ts @@ -29,11 +29,15 @@ export const registerDeleteRoute = (router: IRouter) => { type: schema.string(), id: schema.string(), }), + query: schema.object({ + force: schema.maybe(schema.boolean()), + }), }, }, router.handleLegacyErrors(async (context, req, res) => { const { type, id } = req.params; - const result = await context.core.savedObjects.client.delete(type, id); + const { force } = req.query; + const result = await context.core.savedObjects.client.delete(type, id, { force }); return res.ok({ body: result }); }) ); diff --git a/src/core/server/saved_objects/routes/integration_tests/delete.test.ts b/src/core/server/saved_objects/routes/integration_tests/delete.test.ts index a58f400ec3e1de..ff8642a34929fb 100644 --- a/src/core/server/saved_objects/routes/integration_tests/delete.test.ts +++ b/src/core/server/saved_objects/routes/integration_tests/delete.test.ts @@ -58,6 +58,19 @@ describe('DELETE /api/saved_objects/{type}/{id}', () => { .delete('/api/saved_objects/index-pattern/logstash-*') .expect(200); - expect(savedObjectsClient.delete).toHaveBeenCalledWith('index-pattern', 'logstash-*'); + expect(savedObjectsClient.delete).toHaveBeenCalledWith('index-pattern', 'logstash-*', { + force: undefined, + }); + }); + + it('can specify `force` option', async () => { + await supertest(httpSetup.server.listener) + .delete('/api/saved_objects/index-pattern/logstash-*') + .query({ force: true }) + .expect(200); + + expect(savedObjectsClient.delete).toHaveBeenCalledWith('index-pattern', 'logstash-*', { + force: true, + }); }); }); 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 0e72ad2fec06cd..10c7f143e52b92 100644 --- a/src/core/server/saved_objects/service/lib/repository.test.js +++ b/src/core/server/saved_objects/service/lib/repository.test.js @@ -20,6 +20,7 @@ import { SavedObjectsRepository } from './repository'; import * as getSearchDslNS from './search_dsl/search_dsl'; import { SavedObjectsErrorHelpers } from './errors'; +import { ALL_NAMESPACES_STRING } from './utils'; import { SavedObjectsSerializer } from '../../serialization'; import { encodeHitVersion } from '../../version'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; @@ -634,6 +635,32 @@ describe('SavedObjectsRepository', () => { await test(namespace); }); + it(`adds namespaces instead of namespace`, async () => { + const test = async (namespace) => { + const ns2 = 'bar-namespace'; + const ns3 = 'baz-namespace'; + const objects = [ + { ...obj1, type: MULTI_NAMESPACE_TYPE, namespaces: [ns2] }, + { ...obj2, type: MULTI_NAMESPACE_TYPE, namespaces: [ns3] }, + ]; + await bulkCreateSuccess(objects, { namespace, overwrite: true }); + const body = [ + expect.any(Object), + expect.objectContaining({ namespaces: [ns2] }), + expect.any(Object), + expect.objectContaining({ namespaces: [ns3] }), + ]; + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ body }), + expect.anything() + ); + client.bulk.mockClear(); + client.mget.mockClear(); + }; + await test(undefined); + await test(namespace); + }); + it(`doesn't add namespaces to request body for any types that are not multi-namespace`, async () => { const test = async (namespace) => { const objects = [obj1, { ...obj2, type: NAMESPACE_AGNOSTIC_TYPE }]; @@ -725,6 +752,40 @@ describe('SavedObjectsRepository', () => { }); }; + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.bulkCreate([obj3], { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + + it(`returns error when namespaces is used with a non-multi-namespace object`, async () => { + const test = async (objType) => { + const obj = { ...obj3, type: objType, namespaces: [] }; + await bulkCreateError( + obj, + undefined, + expectErrorResult( + obj, + createBadRequestError('"namespaces" can only be used on multi-namespace types') + ) + ); + }; + await test('dashboard'); + await test(NAMESPACE_AGNOSTIC_TYPE); + }); + + it(`throws when options.namespaces is used with a multi-namespace type and is empty`, async () => { + const obj = { ...obj3, type: MULTI_NAMESPACE_TYPE, namespaces: [] }; + await bulkCreateError( + obj, + undefined, + expectErrorResult( + obj, + createBadRequestError('"namespaces" must be a non-empty array of strings') + ) + ); + }); + it(`returns error when type is invalid`, async () => { const obj = { ...obj3, type: 'unknownType' }; await bulkCreateError(obj, undefined, expectErrorInvalidType(obj)); @@ -1042,6 +1103,13 @@ describe('SavedObjectsRepository', () => { }); }; + it(`throws when options.namespace is '*'`, async () => { + const obj = { type: 'dashboard', id: 'three' }; + await expect( + savedObjectsRepository.bulkGet([obj], { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + it(`returns error when type is invalid`, async () => { const obj = { type: 'unknownType', id: 'three' }; await bulkGetErrorInvalidType([obj1, obj, obj2]); @@ -1467,6 +1535,12 @@ describe('SavedObjectsRepository', () => { }); }; + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.bulkUpdate([obj], { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + it(`returns error when type is invalid`, async () => { const _obj = { ...obj, type: 'unknownType' }; await bulkUpdateError(_obj, undefined, expectErrorNotFound(_obj)); @@ -1477,6 +1551,15 @@ describe('SavedObjectsRepository', () => { await bulkUpdateError(_obj, undefined, expectErrorNotFound(_obj)); }); + it(`returns error when object namespace is '*'`, async () => { + const _obj = { ...obj, namespace: '*' }; + await bulkUpdateError( + _obj, + undefined, + expectErrorResult(obj, createBadRequestError('"namespace" cannot be "*"')) + ); + }); + it(`returns error when ES is unable to find the document (mget)`, async () => { const _obj = { ...obj, type: MULTI_NAMESPACE_TYPE, found: false }; const mgetResponse = getMockMgetResponse([_obj]); @@ -1630,7 +1713,7 @@ describe('SavedObjectsRepository', () => { ); }; - describe('cluster calls', () => { + describe('client calls', () => { it(`doesn't make a cluster call if the objects array is empty`, async () => { await checkConflicts([]); expect(client.mget).not.toHaveBeenCalled(); @@ -1662,6 +1745,14 @@ describe('SavedObjectsRepository', () => { }); }); + describe('errors', () => { + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.checkConflicts([obj1], { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + }); + describe('returns', () => { it(`expected results`, async () => { const unknownTypeObj = { type: 'unknownType', id: 'three' }; @@ -1858,21 +1949,23 @@ describe('SavedObjectsRepository', () => { ); }); - it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { + it(`prepends namespace to the id and adds namespace to the body when providing namespace for single-namespace type`, async () => { await createSuccess(type, attributes, { id, namespace }); expect(client.create).toHaveBeenCalledWith( expect.objectContaining({ id: `${namespace}:${type}:${id}`, + body: expect.objectContaining({ namespace }), }), expect.anything() ); }); - it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { + it(`doesn't prepend namespace to the id or add namespace to the body when providing no namespace for single-namespace type`, async () => { await createSuccess(type, attributes, { id }); expect(client.create).toHaveBeenCalledWith( expect.objectContaining({ id: `${type}:${id}`, + body: expect.not.objectContaining({ namespace: expect.anything() }), }), expect.anything() ); @@ -1883,25 +1976,44 @@ describe('SavedObjectsRepository', () => { expect(client.create).toHaveBeenCalledWith( expect.objectContaining({ id: `${type}:${id}`, + body: expect.not.objectContaining({ namespace: expect.anything() }), }), expect.anything() ); }); - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id, namespace }); + it(`doesn't prepend namespace to the id and adds namespaces to body when using multi-namespace type`, async () => { + await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { id, namespace }); expect(client.create).toHaveBeenCalledWith( expect.objectContaining({ - id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, + id: `${MULTI_NAMESPACE_TYPE}:${id}`, + body: expect.objectContaining({ namespaces: [namespace] }), }), expect.anything() ); - client.create.mockClear(); + }); - await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { id, namespace }); + it(`adds namespaces instead of namespace`, async () => { + const options = { id, namespace, namespaces: ['bar-namespace', 'baz-namespace'] }; + await createSuccess(MULTI_NAMESPACE_TYPE, attributes, options); expect(client.create).toHaveBeenCalledWith( expect.objectContaining({ id: `${MULTI_NAMESPACE_TYPE}:${id}`, + body: expect.objectContaining({ namespaces: options.namespaces }), + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id or add namespace or namespaces fields when using namespace-agnostic type`, async () => { + await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id, namespace }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, + body: expect.not.objectContaining({ + namespace: expect.anything(), + namespaces: expect.anything(), + }), }), expect.anything() ); @@ -1909,6 +2021,32 @@ describe('SavedObjectsRepository', () => { }); describe('errors', () => { + it(`throws when options.namespaces is used with a non-multi-namespace object`, async () => { + const test = async (objType) => { + await expect( + savedObjectsRepository.create(objType, attributes, { namespaces: [namespace] }) + ).rejects.toThrowError( + createBadRequestError('"options.namespaces" can only be used on multi-namespace types') + ); + }; + await test('dashboard'); + await test(NAMESPACE_AGNOSTIC_TYPE); + }); + + it(`throws when options.namespaces is used with a multi-namespace type and is empty`, async () => { + await expect( + savedObjectsRepository.create(MULTI_NAMESPACE_TYPE, attributes, { namespaces: [] }) + ).rejects.toThrowError( + createBadRequestError('"options.namespaces" must be a non-empty array of strings') + ); + }); + + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.create(type, attributes, { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + it(`throws when type is invalid`, async () => { await expect(savedObjectsRepository.create('unknownType', attributes)).rejects.toThrowError( createUnsupportedTypeError('unknownType') @@ -2043,31 +2181,17 @@ describe('SavedObjectsRepository', () => { describe('client calls', () => { it(`should use the ES delete action when not using a multi-namespace type`, async () => { await deleteSuccess(type, id); + expect(client.get).not.toHaveBeenCalled(); expect(client.delete).toHaveBeenCalledTimes(1); }); - it(`should use ES get action then delete action when using a multi-namespace type with no namespaces remaining`, async () => { + it(`should use ES get action then delete action when using a multi-namespace type`, async () => { await deleteSuccess(MULTI_NAMESPACE_TYPE, id); expect(client.get).toHaveBeenCalledTimes(1); expect(client.delete).toHaveBeenCalledTimes(1); }); - it(`should use ES get action then update action when using a multi-namespace type with one or more namespaces remaining`, async () => { - const mockResponse = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id }); - mockResponse._source.namespaces = ['default', 'some-other-nameespace']; - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(mockResponse) - ); - client.update.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ result: 'updated' }) - ); - - await savedObjectsRepository.delete(MULTI_NAMESPACE_TYPE, id); - expect(client.get).toHaveBeenCalledTimes(1); - expect(client.update).toHaveBeenCalledTimes(1); - }); - - it(`includes the version of the existing document when type is multi-namespace`, async () => { + it(`includes the version of the existing document when using a multi-namespace type`, async () => { await deleteSuccess(MULTI_NAMESPACE_TYPE, id); const versionProperties = { if_seq_no: mockVersionProps._seq_no, @@ -2134,6 +2258,12 @@ describe('SavedObjectsRepository', () => { ); }; + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.delete(type, id, { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + it(`throws when type is invalid`, async () => { await expectNotFoundError('unknownType', id); expect(client.delete).not.toHaveBeenCalled(); @@ -2169,19 +2299,32 @@ describe('SavedObjectsRepository', () => { expect(client.get).toHaveBeenCalledTimes(1); }); - it(`throws when ES is unable to find the document during update`, async () => { - const mockResponse = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id }); - mockResponse._source.namespaces = ['default', 'some-other-nameespace']; + it(`throws when the type is multi-namespace and the document has multiple namespaces and the force option is not enabled`, async () => { + const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id, namespace }); + response._source.namespaces = [namespace, 'bar-namespace']; client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(mockResponse) + elasticsearchClientMock.createSuccessTransportRequestPromise(response) ); - client.update.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({}, { statusCode: 404 }) + await expect( + savedObjectsRepository.delete(MULTI_NAMESPACE_TYPE, id, { namespace }) + ).rejects.toThrowError( + 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' ); + expect(client.get).toHaveBeenCalledTimes(1); + }); - await expectNotFoundError(MULTI_NAMESPACE_TYPE, id); + it(`throws when the type is multi-namespace and the document has all namespaces and the force option is not enabled`, async () => { + const response = getMockGetResponse({ type: MULTI_NAMESPACE_TYPE, id, namespace }); + response._source.namespaces = ['*']; + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + await expect( + savedObjectsRepository.delete(MULTI_NAMESPACE_TYPE, id, { namespace }) + ).rejects.toThrowError( + 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' + ); expect(client.get).toHaveBeenCalledTimes(1); - expect(client.update).toHaveBeenCalledTimes(1); }); it(`throws when ES is unable to find the document during delete`, async () => { @@ -2267,7 +2410,7 @@ describe('SavedObjectsRepository', () => { }); describe('errors', () => { - it(`throws when namespace is not a string`, async () => { + it(`throws when namespace is not a string or is '*'`, async () => { const test = async (namespace) => { await expect(savedObjectsRepository.deleteByNamespace(namespace)).rejects.toThrowError( `namespace is required, and must be a string` @@ -2278,6 +2421,7 @@ describe('SavedObjectsRepository', () => { await test(['namespace']); await test(123); await test(true); + await test(ALL_NAMESPACES_STRING); }); }); @@ -2876,6 +3020,12 @@ describe('SavedObjectsRepository', () => { ); }; + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.get(type, id, { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + it(`throws when type is invalid`, async () => { await expectNotFoundError('unknownType', id); expect(client.get).not.toHaveBeenCalled(); @@ -3067,6 +3217,14 @@ describe('SavedObjectsRepository', () => { ); }; + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.incrementCounter(type, id, field, { + namespace: ALL_NAMESPACES_STRING, + }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + it(`throws when type is not a string`, async () => { const test = async (type) => { await expect( @@ -3723,6 +3881,12 @@ describe('SavedObjectsRepository', () => { ); }; + it(`throws when options.namespace is '*'`, async () => { + await expect( + savedObjectsRepository.update(type, id, attributes, { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestError('"options.namespace" cannot be "*"')); + }); + it(`throws when type is invalid`, async () => { await expectNotFoundError('unknownType', id); expect(client.update).not.toHaveBeenCalled(); diff --git a/src/core/server/saved_objects/service/lib/repository.ts b/src/core/server/saved_objects/service/lib/repository.ts index a83c86e5856289..bae96ceec27831 100644 --- a/src/core/server/saved_objects/service/lib/repository.ts +++ b/src/core/server/saved_objects/service/lib/repository.ts @@ -67,7 +67,12 @@ import { } from '../../types'; import { SavedObjectTypeRegistry } from '../../saved_objects_type_registry'; import { validateConvertFilterToKueryNode } from './filter_utils'; -import { FIND_DEFAULT_PAGE, FIND_DEFAULT_PER_PAGE, SavedObjectsUtils } from './utils'; +import { + ALL_NAMESPACES_STRING, + FIND_DEFAULT_PAGE, + FIND_DEFAULT_PER_PAGE, + SavedObjectsUtils, +} from './utils'; // BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository // so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. @@ -225,10 +230,23 @@ export class SavedObjectsRepository { references = [], refresh = DEFAULT_REFRESH_SETTING, originId, + namespaces, version, } = options; const namespace = normalizeNamespace(options.namespace); + if (namespaces) { + if (!this._registry.isMultiNamespace(type)) { + throw SavedObjectsErrorHelpers.createBadRequestError( + '"options.namespaces" can only be used on multi-namespace types' + ); + } else if (!namespaces.length) { + throw SavedObjectsErrorHelpers.createBadRequestError( + '"options.namespaces" must be a non-empty array of strings' + ); + } + } + if (!this._allowedTypes.includes(type)) { throw SavedObjectsErrorHelpers.createUnsupportedTypeError(type); } @@ -242,9 +260,11 @@ export class SavedObjectsRepository { } else if (this._registry.isMultiNamespace(type)) { if (id && overwrite) { // we will overwrite a multi-namespace saved object if it exists; if that happens, ensure we preserve its included namespaces - savedObjectNamespaces = await this.preflightGetNamespaces(type, id, namespace); + // note: this check throws an error if the object is found but does not exist in this namespace + const existingNamespaces = await this.preflightGetNamespaces(type, id, namespace); + savedObjectNamespaces = namespaces || existingNamespaces; } else { - savedObjectNamespaces = getSavedObjectNamespaces(namespace); + savedObjectNamespaces = namespaces || getSavedObjectNamespaces(namespace); } } @@ -300,14 +320,25 @@ export class SavedObjectsRepository { let bulkGetRequestIndexCounter = 0; const expectedResults: Either[] = objects.map((object) => { + let error: DecoratedError | undefined; if (!this._allowedTypes.includes(object.type)) { + error = SavedObjectsErrorHelpers.createUnsupportedTypeError(object.type); + } else if (object.namespaces) { + if (!this._registry.isMultiNamespace(object.type)) { + error = SavedObjectsErrorHelpers.createBadRequestError( + '"namespaces" can only be used on multi-namespace types' + ); + } else if (!object.namespaces.length) { + error = SavedObjectsErrorHelpers.createBadRequestError( + '"namespaces" must be a non-empty array of strings' + ); + } + } + + if (error) { return { tag: 'Left' as 'Left', - error: { - id: object.id, - type: object.type, - error: errorContent(SavedObjectsErrorHelpers.createUnsupportedTypeError(object.type)), - }, + error: { id: object.id, type: object.type, error: errorContent(error) }, }; } @@ -357,7 +388,7 @@ export class SavedObjectsRepository { let versionProperties; const { esRequestIndex, - object: { version, ...object }, + object: { namespaces, version, ...object }, method, } = expectedBulkGetResult.value; if (esRequestIndex !== undefined) { @@ -378,13 +409,14 @@ export class SavedObjectsRepository { }, }; } - savedObjectNamespaces = getSavedObjectNamespaces(namespace, docFound && actualResult); + savedObjectNamespaces = + namespaces || getSavedObjectNamespaces(namespace, docFound && actualResult); versionProperties = getExpectedVersionProperties(version, actualResult); } else { if (this._registry.isSingleNamespace(object.type)) { savedObjectNamespace = namespace; } else if (this._registry.isMultiNamespace(object.type)) { - savedObjectNamespaces = getSavedObjectNamespaces(namespace); + savedObjectNamespaces = namespaces || getSavedObjectNamespaces(namespace); } versionProperties = getExpectedVersionProperties(version); } @@ -553,7 +585,7 @@ export class SavedObjectsRepository { throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); } - const { refresh = DEFAULT_REFRESH_SETTING } = options; + const { refresh = DEFAULT_REFRESH_SETTING, force } = options; const namespace = normalizeNamespace(options.namespace); const rawId = this._serializer.generateRawId(namespace, type, id); @@ -561,38 +593,14 @@ export class SavedObjectsRepository { if (this._registry.isMultiNamespace(type)) { preflightResult = await this.preflightCheckIncludesNamespace(type, id, namespace); - const existingNamespaces = getSavedObjectNamespaces(undefined, preflightResult); - const remainingNamespaces = existingNamespaces?.filter( - (x) => x !== SavedObjectsUtils.namespaceIdToString(namespace) - ); - - if (remainingNamespaces?.length) { - // if there is 1 or more namespace remaining, update the saved object - const time = this._getCurrentTime(); - - const doc = { - updated_at: time, - namespaces: remainingNamespaces, - }; - - const { statusCode } = await this.client.update( - { - id: rawId, - index: this.getIndexForType(type), - ...getExpectedVersionProperties(undefined, preflightResult), - refresh, - body: { - doc, - }, - }, - { ignore: [404] } + const existingNamespaces = getSavedObjectNamespaces(undefined, preflightResult) ?? []; + if ( + !force && + (existingNamespaces.length > 1 || existingNamespaces.includes(ALL_NAMESPACES_STRING)) + ) { + throw SavedObjectsErrorHelpers.createBadRequestError( + 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' ); - - if (statusCode === 404) { - // see "404s from missing index" above - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - return {}; } } @@ -637,8 +645,8 @@ export class SavedObjectsRepository { namespace: string, options: SavedObjectsDeleteByNamespaceOptions = {} ): Promise<any> { - if (!namespace || typeof namespace !== 'string') { - throw new TypeError(`namespace is required, and must be a string`); + if (!namespace || typeof namespace !== 'string' || namespace === '*') { + throw new TypeError(`namespace is required, and must be a string that is not equal to '*'`); } const allTypes = Object.keys(getRootPropertiesObjects(this._mappings)); @@ -1253,6 +1261,19 @@ export class SavedObjectsRepository { } const { attributes, references, version, namespace: objectNamespace } = object; + + if (objectNamespace === ALL_NAMESPACES_STRING) { + return { + tag: 'Left' as 'Left', + error: { + id, + type, + error: errorContent( + SavedObjectsErrorHelpers.createBadRequestError('"namespace" cannot be "*"') + ), + }, + }; + } // `objectNamespace` is a namespace string, while `namespace` is a namespace ID. // The object namespace string, if defined, will supersede the operation's namespace ID. @@ -1568,7 +1589,10 @@ export class SavedObjectsRepository { } const namespaces = raw._source.namespaces; - return namespaces?.includes(SavedObjectsUtils.namespaceIdToString(namespace)) ?? false; + const existsInNamespace = + namespaces?.includes(SavedObjectsUtils.namespaceIdToString(namespace)) || + namespaces?.includes('*'); + return existsInNamespace ?? false; } /** @@ -1695,8 +1719,15 @@ function getSavedObjectNamespaces( * Ensure that a namespace is always in its namespace ID representation. * This allows `'default'` to be used interchangeably with `undefined`. */ -const normalizeNamespace = (namespace?: string) => - namespace === undefined ? namespace : SavedObjectsUtils.namespaceStringToId(namespace); +const normalizeNamespace = (namespace?: string) => { + if (namespace === ALL_NAMESPACES_STRING) { + throw SavedObjectsErrorHelpers.createBadRequestError('"options.namespace" cannot be "*"'); + } else if (namespace === undefined) { + return namespace; + } else { + return SavedObjectsUtils.namespaceStringToId(namespace); + } +}; /** * Extracts the contents of a decorated error to return the attributes for bulk operations. diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts index e13c67a7204000..330fa5066051f2 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.test.ts @@ -22,6 +22,7 @@ import { esKuery } from '../../../es_query'; type KueryNode = any; import { typeRegistryMock } from '../../../saved_objects_type_registry.mock'; +import { ALL_NAMESPACES_STRING } from '../utils'; import { getQueryParams } from './query_params'; const registry = typeRegistryMock.create(); @@ -52,9 +53,10 @@ const ALL_TYPE_SUBSETS = ALL_TYPES.reduce( const createTypeClause = (type: string, namespaces?: string[]) => { if (registry.isMultiNamespace(type)) { + const array = [...(namespaces ?? ['default']), ALL_NAMESPACES_STRING]; return { bool: { - must: expect.arrayContaining([{ terms: { namespaces: namespaces ?? ['default'] } }]), + must: expect.arrayContaining([{ terms: { namespaces: array } }]), must_not: [{ exists: { field: 'namespace' } }], }, }; diff --git a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts index eaddc05fa921c1..8bd9c7d8312eea 100644 --- a/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts +++ b/src/core/server/saved_objects/service/lib/search_dsl/query_params.ts @@ -22,7 +22,7 @@ type KueryNode = any; import { getRootPropertiesObjects, IndexMapping } from '../../../mappings'; import { ISavedObjectTypeRegistry } from '../../../saved_objects_type_registry'; -import { DEFAULT_NAMESPACE_STRING } from '../utils'; +import { ALL_NAMESPACES_STRING, DEFAULT_NAMESPACE_STRING } from '../utils'; /** * Gets the types based on the type. Uses mappings to support @@ -84,7 +84,10 @@ function getClauseForType( if (registry.isMultiNamespace(type)) { return { bool: { - must: [{ term: { type } }, { terms: { namespaces } }], + must: [ + { term: { type } }, + { terms: { namespaces: [...namespaces, ALL_NAMESPACES_STRING] } }, + ], must_not: [{ exists: { field: 'namespace' } }], }, }; diff --git a/src/core/server/saved_objects/service/lib/utils.ts b/src/core/server/saved_objects/service/lib/utils.ts index 3efe8614da1d79..69abc370892184 100644 --- a/src/core/server/saved_objects/service/lib/utils.ts +++ b/src/core/server/saved_objects/service/lib/utils.ts @@ -21,6 +21,7 @@ import { SavedObjectsFindOptions } from '../../types'; import { SavedObjectsFindResponse } from '..'; export const DEFAULT_NAMESPACE_STRING = 'default'; +export const ALL_NAMESPACES_STRING = '*'; export const FIND_DEFAULT_PAGE = 1; export const FIND_DEFAULT_PER_PAGE = 20; diff --git a/src/core/server/saved_objects/service/saved_objects_client.ts b/src/core/server/saved_objects/service/saved_objects_client.ts index 8c96116de49cb7..d2b3b89b928c7f 100644 --- a/src/core/server/saved_objects/service/saved_objects_client.ts +++ b/src/core/server/saved_objects/service/saved_objects_client.ts @@ -50,6 +50,13 @@ export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { refresh?: MutatingOperationRefreshSetting; /** Optional ID of the original saved object, if this object's `id` was regenerated */ originId?: string; + /** + * Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in + * {@link SavedObjectsCreateOptions}. + * + * Note: this can only be used for multi-namespace object types. + */ + namespaces?: string[]; } /** @@ -66,6 +73,13 @@ export interface SavedObjectsBulkCreateObject<T = unknown> { migrationVersion?: SavedObjectsMigrationVersion; /** Optional ID of the original saved object, if this object's `id` was regenerated */ originId?: string; + /** + * Optional initial namespaces for the object to be created in. If this is defined, it will supersede the namespace ID that is in + * {@link SavedObjectsCreateOptions}. + * + * Note: this can only be used for multi-namespace object types. + */ + namespaces?: string[]; } /** @@ -211,6 +225,8 @@ export interface SavedObjectsBulkUpdateOptions extends SavedObjectsBaseOptions { export interface SavedObjectsDeleteOptions extends SavedObjectsBaseOptions { /** The Elasticsearch Refresh setting for this operation */ refresh?: MutatingOperationRefreshSetting; + /** Force deletion of an object that exists in multiple namespaces */ + force?: boolean; } /** diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 450be3b0e9a6c2..7742dad150cfab 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -1820,6 +1820,7 @@ export interface SavedObjectsBulkCreateObject<T = unknown> { // (undocumented) id?: string; migrationVersion?: SavedObjectsMigrationVersion; + namespaces?: string[]; originId?: string; // (undocumented) references?: SavedObjectReference[]; @@ -1977,6 +1978,7 @@ export interface SavedObjectsCoreFieldMapping { export interface SavedObjectsCreateOptions extends SavedObjectsBaseOptions { id?: string; migrationVersion?: SavedObjectsMigrationVersion; + namespaces?: string[]; originId?: string; overwrite?: boolean; // (undocumented) @@ -2002,6 +2004,7 @@ export interface SavedObjectsDeleteFromNamespacesResponse { // @public (undocumented) export interface SavedObjectsDeleteOptions extends SavedObjectsBaseOptions { + force?: boolean; refresh?: MutatingOperationRefreshSetting; } diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx index 1bc3dc80665202..0adf55ed6bebb0 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx @@ -544,11 +544,13 @@ describe('SavedObjectsTable', () => { expect(mockSavedObjectsClient.bulkGet).toHaveBeenCalledWith(mockSelectedSavedObjects); expect(mockSavedObjectsClient.delete).toHaveBeenCalledWith( mockSavedObjects[0].type, - mockSavedObjects[0].id + mockSavedObjects[0].id, + { force: true } ); expect(mockSavedObjectsClient.delete).toHaveBeenCalledWith( mockSavedObjects[1].type, - mockSavedObjects[1].id + mockSavedObjects[1].id, + { force: true } ); expect(component.state('selectedSavedObjects').length).toBe(0); }); diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx index d879a71cc22699..5011c0299abe8d 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx @@ -468,7 +468,7 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb const objects = await savedObjectsClient.bulkGet(selectedSavedObjects); const deletes = objects.savedObjects.map((object) => - savedObjectsClient.delete(object.type, object.id) + savedObjectsClient.delete(object.type, object.id, { force: true }) ); await Promise.all(deletes); diff --git a/x-pack/plugins/security/common/constants.ts b/x-pack/plugins/security/common/constants.ts index 44b6601daa7ff9..a0d63c0a9dd6f3 100644 --- a/x-pack/plugins/security/common/constants.ts +++ b/x-pack/plugins/security/common/constants.ts @@ -4,6 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ +/** + * The identifier in a saved object's `namespaces` array when it is shared globally to all spaces. + */ +export const ALL_SPACES_ID = '*'; + +/** + * The identifier in a saved object's `namespaces` array when it is shared to an unknown space (e.g., one that the end user is not authorized to see). + */ +export const UNKNOWN_SPACE = '?'; + export const GLOBAL_RESOURCE = '*'; export const APPLICATION_PREFIX = 'kibana-'; export const RESERVED_PRIVILEGES_APPLICATION_WILDCARD = 'kibana-*'; diff --git a/x-pack/plugins/security/server/authorization/actions/__snapshots__/saved_object.test.ts.snap b/x-pack/plugins/security/server/authorization/actions/__snapshots__/saved_object.test.ts.snap deleted file mode 100644 index 117947fc22f50e..00000000000000 --- a/x-pack/plugins/security/server/authorization/actions/__snapshots__/saved_object.test.ts.snap +++ /dev/null @@ -1,25 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`#get operation of "" throws error 1`] = `"type is required and must be a string"`; - -exports[`#get operation of {} throws error 1`] = `"type is required and must be a string"`; - -exports[`#get operation of 1 throws error 1`] = `"type is required and must be a string"`; - -exports[`#get operation of null throws error 1`] = `"type is required and must be a string"`; - -exports[`#get operation of true throws error 1`] = `"type is required and must be a string"`; - -exports[`#get operation of undefined throws error 1`] = `"type is required and must be a string"`; - -exports[`#get type of "" throws error 1`] = `"type is required and must be a string"`; - -exports[`#get type of {} throws error 1`] = `"type is required and must be a string"`; - -exports[`#get type of 1 throws error 1`] = `"type is required and must be a string"`; - -exports[`#get type of null throws error 1`] = `"type is required and must be a string"`; - -exports[`#get type of true throws error 1`] = `"type is required and must be a string"`; - -exports[`#get type of undefined throws error 1`] = `"type is required and must be a string"`; diff --git a/x-pack/plugins/security/server/authorization/actions/saved_object.test.ts b/x-pack/plugins/security/server/authorization/actions/saved_object.test.ts index 9e8bfb6ad795f1..90448a5dd0422d 100644 --- a/x-pack/plugins/security/server/authorization/actions/saved_object.test.ts +++ b/x-pack/plugins/security/server/authorization/actions/saved_object.test.ts @@ -12,14 +12,18 @@ describe('#get', () => { [null, undefined, '', 1, true, {}].forEach((type: any) => { test(`type of ${JSON.stringify(type)} throws error`, () => { const savedObjectActions = new SavedObjectActions(version); - expect(() => savedObjectActions.get(type, 'foo-action')).toThrowErrorMatchingSnapshot(); + expect(() => savedObjectActions.get(type, 'foo-action')).toThrowError( + 'type is required and must be a string' + ); }); }); [null, undefined, '', 1, true, {}].forEach((operation: any) => { test(`operation of ${JSON.stringify(operation)} throws error`, () => { const savedObjectActions = new SavedObjectActions(version); - expect(() => savedObjectActions.get('foo-type', operation)).toThrowErrorMatchingSnapshot(); + expect(() => savedObjectActions.get('foo-type', operation)).toThrowError( + 'operation is required and must be a string' + ); }); }); diff --git a/x-pack/plugins/security/server/authorization/actions/saved_object.ts b/x-pack/plugins/security/server/authorization/actions/saved_object.ts index e3a02d38073998..6bd094d64ec744 100644 --- a/x-pack/plugins/security/server/authorization/actions/saved_object.ts +++ b/x-pack/plugins/security/server/authorization/actions/saved_object.ts @@ -19,7 +19,7 @@ export class SavedObjectActions { } if (!operation || !isString(operation)) { - throw new Error('type is required and must be a string'); + throw new Error('operation is required and must be a string'); } return `${this.prefix}${type}/${operation}`; diff --git a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts index f287cc04280ac9..4a2426a9e8a40f 100644 --- a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.test.ts @@ -95,6 +95,38 @@ describe('#checkSavedObjectsPrivileges', () => { ]; expect(mockCheckPrivileges.atSpaces).toHaveBeenCalledWith(spaceIds, { kibana: actions }); }); + + test(`uses checkPrivileges.globally when checking for "all spaces" (*)`, async () => { + const expectedResult = Symbol(); + mockCheckPrivileges.globally.mockReturnValue(expectedResult as any); + mockSpacesService = undefined; + const checkSavedObjectsPrivileges = createFactory(); + + const namespaces = [undefined, 'default', namespace1, namespace1, '*']; + const result = await checkSavedObjectsPrivileges(actions, namespaces); + + expect(result).toBe(expectedResult); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledTimes(1); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); + expect(mockCheckPrivileges.globally).toHaveBeenCalledTimes(1); + expect(mockCheckPrivileges.globally).toHaveBeenCalledWith({ kibana: actions }); + }); + + test(`uses checkPrivileges.globally when Spaces is disabled`, async () => { + const expectedResult = Symbol(); + mockCheckPrivileges.globally.mockReturnValue(expectedResult as any); + mockSpacesService = undefined; + const checkSavedObjectsPrivileges = createFactory(); + + const namespaces = [undefined, 'default', namespace1, namespace1, '*']; + const result = await checkSavedObjectsPrivileges(actions, namespaces); + + expect(result).toBe(expectedResult); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledTimes(1); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); + expect(mockCheckPrivileges.globally).toHaveBeenCalledTimes(1); + expect(mockCheckPrivileges.globally).toHaveBeenCalledWith({ kibana: actions }); + }); }); describe('when checking a single namespace', () => { @@ -115,6 +147,21 @@ describe('#checkSavedObjectsPrivileges', () => { expect(mockCheckPrivileges.atSpace).toHaveBeenCalledWith(spaceId, { kibana: actions }); }); + test(`uses checkPrivileges.globally when checking for "all spaces" (*)`, async () => { + const expectedResult = Symbol(); + mockCheckPrivileges.globally.mockReturnValue(expectedResult as any); + mockSpacesService = undefined; + const checkSavedObjectsPrivileges = createFactory(); + + const result = await checkSavedObjectsPrivileges(actions, '*'); + + expect(result).toBe(expectedResult); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledTimes(1); + expect(mockCheckPrivilegesWithRequest).toHaveBeenCalledWith(request); + expect(mockCheckPrivileges.globally).toHaveBeenCalledTimes(1); + expect(mockCheckPrivileges.globally).toHaveBeenCalledWith({ kibana: actions }); + }); + test(`uses checkPrivileges.globally when Spaces is disabled`, async () => { const expectedResult = Symbol(); mockCheckPrivileges.globally.mockReturnValue(expectedResult as any); diff --git a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts index 7c0ca7dcaa3922..6b70e25eb448d3 100644 --- a/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts +++ b/x-pack/plugins/security/server/authorization/check_saved_objects_privileges.ts @@ -5,6 +5,7 @@ */ import { KibanaRequest } from '../../../../../src/core/server'; +import { ALL_SPACES_ID } from '../../common/constants'; import { SpacesService } from '../plugin'; import { CheckPrivilegesWithRequest, CheckPrivilegesResponse } from './types'; @@ -33,24 +34,32 @@ export const checkSavedObjectsPrivilegesWithRequestFactory = ( namespaceOrNamespaces?: string | Array<undefined | string> ) { const spacesService = getSpacesService(); - if (!spacesService) { - // Spaces disabled, authorizing globally - return await checkPrivilegesWithRequest(request).globally({ kibana: actions }); - } else if (Array.isArray(namespaceOrNamespaces)) { - // Spaces enabled, authorizing against multiple spaces - if (!namespaceOrNamespaces.length) { - throw new Error(`Can't check saved object privileges for 0 namespaces`); + const privileges = { kibana: actions }; + + if (spacesService) { + if (Array.isArray(namespaceOrNamespaces)) { + // Spaces enabled, authorizing against multiple spaces + if (!namespaceOrNamespaces.length) { + throw new Error(`Can't check saved object privileges for 0 namespaces`); + } + const spaceIds = uniq( + namespaceOrNamespaces.map((x) => spacesService.namespaceToSpaceId(x)) + ); + + if (!spaceIds.includes(ALL_SPACES_ID)) { + return await checkPrivilegesWithRequest(request).atSpaces(spaceIds, privileges); + } + } else { + // Spaces enabled, authorizing against a single space + const spaceId = spacesService.namespaceToSpaceId(namespaceOrNamespaces); + if (spaceId !== ALL_SPACES_ID) { + return await checkPrivilegesWithRequest(request).atSpace(spaceId, privileges); + } } - const spaceIds = uniq( - namespaceOrNamespaces.map((x) => spacesService.namespaceToSpaceId(x)) - ); - - return await checkPrivilegesWithRequest(request).atSpaces(spaceIds, { kibana: actions }); - } else { - // Spaces enabled, authorizing against a single space - const spaceId = spacesService.namespaceToSpaceId(namespaceOrNamespaces); - return await checkPrivilegesWithRequest(request).atSpace(spaceId, { kibana: actions }); } + + // Spaces plugin is disabled OR we are checking privileges for "all spaces", authorizing globally + return await checkPrivilegesWithRequest(request).globally(privileges); }; }; }; diff --git a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts index 0dd89f2c5f3c1d..6da0b93e1461fc 100644 --- a/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts +++ b/x-pack/plugins/security/server/authorization/privileges/feature_privilege_builder/saved_object.ts @@ -9,7 +9,14 @@ import { FeatureKibanaPrivileges } from '../../../../../features/server'; import { BaseFeaturePrivilegeBuilder } from './feature_privilege_builder'; const readOperations: string[] = ['bulk_get', 'get', 'find']; -const writeOperations: string[] = ['create', 'bulk_create', 'update', 'bulk_update', 'delete']; +const writeOperations: string[] = [ + 'create', + 'bulk_create', + 'update', + 'bulk_update', + 'delete', + 'share_to_space', +]; const allOperations: string[] = [...readOperations, ...writeOperations]; export class FeaturePrivilegeSavedObjectBuilder extends BaseFeaturePrivilegeBuilder { diff --git a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts index 6f721c91fbd67a..2b1268b11a0ffa 100644 --- a/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts +++ b/x-pack/plugins/security/server/authorization/privileges/privileges.test.ts @@ -106,6 +106,7 @@ describe('features', () => { actions.savedObject.get('all-savedObject-all-1', 'update'), actions.savedObject.get('all-savedObject-all-1', 'bulk_update'), actions.savedObject.get('all-savedObject-all-1', 'delete'), + actions.savedObject.get('all-savedObject-all-1', 'share_to_space'), actions.savedObject.get('all-savedObject-all-2', 'bulk_get'), actions.savedObject.get('all-savedObject-all-2', 'get'), actions.savedObject.get('all-savedObject-all-2', 'find'), @@ -114,6 +115,7 @@ describe('features', () => { actions.savedObject.get('all-savedObject-all-2', 'update'), actions.savedObject.get('all-savedObject-all-2', 'bulk_update'), actions.savedObject.get('all-savedObject-all-2', 'delete'), + actions.savedObject.get('all-savedObject-all-2', 'share_to_space'), actions.savedObject.get('all-savedObject-read-1', 'bulk_get'), actions.savedObject.get('all-savedObject-read-1', 'get'), actions.savedObject.get('all-savedObject-read-1', 'find'), @@ -135,6 +137,7 @@ describe('features', () => { actions.savedObject.get('read-savedObject-all-1', 'update'), actions.savedObject.get('read-savedObject-all-1', 'bulk_update'), actions.savedObject.get('read-savedObject-all-1', 'delete'), + actions.savedObject.get('read-savedObject-all-1', 'share_to_space'), actions.savedObject.get('read-savedObject-all-2', 'bulk_get'), actions.savedObject.get('read-savedObject-all-2', 'get'), actions.savedObject.get('read-savedObject-all-2', 'find'), @@ -143,6 +146,7 @@ describe('features', () => { actions.savedObject.get('read-savedObject-all-2', 'update'), actions.savedObject.get('read-savedObject-all-2', 'bulk_update'), actions.savedObject.get('read-savedObject-all-2', 'delete'), + actions.savedObject.get('read-savedObject-all-2', 'share_to_space'), actions.savedObject.get('read-savedObject-read-1', 'bulk_get'), actions.savedObject.get('read-savedObject-read-1', 'get'), actions.savedObject.get('read-savedObject-read-1', 'find'), @@ -276,6 +280,7 @@ describe('features', () => { actions.savedObject.get('all-savedObject-all-1', 'update'), actions.savedObject.get('all-savedObject-all-1', 'bulk_update'), actions.savedObject.get('all-savedObject-all-1', 'delete'), + actions.savedObject.get('all-savedObject-all-1', 'share_to_space'), actions.savedObject.get('all-savedObject-all-2', 'bulk_get'), actions.savedObject.get('all-savedObject-all-2', 'get'), actions.savedObject.get('all-savedObject-all-2', 'find'), @@ -284,6 +289,7 @@ describe('features', () => { actions.savedObject.get('all-savedObject-all-2', 'update'), actions.savedObject.get('all-savedObject-all-2', 'bulk_update'), actions.savedObject.get('all-savedObject-all-2', 'delete'), + actions.savedObject.get('all-savedObject-all-2', 'share_to_space'), actions.savedObject.get('all-savedObject-read-1', 'bulk_get'), actions.savedObject.get('all-savedObject-read-1', 'get'), actions.savedObject.get('all-savedObject-read-1', 'find'), @@ -304,6 +310,7 @@ describe('features', () => { actions.savedObject.get('read-savedObject-all-1', 'update'), actions.savedObject.get('read-savedObject-all-1', 'bulk_update'), actions.savedObject.get('read-savedObject-all-1', 'delete'), + actions.savedObject.get('read-savedObject-all-1', 'share_to_space'), actions.savedObject.get('read-savedObject-all-2', 'bulk_get'), actions.savedObject.get('read-savedObject-all-2', 'get'), actions.savedObject.get('read-savedObject-all-2', 'find'), @@ -312,6 +319,7 @@ describe('features', () => { actions.savedObject.get('read-savedObject-all-2', 'update'), actions.savedObject.get('read-savedObject-all-2', 'bulk_update'), actions.savedObject.get('read-savedObject-all-2', 'delete'), + actions.savedObject.get('read-savedObject-all-2', 'share_to_space'), actions.savedObject.get('read-savedObject-read-1', 'bulk_get'), actions.savedObject.get('read-savedObject-read-1', 'get'), actions.savedObject.get('read-savedObject-read-1', 'find'), @@ -387,6 +395,7 @@ describe('features', () => { actions.savedObject.get('read-savedObject-all-1', 'update'), actions.savedObject.get('read-savedObject-all-1', 'bulk_update'), actions.savedObject.get('read-savedObject-all-1', 'delete'), + actions.savedObject.get('read-savedObject-all-1', 'share_to_space'), actions.savedObject.get('read-savedObject-all-2', 'bulk_get'), actions.savedObject.get('read-savedObject-all-2', 'get'), actions.savedObject.get('read-savedObject-all-2', 'find'), @@ -395,6 +404,7 @@ describe('features', () => { actions.savedObject.get('read-savedObject-all-2', 'update'), actions.savedObject.get('read-savedObject-all-2', 'bulk_update'), actions.savedObject.get('read-savedObject-all-2', 'delete'), + actions.savedObject.get('read-savedObject-all-2', 'share_to_space'), actions.savedObject.get('read-savedObject-read-1', 'bulk_get'), actions.savedObject.get('read-savedObject-read-1', 'get'), actions.savedObject.get('read-savedObject-read-1', 'find'), @@ -692,6 +702,7 @@ describe('reserved', () => { actions.savedObject.get('savedObject-all-1', 'update'), actions.savedObject.get('savedObject-all-1', 'bulk_update'), actions.savedObject.get('savedObject-all-1', 'delete'), + actions.savedObject.get('savedObject-all-1', 'share_to_space'), actions.savedObject.get('savedObject-all-2', 'bulk_get'), actions.savedObject.get('savedObject-all-2', 'get'), actions.savedObject.get('savedObject-all-2', 'find'), @@ -700,6 +711,7 @@ describe('reserved', () => { actions.savedObject.get('savedObject-all-2', 'update'), actions.savedObject.get('savedObject-all-2', 'bulk_update'), actions.savedObject.get('savedObject-all-2', 'delete'), + actions.savedObject.get('savedObject-all-2', 'share_to_space'), actions.savedObject.get('savedObject-read-1', 'bulk_get'), actions.savedObject.get('savedObject-read-1', 'get'), actions.savedObject.get('savedObject-read-1', 'find'), @@ -822,6 +834,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -950,6 +963,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -967,6 +981,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -991,6 +1006,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1021,6 +1037,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1038,6 +1055,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1056,6 +1074,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1073,6 +1092,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1151,6 +1171,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1168,6 +1189,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1192,6 +1214,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1292,6 +1315,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1309,6 +1333,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1351,6 +1376,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1374,6 +1400,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1457,6 +1484,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1474,6 +1502,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1588,6 +1617,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1608,6 +1638,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1634,6 +1665,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1651,6 +1683,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1669,6 +1702,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), @@ -1686,6 +1720,7 @@ describe('subFeatures', () => { actions.savedObject.get('all-sub-feature-type', 'update'), actions.savedObject.get('all-sub-feature-type', 'bulk_update'), actions.savedObject.get('all-sub-feature-type', 'delete'), + actions.savedObject.get('all-sub-feature-type', 'share_to_space'), actions.savedObject.get('read-sub-feature-type', 'bulk_get'), actions.savedObject.get('read-sub-feature-type', 'get'), actions.savedObject.get('read-sub-feature-type', 'find'), diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts index 86d1b68ba761ed..c7e5cee1ed18c2 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.test.ts @@ -120,7 +120,7 @@ const expectSuccess = async (fn: Function, args: Record<string, any>, action?: s const expectPrivilegeCheck = async ( fn: Function, args: Record<string, any>, - namespacesOverride?: Array<undefined | string> + namespaceOrNamespaces: string | undefined | Array<undefined | string> ) => { clientOpts.checkSavedObjectsPrivilegesAsCurrentUser.mockImplementation( getMockCheckPrivilegesFailure @@ -135,7 +135,7 @@ const expectPrivilegeCheck = async ( expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledTimes(1); expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledWith( actions, - namespacesOverride ?? args.options?.namespace ?? args.options?.namespaces + namespaceOrNamespaces ); }; @@ -154,7 +154,7 @@ const expectObjectNamespaceFiltering = async ( ); const authorizedNamespace = args.options?.namespace || 'default'; - const namespaces = ['some-other-namespace', authorizedNamespace]; + const namespaces = ['some-other-namespace', '*', authorizedNamespace]; const returnValue = { namespaces, foo: 'bar' }; // we don't know which base client method will be called; mock them all clientOpts.baseClient.create.mockReturnValue(returnValue as any); @@ -164,14 +164,15 @@ const expectObjectNamespaceFiltering = async ( clientOpts.baseClient.deleteFromNamespaces.mockReturnValue(returnValue as any); const result = await fn.bind(client)(...Object.values(args)); - expect(result).toEqual(expect.objectContaining({ namespaces: [authorizedNamespace, '?'] })); + // we will never redact the "All Spaces" ID + expect(result).toEqual(expect.objectContaining({ namespaces: ['*', authorizedNamespace, '?'] })); expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledTimes( privilegeChecks + 1 ); expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenLastCalledWith( 'login:', - namespaces + namespaces.filter((x) => x !== '*') // when we check what namespaces to redact, we don't check privileges for '*', only actual space IDs ); }; @@ -282,8 +283,7 @@ describe('#addToNamespaces', () => { const newNs2 = 'bar-namespace'; const namespaces = [newNs1, newNs2]; const currentNs = 'default'; - const privilege1 = `mock-saved_object:${type}/create`; - const privilege2 = `mock-saved_object:${type}/update`; + const privilege = `mock-saved_object:${type}/share_to_space`; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { await expectGeneralError(client.addToNamespaces, { type, id, namespaces }); @@ -305,7 +305,7 @@ describe('#addToNamespaces', () => { 'addToNamespacesCreate', [type], namespaces.sort(), - [{ privilege: privilege1, spaceId: newNs1 }], + [{ privilege, spaceId: newNs1 }], { id, type, namespaces, options: {} } ); expect(clientOpts.auditLogger.savedObjectsAuthorizationSuccess).not.toHaveBeenCalled(); @@ -332,7 +332,7 @@ describe('#addToNamespaces', () => { 'addToNamespacesUpdate', [type], [currentNs], - [{ privilege: privilege2, spaceId: currentNs }], + [{ privilege, spaceId: currentNs }], { id, type, namespaces, options: {} } ); expect(clientOpts.auditLogger.savedObjectsAuthorizationSuccess).toHaveBeenCalledTimes(1); @@ -350,7 +350,7 @@ describe('#addToNamespaces', () => { expect(clientOpts.auditLogger.savedObjectsAuthorizationSuccess).toHaveBeenNthCalledWith( 1, USERNAME, - 'addToNamespacesCreate', // action for privilege check is 'create', but auditAction is 'addToNamespacesCreate' + 'addToNamespacesCreate', // action for privilege check is 'share_to_space', but auditAction is 'addToNamespacesCreate' [type], namespaces.sort(), { type, id, namespaces, options: {} } @@ -358,7 +358,7 @@ describe('#addToNamespaces', () => { expect(clientOpts.auditLogger.savedObjectsAuthorizationSuccess).toHaveBeenNthCalledWith( 2, USERNAME, - 'addToNamespacesUpdate', // action for privilege check is 'update', but auditAction is 'addToNamespacesUpdate' + 'addToNamespacesUpdate', // action for privilege check is 'share_to_space', but auditAction is 'addToNamespacesUpdate' [type], [currentNs], { type, id, namespaces, options: {} } @@ -378,12 +378,12 @@ describe('#addToNamespaces', () => { expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenCalledTimes(2); expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenNthCalledWith( 1, - [privilege1], + [privilege], namespaces ); expect(clientOpts.checkSavedObjectsPrivilegesAsCurrentUser).toHaveBeenNthCalledWith( 2, - [privilege2], + [privilege], undefined // default namespace ); }); @@ -398,7 +398,7 @@ describe('#bulkCreate', () => { const attributes = { some: 'attr' }; const obj1 = Object.freeze({ type: 'foo', otherThing: 'sup', attributes }); const obj2 = Object.freeze({ type: 'bar', otherThing: 'everyone', attributes }); - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { const objects = [obj1]; @@ -407,6 +407,7 @@ describe('#bulkCreate', () => { test(`throws decorated ForbiddenError when unauthorized`, async () => { const objects = [obj1, obj2]; + const options = { namespace }; await expectForbiddenError(client.bulkCreate, { objects, options }); }); @@ -415,17 +416,33 @@ describe('#bulkCreate', () => { clientOpts.baseClient.bulkCreate.mockReturnValue(apiCallReturnValue as any); const objects = [obj1, obj2]; + const options = { namespace }; const result = await expectSuccess(client.bulkCreate, { objects, options }); expect(result).toEqual(apiCallReturnValue); }); test(`checks privileges for user, actions, and namespace`, async () => { const objects = [obj1, obj2]; - await expectPrivilegeCheck(client.bulkCreate, { objects, options }); + const options = { namespace }; + await expectPrivilegeCheck(client.bulkCreate, { objects, options }, [namespace]); + }); + + test(`checks privileges for user, actions, namespace, and namespaces`, async () => { + const objects = [ + { ...obj1, namespaces: 'another-ns' }, + { ...obj2, namespaces: 'yet-another-ns' }, + ]; + const options = { namespace }; + await expectPrivilegeCheck(client.bulkCreate, { objects, options }, [ + namespace, + 'another-ns', + 'yet-another-ns', + ]); }); test(`filters namespaces that the user doesn't have access to`, async () => { const objects = [obj1, obj2]; + const options = { namespace }; await expectObjectsNamespaceFiltering(client.bulkCreate, { objects, options }); }); }); @@ -433,7 +450,7 @@ describe('#bulkCreate', () => { describe('#bulkGet', () => { const obj1 = Object.freeze({ type: 'foo', id: 'foo-id' }); const obj2 = Object.freeze({ type: 'bar', id: 'bar-id' }); - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { const objects = [obj1]; @@ -442,6 +459,7 @@ describe('#bulkGet', () => { test(`throws decorated ForbiddenError when unauthorized`, async () => { const objects = [obj1, obj2]; + const options = { namespace }; await expectForbiddenError(client.bulkGet, { objects, options }); }); @@ -450,17 +468,20 @@ describe('#bulkGet', () => { clientOpts.baseClient.bulkGet.mockReturnValue(apiCallReturnValue as any); const objects = [obj1, obj2]; + const options = { namespace }; const result = await expectSuccess(client.bulkGet, { objects, options }); expect(result).toEqual(apiCallReturnValue); }); test(`checks privileges for user, actions, and namespace`, async () => { const objects = [obj1, obj2]; - await expectPrivilegeCheck(client.bulkGet, { objects, options }); + const options = { namespace }; + await expectPrivilegeCheck(client.bulkGet, { objects, options }, namespace); }); test(`filters namespaces that the user doesn't have access to`, async () => { const objects = [obj1, obj2]; + const options = { namespace }; await expectObjectsNamespaceFiltering(client.bulkGet, { objects, options }); }); }); @@ -468,7 +489,7 @@ describe('#bulkGet', () => { describe('#bulkUpdate', () => { const obj1 = Object.freeze({ type: 'foo', id: 'foo-id', attributes: { some: 'attr' } }); const obj2 = Object.freeze({ type: 'bar', id: 'bar-id', attributes: { other: 'attr' } }); - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { const objects = [obj1]; @@ -477,6 +498,7 @@ describe('#bulkUpdate', () => { test(`throws decorated ForbiddenError when unauthorized`, async () => { const objects = [obj1, obj2]; + const options = { namespace }; await expectForbiddenError(client.bulkUpdate, { objects, options }); }); @@ -485,14 +507,16 @@ describe('#bulkUpdate', () => { clientOpts.baseClient.bulkUpdate.mockReturnValue(apiCallReturnValue as any); const objects = [obj1, obj2]; + const options = { namespace }; const result = await expectSuccess(client.bulkUpdate, { objects, options }); expect(result).toEqual(apiCallReturnValue); }); test(`checks privileges for user, actions, and namespace`, async () => { const objects = [obj1, obj2]; - const namespacesOverride = [options.namespace]; // the bulkCreate function checks privileges as an array - await expectPrivilegeCheck(client.bulkUpdate, { objects, options }, namespacesOverride); + const options = { namespace }; + const namespaces = [options.namespace]; // the bulkUpdate function always checks privileges as an array + await expectPrivilegeCheck(client.bulkUpdate, { objects, options }, namespaces); }); test(`checks privileges for object namespaces if present`, async () => { @@ -500,13 +524,14 @@ describe('#bulkUpdate', () => { { ...obj1, namespace: 'foo-ns' }, { ...obj2, namespace: 'bar-ns' }, ]; - const namespacesOverride = [undefined, 'foo-ns', 'bar-ns']; - // use the default namespace for the options - await expectPrivilegeCheck(client.bulkUpdate, { objects, options: {} }, namespacesOverride); + const namespaces = [undefined, 'foo-ns', 'bar-ns']; + const options = {}; // use the default namespace for the options + await expectPrivilegeCheck(client.bulkUpdate, { objects, options }, namespaces); }); test(`filters namespaces that the user doesn't have access to`, async () => { const objects = [obj1, obj2]; + const options = { namespace }; await expectObjectsNamespaceFiltering(client.bulkUpdate, { objects, options }); }); }); @@ -514,7 +539,7 @@ describe('#bulkUpdate', () => { describe('#checkConflicts', () => { const obj1 = Object.freeze({ type: 'foo', id: 'foo-id' }); const obj2 = Object.freeze({ type: 'bar', id: 'bar-id' }); - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when checkPrivileges.globally rejects promise`, async () => { const objects = [obj1, obj2]; @@ -523,6 +548,7 @@ describe('#checkConflicts', () => { test(`throws decorated ForbiddenError when unauthorized`, async () => { const objects = [obj1, obj2]; + const options = { namespace }; await expectForbiddenError(client.checkConflicts, { objects, options }, 'checkConflicts'); }); @@ -531,6 +557,7 @@ describe('#checkConflicts', () => { clientOpts.baseClient.checkConflicts.mockResolvedValue(apiCallReturnValue as any); const objects = [obj1, obj2]; + const options = { namespace }; const result = await expectSuccess( client.checkConflicts, { objects, options }, @@ -541,20 +568,22 @@ describe('#checkConflicts', () => { test(`checks privileges for user, actions, and namespace`, async () => { const objects = [obj1, obj2]; - await expectPrivilegeCheck(client.checkConflicts, { objects, options }); + const options = { namespace }; + await expectPrivilegeCheck(client.checkConflicts, { objects, options }, namespace); }); }); describe('#create', () => { const type = 'foo'; const attributes = { some_attr: 's' }; - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when checkPrivileges.globally rejects promise`, async () => { await expectGeneralError(client.create, { type }); }); test(`throws decorated ForbiddenError when unauthorized`, async () => { + const options = { namespace }; await expectForbiddenError(client.create, { type, attributes, options }); }); @@ -562,15 +591,27 @@ describe('#create', () => { const apiCallReturnValue = Symbol(); clientOpts.baseClient.create.mockResolvedValue(apiCallReturnValue as any); + const options = { namespace }; const result = await expectSuccess(client.create, { type, attributes, options }); expect(result).toBe(apiCallReturnValue); }); test(`checks privileges for user, actions, and namespace`, async () => { - await expectPrivilegeCheck(client.create, { type, attributes, options }); + const options = { namespace }; + await expectPrivilegeCheck(client.create, { type, attributes, options }, [namespace]); + }); + + test(`checks privileges for user, actions, namespace, and namespaces`, async () => { + const options = { namespace, namespaces: ['another-ns', 'yet-another-ns'] }; + await expectPrivilegeCheck(client.create, { type, attributes, options }, [ + namespace, + 'another-ns', + 'yet-another-ns', + ]); }); test(`filters namespaces that the user doesn't have access to`, async () => { + const options = { namespace }; await expectObjectNamespaceFiltering(client.create, { type, attributes, options }); }); }); @@ -578,13 +619,14 @@ describe('#create', () => { describe('#delete', () => { const type = 'foo'; const id = `${type}-id`; - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { await expectGeneralError(client.delete, { type, id }); }); test(`throws decorated ForbiddenError when unauthorized`, async () => { + const options = { namespace }; await expectForbiddenError(client.delete, { type, id, options }); }); @@ -592,18 +634,21 @@ describe('#delete', () => { const apiCallReturnValue = Symbol(); clientOpts.baseClient.delete.mockReturnValue(apiCallReturnValue as any); + const options = { namespace }; const result = await expectSuccess(client.delete, { type, id, options }); expect(result).toBe(apiCallReturnValue); }); test(`checks privileges for user, actions, and namespace`, async () => { - await expectPrivilegeCheck(client.delete, { type, id, options }); + const options = { namespace }; + await expectPrivilegeCheck(client.delete, { type, id, options }, namespace); }); }); describe('#find', () => { const type1 = 'foo'; const type2 = 'bar'; + const namespaces = ['some-ns']; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { await expectGeneralError(client.find, { type: type1 }); @@ -634,7 +679,7 @@ describe('#find', () => { const apiCallReturnValue = { saved_objects: [], foo: 'bar' }; clientOpts.baseClient.find.mockReturnValue(apiCallReturnValue as any); - const options = Object.freeze({ type: type1, namespaces: ['some-ns'] }); + const options = { type: type1, namespaces }; const result = await expectSuccess(client.find, { options }); expect(clientOpts.baseClient.find.mock.calls[0][0]).toEqual({ ...options, @@ -699,19 +744,19 @@ describe('#find', () => { getMockCheckPrivilegesSuccess ); - const options = Object.freeze({ type: [type1, type2], namespaces: ['some-ns'] }); + const options = { type: [type1, type2], namespaces }; await expect(client.find(options)).rejects.toThrowErrorMatchingInlineSnapshot( `"_find across namespaces is not permitted when the Spaces plugin is disabled."` ); }); test(`checks privileges for user, actions, and namespaces`, async () => { - const options = Object.freeze({ type: [type1, type2], namespaces: ['some-ns'] }); - await expectPrivilegeCheck(client.find, { options }); + const options = { type: [type1, type2], namespaces }; + await expectPrivilegeCheck(client.find, { options }, namespaces); }); test(`filters namespaces that the user doesn't have access to`, async () => { - const options = Object.freeze({ type: [type1, type2], namespaces: ['some-ns'] }); + const options = { type: [type1, type2], namespaces }; await expectObjectsNamespaceFiltering(client.find, { options }); }); }); @@ -719,13 +764,14 @@ describe('#find', () => { describe('#get', () => { const type = 'foo'; const id = `${type}-id`; - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { await expectGeneralError(client.get, { type, id }); }); test(`throws decorated ForbiddenError when unauthorized`, async () => { + const options = { namespace }; await expectForbiddenError(client.get, { type, id, options }); }); @@ -733,15 +779,18 @@ describe('#get', () => { const apiCallReturnValue = Symbol(); clientOpts.baseClient.get.mockReturnValue(apiCallReturnValue as any); + const options = { namespace }; const result = await expectSuccess(client.get, { type, id, options }); expect(result).toBe(apiCallReturnValue); }); test(`checks privileges for user, actions, and namespace`, async () => { - await expectPrivilegeCheck(client.get, { type, id, options }); + const options = { namespace }; + await expectPrivilegeCheck(client.get, { type, id, options }, namespace); }); test(`filters namespaces that the user doesn't have access to`, async () => { + const options = { namespace }; await expectObjectNamespaceFiltering(client.get, { type, id, options }); }); }); @@ -752,7 +801,7 @@ describe('#deleteFromNamespaces', () => { const namespace1 = 'foo-namespace'; const namespace2 = 'bar-namespace'; const namespaces = [namespace1, namespace2]; - const privilege = `mock-saved_object:${type}/delete`; + const privilege = `mock-saved_object:${type}/share_to_space`; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { await expectGeneralError(client.deleteFromNamespaces, { type, id, namespaces }); @@ -771,7 +820,7 @@ describe('#deleteFromNamespaces', () => { expect(clientOpts.auditLogger.savedObjectsAuthorizationFailure).toHaveBeenCalledTimes(1); expect(clientOpts.auditLogger.savedObjectsAuthorizationFailure).toHaveBeenCalledWith( USERNAME, - 'deleteFromNamespaces', // action for privilege check is 'delete', but auditAction is 'deleteFromNamespaces' + 'deleteFromNamespaces', // action for privilege check is 'share_to_space', but auditAction is 'deleteFromNamespaces' [type], namespaces.sort(), [{ privilege, spaceId: namespace1 }], @@ -791,7 +840,7 @@ describe('#deleteFromNamespaces', () => { expect(clientOpts.auditLogger.savedObjectsAuthorizationSuccess).toHaveBeenCalledTimes(1); expect(clientOpts.auditLogger.savedObjectsAuthorizationSuccess).toHaveBeenCalledWith( USERNAME, - 'deleteFromNamespaces', // action for privilege check is 'delete', but auditAction is 'deleteFromNamespaces' + 'deleteFromNamespaces', // action for privilege check is 'share_to_space', but auditAction is 'deleteFromNamespaces' [type], namespaces.sort(), { type, id, namespaces, options: {} } @@ -821,13 +870,14 @@ describe('#update', () => { const type = 'foo'; const id = `${type}-id`; const attributes = { some: 'attr' }; - const options = Object.freeze({ namespace: 'some-ns' }); + const namespace = 'some-ns'; test(`throws decorated GeneralError when hasPrivileges rejects promise`, async () => { await expectGeneralError(client.update, { type, id, attributes }); }); test(`throws decorated ForbiddenError when unauthorized`, async () => { + const options = { namespace }; await expectForbiddenError(client.update, { type, id, attributes, options }); }); @@ -835,15 +885,18 @@ describe('#update', () => { const apiCallReturnValue = Symbol(); clientOpts.baseClient.update.mockReturnValue(apiCallReturnValue as any); + const options = { namespace }; const result = await expectSuccess(client.update, { type, id, attributes, options }); expect(result).toBe(apiCallReturnValue); }); test(`checks privileges for user, actions, and namespace`, async () => { - await expectPrivilegeCheck(client.update, { type, id, attributes, options }); + const options = { namespace }; + await expectPrivilegeCheck(client.update, { type, id, attributes, options }, namespace); }); test(`filters namespaces that the user doesn't have access to`, async () => { + const options = { namespace }; await expectObjectNamespaceFiltering(client.update, { type, id, attributes, options }); }); }); diff --git a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts index f5de8f4b226f34..ad0bc085eb8e2f 100644 --- a/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts +++ b/x-pack/plugins/security/server/saved_objects/secure_saved_objects_client_wrapper.ts @@ -18,6 +18,7 @@ import { SavedObjectsDeleteFromNamespacesOptions, SavedObjectsUtils, } from '../../../../../src/core/server'; +import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants'; import { SecurityAuditLogger } from '../audit'; import { Actions, CheckSavedObjectsPrivileges } from '../authorization'; import { CheckPrivilegesResponse } from '../authorization/types'; @@ -85,7 +86,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra options: SavedObjectsCreateOptions = {} ) { const args = { type, attributes, options }; - await this.ensureAuthorized(type, 'create', options.namespace, { args }); + const namespaces = [options.namespace, ...(options.namespaces || [])]; + await this.ensureAuthorized(type, 'create', namespaces, { args }); const savedObject = await this.baseClient.create(type, attributes, options); return await this.redactSavedObjectNamespaces(savedObject); @@ -111,13 +113,17 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra options: SavedObjectsBaseOptions = {} ) { const args = { objects, options }; - await this.ensureAuthorized( - this.getUniqueObjectTypes(objects), - 'bulk_create', - options.namespace, - { args } + const namespaces = objects.reduce( + (acc, { namespaces: initialNamespaces = [] }) => { + return acc.concat(initialNamespaces); + }, + [options.namespace] ); + await this.ensureAuthorized(this.getUniqueObjectTypes(objects), 'bulk_create', namespaces, { + args, + }); + const response = await this.baseClient.bulkCreate(objects, options); return await this.redactSavedObjectsNamespaces(response); } @@ -207,17 +213,17 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra ) { const args = { type, id, namespaces, options }; const { namespace } = options; - // To share an object, the user must have the "create" permission in each of the destination namespaces. - await this.ensureAuthorized(type, 'create', namespaces, { + // To share an object, the user must have the "share_to_space" permission in each of the destination namespaces. + await this.ensureAuthorized(type, 'share_to_space', namespaces, { args, auditAction: 'addToNamespacesCreate', }); - // To share an object, the user must also have the "update" permission in one or more of the source namespaces. Because the - // `addToNamespaces` operation is scoped to the current namespace, we can just check if the user has the "update" permission in the - // current namespace. If the user has permission, but the saved object doesn't exist in this namespace, the base client operation will - // result in a 404 error. - await this.ensureAuthorized(type, 'update', namespace, { + // To share an object, the user must also have the "share_to_space" permission in one or more of the source namespaces. Because the + // `addToNamespaces` operation is scoped to the current namespace, we can just check if the user has the "share_to_space" permission in + // the current namespace. If the user has permission, but the saved object doesn't exist in this namespace, the base client operation + // will result in a 404 error. + await this.ensureAuthorized(type, 'share_to_space', namespace, { args, auditAction: 'addToNamespacesUpdate', }); @@ -233,8 +239,8 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra options: SavedObjectsDeleteFromNamespacesOptions = {} ) { const args = { type, id, namespaces, options }; - // To un-share an object, the user must have the "delete" permission in each of the target namespaces. - await this.ensureAuthorized(type, 'delete', namespaces, { + // To un-share an object, the user must have the "share_to_space" permission in each of the target namespaces. + await this.ensureAuthorized(type, 'share_to_space', namespaces, { args, auditAction: 'deleteFromNamespaces', }); @@ -383,7 +389,9 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra } private redactAndSortNamespaces(spaceIds: string[], privilegeMap: Record<string, boolean>) { - return spaceIds.map((x) => (privilegeMap[x] ? x : '?')).sort(namespaceComparator); + return spaceIds + .map((x) => (x === ALL_SPACES_ID || privilegeMap[x] ? x : UNKNOWN_SPACE)) + .sort(namespaceComparator); } private async redactSavedObjectNamespaces<T extends SavedObjectNamespaces>( @@ -397,7 +405,12 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra return savedObject; } - const privilegeMap = await this.getNamespacesPrivilegeMap(savedObject.namespaces); + const namespaces = savedObject.namespaces.filter((x) => x !== ALL_SPACES_ID); // all users can see the "all spaces" ID + if (namespaces.length === 0) { + return savedObject; + } + + const privilegeMap = await this.getNamespacesPrivilegeMap(namespaces); return { ...savedObject, @@ -412,7 +425,9 @@ export class SecureSavedObjectsClientWrapper implements SavedObjectsClientContra return response; } const { saved_objects: savedObjects } = response; - const namespaces = uniq(savedObjects.flatMap((savedObject) => savedObject.namespaces || [])); + const namespaces = uniq( + savedObjects.flatMap((savedObject) => savedObject.namespaces || []) + ).filter((x) => x !== ALL_SPACES_ID); // all users can see the "all spaces" ID if (namespaces.length === 0) { return response; } @@ -445,9 +460,9 @@ function uniq<T>(arr: T[]): T[] { function namespaceComparator(a: string, b: string) { const A = a.toUpperCase(); const B = b.toUpperCase(); - if (A === '?' && B !== '?') { + if (A === UNKNOWN_SPACE && B !== UNKNOWN_SPACE) { return 1; - } else if (A !== '?' && B === '?') { + } else if (A !== UNKNOWN_SPACE && B === UNKNOWN_SPACE) { return -1; } return A > B ? 1 : A < B ? -1 : 0; diff --git a/x-pack/plugins/spaces/common/constants.ts b/x-pack/plugins/spaces/common/constants.ts index 33f1aae70ea009..bd47fe7b8b8777 100644 --- a/x-pack/plugins/spaces/common/constants.ts +++ b/x-pack/plugins/spaces/common/constants.ts @@ -6,6 +6,16 @@ export const DEFAULT_SPACE_ID = `default`; +/** + * The identifier in a saved object's `namespaces` array when it is shared globally to all spaces. + */ +export const ALL_SPACES_ID = '*'; + +/** + * The identifier in a saved object's `namespaces` array when it is shared to an unknown space (e.g., one that the end user is not authorized to see). + */ +export const UNKNOWN_SPACE = '?'; + /** * The minimum number of spaces required to show a search control. */ diff --git a/x-pack/plugins/spaces/public/lib/documentation_links.test.ts b/x-pack/plugins/spaces/public/lib/documentation_links.test.ts new file mode 100644 index 00000000000000..55319530304050 --- /dev/null +++ b/x-pack/plugins/spaces/public/lib/documentation_links.test.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { docLinksServiceMock } from '../../../../../src/core/public/mocks'; +import { DocumentationLinksService } from './documentation_links'; + +describe('DocumentationLinksService', () => { + const setup = () => { + const docLinks = docLinksServiceMock.createStartContract(); + const service = new DocumentationLinksService(docLinks); + return { docLinks, service }; + }; + + describe('#getKibanaPrivilegesDocUrl', () => { + it('returns expected value', () => { + const { service } = setup(); + expect(service.getKibanaPrivilegesDocUrl()).toMatchInlineSnapshot( + `"https://www.elastic.co/guide/en/kibana/mocked-test-branch/kibana-privileges.html"` + ); + }); + }); +}); diff --git a/x-pack/plugins/spaces/public/lib/documentation_links.ts b/x-pack/plugins/spaces/public/lib/documentation_links.ts new file mode 100644 index 00000000000000..71ba89d5b87e29 --- /dev/null +++ b/x-pack/plugins/spaces/public/lib/documentation_links.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { DocLinksStart } from 'src/core/public'; + +export class DocumentationLinksService { + private readonly kbn: string; + + constructor(docLinks: DocLinksStart) { + this.kbn = `${docLinks.ELASTIC_WEBSITE_URL}guide/en/kibana/${docLinks.DOC_LINK_VERSION}/`; + } + + public getKibanaPrivilegesDocUrl() { + return `${this.kbn}kibana-privileges.html`; + } +} diff --git a/x-pack/plugins/spaces/public/lib/index.ts b/x-pack/plugins/spaces/public/lib/index.ts new file mode 100644 index 00000000000000..87b54dd4e2ef39 --- /dev/null +++ b/x-pack/plugins/spaces/public/lib/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { DocumentationLinksService } from './documentation_links'; diff --git a/x-pack/plugins/spaces/public/plugin.tsx b/x-pack/plugins/spaces/public/plugin.tsx index cd31a4aa17fc38..2a08f412664567 100644 --- a/x-pack/plugins/spaces/public/plugin.tsx +++ b/x-pack/plugins/spaces/public/plugin.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CoreSetup, CoreStart, Plugin } from 'src/core/public'; +import { CoreSetup, CoreStart, Plugin, StartServicesAccessor } from 'src/core/public'; import { HomePublicPluginSetup } from 'src/plugins/home/public'; import { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; import { ManagementStart, ManagementSetup } from 'src/plugins/management/public'; @@ -53,7 +53,7 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart this.managementService = new ManagementService(); this.managementService.setup({ management: plugins.management, - getStartServices: core.getStartServices as CoreSetup<PluginsStart>['getStartServices'], + getStartServices: core.getStartServices as StartServicesAccessor<PluginsStart>, spacesManager: this.spacesManager, securityLicense: plugins.security?.license, }); @@ -73,6 +73,7 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart spacesManager: this.spacesManager, notificationsSetup: core.notifications, savedObjectsManagementSetup: plugins.savedObjectsManagement, + getStartServices: core.getStartServices as StartServicesAccessor<PluginsStart>, }); const copySavedObjectsToSpaceService = new CopySavedObjectsToSpaceService(); copySavedObjectsToSpaceService.setup({ diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.tsx new file mode 100644 index 00000000000000..afa65cc7ad7dfa --- /dev/null +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/no_spaces_available.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { ApplicationStart } from 'src/core/public'; + +interface Props { + application: ApplicationStart; +} + +export const NoSpacesAvailable = (props: Props) => { + const { capabilities, getUrlForApp } = props.application; + const canCreateNewSpaces = capabilities.spaces.manage; + if (!canCreateNewSpaces) { + return null; + } + + return ( + <> + <EuiSpacer size="xs" /> + <EuiText size="s" color="subdued"> + <FormattedMessage + id="xpack.spaces.management.shareToSpace.noAvailableSpaces.canCreateNewSpace.text" + defaultMessage="You can {createANewSpaceLink} for sharing your objects." + values={{ + createANewSpaceLink: ( + <EuiLink + data-test-subj="sts-new-space-link" + href={getUrlForApp('management', { path: 'kibana/spaces/create' })} + > + <FormattedMessage + id="xpack.spaces.management.shareToSpace.noAvailableSpaces.canCreateNewSpace.linkText" + defaultMessage="create a new space" + /> + </EuiLink> + ), + }} + /> + </EuiText> + </> + ); +}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx index 82a30dabe5beb8..29303a478daeb9 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/selectable_spaces_control.tsx @@ -5,8 +5,24 @@ */ import './selectable_spaces_control.scss'; -import React, { Fragment } from 'react'; -import { EuiBadge, EuiSelectable, EuiSelectableOption, EuiLoadingSpinner } from '@elastic/eui'; +import React from 'react'; +import { + EuiBadge, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiLink, + EuiSelectable, + EuiSelectableOption, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { NoSpacesAvailable } from './no_spaces_available'; +import { useKibana } from '../../../../../../src/plugins/kibana_react/public'; +import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants'; +import { DocumentationLinksService } from '../../lib'; import { SpaceAvatar } from '../../space_avatar'; import { SpaceTarget } from '../types'; @@ -14,11 +30,11 @@ interface Props { spaces: SpaceTarget[]; selectedSpaceIds: string[]; onChange: (selectedSpaceIds: string[]) => void; - disabled?: boolean; } type SpaceOption = EuiSelectableOption & { ['data-space-id']: string }; +const ROW_HEIGHT = 40; const activeSpaceProps = { append: <EuiBadge color="hollow">Current</EuiBadge>, disabled: true, @@ -26,51 +42,127 @@ const activeSpaceProps = { }; export const SelectableSpacesControl = (props: Props) => { - if (props.spaces.length === 0) { - return <EuiLoadingSpinner />; - } + const { spaces, selectedSpaceIds, onChange } = props; + const { services } = useKibana(); + const { application, docLinks } = services; - const options = props.spaces + const isGlobalControlChecked = selectedSpaceIds.includes(ALL_SPACES_ID); + const options = spaces .sort((a, b) => (a.isActiveSpace ? -1 : b.isActiveSpace ? 1 : 0)) .map<SpaceOption>((space) => ({ label: space.name, prepend: <SpaceAvatar space={space} size={'s'} />, - checked: props.selectedSpaceIds.includes(space.id) ? 'on' : undefined, + checked: selectedSpaceIds.includes(space.id) ? 'on' : undefined, ['data-space-id']: space.id, ['data-test-subj']: `sts-space-selector-row-${space.id}`, ...(space.isActiveSpace ? activeSpaceProps : {}), + ...(isGlobalControlChecked && { disabled: true }), })); - function updateSelectedSpaces(selectedOptions: SpaceOption[]) { - if (props.disabled) return; - - const selectedSpaceIds = selectedOptions - .filter((opt) => opt.checked && !opt.disabled) - .map((opt) => opt['data-space-id']); + function updateSelectedSpaces(spaceOptions: SpaceOption[]) { + const selectedOptions = spaceOptions + .filter(({ checked, disabled }) => checked && !disabled) + .map((x) => x['data-space-id']); + const updatedSpaceIds = [ + ...selectedOptions, + ...selectedSpaceIds.filter((x) => x === UNKNOWN_SPACE), // preserve any unknown spaces (to keep the "selected spaces" count accurate) + ]; - props.onChange(selectedSpaceIds); + onChange(updatedSpaceIds); } + const getUnknownSpacesLabel = () => { + const count = selectedSpaceIds.filter((id) => id === UNKNOWN_SPACE).length; + if (!count) { + return null; + } + + const kibanaPrivilegesUrl = new DocumentationLinksService( + docLinks! + ).getKibanaPrivilegesDocUrl(); + return ( + <> + <EuiSpacer size="xs" /> + <EuiText size="s" color="subdued"> + <FormattedMessage + id="xpack.spaces.management.shareToSpace.unknownSpacesLabel.text" + defaultMessage="To view hidden spaces, you need {additionalPrivilegesLink}." + values={{ + additionalPrivilegesLink: ( + <EuiLink href={kibanaPrivilegesUrl}> + <FormattedMessage + id="xpack.spaces.management.shareToSpace.unknownSpacesLabel.additionalPrivilegesLink" + defaultMessage="additional privileges" + /> + </EuiLink> + ), + }} + /> + </EuiText> + </> + ); + }; + const getNoSpacesAvailable = () => { + if (spaces.length < 2) { + return <NoSpacesAvailable application={application!} />; + } + return null; + }; + + const selectedCount = + selectedSpaceIds.filter((id) => id !== ALL_SPACES_ID && id !== UNKNOWN_SPACE).length + 1; + const hiddenCount = selectedSpaceIds.filter((id) => id === UNKNOWN_SPACE).length; + const selectSpacesLabel = i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.selectSpacesLabel', + { defaultMessage: 'Select spaces' } + ); + const selectedSpacesLabel = i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.selectedCountLabel', + { defaultMessage: '{selectedCount} selected', values: { selectedCount } } + ); + const hiddenSpacesLabel = i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.hiddenCountLabel', + { defaultMessage: '+{hiddenCount} hidden', values: { hiddenCount } } + ); + const hiddenSpaces = hiddenCount ? <EuiText size="xs">{hiddenSpacesLabel}</EuiText> : null; return ( - <EuiSelectable - options={options} - onChange={(newOptions) => updateSelectedSpaces(newOptions as SpaceOption[])} - listProps={{ - bordered: true, - rowHeight: 40, - className: 'spcShareToSpace__spacesList', - 'data-test-subj': 'sts-form-space-selector', - }} - searchable + <EuiFormRow + label={selectSpacesLabel} + labelAppend={ + <EuiFlexGroup direction="column" gutterSize="none" alignItems="flexEnd"> + <EuiFlexItem grow={false}> + <EuiText size="xs">{selectedSpacesLabel}</EuiText> + </EuiFlexItem> + <EuiFlexItem grow={false}>{hiddenSpaces}</EuiFlexItem> + </EuiFlexGroup> + } + fullWidth > - {(list, search) => { - return ( - <Fragment> - {search} - {list} - </Fragment> - ); - }} - </EuiSelectable> + <> + <EuiSelectable + options={options} + onChange={(newOptions) => updateSelectedSpaces(newOptions as SpaceOption[])} + listProps={{ + bordered: true, + rowHeight: ROW_HEIGHT, + className: 'spcShareToSpace__spacesList', + 'data-test-subj': 'sts-form-space-selector', + }} + height={ROW_HEIGHT * 3.5} + searchable + > + {(list, search) => { + return ( + <> + {search} + {list} + </> + ); + }} + </EuiSelectable> + {getUnknownSpacesLabel()} + {getNoSpacesAvailable()} + </> + </EuiFormRow> ); }; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.scss b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.scss new file mode 100644 index 00000000000000..3baa21f68d4f36 --- /dev/null +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.scss @@ -0,0 +1,3 @@ +.euiCheckableCard__children { + width: 100%; // required to expand the contents of EuiCheckableCard to the full width +} diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx new file mode 100644 index 00000000000000..dca52e6e529a16 --- /dev/null +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_mode_control.tsx @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import './share_mode_control.scss'; +import React from 'react'; +import { + EuiCheckableCard, + EuiFlexGroup, + EuiFlexItem, + EuiFormFieldset, + EuiIconTip, + EuiLoadingSpinner, + EuiSpacer, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { SelectableSpacesControl } from './selectable_spaces_control'; +import { ALL_SPACES_ID } from '../../../common/constants'; +import { SpaceTarget } from '../types'; + +interface Props { + spaces: SpaceTarget[]; + canShareToAllSpaces: boolean; + selectedSpaceIds: string[]; + onChange: (selectedSpaceIds: string[]) => void; + disabled?: boolean; +} + +function createLabel({ + title, + text, + disabled, + tooltip, +}: { + title: string; + text: string; + disabled: boolean; + tooltip?: string; +}) { + return ( + <> + <EuiFlexGroup> + <EuiFlexItem> + <EuiText>{title}</EuiText> + </EuiFlexItem> + {tooltip && ( + <EuiFlexItem grow={false}> + <EuiIconTip content={tooltip} position="left" type="iInCircle" /> + </EuiFlexItem> + )} + </EuiFlexGroup> + <EuiSpacer size="xs" /> + <EuiText color={disabled ? undefined : 'subdued'} size="s"> + {text} + </EuiText> + </> + ); +} + +export const ShareModeControl = (props: Props) => { + const { spaces, canShareToAllSpaces, selectedSpaceIds, onChange } = props; + + if (spaces.length === 0) { + return <EuiLoadingSpinner />; + } + + const isGlobalControlChecked = selectedSpaceIds.includes(ALL_SPACES_ID); + const shareToAllSpaces = { + id: 'shareToAllSpaces', + title: i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.shareToAllSpaces.title', + { defaultMessage: 'All spaces' } + ), + text: i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.shareToAllSpaces.text', + { defaultMessage: 'Make object available in all current and future spaces.' } + ), + ...(!canShareToAllSpaces && { + tooltip: isGlobalControlChecked + ? i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.shareToAllSpaces.cannotUncheckTooltip', + { defaultMessage: 'You need additional privileges to change this option.' } + ) + : i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.shareToAllSpaces.cannotCheckTooltip', + { defaultMessage: 'You need additional privileges to use this option.' } + ), + }), + disabled: !canShareToAllSpaces, + }; + const shareToExplicitSpaces = { + id: 'shareToExplicitSpaces', + title: i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.shareToExplicitSpaces.title', + { defaultMessage: 'Select spaces' } + ), + text: i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.shareToExplicitSpaces.text', + { defaultMessage: 'Make object available in selected spaces only.' } + ), + disabled: !canShareToAllSpaces && isGlobalControlChecked, + }; + const shareOptionsTitle = i18n.translate( + 'xpack.spaces.management.shareToSpace.shareModeControl.shareOptionsTitle', + { defaultMessage: 'Share options' } + ); + + const toggleShareOption = (allSpaces: boolean) => { + const updatedSpaceIds = allSpaces + ? [ALL_SPACES_ID, ...selectedSpaceIds] + : selectedSpaceIds.filter((id) => id !== ALL_SPACES_ID); + onChange(updatedSpaceIds); + }; + + return ( + <> + <EuiFormFieldset + legend={{ + children: ( + <EuiTitle size="xs"> + <span>{shareOptionsTitle}</span> + </EuiTitle> + ), + }} + > + <EuiCheckableCard + id={shareToExplicitSpaces.id} + label={createLabel(shareToExplicitSpaces)} + checked={!isGlobalControlChecked} + onChange={() => toggleShareOption(false)} + disabled={shareToExplicitSpaces.disabled} + > + <SelectableSpacesControl {...props} /> + </EuiCheckableCard> + <EuiSpacer size="s" /> + <EuiCheckableCard + id={shareToAllSpaces.id} + label={createLabel(shareToAllSpaces)} + checked={isGlobalControlChecked} + onChange={() => toggleShareOption(true)} + disabled={shareToAllSpaces.disabled} + /> + </EuiFormFieldset> + </> + ); +}; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.test.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.test.tsx index c17a2dcb1a831d..ad49161ddd7053 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.test.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.test.tsx @@ -8,16 +8,18 @@ import Boom from 'boom'; import { mountWithIntl, nextTick } from 'test_utils/enzyme_helpers'; import { ShareSavedObjectsToSpaceFlyout } from './share_to_space_flyout'; import { ShareToSpaceForm } from './share_to_space_form'; -import { EuiLoadingSpinner, EuiEmptyPrompt } from '@elastic/eui'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { Space } from '../../../common/model/space'; import { findTestSubject } from 'test_utils/find_test_subject'; import { SelectableSpacesControl } from './selectable_spaces_control'; import { act } from '@testing-library/react'; import { spacesManagerMock } from '../../spaces_manager/mocks'; import { SpacesManager } from '../../spaces_manager'; +import { coreMock } from '../../../../../../src/core/public/mocks'; import { ToastsApi } from 'src/core/public'; import { EuiCallOut } from '@elastic/eui'; import { CopySavedObjectsToSpaceFlyout } from '../../copy_saved_objects_to_space/components'; +import { NoSpacesAvailable } from './no_spaces_available'; import { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public'; interface SetupOpts { @@ -63,6 +65,8 @@ const setup = async (opts: SetupOpts = {}) => { ] ); + mockSpacesManager.getShareSavedObjectPermissions.mockResolvedValue({ shareToAllSpaces: true }); + const mockToastNotifications = { addError: jest.fn(), addSuccess: jest.fn(), @@ -81,6 +85,14 @@ const setup = async (opts: SetupOpts = {}) => { namespaces: opts.namespaces || ['my-active-space', 'space-1'], } as SavedObjectsManagementRecord; + const { getStartServices } = coreMock.createSetup(); + const startServices = coreMock.createStart(); + startServices.application.capabilities = { + ...startServices.application.capabilities, + spaces: { manage: true }, + }; + getStartServices.mockResolvedValue([startServices, , ,]); + const wrapper = mountWithIntl( <ShareSavedObjectsToSpaceFlyout savedObject={savedObjectToShare} @@ -88,6 +100,7 @@ const setup = async (opts: SetupOpts = {}) => { toastNotifications={(mockToastNotifications as unknown) as ToastsApi} onClose={onClose} onObjectUpdated={onObjectUpdated} + getStartServices={getStartServices} /> ); @@ -111,7 +124,7 @@ describe('ShareToSpaceFlyout', () => { const { wrapper } = await setup({ returnBeforeSpacesLoad: true }); expect(wrapper.find(ShareToSpaceForm)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(1); await act(async () => { @@ -121,26 +134,28 @@ describe('ShareToSpaceFlyout', () => { expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); }); - it('shows a message within an EuiEmptyPrompt when no spaces are available', async () => { - const { wrapper, onClose } = await setup({ mockSpaces: [] }); + it('shows a message within a NoSpacesAvailable when no spaces are available', async () => { + const { wrapper, onClose } = await setup({ + mockSpaces: [{ id: 'my-active-space', name: 'my active space', disabledFeatures: [] }], + }); - expect(wrapper.find(ShareToSpaceForm)).toHaveLength(0); + expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(1); expect(onClose).toHaveBeenCalledTimes(0); }); - it('shows a message within an EuiEmptyPrompt when only the active space is available', async () => { + it('shows a message within a NoSpacesAvailable when only the active space is available', async () => { const { wrapper, onClose } = await setup({ mockSpaces: [{ id: 'my-active-space', name: '', disabledFeatures: [] }], }); - expect(wrapper.find(ShareToSpaceForm)).toHaveLength(0); + expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(1); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(1); expect(onClose).toHaveBeenCalledTimes(0); }); @@ -176,9 +191,9 @@ describe('ShareToSpaceFlyout', () => { expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); - const copyButton = findTestSubject(wrapper, 'sts-copy-button'); // this button is only present in the warning callout + const copyButton = findTestSubject(wrapper, 'sts-copy-link'); // this link is only present in the warning callout await act(async () => { copyButton.simulate('click'); @@ -199,7 +214,7 @@ describe('ShareToSpaceFlyout', () => { expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects @@ -230,7 +245,7 @@ describe('ShareToSpaceFlyout', () => { expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects @@ -263,7 +278,7 @@ describe('ShareToSpaceFlyout', () => { expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects @@ -302,7 +317,7 @@ describe('ShareToSpaceFlyout', () => { expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects @@ -341,7 +356,7 @@ describe('ShareToSpaceFlyout', () => { expect(wrapper.find(ShareToSpaceForm)).toHaveLength(1); expect(wrapper.find(EuiLoadingSpinner)).toHaveLength(0); - expect(wrapper.find(EuiEmptyPrompt)).toHaveLength(0); + expect(wrapper.find(NoSpacesAvailable)).toHaveLength(0); // Using props callback instead of simulating clicks, // because EuiSelectable uses a virtualized list, which isn't easily testable via test subjects diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx index 053fcb4fdabf80..6461a3299f09bf 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_flyout.tsx @@ -16,19 +16,21 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, - EuiEmptyPrompt, EuiButton, EuiButtonEmpty, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { ToastsStart } from 'src/core/public'; +import { ToastsStart, StartServicesAccessor, CoreStart } from 'src/core/public'; import { SavedObjectsManagementRecord } from '../../../../../../src/plugins/saved_objects_management/public'; +import { createKibanaReactContext } from '../../../../../../src/plugins/kibana_react/public'; +import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants'; import { Space } from '../../../common/model/space'; import { SpacesManager } from '../../spaces_manager'; import { ShareToSpaceForm } from './share_to_space_form'; import { ShareOptions, SpaceTarget } from '../types'; import { CopySavedObjectsToSpaceFlyout } from '../../copy_saved_objects_to_space/components'; +import { PluginsStart } from '../../plugin'; interface Props { onClose: () => void; @@ -36,15 +38,25 @@ interface Props { savedObject: SavedObjectsManagementRecord; spacesManager: SpacesManager; toastNotifications: ToastsStart; + getStartServices: StartServicesAccessor<PluginsStart>; } const arraysAreEqual = (a: unknown[], b: unknown[]) => a.every((x) => b.includes(x)) && b.every((x) => a.includes(x)); export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { - const { onClose, onObjectUpdated, savedObject, spacesManager, toastNotifications } = props; + const { + getStartServices, + onClose, + onObjectUpdated, + savedObject, + spacesManager, + toastNotifications, + } = props; const { namespaces: currentNamespaces = [] } = savedObject; + const [coreStart, setCoreStart] = useState<CoreStart>(); const [shareOptions, setShareOptions] = useState<ShareOptions>({ selectedSpaceIds: [] }); + const [canShareToAllSpaces, setCanShareToAllSpaces] = useState<boolean>(false); const [showMakeCopy, setShowMakeCopy] = useState<boolean>(false); const [{ isLoading, spaces }, setSpacesState] = useState<{ @@ -54,8 +66,15 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { useEffect(() => { const getSpaces = spacesManager.getSpaces('shareSavedObjectsIntoSpace'); const getActiveSpace = spacesManager.getActiveSpace(); - Promise.all([getSpaces, getActiveSpace]) - .then(([allSpaces, activeSpace]) => { + const getPermissions = spacesManager.getShareSavedObjectPermissions(savedObject.type); + Promise.all([getSpaces, getActiveSpace, getPermissions, getStartServices()]) + .then(([allSpaces, activeSpace, permissions, startServices]) => { + const [coreStartValue] = startServices; + setCoreStart(coreStartValue); + setShareOptions({ + selectedSpaceIds: currentNamespaces.filter((spaceId) => spaceId !== activeSpace.id), + }); + setCanShareToAllSpaces(permissions.shareToAllSpaces); const createSpaceTarget = (space: Space): SpaceTarget => ({ ...space, isActiveSpace: space.id === activeSpace.id, @@ -64,9 +83,6 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { isLoading: false, spaces: allSpaces.map((space) => createSpaceTarget(space)), }); - setShareOptions({ - selectedSpaceIds: currentNamespaces.filter((spaceId) => spaceId !== activeSpace.id), - }); }) .catch((e) => { toastNotifications.addError(e, { @@ -75,25 +91,50 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { }), }); }); - }, [currentNamespaces, spacesManager, toastNotifications]); + }, [currentNamespaces, spacesManager, savedObject, toastNotifications, getStartServices]); const getSelectionChanges = () => { const activeSpace = spaces.find((space) => space.isActiveSpace); if (!activeSpace) { - return { changed: false, spacesToAdd: [], spacesToRemove: [] }; + return { isSelectionChanged: false, spacesToAdd: [], spacesToRemove: [] }; } const initialSelection = currentNamespaces.filter( - (spaceId) => spaceId !== activeSpace.id && spaceId !== '?' + (spaceId) => spaceId !== activeSpace.id && spaceId !== UNKNOWN_SPACE ); const { selectedSpaceIds } = shareOptions; - const changed = !arraysAreEqual(initialSelection, selectedSpaceIds); - const spacesToAdd = selectedSpaceIds.filter((spaceId) => !initialSelection.includes(spaceId)); - const spacesToRemove = initialSelection.filter( - (spaceId) => !selectedSpaceIds.includes(spaceId) + const filteredSelection = selectedSpaceIds.filter((x) => x !== UNKNOWN_SPACE); + const isSharedToAllSpaces = + !initialSelection.includes(ALL_SPACES_ID) && filteredSelection.includes(ALL_SPACES_ID); + const isUnsharedFromAllSpaces = + initialSelection.includes(ALL_SPACES_ID) && !filteredSelection.includes(ALL_SPACES_ID); + const selectedSpacesChanged = + !filteredSelection.includes(ALL_SPACES_ID) && + !arraysAreEqual(initialSelection, filteredSelection); + const isSelectionChanged = + isSharedToAllSpaces || + isUnsharedFromAllSpaces || + (!isSharedToAllSpaces && !isUnsharedFromAllSpaces && selectedSpacesChanged); + + const selectedSpacesToAdd = filteredSelection.filter( + (spaceId) => !initialSelection.includes(spaceId) + ); + const selectedSpacesToRemove = initialSelection.filter( + (spaceId) => !filteredSelection.includes(spaceId) ); - return { changed, spacesToAdd, spacesToRemove }; + + const spacesToAdd = isSharedToAllSpaces + ? [ALL_SPACES_ID] + : isUnsharedFromAllSpaces + ? [activeSpace.id, ...selectedSpacesToAdd] + : selectedSpacesToAdd; + const spacesToRemove = isUnsharedFromAllSpaces + ? [ALL_SPACES_ID] + : isSharedToAllSpaces + ? [activeSpace.id, ...initialSelection] + : selectedSpacesToRemove; + return { isSelectionChanged, spacesToAdd, spacesToRemove }; }; - const { changed: isSelectionChanged, spacesToAdd, spacesToRemove } = getSelectionChanges(); + const { isSelectionChanged, spacesToAdd, spacesToRemove } = getSelectionChanges(); const [shareInProgress, setShareInProgress] = useState(false); @@ -109,32 +150,28 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { : i18n.translate('xpack.spaces.management.shareToSpace.shareEditSuccessTitle', { defaultMessage: 'Object was updated', }); + const isSharedToAllSpaces = spacesToAdd.includes(ALL_SPACES_ID); if (spacesToAdd.length > 0) { await spacesManager.shareSavedObjectAdd({ type, id }, spacesToAdd); - const spaceNames = spacesToAdd.map( - (spaceId) => spaces.find((space) => space.id === spaceId)!.name - ); - const spaceCount = spaceNames.length; + const spaceTargets = isSharedToAllSpaces ? 'all' : `${spacesToAdd.length}`; const text = - spaceCount === 1 + !isSharedToAllSpaces && spacesToAdd.length === 1 ? i18n.translate('xpack.spaces.management.shareToSpace.shareAddSuccessTextSingular', { defaultMessage: `'{object}' was added to 1 space.`, values: { object: meta.title }, }) : i18n.translate('xpack.spaces.management.shareToSpace.shareAddSuccessTextPlural', { - defaultMessage: `'{object}' was added to {spaceCount} spaces.`, - values: { object: meta.title, spaceCount }, + defaultMessage: `'{object}' was added to {spaceTargets} spaces.`, + values: { object: meta.title, spaceTargets }, }); toastNotifications.addSuccess({ title, text }); } if (spacesToRemove.length > 0) { await spacesManager.shareSavedObjectRemove({ type, id }, spacesToRemove); - const spaceNames = spacesToRemove.map( - (spaceId) => spaces.find((space) => space.id === spaceId)!.name - ); - const spaceCount = spaceNames.length; + const isUnsharedFromAllSpaces = spacesToRemove.includes(ALL_SPACES_ID); + const spaceTargets = isUnsharedFromAllSpaces ? 'all' : `${spacesToRemove.length}`; const text = - spaceCount === 1 + !isUnsharedFromAllSpaces && spacesToRemove.length === 1 ? i18n.translate( 'xpack.spaces.management.shareToSpace.shareRemoveSuccessTextSingular', { @@ -143,10 +180,12 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { } ) : i18n.translate('xpack.spaces.management.shareToSpace.shareRemoveSuccessTextPlural', { - defaultMessage: `'{object}' was removed from {spaceCount} spaces.`, - values: { object: meta.title, spaceCount }, + defaultMessage: `'{object}' was removed from {spaceTargets} spaces.`, + values: { object: meta.title, spaceTargets }, }); - toastNotifications.addSuccess({ title, text }); + if (!isSharedToAllSpaces) { + toastNotifications.addSuccess({ title, text }); + } } onObjectUpdated(); onClose(); @@ -166,41 +205,26 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { return <EuiLoadingSpinner />; } - // Step 1a: assets loaded, but no spaces are available for share. - // The `spaces` array includes the current space, so at minimum it will have a length of 1. - if (spaces.length < 2) { - return ( - <EuiEmptyPrompt - body={ - <p> - <FormattedMessage - id="xpack.spaces.management.shareToSpace.noSpacesBody" - defaultMessage="There are no eligible spaces to share into." - /> - </p> - } - title={ - <h3> - <FormattedMessage - id="xpack.spaces.management.shareToSpace.noSpacesTitle" - defaultMessage="No spaces available" - /> - </h3> - } - /> - ); - } - - const showShareWarning = currentNamespaces.length === 1; + const activeSpace = spaces.find((x) => x.isActiveSpace)!; + const showShareWarning = + spaces.length > 1 && arraysAreEqual(currentNamespaces, [activeSpace.id]); + const { application, docLinks } = coreStart!; + const { Provider: KibanaReactContextProvider } = createKibanaReactContext({ + application, + docLinks, + }); // Step 2: Share has not been initiated yet; User must fill out form to continue. return ( - <ShareToSpaceForm - spaces={spaces} - shareOptions={shareOptions} - onUpdate={setShareOptions} - showShareWarning={showShareWarning} - makeCopy={() => setShowMakeCopy(true)} - /> + <KibanaReactContextProvider> + <ShareToSpaceForm + spaces={spaces} + shareOptions={shareOptions} + onUpdate={setShareOptions} + showShareWarning={showShareWarning} + canShareToAllSpaces={canShareToAllSpaces} + makeCopy={() => setShowMakeCopy(true)} + /> + </KibanaReactContextProvider> ); }; @@ -216,7 +240,7 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { } return ( - <EuiFlyout onClose={onClose} maxWidth={460} data-test-subj="share-to-space-flyout"> + <EuiFlyout onClose={onClose} maxWidth={500} data-test-subj="share-to-space-flyout"> <EuiFlyoutHeader hasBorder> <EuiFlexGroup alignItems="center" gutterSize="m"> <EuiFlexItem grow={false}> @@ -274,7 +298,7 @@ export const ShareSavedObjectsToSpaceFlyout = (props: Props) => { > <FormattedMessage id="xpack.spaces.management.shareToSpace.shareToSpacesButton" - defaultMessage="Share" + defaultMessage="Save & close" /> </EuiButton> </EuiFlexItem> diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx index ad84ea85d5e54a..bc196208ab35c9 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/components/share_to_space_form.tsx @@ -6,25 +6,28 @@ import './share_to_space_form.scss'; import React, { Fragment } from 'react'; -import { EuiHorizontalRule, EuiFormRow, EuiCallOut, EuiButton, EuiSpacer } from '@elastic/eui'; +import { EuiHorizontalRule, EuiCallOut, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { ShareOptions, SpaceTarget } from '../types'; -import { SelectableSpacesControl } from './selectable_spaces_control'; +import { ShareModeControl } from './share_mode_control'; interface Props { spaces: SpaceTarget[]; onUpdate: (shareOptions: ShareOptions) => void; shareOptions: ShareOptions; showShareWarning: boolean; + canShareToAllSpaces: boolean; makeCopy: () => void; } export const ShareToSpaceForm = (props: Props) => { + const { spaces, onUpdate, shareOptions, showShareWarning, canShareToAllSpaces, makeCopy } = props; + const setSelectedSpaceIds = (selectedSpaceIds: string[]) => - props.onUpdate({ ...props.shareOptions, selectedSpaceIds }); + onUpdate({ ...shareOptions, selectedSpaceIds }); const getShareWarning = () => { - if (!props.showShareWarning) { + if (!showShareWarning) { return null; } @@ -42,20 +45,18 @@ export const ShareToSpaceForm = (props: Props) => { > <FormattedMessage id="xpack.spaces.management.shareToSpace.shareWarningBody" - defaultMessage="To edit in only one space, make a copy instead." + defaultMessage="To edit in only one space, {makeACopyLink} instead." + values={{ + makeACopyLink: ( + <EuiLink data-test-subj="sts-copy-link" onClick={() => makeCopy()}> + <FormattedMessage + id="xpack.spaces.management.shareToSpace.shareWarningLink" + defaultMessage="make a copy" + /> + </EuiLink> + ), + }} /> - <EuiSpacer size="s" /> - <EuiButton - onClick={() => props.makeCopy()} - color="warning" - data-test-subj="sts-copy-button" - size="s" - > - <FormattedMessage - id="xpack.spaces.management.shareToSpace.shareWarningButton" - defaultMessage="Make a copy" - /> - </EuiButton> </EuiCallOut> <EuiHorizontalRule margin="m" /> @@ -67,28 +68,12 @@ export const ShareToSpaceForm = (props: Props) => { <div data-test-subj="share-to-space-form"> {getShareWarning()} - <EuiFormRow - label={ - <FormattedMessage - id="xpack.spaces.management.shareToSpace.selectSpacesLabel" - defaultMessage="Select spaces" - /> - } - labelAppend={ - <FormattedMessage - id="xpack.spaces.management.shareToSpace.selectSpacesLabelAppend" - defaultMessage="{selectedCount} selected" - values={{ selectedCount: props.shareOptions.selectedSpaceIds.length }} - /> - } - fullWidth - > - <SelectableSpacesControl - spaces={props.spaces} - selectedSpaceIds={props.shareOptions.selectedSpaceIds} - onChange={(selection) => setSelectedSpaceIds(selection)} - /> - </EuiFormRow> + <ShareModeControl + spaces={spaces} + canShareToAllSpaces={canShareToAllSpaces} + selectedSpaceIds={shareOptions.selectedSpaceIds} + onChange={(selection) => setSelectedSpaceIds(selection)} + /> </div> ); }; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx index ba9a6473999df3..677632e942e9ad 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_action.tsx @@ -5,13 +5,14 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { NotificationsStart } from 'src/core/public'; +import { NotificationsStart, StartServicesAccessor } from 'src/core/public'; import { SavedObjectsManagementAction, SavedObjectsManagementRecord, } from '../../../../../src/plugins/saved_objects_management/public'; import { ShareSavedObjectsToSpaceFlyout } from './components'; import { SpacesManager } from '../spaces_manager'; +import { PluginsStart } from '../plugin'; export class ShareToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction { public id: string = 'share_saved_objects_to_space'; @@ -39,7 +40,8 @@ export class ShareToSpaceSavedObjectsManagementAction extends SavedObjectsManage constructor( private readonly spacesManager: SpacesManager, - private readonly notifications: NotificationsStart + private readonly notifications: NotificationsStart, + private readonly getStartServices: StartServicesAccessor<PluginsStart> ) { super(); } @@ -56,6 +58,7 @@ export class ShareToSpaceSavedObjectsManagementAction extends SavedObjectsManage savedObject={this.record} spacesManager={this.spacesManager} toastNotifications={this.notifications.toasts} + getStartServices={this.getStartServices} /> ); }; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.test.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.test.tsx new file mode 100644 index 00000000000000..041728a3eac0da --- /dev/null +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.test.tsx @@ -0,0 +1,206 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallowWithIntl } from 'test_utils/enzyme_helpers'; +import { SpacesManager } from '../spaces_manager'; +import { spacesManagerMock } from '../spaces_manager/mocks'; +import { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; +import { SpaceTarget } from './types'; + +const ACTIVE_SPACE: SpaceTarget = { + id: 'default', + name: 'Default', + color: '#ffffff', + isActiveSpace: true, +}; +const getSpaceData = (inactiveSpaceCount: number = 0) => { + const inactive = ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel'] + .map<SpaceTarget>((name) => ({ + id: name.toLowerCase(), + name, + color: `#123456`, // must be a valid color as `render()` is used below + isActiveSpace: false, + })) + .slice(0, inactiveSpaceCount); + const spaceTargets = [ACTIVE_SPACE, ...inactive]; + const namespaces = spaceTargets.map(({ id }) => id); + return { spaceTargets, namespaces }; +}; + +describe('ShareToSpaceSavedObjectsManagementColumn', () => { + let spacesManager: SpacesManager; + beforeEach(() => { + spacesManager = spacesManagerMock.create(); + }); + + const createColumn = (spaceTargets: SpaceTarget[], namespaces: string[]) => { + const column = new ShareToSpaceSavedObjectsManagementColumn(spacesManager); + column.data = spaceTargets.reduce( + (acc, cur) => acc.set(cur.id, cur), + new Map<string, SpaceTarget>() + ); + const element = column.euiColumn.render(namespaces); + return shallowWithIntl(element); + }; + + /** + * This node displays up to five named spaces (and an indicator for any number of unauthorized spaces) by default. The active space is + * omitted from this list. If more than five named spaces would be displayed, the extras (along with the unauthorized spaces indicator, if + * present) are hidden behind a button. + * If '*' (aka "All spaces") is present, it supersedes all of the above and just displays a single badge without a button. + */ + describe('#euiColumn.render', () => { + describe('with only the active space', () => { + const { spaceTargets, namespaces } = getSpaceData(); + const wrapper = createColumn(spaceTargets, namespaces); + + it('does not show badges or button', async () => { + const badges = wrapper.find('EuiBadge'); + expect(badges).toHaveLength(0); + const button = wrapper.find('EuiButtonEmpty'); + expect(button).toHaveLength(0); + }); + }); + + describe('with the active space and one inactive space', () => { + const { spaceTargets, namespaces } = getSpaceData(1); + const wrapper = createColumn(spaceTargets, namespaces); + + it('shows one badge without button', async () => { + const badges = wrapper.find('EuiBadge'); + expect(badges).toMatchInlineSnapshot(` + <EuiBadge + color="#123456" + > + Alpha + </EuiBadge> + `); + const button = wrapper.find('EuiButtonEmpty'); + expect(button).toHaveLength(0); + }); + }); + + describe('with the active space and five inactive spaces', () => { + const { spaceTargets, namespaces } = getSpaceData(5); + const wrapper = createColumn(spaceTargets, namespaces); + + it('shows badges without button', async () => { + const badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button).toHaveLength(0); + }); + }); + + describe('with the active space, five inactive spaces, and one unauthorized space', () => { + const { spaceTargets, namespaces } = getSpaceData(5); + const wrapper = createColumn(spaceTargets, [...namespaces, '?']); + + it('shows badges without button', async () => { + const badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', '+1']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button).toHaveLength(0); + }); + }); + + describe('with the active space, five inactive spaces, and two unauthorized spaces', () => { + const { spaceTargets, namespaces } = getSpaceData(5); + const wrapper = createColumn(spaceTargets, [...namespaces, '?', '?']); + + it('shows badges without button', async () => { + const badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', '+2']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button).toHaveLength(0); + }); + }); + + describe('with the active space and six inactive spaces', () => { + const { spaceTargets, namespaces } = getSpaceData(6); + const wrapper = createColumn(spaceTargets, namespaces); + + it('shows badges with button', async () => { + let badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button.find('FormattedMessage').props()).toEqual({ + defaultMessage: '+{count} more', + id: 'xpack.spaces.management.shareToSpace.showMoreSpacesLink', + values: { count: 1 }, + }); + + button.simulate('click'); + badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot']); + }); + }); + + describe('with the active space, six inactive spaces, and one unauthorized space', () => { + const { spaceTargets, namespaces } = getSpaceData(6); + const wrapper = createColumn(spaceTargets, [...namespaces, '?']); + + it('shows badges with button', async () => { + let badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button.find('FormattedMessage').props()).toEqual({ + defaultMessage: '+{count} more', + id: 'xpack.spaces.management.shareToSpace.showMoreSpacesLink', + values: { count: 2 }, + }); + + button.simulate('click'); + badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', '+1']); + }); + }); + + describe('with the active space, six inactive spaces, and two unauthorized spaces', () => { + const { spaceTargets, namespaces } = getSpaceData(6); + const wrapper = createColumn(spaceTargets, [...namespaces, '?', '?']); + + it('shows badges with button', async () => { + let badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button.find('FormattedMessage').props()).toEqual({ + defaultMessage: '+{count} more', + id: 'xpack.spaces.management.shareToSpace.showMoreSpacesLink', + values: { count: 3 }, + }); + + button.simulate('click'); + badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', '+2']); + }); + }); + + describe('with only "all spaces"', () => { + const wrapper = createColumn([], ['*']); + + it('shows one badge without button', async () => { + const badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['* All spaces']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button).toHaveLength(0); + }); + }); + + describe('with "all spaces", the active space, six inactive spaces, and one unauthorized space', () => { + // same as assertions 'with only "all spaces"' test case; if "all spaces" is present, it supersedes everything else + const { spaceTargets, namespaces } = getSpaceData(6); + const wrapper = createColumn(spaceTargets, ['*', ...namespaces, '?']); + + it('shows one badge without button', async () => { + const badgeText = wrapper.find('EuiBadge').map((x) => x.render().text()); + expect(badgeText).toEqual(['* All spaces']); + const button = wrapper.find('EuiButtonEmpty'); + expect(button).toHaveLength(0); + }); + }); + }); +}); diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx index 93d7bb0170519f..0f988cf5a152db 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_column.tsx @@ -10,12 +10,10 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { EuiToolTip } from '@elastic/eui'; import { EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { - SavedObjectsManagementColumn, - SavedObjectsManagementRecord, -} from '../../../../../src/plugins/saved_objects_management/public'; +import { SavedObjectsManagementColumn } from '../../../../../src/plugins/saved_objects_management/public'; import { SpaceTarget } from './types'; import { SpacesManager } from '../spaces_manager'; +import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../common/constants'; import { getSpaceColor } from '..'; const SPACES_DISPLAY_COUNT = 5; @@ -33,64 +31,77 @@ const ColumnDisplay = ({ namespaces, data }: ColumnDataProps) => { return null; } - const authorized = namespaces?.filter((namespace) => namespace !== '?') ?? []; - const authorizedSpaceTargets: SpaceTarget[] = []; - authorized.forEach((namespace) => { - const spaceTarget = data.get(namespace); - if (spaceTarget === undefined) { - // in the event that a new space was created after this page has loaded, fall back to displaying the space ID - authorizedSpaceTargets.push({ - id: namespace, - name: namespace, - disabledFeatures: [], + const isSharedToAllSpaces = namespaces?.includes(ALL_SPACES_ID); + const unauthorizedCount = (namespaces?.filter((namespace) => namespace === UNKNOWN_SPACE) ?? []) + .length; + let displayedSpaces: SpaceTarget[]; + let button: ReactNode = null; + + if (isSharedToAllSpaces) { + displayedSpaces = [ + { + id: ALL_SPACES_ID, + name: i18n.translate('xpack.spaces.management.shareToSpace.allSpacesLabel', { + defaultMessage: `* All spaces`, + }), isActiveSpace: false, - }); - } else if (!spaceTarget.isActiveSpace) { - authorizedSpaceTargets.push(spaceTarget); - } - }); - const unauthorizedCount = (namespaces?.filter((namespace) => namespace === '?') ?? []).length; - const unauthorizedTooltip = i18n.translate( - 'xpack.spaces.management.shareToSpace.columnUnauthorizedLabel', - { defaultMessage: `You don't have permission to view these spaces.` } - ); + color: '#D3DAE6', + }, + ]; + } else { + const authorized = namespaces?.filter((namespace) => namespace !== UNKNOWN_SPACE) ?? []; + const authorizedSpaceTargets: SpaceTarget[] = []; + authorized.forEach((namespace) => { + const spaceTarget = data.get(namespace); + if (spaceTarget === undefined) { + // in the event that a new space was created after this page has loaded, fall back to displaying the space ID + authorizedSpaceTargets.push({ id: namespace, name: namespace, isActiveSpace: false }); + } else if (!spaceTarget.isActiveSpace) { + authorizedSpaceTargets.push(spaceTarget); + } + }); + displayedSpaces = isExpanded + ? authorizedSpaceTargets + : authorizedSpaceTargets.slice(0, SPACES_DISPLAY_COUNT); - const displayedSpaces = isExpanded - ? authorizedSpaceTargets - : authorizedSpaceTargets.slice(0, SPACES_DISPLAY_COUNT); - const showButton = authorizedSpaceTargets.length > SPACES_DISPLAY_COUNT; + if (authorizedSpaceTargets.length > SPACES_DISPLAY_COUNT) { + button = isExpanded ? ( + <EuiButtonEmpty size="xs" onClick={() => setIsExpanded(false)}> + <FormattedMessage + id="xpack.spaces.management.shareToSpace.showLessSpacesLink" + defaultMessage="show less" + /> + </EuiButtonEmpty> + ) : ( + <EuiButtonEmpty size="xs" onClick={() => setIsExpanded(true)}> + <FormattedMessage + id="xpack.spaces.management.shareToSpace.showMoreSpacesLink" + defaultMessage="+{count} more" + values={{ + count: authorizedSpaceTargets.length + unauthorizedCount - displayedSpaces.length, + }} + /> + </EuiButtonEmpty> + ); + } + } const unauthorizedCountBadge = - (isExpanded || !showButton) && unauthorizedCount > 0 ? ( + !isSharedToAllSpaces && (isExpanded || button === null) && unauthorizedCount > 0 ? ( <EuiFlexItem grow={false}> - <EuiToolTip content={unauthorizedTooltip}> + <EuiToolTip + content={ + <FormattedMessage + id="xpack.spaces.management.shareToSpace.columnUnauthorizedLabel" + defaultMessage="You don't have permission to view these spaces." + /> + } + > <EuiBadge color="#DDD">+{unauthorizedCount}</EuiBadge> </EuiToolTip> </EuiFlexItem> ) : null; - let button: ReactNode = null; - if (showButton) { - button = isExpanded ? ( - <EuiButtonEmpty size="xs" onClick={() => setIsExpanded(false)}> - <FormattedMessage - id="xpack.spaces.management.shareToSpace.showLessSpacesLink" - defaultMessage="show less" - /> - </EuiButtonEmpty> - ) : ( - <EuiButtonEmpty size="xs" onClick={() => setIsExpanded(true)}> - <FormattedMessage - id="xpack.spaces.management.shareToSpace.showMoreSpacesLink" - defaultMessage="+{count} more" - values={{ - count: authorizedSpaceTargets.length + unauthorizedCount - displayedSpaces.length, - }} - /> - </EuiButtonEmpty> - ); - } - return ( <EuiFlexGroup wrap responsive={false} gutterSize="xs"> {displayedSpaces.map(({ id, name, color }) => ( @@ -117,7 +128,7 @@ export class ShareToSpaceSavedObjectsManagementColumn description: i18n.translate('xpack.spaces.management.shareToSpace.columnDescription', { defaultMessage: 'The other spaces that this object is currently shared to', }), - render: (namespaces: string[] | undefined, _object: SavedObjectsManagementRecord) => ( + render: (namespaces: string[] | undefined) => ( <ColumnDisplay namespaces={namespaces} data={this.data} /> ), }; diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts index 0f0fa7d22214f2..6ce4c49c528af8 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.test.ts @@ -8,7 +8,7 @@ import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_ // import { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; import { spacesManagerMock } from '../spaces_manager/mocks'; import { ShareSavedObjectsToSpaceService } from '.'; -import { notificationServiceMock } from 'src/core/public/mocks'; +import { coreMock, notificationServiceMock } from 'src/core/public/mocks'; import { savedObjectsManagementPluginMock } from '../../../../../src/plugins/saved_objects_management/public/mocks'; describe('ShareSavedObjectsToSpaceService', () => { @@ -18,6 +18,7 @@ describe('ShareSavedObjectsToSpaceService', () => { spacesManager: spacesManagerMock.create(), notificationsSetup: notificationServiceMock.createSetupContract(), savedObjectsManagementSetup: savedObjectsManagementPluginMock.createSetupContract(), + getStartServices: coreMock.createSetup().getStartServices, }; const service = new ShareSavedObjectsToSpaceService(); diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts index 9f6e57c355380b..892731a0c57391 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/share_saved_objects_to_space_service.ts @@ -4,21 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import { NotificationsSetup } from 'src/core/public'; +import { NotificationsSetup, StartServicesAccessor } from 'src/core/public'; import { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public'; import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action'; // import { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column'; import { SpacesManager } from '../spaces_manager'; +import { PluginsStart } from '../plugin'; interface SetupDeps { spacesManager: SpacesManager; savedObjectsManagementSetup: SavedObjectsManagementPluginSetup; notificationsSetup: NotificationsSetup; + getStartServices: StartServicesAccessor<PluginsStart>; } export class ShareSavedObjectsToSpaceService { - public setup({ spacesManager, savedObjectsManagementSetup, notificationsSetup }: SetupDeps) { - const action = new ShareToSpaceSavedObjectsManagementAction(spacesManager, notificationsSetup); + public setup({ + spacesManager, + savedObjectsManagementSetup, + notificationsSetup, + getStartServices, + }: SetupDeps) { + const action = new ShareToSpaceSavedObjectsManagementAction( + spacesManager, + notificationsSetup, + getStartServices + ); savedObjectsManagementSetup.actions.register(action); // Note: this column is hidden for now because no saved objects are shareable. It should be uncommented when at least one saved object type is multi-namespace. // const column = new ShareToSpaceSavedObjectsManagementColumn(spacesManager); diff --git a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts index fe41f4a5fadc8b..8b62166baaaa64 100644 --- a/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts +++ b/x-pack/plugins/spaces/public/share_saved_objects_to_space/types.ts @@ -17,6 +17,6 @@ export interface ShareSavedObjectsToSpaceResponse { [spaceId: string]: SavedObjectsImportResponse; } -export interface SpaceTarget extends Space { +export interface SpaceTarget extends Omit<Space, 'disabledFeatures'> { isActiveSpace: boolean; } diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts index f666c823bd3654..0888448753353a 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.mock.ts @@ -21,6 +21,7 @@ function createSpacesManagerMock() { shareSavedObjectAdd: jest.fn().mockResolvedValue(undefined), shareSavedObjectRemove: jest.fn().mockResolvedValue(undefined), resolveCopySavedObjectsErrors: jest.fn().mockResolvedValue(undefined), + getShareSavedObjectPermissions: jest.fn().mockResolvedValue(undefined), redirectToSpaceSelector: jest.fn().mockResolvedValue(undefined), } as unknown) as jest.Mocked<SpacesManager>; } diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts index 508669361c23ff..7f005e37d96e90 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.test.ts @@ -104,4 +104,24 @@ describe('SpacesManager', () => { ); }); }); + + describe('#getShareSavedObjectPermissions', () => { + it('retrieves share permissions for the specified type and returns result', async () => { + const coreStart = coreMock.createStart(); + const shareToAllSpaces = Symbol(); + coreStart.http.get.mockResolvedValue({ shareToAllSpaces }); + const spacesManager = new SpacesManager(coreStart.http); + expect(coreStart.http.get).toHaveBeenCalledTimes(1); // initial call to get active space + + const result = await spacesManager.getShareSavedObjectPermissions('foo'); + expect(coreStart.http.get).toHaveBeenCalledTimes(2); + expect(coreStart.http.get).toHaveBeenLastCalledWith( + '/internal/spaces/_share_saved_object_permissions', + { + query: { type: 'foo' }, + } + ); + expect(result).toEqual({ shareToAllSpaces }); + }); + }); }); diff --git a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts index 2daf9ab420efc5..c81f7c17b7770d 100644 --- a/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts +++ b/x-pack/plugins/spaces/public/spaces_manager/spaces_manager.ts @@ -106,6 +106,12 @@ export class SpacesManager { }); } + public async getShareSavedObjectPermissions( + type: string + ): Promise<{ shareToAllSpaces: boolean }> { + return this.http.get('/internal/spaces/_share_saved_object_permissions', { query: { type } }); + } + public async shareSavedObjectAdd(object: SavedObject, spaces: string[]): Promise<void> { return this.http.post(`/api/spaces/_share_saved_object_add`, { body: JSON.stringify({ object, spaces }), diff --git a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.mock.ts b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.mock.ts index 10f6292abf319b..e38842b8799acc 100644 --- a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.mock.ts +++ b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.mock.ts @@ -10,7 +10,6 @@ import { SpacesClient } from './spaces_client'; const createSpacesClientMock = () => (({ - canEnumerateSpaces: jest.fn().mockResolvedValue(true), getAll: jest.fn().mockResolvedValue([ { id: DEFAULT_SPACE_ID, diff --git a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts index 1090b029069d21..4502b081034aa4 100644 --- a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts +++ b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.test.ts @@ -375,131 +375,6 @@ describe('#getAll', () => { }); }); -describe('#canEnumerateSpaces', () => { - describe(`authorization is null`, () => { - test(`returns true`, async () => { - const mockAuditLogger = createMockAuditLogger(); - const mockDebugLogger = createMockDebugLogger(); - const mockConfig = createMockConfig(); - const authorization = null; - const request = Symbol() as any; - - const client = new SpacesClient( - mockAuditLogger as any, - mockDebugLogger, - authorization, - null, - mockConfig, - null, - request - ); - - const canEnumerateSpaces = await client.canEnumerateSpaces(); - expect(canEnumerateSpaces).toEqual(true); - expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); - expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); - }); - }); - - describe(`authorization.mode.useRbacForRequest is false`, () => { - test(`returns true`, async () => { - const mockAuditLogger = createMockAuditLogger(); - const mockDebugLogger = createMockDebugLogger(); - const mockConfig = createMockConfig(); - const { mockAuthorization } = createMockAuthorization(); - mockAuthorization.mode.useRbacForRequest.mockReturnValue(false); - const request = Symbol() as any; - - const client = new SpacesClient( - mockAuditLogger as any, - mockDebugLogger, - mockAuthorization, - null, - mockConfig, - null, - request - ); - const canEnumerateSpaces = await client.canEnumerateSpaces(); - - expect(canEnumerateSpaces).toEqual(true); - expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); - expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); - }); - }); - - describe('useRbacForRequest is true', () => { - test(`returns false if user is not authorized to enumerate spaces`, async () => { - const username = Symbol(); - const mockAuditLogger = createMockAuditLogger(); - const mockDebugLogger = createMockDebugLogger(); - const mockConfig = createMockConfig(); - const { mockAuthorization, mockCheckPrivilegesGlobally } = createMockAuthorization(); - mockAuthorization.mode.useRbacForRequest.mockReturnValue(true); - mockCheckPrivilegesGlobally.mockReturnValue({ - username, - hasAllRequested: false, - }); - const request = Symbol() as any; - - const client = new SpacesClient( - mockAuditLogger as any, - mockDebugLogger, - mockAuthorization, - null, - mockConfig, - null, - request - ); - - const canEnumerateSpaces = await client.canEnumerateSpaces(); - expect(canEnumerateSpaces).toEqual(false); - - expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ - kibana: mockAuthorization.actions.space.manage, - }); - - expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); - expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); - }); - - test(`returns true if user is authorized to enumerate spaces`, async () => { - const username = Symbol(); - const mockAuditLogger = createMockAuditLogger(); - const mockDebugLogger = createMockDebugLogger(); - const mockConfig = createMockConfig(); - const { mockAuthorization, mockCheckPrivilegesGlobally } = createMockAuthorization(); - mockAuthorization.mode.useRbacForRequest.mockReturnValue(true); - mockCheckPrivilegesGlobally.mockReturnValue({ - username, - hasAllRequested: true, - }); - const request = Symbol() as any; - - const client = new SpacesClient( - mockAuditLogger as any, - mockDebugLogger, - mockAuthorization, - null, - mockConfig, - null, - request - ); - - const canEnumerateSpaces = await client.canEnumerateSpaces(); - expect(canEnumerateSpaces).toEqual(true); - - expect(mockAuthorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); - expect(mockCheckPrivilegesGlobally).toHaveBeenCalledWith({ - kibana: mockAuthorization.actions.space.manage, - }); - - expect(mockAuditLogger.spacesAuthorizationFailure).toHaveBeenCalledTimes(0); - expect(mockAuditLogger.spacesAuthorizationSuccess).toHaveBeenCalledTimes(0); - }); - }); -}); - describe('#get', () => { const savedObject = { id: 'foo', diff --git a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts index 5ef0b5375d7969..50e7182b766863 100644 --- a/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts +++ b/x-pack/plugins/spaces/server/lib/spaces_client/spaces_client.ts @@ -47,21 +47,6 @@ export class SpacesClient { private readonly request: KibanaRequest ) {} - public async canEnumerateSpaces(): Promise<boolean> { - if (this.useRbac()) { - const checkPrivileges = this.authorization!.checkPrivilegesWithRequest(this.request); - const { hasAllRequested } = await checkPrivileges.globally({ - kibana: this.authorization!.actions.space.manage, - }); - this.debugLogger(`SpacesClient.canEnumerateSpaces, using RBAC. Result: ${hasAllRequested}`); - return hasAllRequested; - } - - // If not RBAC, then security isn't enabled and we can enumerate all spaces - this.debugLogger(`SpacesClient.canEnumerateSpaces, NOT USING RBAC. Result: true`); - return true; - } - public async getAll(purpose: GetSpacePurpose = 'any'): Promise<Space[]> { if (!SUPPORTED_GET_SPACE_PURPOSES.includes(purpose)) { throw Boom.badRequest(`unsupported space purpose: ${purpose}`); diff --git a/x-pack/plugins/spaces/server/plugin.ts b/x-pack/plugins/spaces/server/plugin.ts index af54effcaeca69..a9ba5ac2dc6dea 100644 --- a/x-pack/plugins/spaces/server/plugin.ts +++ b/x-pack/plugins/spaces/server/plugin.ts @@ -107,6 +107,7 @@ export class Plugin { getStartServices: core.getStartServices, getImportExportObjectLimit: core.savedObjects.getImportExportObjectLimit, spacesService, + authorization: plugins.security ? plugins.security.authz : null, }); const internalRouter = core.http.createRouter(); diff --git a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts index ce93591f492f16..fa8ef1882099cf 100644 --- a/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts +++ b/x-pack/plugins/spaces/server/routes/api/__fixtures__/create_mock_so_service.ts @@ -4,48 +4,45 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SavedObjectsClientContract, SavedObjectsErrorHelpers } from 'src/core/server'; -import { coreMock, savedObjectsTypeRegistryMock } from '../../../../../../../src/core/server/mocks'; +import { SavedObject, SavedObjectsUpdateResponse, SavedObjectsErrorHelpers } from 'src/core/server'; +import { + coreMock, + savedObjectsClientMock, + savedObjectsTypeRegistryMock, +} from '../../../../../../../src/core/server/mocks'; export const createMockSavedObjectsService = (spaces: any[] = []) => { - const mockSavedObjectsClientContract = ({ - get: jest.fn((type, id) => { - const result = spaces.filter((s) => s.id === id); - if (!result.length) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - return result[0]; - }), - find: jest.fn(() => { - return { - total: spaces.length, - saved_objects: spaces, - }; - }), - create: jest.fn((type, attributes, { id }) => { - if (spaces.find((s) => s.id === id)) { - throw SavedObjectsErrorHelpers.decorateConflictError(new Error(), 'space conflict'); - } - return {}; - }), - update: jest.fn((type, id) => { - if (!spaces.find((s) => s.id === id)) { - throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); - } - return {}; - }), - delete: jest.fn((type: string, id: string) => { - return {}; - }), - deleteByNamespace: jest.fn(), - } as unknown) as jest.Mocked<SavedObjectsClientContract>; + const typeRegistry = savedObjectsTypeRegistryMock.create(); + const savedObjectsClient = savedObjectsClientMock.create(); + savedObjectsClient.get.mockImplementation((type, id) => { + const result = spaces.filter((s) => s.id === id); + if (!result.length) { + throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); + } + return Promise.resolve(result[0]); + }); + savedObjectsClient.find.mockResolvedValue({ + page: 1, + per_page: 20, + total: spaces.length, + saved_objects: spaces, + }); + savedObjectsClient.create.mockImplementation((_type, _attributes, options) => { + if (spaces.find((s) => s.id === options?.id)) { + throw SavedObjectsErrorHelpers.decorateConflictError(new Error(), 'space conflict'); + } + return Promise.resolve({} as SavedObject); + }); + savedObjectsClient.update.mockImplementation((type, id, _attributes, _options) => { + if (!spaces.find((s) => s.id === id)) { + throw SavedObjectsErrorHelpers.createGenericNotFoundError(type, id); + } + return Promise.resolve({} as SavedObjectsUpdateResponse); + }); const { savedObjects } = coreMock.createStart(); - - const typeRegistry = savedObjectsTypeRegistryMock.create(); savedObjects.getTypeRegistry.mockReturnValue(typeRegistry); + savedObjects.getScopedClient.mockReturnValue(savedObjectsClient); - savedObjects.getScopedClient.mockReturnValue(mockSavedObjectsClientContract); - - return savedObjects; + return { savedObjects, typeRegistry, savedObjectsClient }; }; diff --git a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts index dce6de908cfcbc..341e5cf3bfbe0f 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts @@ -71,7 +71,8 @@ describe('copy to space', () => { const log = loggingSystemMock.create().get('spaces'); const coreStart = coreMock.createStart(); - coreStart.savedObjects = createMockSavedObjectsService(spaces); + const { savedObjects } = createMockSavedObjectsService(spaces); + coreStart.savedObjects = savedObjects; const service = new SpacesService(log); const spacesService = await service.setup({ @@ -102,6 +103,7 @@ describe('copy to space', () => { getImportExportObjectLimit: () => 1000, log, spacesService, + authorization: null, // not needed for this route }); const [ diff --git a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts index 5461aaf1e36ea8..4fe81027c35085 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts @@ -73,6 +73,7 @@ describe('Spaces Public API', () => { getImportExportObjectLimit: () => 1000, log, spacesService, + authorization: null, // not needed for this route }); const [routeDefinition, routeHandler] = router.delete.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts index ac9a46ee9c3fac..47863999366624 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get.test.ts @@ -67,6 +67,7 @@ describe('GET space', () => { getImportExportObjectLimit: () => 1000, log, spacesService, + authorization: null, // not needed for this route }); return { diff --git a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts index a9b701a8ea395e..deb8434497ae7c 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/get_all.test.ts @@ -67,6 +67,7 @@ describe('GET /spaces/space', () => { getImportExportObjectLimit: () => 1000, log, spacesService, + authorization: null, // not needed for this route }); return { diff --git a/x-pack/plugins/spaces/server/routes/api/external/index.ts b/x-pack/plugins/spaces/server/routes/api/external/index.ts index 079f690bfe5463..f093f26b4bdee1 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/index.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/index.ts @@ -5,6 +5,7 @@ */ import { Logger, IRouter, CoreSetup } from 'src/core/server'; +import { SecurityPluginSetup } from '../../../../../security/server'; import { initDeleteSpacesApi } from './delete'; import { initGetSpaceApi } from './get'; import { initGetAllSpacesApi } from './get_all'; @@ -12,8 +13,7 @@ import { initPostSpacesApi } from './post'; import { initPutSpacesApi } from './put'; import { SpacesServiceSetup } from '../../../spaces_service/spaces_service'; import { initCopyToSpacesApi } from './copy_to_space'; -import { initShareAddSpacesApi } from './share_add_spaces'; -import { initShareRemoveSpacesApi } from './share_remove_spaces'; +import { initShareToSpacesApi } from './share_to_space'; export interface ExternalRouteDeps { externalRouter: IRouter; @@ -21,6 +21,7 @@ export interface ExternalRouteDeps { getImportExportObjectLimit: () => number; spacesService: SpacesServiceSetup; log: Logger; + authorization: SecurityPluginSetup['authz'] | null; } export function initExternalSpacesApi(deps: ExternalRouteDeps) { @@ -30,6 +31,5 @@ export function initExternalSpacesApi(deps: ExternalRouteDeps) { initPostSpacesApi(deps); initPutSpacesApi(deps); initCopyToSpacesApi(deps); - initShareAddSpacesApi(deps); - initShareRemoveSpacesApi(deps); + initShareToSpacesApi(deps); } diff --git a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts index 6aa89b36b020ab..6aeec251e33e48 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/post.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/post.test.ts @@ -67,6 +67,7 @@ describe('Spaces Public API', () => { getImportExportObjectLimit: () => 1000, log, spacesService, + authorization: null, // not needed for this route }); const [routeDefinition, routeHandler] = router.post.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts index ebdffa20a6c8e9..326837f8995f0b 100644 --- a/x-pack/plugins/spaces/server/routes/api/external/put.test.ts +++ b/x-pack/plugins/spaces/server/routes/api/external/put.test.ts @@ -68,6 +68,7 @@ describe('PUT /api/spaces/space', () => { getImportExportObjectLimit: () => 1000, log, spacesService, + authorization: null, // not needed for this route }); const [routeDefinition, routeHandler] = router.put.mock.calls[0]; diff --git a/x-pack/plugins/spaces/server/routes/api/external/share_add_spaces.ts b/x-pack/plugins/spaces/server/routes/api/external/share_add_spaces.ts deleted file mode 100644 index ee61ccd2d5e410..00000000000000 --- a/x-pack/plugins/spaces/server/routes/api/external/share_add_spaces.ts +++ /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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { schema } from '@kbn/config-schema'; -import { wrapError } from '../../../lib/errors'; -import { ExternalRouteDeps } from '.'; -import { SPACE_ID_REGEX } from '../../../lib/space_schema'; -import { createLicensedRouteHandler } from '../../lib'; - -const uniq = <T>(arr: T[]): T[] => Array.from(new Set<T>(arr)); -export function initShareAddSpacesApi(deps: ExternalRouteDeps) { - const { externalRouter, getStartServices } = deps; - - externalRouter.post( - { - path: '/api/spaces/_share_saved_object_add', - validate: { - body: schema.object({ - spaces: schema.arrayOf( - schema.string({ - validate: (value) => { - if (!SPACE_ID_REGEX.test(value)) { - return `lower case, a-z, 0-9, "_", and "-" are allowed`; - } - }, - }), - { - validate: (spaceIds) => { - if (!spaceIds.length) { - return 'must specify one or more space ids'; - } else if (uniq(spaceIds).length !== spaceIds.length) { - return 'duplicate space ids are not allowed'; - } - }, - } - ), - object: schema.object({ - type: schema.string(), - id: schema.string(), - }), - }), - }, - }, - createLicensedRouteHandler(async (_context, request, response) => { - const [startServices] = await getStartServices(); - const scopedClient = startServices.savedObjects.getScopedClient(request); - - const spaces = request.body.spaces; - const { type, id } = request.body.object; - - try { - await scopedClient.addToNamespaces(type, id, spaces); - } catch (error) { - return response.customError(wrapError(error)); - } - return response.noContent(); - }) - ); -} diff --git a/x-pack/plugins/spaces/server/routes/api/external/share_remove_spaces.ts b/x-pack/plugins/spaces/server/routes/api/external/share_remove_spaces.ts deleted file mode 100644 index d03185ea7aa095..00000000000000 --- a/x-pack/plugins/spaces/server/routes/api/external/share_remove_spaces.ts +++ /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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { schema } from '@kbn/config-schema'; -import { wrapError } from '../../../lib/errors'; -import { ExternalRouteDeps } from '.'; -import { SPACE_ID_REGEX } from '../../../lib/space_schema'; -import { createLicensedRouteHandler } from '../../lib'; - -const uniq = <T>(arr: T[]): T[] => Array.from(new Set<T>(arr)); -export function initShareRemoveSpacesApi(deps: ExternalRouteDeps) { - const { externalRouter, getStartServices } = deps; - - externalRouter.post( - { - path: '/api/spaces/_share_saved_object_remove', - validate: { - body: schema.object({ - spaces: schema.arrayOf( - schema.string({ - validate: (value) => { - if (!SPACE_ID_REGEX.test(value)) { - return `lower case, a-z, 0-9, "_", and "-" are allowed`; - } - }, - }), - { - validate: (spaceIds) => { - if (!spaceIds.length) { - return 'must specify one or more space ids'; - } else if (uniq(spaceIds).length !== spaceIds.length) { - return 'duplicate space ids are not allowed'; - } - }, - } - ), - object: schema.object({ - type: schema.string(), - id: schema.string(), - }), - }), - }, - }, - createLicensedRouteHandler(async (_context, request, response) => { - const [startServices] = await getStartServices(); - const scopedClient = startServices.savedObjects.getScopedClient(request); - - const spaces = request.body.spaces; - const { type, id } = request.body.object; - - try { - await scopedClient.deleteFromNamespaces(type, id, spaces); - } catch (error) { - return response.customError(wrapError(error)); - } - return response.noContent(); - }) - ); -} diff --git a/x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts new file mode 100644 index 00000000000000..3af1d9d245d10b --- /dev/null +++ b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.test.ts @@ -0,0 +1,325 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import * as Rx from 'rxjs'; +import { + createSpaces, + createMockSavedObjectsRepository, + mockRouteContext, + mockRouteContextWithInvalidLicense, + createMockSavedObjectsService, +} from '../__fixtures__'; +import { CoreSetup, kibanaResponseFactory, RouteValidatorConfig } from 'src/core/server'; +import { + loggingSystemMock, + httpServiceMock, + httpServerMock, + coreMock, +} from 'src/core/server/mocks'; +import { SpacesService } from '../../../spaces_service'; +import { SpacesAuditLogger } from '../../../lib/audit_logger'; +import { SpacesClient } from '../../../lib/spaces_client'; +import { initShareToSpacesApi } from './share_to_space'; +import { spacesConfig } from '../../../lib/__fixtures__'; +import { securityMock } from '../../../../../security/server/mocks'; +import { ObjectType } from '@kbn/config-schema'; +import { SecurityPluginSetup } from '../../../../../security/server'; + +describe('share to space', () => { + const spacesSavedObjects = createSpaces(); + const spaces = spacesSavedObjects.map((s) => ({ id: s.id, ...s.attributes })); + + const setup = async ({ + authorization = null, + }: { authorization?: SecurityPluginSetup['authz'] | null } = {}) => { + const httpService = httpServiceMock.createSetupContract(); + const router = httpService.createRouter(); + const savedObjectsRepositoryMock = createMockSavedObjectsRepository(spacesSavedObjects); + const log = loggingSystemMock.create().get('spaces'); + const coreStart = coreMock.createStart(); + const { savedObjects, savedObjectsClient } = createMockSavedObjectsService(spaces); + coreStart.savedObjects = savedObjects; + + const service = new SpacesService(log); + const spacesService = await service.setup({ + http: (httpService as unknown) as CoreSetup['http'], + getStartServices: async () => [coreStart, {}, {}], + authorization, + auditLogger: {} as SpacesAuditLogger, + config$: Rx.of(spacesConfig), + }); + + spacesService.scopedClient = jest.fn((req: any) => { + return Promise.resolve( + new SpacesClient( + null as any, + () => null, + null, + savedObjectsRepositoryMock, + spacesConfig, + savedObjectsRepositoryMock, + req + ) + ); + }); + + initShareToSpacesApi({ + externalRouter: router, + getStartServices: async () => [coreStart, {}, {}], + getImportExportObjectLimit: () => 1000, + log, + spacesService, + authorization, + }); + + const [ + [shareAdd, ctsRouteHandler], + [shareRemove, resolveRouteHandler], + ] = router.post.mock.calls; + + const [[, permissionsRouteHandler]] = router.get.mock.calls; + + return { + coreStart, + savedObjectsClient, + shareAdd: { + routeValidation: shareAdd.validate as RouteValidatorConfig<{}, {}, {}>, + routeHandler: ctsRouteHandler, + }, + shareRemove: { + routeValidation: shareRemove.validate as RouteValidatorConfig<{}, {}, {}>, + routeHandler: resolveRouteHandler, + }, + sharePermissions: { + routeHandler: permissionsRouteHandler, + }, + savedObjectsRepositoryMock, + }; + }; + + describe('GET /internal/spaces/_share_saved_object_permissions', () => { + it('returns true when security is not enabled', async () => { + const { sharePermissions } = await setup(); + + const request = httpServerMock.createKibanaRequest({ query: { type: 'foo' }, method: 'get' }); + const response = await sharePermissions.routeHandler( + mockRouteContext, + request, + kibanaResponseFactory + ); + + const { status, payload } = response; + expect(status).toEqual(200); + expect(payload).toEqual({ shareToAllSpaces: true }); + }); + + it('returns false when the user is not authorized globally', async () => { + const authorization = securityMock.createSetup().authz; + const globalPrivilegesCheck = jest.fn().mockResolvedValue({ hasAllRequested: false }); + authorization.checkPrivilegesWithRequest.mockReturnValue({ + globally: globalPrivilegesCheck, + }); + const { sharePermissions } = await setup({ authorization }); + + const request = httpServerMock.createKibanaRequest({ query: { type: 'foo' }, method: 'get' }); + const response = await sharePermissions.routeHandler( + mockRouteContext, + request, + kibanaResponseFactory + ); + + const { status, payload } = response; + expect(status).toEqual(200); + expect(payload).toEqual({ shareToAllSpaces: false }); + + expect(authorization.checkPrivilegesWithRequest).toHaveBeenCalledTimes(1); + expect(authorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); + }); + + it('returns true when the user is authorized globally', async () => { + const authorization = securityMock.createSetup().authz; + const globalPrivilegesCheck = jest.fn().mockResolvedValue({ hasAllRequested: true }); + authorization.checkPrivilegesWithRequest.mockReturnValue({ + globally: globalPrivilegesCheck, + }); + const { sharePermissions } = await setup({ authorization }); + + const request = httpServerMock.createKibanaRequest({ query: { type: 'foo' }, method: 'get' }); + const response = await sharePermissions.routeHandler( + mockRouteContext, + request, + kibanaResponseFactory + ); + + const { status, payload } = response; + expect(status).toEqual(200); + expect(payload).toEqual({ shareToAllSpaces: true }); + + expect(authorization.checkPrivilegesWithRequest).toHaveBeenCalledTimes(1); + expect(authorization.checkPrivilegesWithRequest).toHaveBeenCalledWith(request); + }); + }); + + describe('POST /api/spaces/_share_saved_object_add', () => { + const object = { id: 'foo', type: 'bar' }; + + it(`returns http/403 when the license is invalid`, async () => { + const { shareAdd } = await setup(); + + const request = httpServerMock.createKibanaRequest({ method: 'post' }); + const response = await shareAdd.routeHandler( + mockRouteContextWithInvalidLicense, + request, + kibanaResponseFactory + ); + + expect(response.status).toEqual(403); + expect(response.payload).toEqual({ + message: 'License is invalid for spaces', + }); + }); + + it(`requires at least 1 space ID`, async () => { + const { shareAdd } = await setup(); + const payload = { spaces: [], object }; + + expect(() => + (shareAdd.routeValidation.body as ObjectType).validate(payload) + ).toThrowErrorMatchingInlineSnapshot(`"[spaces]: must specify one or more space ids"`); + }); + + it(`requires space IDs to be unique`, async () => { + const { shareAdd } = await setup(); + const payload = { spaces: ['a-space', 'a-space'], object }; + + expect(() => + (shareAdd.routeValidation.body as ObjectType).validate(payload) + ).toThrowErrorMatchingInlineSnapshot(`"[spaces]: duplicate space ids are not allowed"`); + }); + + it(`requires well-formed space IDS`, async () => { + const { shareAdd } = await setup(); + const payload = { spaces: ['a-space', 'a-space-invalid-!@#$%^&*()'], object }; + + expect(() => + (shareAdd.routeValidation.body as ObjectType).validate(payload) + ).toThrowErrorMatchingInlineSnapshot( + `"[spaces.1]: lower case, a-z, 0-9, \\"_\\", and \\"-\\" are allowed, OR \\"*\\""` + ); + }); + + it(`allows all spaces ("*")`, async () => { + const { shareAdd } = await setup(); + const payload = { spaces: ['*'], object }; + + expect(() => + (shareAdd.routeValidation.body as ObjectType).validate(payload) + ).not.toThrowError(); + }); + + it('adds the object to the specified space(s)', async () => { + const { shareAdd, savedObjectsClient } = await setup(); + const payload = { spaces: ['a-space', 'b-space'], object }; + + const request = httpServerMock.createKibanaRequest({ body: payload, method: 'post' }); + const response = await shareAdd.routeHandler( + mockRouteContext, + request, + kibanaResponseFactory + ); + + const { status } = response; + expect(status).toEqual(204); + expect(savedObjectsClient.addToNamespaces).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.addToNamespaces).toHaveBeenCalledWith( + payload.object.type, + payload.object.id, + payload.spaces + ); + }); + }); + + describe('POST /api/spaces/_share_saved_object_remove', () => { + const object = { id: 'foo', type: 'bar' }; + + it(`returns http/403 when the license is invalid`, async () => { + const { shareRemove } = await setup(); + + const request = httpServerMock.createKibanaRequest({ + method: 'post', + }); + + const response = await shareRemove.routeHandler( + mockRouteContextWithInvalidLicense, + request, + kibanaResponseFactory + ); + + expect(response.status).toEqual(403); + expect(response.payload).toEqual({ + message: 'License is invalid for spaces', + }); + }); + + it(`requires at least 1 space ID`, async () => { + const { shareRemove } = await setup(); + const payload = { spaces: [], object }; + + expect(() => + (shareRemove.routeValidation.body as ObjectType).validate(payload) + ).toThrowErrorMatchingInlineSnapshot(`"[spaces]: must specify one or more space ids"`); + }); + + it(`requires space IDs to be unique`, async () => { + const { shareRemove } = await setup(); + const payload = { spaces: ['a-space', 'a-space'], object }; + + expect(() => + (shareRemove.routeValidation.body as ObjectType).validate(payload) + ).toThrowErrorMatchingInlineSnapshot(`"[spaces]: duplicate space ids are not allowed"`); + }); + + it(`requires well-formed space IDS`, async () => { + const { shareRemove } = await setup(); + const payload = { spaces: ['a-space', 'a-space-invalid-!@#$%^&*()'], object }; + + expect(() => + (shareRemove.routeValidation.body as ObjectType).validate(payload) + ).toThrowErrorMatchingInlineSnapshot( + `"[spaces.1]: lower case, a-z, 0-9, \\"_\\", and \\"-\\" are allowed, OR \\"*\\""` + ); + }); + + it(`allows all spaces ("*")`, async () => { + const { shareRemove } = await setup(); + const payload = { spaces: ['*'], object }; + + expect(() => + (shareRemove.routeValidation.body as ObjectType).validate(payload) + ).not.toThrowError(); + }); + + it('removes the object from the specified space(s)', async () => { + const { shareRemove, savedObjectsClient } = await setup(); + const payload = { spaces: ['a-space', 'b-space'], object }; + + const request = httpServerMock.createKibanaRequest({ body: payload, method: 'post' }); + const response = await shareRemove.routeHandler( + mockRouteContext, + request, + kibanaResponseFactory + ); + + const { status } = response; + expect(status).toEqual(204); + expect(savedObjectsClient.deleteFromNamespaces).toHaveBeenCalledTimes(1); + expect(savedObjectsClient.deleteFromNamespaces).toHaveBeenCalledWith( + payload.object.type, + payload.object.id, + payload.spaces + ); + }); + }); +}); diff --git a/x-pack/plugins/spaces/server/routes/api/external/share_to_space.ts b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.ts new file mode 100644 index 00000000000000..7acf9e3e6e3d02 --- /dev/null +++ b/x-pack/plugins/spaces/server/routes/api/external/share_to_space.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; + * you may not use this file except in compliance with the Elastic License. + */ + +import { schema } from '@kbn/config-schema'; +import { wrapError } from '../../../lib/errors'; +import { ExternalRouteDeps } from '.'; +import { ALL_SPACES_ID } from '../../../../common/constants'; +import { SPACE_ID_REGEX } from '../../../lib/space_schema'; +import { createLicensedRouteHandler } from '../../lib'; + +const uniq = <T>(arr: T[]): T[] => Array.from(new Set<T>(arr)); +export function initShareToSpacesApi(deps: ExternalRouteDeps) { + const { externalRouter, getStartServices, authorization } = deps; + + const shareSchema = schema.object({ + spaces: schema.arrayOf( + schema.string({ + validate: (value) => { + if (value !== ALL_SPACES_ID && !SPACE_ID_REGEX.test(value)) { + return `lower case, a-z, 0-9, "_", and "-" are allowed, OR "*"`; + } + }, + }), + { + validate: (spaceIds) => { + if (!spaceIds.length) { + return 'must specify one or more space ids'; + } else if (uniq(spaceIds).length !== spaceIds.length) { + return 'duplicate space ids are not allowed'; + } + }, + } + ), + object: schema.object({ type: schema.string(), id: schema.string() }), + }); + + externalRouter.get( + { + path: '/internal/spaces/_share_saved_object_permissions', + validate: { query: schema.object({ type: schema.string() }) }, + }, + createLicensedRouteHandler(async (_context, request, response) => { + let shareToAllSpaces = true; + const { type } = request.query; + + if (authorization) { + try { + const checkPrivileges = authorization.checkPrivilegesWithRequest(request); + shareToAllSpaces = ( + await checkPrivileges.globally({ + kibana: authorization.actions.savedObject.get(type, 'share_to_space'), + }) + ).hasAllRequested; + } catch (error) { + return response.customError(wrapError(error)); + } + } + return response.ok({ body: { shareToAllSpaces } }); + }) + ); + + externalRouter.post( + { path: '/api/spaces/_share_saved_object_add', validate: { body: shareSchema } }, + createLicensedRouteHandler(async (_context, request, response) => { + const [startServices] = await getStartServices(); + const scopedClient = startServices.savedObjects.getScopedClient(request); + + const spaces = request.body.spaces; + const { type, id } = request.body.object; + + try { + await scopedClient.addToNamespaces(type, id, spaces); + } catch (error) { + return response.customError(wrapError(error)); + } + return response.noContent(); + }) + ); + + externalRouter.post( + { path: '/api/spaces/_share_saved_object_remove', validate: { body: shareSchema } }, + createLicensedRouteHandler(async (_context, request, response) => { + const [startServices] = await getStartServices(); + const scopedClient = startServices.savedObjects.getScopedClient(request); + + const spaces = request.body.spaces; + const { type, id } = request.body.object; + + try { + await scopedClient.deleteFromNamespaces(type, id, spaces); + } catch (error) { + return response.customError(wrapError(error)); + } + return response.noContent(); + }) + ); +} diff --git a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts index a65e0431aef920..4c8e93acd68ac0 100644 --- a/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts +++ b/x-pack/plugins/spaces/server/saved_objects/spaces_saved_objects_client.ts @@ -20,6 +20,7 @@ import { SavedObjectsUtils, ISavedObjectTypeRegistry, } from '../../../../../src/core/server'; +import { ALL_SPACES_ID } from '../../common/constants'; import { SpacesServiceSetup } from '../spaces_service/spaces_service'; import { spaceIdToNamespace } from '../lib/utils/namespace'; import { SpacesClient } from '../lib/spaces_client'; @@ -169,7 +170,7 @@ export class SpacesSavedObjectsClient implements SavedObjectsClientContract { try { const availableSpaces = await spacesClient.getAll('findSavedObjects'); - if (namespaces.includes('*')) { + if (namespaces.includes(ALL_SPACES_ID)) { namespaces = availableSpaces.map((space) => space.id); } else { namespaces = namespaces.filter((namespace) => diff --git a/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts b/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts index 6b8680271635be..00a364bb7543ef 100644 --- a/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts +++ b/x-pack/test/functional/page_objects/copy_saved_objects_to_space_page.ts @@ -37,9 +37,9 @@ export function CopySavedObjectsToSpacePageProvider({ if (!overwrite) { const radio = await testSubjects.find('cts-copyModeControl-overwriteRadioGroup'); // a radio button consists of a div tag that contains an input, a div, and a label - // we can't click the input directly, need to go up one level and click the parent div - const div = await radio.findByXpath("//div[input[@id='overwriteDisabled']]"); - await div.click(); + // we can't click the input directly, need to click the label + const label = await radio.findByCssSelector('label[for="overwriteDisabled"]'); + await label.click(); } await testSubjects.click(`cts-space-selector-row-${destinationSpaceId}`); }, diff --git a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json index 4c0447c29c8f9c..d9d5c6f9c58081 100644 --- a/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json +++ b/x-pack/test/saved_object_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json @@ -347,6 +347,23 @@ } } +{ + "type": "doc", + "value": { + "id": "sharedtype:all_spaces", + "index": ".kibana", + "source": { + "sharedtype": { + "title": "A shared saved-object in all spaces" + }, + "type": "sharedtype", + "namespaces": ["*"], + "updated_at": "2017-09-21T18:59:16.270Z" + }, + "type": "doc" + } +} + { "type": "doc", "value": { diff --git a/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_cases.ts b/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_cases.ts index 190b12e038b276..e8558acc2c1f7d 100644 --- a/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_cases.ts +++ b/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_cases.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { SPACES } from './spaces'; +import { SPACES, ALL_SPACES_ID } from './spaces'; import { TestCase } from './types'; const { @@ -32,6 +32,11 @@ export const SAVED_OBJECT_TEST_CASES: Record<string, CommonTestCase> = Object.fr id: 'space2-isolatedtype-id', expectedNamespaces: [SPACE_2_ID], }), + MULTI_NAMESPACE_ALL_SPACES: Object.freeze({ + type: 'sharedtype', + id: 'all_spaces', + expectedNamespaces: [ALL_SPACES_ID], + }), MULTI_NAMESPACE_DEFAULT_AND_SPACE_1: Object.freeze({ type: 'sharedtype', id: 'default_and_space_1', diff --git a/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts b/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts index 9d4b5e80e9c3db..511d183145a305 100644 --- a/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts +++ b/x-pack/test/saved_object_api_integration/common/lib/saved_object_test_utils.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; -import { SPACES } from './spaces'; +import { SPACES, ALL_SPACES_ID } from './spaces'; import { AUTHENTICATION } from './authentication'; import { TestCase, TestUser, ExpectResponseBody } from './types'; @@ -73,7 +73,10 @@ export const getTestTitle = ( }; export const isUserAuthorizedAtSpace = (user: TestUser | undefined, namespace: string) => - !user || user.authorizedAtSpaces.includes('*') || user.authorizedAtSpaces.includes(namespace); + !user || + (user.authorizedAtSpaces.length > 0 && namespace === ALL_SPACES_ID) || + user.authorizedAtSpaces.includes('*') || + user.authorizedAtSpaces.includes(namespace); export const getRedactedNamespaces = ( user: TestUser | undefined, diff --git a/x-pack/test/saved_object_api_integration/common/lib/spaces.ts b/x-pack/test/saved_object_api_integration/common/lib/spaces.ts index a9c552d4ccd789..7b21c2f0245fa8 100644 --- a/x-pack/test/saved_object_api_integration/common/lib/spaces.ts +++ b/x-pack/test/saved_object_api_integration/common/lib/spaces.ts @@ -15,3 +15,5 @@ export const SPACES = { spaceId: 'default', }, }; + +export const ALL_SPACES_ID = '*'; diff --git a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts index b1608946b8e62f..e10c1905d85418 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/bulk_create.ts @@ -8,9 +8,8 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; import { SavedObjectsErrorHelpers } from '../../../../../src/core/server'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; -import { SPACES } from '../lib/spaces'; +import { SPACES, ALL_SPACES_ID } from '../lib/spaces'; import { - createRequest, expectResponses, getUrlPrefix, getTestTitle, @@ -18,29 +17,57 @@ import { } from '../lib/saved_object_test_utils'; import { ExpectResponseBody, TestCase, TestDefinition, TestSuite, TestUser } from '../lib/types'; +const { + DEFAULT: { spaceId: DEFAULT_SPACE_ID }, + SPACE_1: { spaceId: SPACE_1_ID }, + SPACE_2: { spaceId: SPACE_2_ID }, +} = SPACES; + export interface BulkCreateTestDefinition extends TestDefinition { request: Array<{ type: string; id: string }>; overwrite: boolean; } export type BulkCreateTestSuite = TestSuite<BulkCreateTestDefinition>; export interface BulkCreateTestCase extends TestCase { + initialNamespaces?: string[]; failure?: 400 | 409; // only used for permitted response case fail409Param?: string; } const NEW_ATTRIBUTE_KEY = 'title'; // all type mappings include this attribute, for simplicity's sake const NEW_ATTRIBUTE_VAL = `New attribute value ${Date.now()}`; +const EACH_SPACE = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID]; const NEW_SINGLE_NAMESPACE_OBJ = Object.freeze({ type: 'dashboard', id: 'new-dashboard-id' }); const NEW_MULTI_NAMESPACE_OBJ = Object.freeze({ type: 'sharedtype', id: 'new-sharedtype-id' }); +const NEW_EACH_SPACE_OBJ = Object.freeze({ + type: 'sharedtype', + id: 'new-each-space-id', + expectedNamespaces: EACH_SPACE, // expected namespaces of resulting object + initialNamespaces: EACH_SPACE, // args passed to the bulkCreate method +}); +const NEW_ALL_SPACES_OBJ = Object.freeze({ + type: 'sharedtype', + id: 'new-all-spaces-id', + expectedNamespaces: [ALL_SPACES_ID], // expected namespaces of resulting object + initialNamespaces: [ALL_SPACES_ID], // args passed to the bulkCreate method +}); const NEW_NAMESPACE_AGNOSTIC_OBJ = Object.freeze({ type: 'globaltype', id: 'new-globaltype-id' }); export const TEST_CASES: Record<string, BulkCreateTestCase> = Object.freeze({ ...CASES, NEW_SINGLE_NAMESPACE_OBJ, NEW_MULTI_NAMESPACE_OBJ, + NEW_EACH_SPACE_OBJ, + NEW_ALL_SPACES_OBJ, NEW_NAMESPACE_AGNOSTIC_OBJ, }); +const createRequest = ({ type, id, initialNamespaces }: BulkCreateTestCase) => ({ + type, + id, + ...(initialNamespaces && { namespaces: initialNamespaces }), +}); + export function bulkCreateTestSuiteFactory(es: any, esArchiver: any, supertest: SuperTest<any>) { const expectForbidden = expectResponses.forbiddenTypes('bulk_create'); const expectResponseBody = ( diff --git a/x-pack/test/saved_object_api_integration/common/suites/create.ts b/x-pack/test/saved_object_api_integration/common/suites/create.ts index 7e28d5ed9ed94f..1b3902be37d722 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/create.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/create.ts @@ -7,9 +7,8 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; -import { SPACES } from '../lib/spaces'; +import { SPACES, ALL_SPACES_ID } from '../lib/spaces'; import { - createRequest, expectResponses, getUrlPrefix, getTestTitle, @@ -17,30 +16,58 @@ import { } from '../lib/saved_object_test_utils'; import { ExpectResponseBody, TestCase, TestDefinition, TestSuite, TestUser } from '../lib/types'; +const { + DEFAULT: { spaceId: DEFAULT_SPACE_ID }, + SPACE_1: { spaceId: SPACE_1_ID }, + SPACE_2: { spaceId: SPACE_2_ID }, +} = SPACES; + export interface CreateTestDefinition extends TestDefinition { - request: { type: string; id: string }; + request: { type: string; id: string; initialNamespaces?: string[] }; overwrite: boolean; } export type CreateTestSuite = TestSuite<CreateTestDefinition>; export interface CreateTestCase extends TestCase { + initialNamespaces?: string[]; failure?: 400 | 403 | 409; } const NEW_ATTRIBUTE_KEY = 'title'; // all type mappings include this attribute, for simplicity's sake const NEW_ATTRIBUTE_VAL = `New attribute value ${Date.now()}`; +const EACH_SPACE = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID]; // ID intentionally left blank on NEW_SINGLE_NAMESPACE_OBJ to ensure we can create saved objects without specifying the ID // we could create six separate test cases to test every permutation, but there's no real value in doing so const NEW_SINGLE_NAMESPACE_OBJ = Object.freeze({ type: 'dashboard', id: '' }); const NEW_MULTI_NAMESPACE_OBJ = Object.freeze({ type: 'sharedtype', id: 'new-sharedtype-id' }); +const NEW_EACH_SPACE_OBJ = Object.freeze({ + type: 'sharedtype', + id: 'new-each-space-id', + expectedNamespaces: EACH_SPACE, // expected namespaces of resulting object + initialNamespaces: EACH_SPACE, // args passed to the bulkCreate method +}); +const NEW_ALL_SPACES_OBJ = Object.freeze({ + type: 'sharedtype', + id: 'new-all-spaces-id', + expectedNamespaces: [ALL_SPACES_ID], // expected namespaces of resulting object + initialNamespaces: [ALL_SPACES_ID], // args passed to the bulkCreate method +}); const NEW_NAMESPACE_AGNOSTIC_OBJ = Object.freeze({ type: 'globaltype', id: 'new-globaltype-id' }); export const TEST_CASES: Record<string, CreateTestCase> = Object.freeze({ ...CASES, NEW_SINGLE_NAMESPACE_OBJ, NEW_MULTI_NAMESPACE_OBJ, + NEW_EACH_SPACE_OBJ, + NEW_ALL_SPACES_OBJ, NEW_NAMESPACE_AGNOSTIC_OBJ, }); +const createRequest = ({ type, id, initialNamespaces }: CreateTestCase) => ({ + type, + id, + initialNamespaces, +}); + export function createTestSuiteFactory(es: any, esArchiver: any, supertest: SuperTest<any>) { const expectForbidden = expectResponses.forbiddenTypes('create'); const expectResponseBody = ( @@ -96,9 +123,12 @@ export function createTestSuiteFactory(es: any, esArchiver: any, supertest: Supe for (const test of tests) { it(`should return ${test.responseStatusCode} ${test.title}`, async () => { - const { type, id } = test.request; + const { type, id, initialNamespaces } = test.request; const path = `${type}${id ? `/${id}` : ''}`; - const requestBody = { attributes: { [NEW_ATTRIBUTE_KEY]: NEW_ATTRIBUTE_VAL } }; + const requestBody = { + attributes: { [NEW_ATTRIBUTE_KEY]: NEW_ATTRIBUTE_VAL }, + ...(initialNamespaces && { namespaces: initialNamespaces }), + }; const query = test.overwrite ? '?overwrite=true' : ''; await supertest .post(`${getUrlPrefix(spaceId)}/api/saved_objects/${path}${query}`) diff --git a/x-pack/test/saved_object_api_integration/common/suites/delete.ts b/x-pack/test/saved_object_api_integration/common/suites/delete.ts index 228e7977f99ac0..859bb2b7e8fe29 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/delete.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/delete.ts @@ -8,20 +8,16 @@ import { SuperTest } from 'supertest'; import expect from '@kbn/expect/expect.js'; import { SAVED_OBJECT_TEST_CASES as CASES } from '../lib/saved_object_test_cases'; import { SPACES } from '../lib/spaces'; -import { - createRequest, - expectResponses, - getUrlPrefix, - getTestTitle, -} from '../lib/saved_object_test_utils'; +import { expectResponses, getUrlPrefix, getTestTitle } from '../lib/saved_object_test_utils'; import { ExpectResponseBody, TestCase, TestDefinition, TestSuite } from '../lib/types'; export interface DeleteTestDefinition extends TestDefinition { - request: { type: string; id: string }; + request: { type: string; id: string; force?: boolean }; } export type DeleteTestSuite = TestSuite<DeleteTestDefinition>; export interface DeleteTestCase extends TestCase { - failure?: 403 | 404; + force?: boolean; + failure?: 400 | 403 | 404; } const DOES_NOT_EXIST = Object.freeze({ type: 'dashboard', id: 'does-not-exist' }); @@ -30,6 +26,11 @@ export const TEST_CASES: Record<string, DeleteTestCase> = Object.freeze({ DOES_NOT_EXIST, }); +/** + * Test cases have additional properties that we don't want to send in HTTP Requests + */ +const createRequest = ({ type, id, force }: DeleteTestCase) => ({ type, id, force }); + export function deleteTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>) { const expectForbidden = expectResponses.forbiddenTypes('delete'); const expectResponseBody = (testCase: DeleteTestCase): ExpectResponseBody => async ( @@ -81,9 +82,10 @@ export function deleteTestSuiteFactory(esArchiver: any, supertest: SuperTest<any for (const test of tests) { it(`should return ${test.responseStatusCode} ${test.title}`, async () => { - const { type, id } = test.request; + const { type, id, force } = test.request; await supertest .delete(`${getUrlPrefix(spaceId)}/api/saved_objects/${type}/${id}`) + .query({ ...(force && { force }) }) .auth(user?.username, user?.password) .expect(test.responseStatusCode) .then(test.responseBody); diff --git a/x-pack/test/saved_object_api_integration/common/suites/export.ts b/x-pack/test/saved_object_api_integration/common/suites/export.ts index 4eb967a952c604..a1addda1cdd1ff 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/export.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/export.ts @@ -75,14 +75,17 @@ export const getTestCases = (spaceId?: string): { [key: string]: ExportTestCase multiNamespaceType: { title: 'multi-namespace type', type: 'sharedtype', - successResult: (spaceId === SPACE_1_ID - ? [CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, CASES.MULTI_NAMESPACE_ONLY_SPACE_1] - : spaceId === SPACE_2_ID - ? [CASES.MULTI_NAMESPACE_ONLY_SPACE_2] - : [CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1] - ) - .concat([CONFLICT_1_OBJ, CONFLICT_2A_OBJ, CONFLICT_2B_OBJ, CONFLICT_3_OBJ, CONFLICT_4A_OBJ]) - .flat(), + successResult: [ + CASES.MULTI_NAMESPACE_ALL_SPACES, + ...(spaceId === SPACE_1_ID + ? [CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, CASES.MULTI_NAMESPACE_ONLY_SPACE_1] + : spaceId === SPACE_2_ID + ? [CASES.MULTI_NAMESPACE_ONLY_SPACE_2] + : [CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1] + ) + .concat([CONFLICT_1_OBJ, CONFLICT_2A_OBJ, CONFLICT_2B_OBJ, CONFLICT_3_OBJ, CONFLICT_4A_OBJ]) + .flat(), + ], }, namespaceAgnosticObject: { title: 'namespace-agnostic object', diff --git a/x-pack/test/saved_object_api_integration/common/suites/find.ts b/x-pack/test/saved_object_api_integration/common/suites/find.ts index 381306f8101223..c7243d8db25fe7 100644 --- a/x-pack/test/saved_object_api_integration/common/suites/find.ts +++ b/x-pack/test/saved_object_api_integration/common/suites/find.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { SuperTest } from 'supertest'; import querystring from 'querystring'; import { SAVED_OBJECT_TEST_CASES, CONFLICT_TEST_CASES } from '../lib/saved_object_test_cases'; -import { SPACES } from '../lib/spaces'; +import { SPACES, ALL_SPACES_ID } from '../lib/spaces'; import { getUrlPrefix, isUserAuthorizedAtSpace, @@ -75,13 +75,16 @@ export const getTestCases = ( return TEST_CASES.filter((t) => { const hasOtherNamespaces = !t.expectedNamespaces || // namespace-agnostic types do not have an expectedNamespaces field - t.expectedNamespaces.some((ns) => ns !== (currentSpace ?? DEFAULT_SPACE_ID)); + t.expectedNamespaces.some( + (ns) => ns === ALL_SPACES_ID || ns !== (currentSpace ?? DEFAULT_SPACE_ID) + ); return hasOtherNamespaces && predicate(t); }); } return TEST_CASES.filter( (t) => (!t.expectedNamespaces || + t.expectedNamespaces.includes(ALL_SPACES_ID) || t.expectedNamespaces.includes(currentSpace ?? DEFAULT_SPACE_ID)) && predicate(t) ); diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts index 93ae439d011667..a34bb4b3e78e7b 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_create.ts @@ -43,6 +43,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { ...fail409(!overwrite && spaceId === SPACE_2_ID), expectedNamespaces, }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite || (spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID)), @@ -63,9 +64,10 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { { ...CASES.NEW_MULTI_NAMESPACE_OBJ, expectedNamespaces }, CASES.NEW_NAMESPACE_AGNOSTIC_OBJ, ]; + const crossNamespace = [CASES.NEW_EACH_SPACE_OBJ, CASES.NEW_ALL_SPACES_OBJ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail400() }]; const allTypes = normalTypes.concat(hiddenType); - return { normalTypes, hiddenType, allTypes }; + return { normalTypes, crossNamespace, hiddenType, allTypes }; }; export default function ({ getService }: FtrProviderContext) { @@ -79,22 +81,37 @@ export default function ({ getService }: FtrProviderContext) { supertest ); const createTests = (overwrite: boolean, spaceId: string, user: TestUser) => { - const { normalTypes, hiddenType, allTypes } = createTestCases(overwrite, spaceId); + const { normalTypes, crossNamespace, hiddenType, allTypes } = createTestCases( + overwrite, + spaceId + ); // use singleRequest to reduce execution time and/or test combined cases + const authorizedCommon = [ + createTestDefinitions(normalTypes, false, overwrite, { + spaceId, + user, + singleRequest: true, + }), + createTestDefinitions(hiddenType, true, overwrite, { spaceId, user }), + createTestDefinitions(allTypes, true, overwrite, { + spaceId, + user, + singleRequest: true, + responseBodyOverride: expectForbidden(['hiddentype']), + }), + ].flat(); return { unauthorized: createTestDefinitions(allTypes, true, overwrite, { spaceId, user }), - authorized: [ - createTestDefinitions(normalTypes, false, overwrite, { - spaceId, - user, - singleRequest: true, - }), - createTestDefinitions(hiddenType, true, overwrite, { spaceId, user }), - createTestDefinitions(allTypes, true, overwrite, { + authorizedAtSpace: [ + authorizedCommon, + createTestDefinitions(crossNamespace, true, overwrite, { spaceId, user }), + ].flat(), + authorizedEverywhere: [ + authorizedCommon, + createTestDefinitions(crossNamespace, false, overwrite, { spaceId, user, singleRequest: true, - responseBodyOverride: expectForbidden(['hiddentype']), }), ].flat(), superuser: createTestDefinitions(allTypes, false, overwrite, { @@ -124,10 +141,15 @@ export default function ({ getService }: FtrProviderContext) { const { unauthorized } = createTests(overwrite!, spaceId, user); _addTests(user, unauthorized); }); - [users.dualAll, users.allGlobally, users.allAtSpace].forEach((user) => { - const { authorized } = createTests(overwrite!, spaceId, user); - _addTests(user, authorized); + + const { authorizedAtSpace } = createTests(overwrite!, spaceId, users.allAtSpace); + _addTests(users.allAtSpace, authorizedAtSpace); + + [users.dualAll, users.allGlobally].forEach((user) => { + const { authorizedEverywhere } = createTests(overwrite!, spaceId, user); + _addTests(user, authorizedEverywhere); }); + const { superuser } = createTests(overwrite!, spaceId, users.superuser); _addTests(users.superuser, superuser); } diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_get.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_get.ts index 4a35bdd03e4dd5..4878d9d81dbf69 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_get.ts @@ -28,6 +28,7 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts index 1e11d1fc61110f..3f4341de6cfc56 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/bulk_update.ts @@ -28,6 +28,7 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), @@ -44,6 +45,7 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, namespace: DEFAULT_SPACE_ID }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, namespace: SPACE_1_ID }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespace: SPACE_1_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, namespace: DEFAULT_SPACE_ID }, // any spaceId will work (not '*') { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespace: DEFAULT_SPACE_ID }, // SPACE_1_ID would also work { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespace: SPACE_2_ID, ...fail404() }, // intentional 404 test case { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, namespace: SPACE_2_ID }, diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts index 7353dafb5e1b5b..37328c0ffc3424 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/create.ts @@ -41,6 +41,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { ...fail409(!overwrite && spaceId === SPACE_2_ID), expectedNamespaces, }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite || (spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID)), @@ -52,9 +53,10 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { { ...CASES.NEW_MULTI_NAMESPACE_OBJ, expectedNamespaces }, CASES.NEW_NAMESPACE_AGNOSTIC_OBJ, ]; + const crossNamespace = [CASES.NEW_EACH_SPACE_OBJ, CASES.NEW_ALL_SPACES_OBJ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail400() }]; - const allTypes = normalTypes.concat(hiddenType); - return { normalTypes, hiddenType, allTypes }; + const allTypes = normalTypes.concat(crossNamespace, hiddenType); + return { normalTypes, crossNamespace, hiddenType, allTypes }; }; export default function ({ getService }: FtrProviderContext) { @@ -64,11 +66,20 @@ export default function ({ getService }: FtrProviderContext) { const { addTests, createTestDefinitions } = createTestSuiteFactory(es, esArchiver, supertest); const createTests = (overwrite: boolean, spaceId: string, user: TestUser) => { - const { normalTypes, hiddenType, allTypes } = createTestCases(overwrite, spaceId); + const { normalTypes, crossNamespace, hiddenType, allTypes } = createTestCases( + overwrite, + spaceId + ); return { unauthorized: createTestDefinitions(allTypes, true, overwrite, { spaceId, user }), - authorized: [ + authorizedAtSpace: [ + createTestDefinitions(normalTypes, false, overwrite, { spaceId, user }), + createTestDefinitions(crossNamespace, true, overwrite, { spaceId, user }), + createTestDefinitions(hiddenType, true, overwrite, { spaceId, user }), + ].flat(), + authorizedEverywhere: [ createTestDefinitions(normalTypes, false, overwrite, { spaceId, user }), + createTestDefinitions(crossNamespace, false, overwrite, { spaceId, user }), createTestDefinitions(hiddenType, true, overwrite, { spaceId, user }), ].flat(), superuser: createTestDefinitions(allTypes, false, overwrite, { spaceId, user }), @@ -94,10 +105,15 @@ export default function ({ getService }: FtrProviderContext) { const { unauthorized } = createTests(overwrite!, spaceId, user); _addTests(user, unauthorized); }); - [users.dualAll, users.allGlobally, users.allAtSpace].forEach((user) => { - const { authorized } = createTests(overwrite!, spaceId, user); - _addTests(user, authorized); + + const { authorizedAtSpace } = createTests(overwrite!, spaceId, users.allAtSpace); + _addTests(users.allAtSpace, authorizedAtSpace); + + [users.dualAll, users.allGlobally].forEach((user) => { + const { authorizedEverywhere } = createTests(overwrite!, spaceId, user); + _addTests(user, authorizedEverywhere); }); + const { superuser } = createTests(overwrite!, spaceId, users.superuser); _addTests(users.superuser, superuser); } diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/delete.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/delete.ts index 62b229f8315622..436f09e8d2ee01 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/delete.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/delete.ts @@ -19,7 +19,7 @@ const { SPACE_1: { spaceId: SPACE_1_ID }, SPACE_2: { spaceId: SPACE_2_ID }, } = SPACES; -const { fail404 } = testCaseFailures; +const { fail400, fail404 } = testCaseFailures; const createTestCases = (spaceId: string) => { // for each permitted (non-403) outcome, if failure !== undefined then we expect @@ -28,8 +28,18 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail400() }, + // try to delete this object again, this time using the `force` option + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, force: true }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, + ...fail400(spaceId === DEFAULT_SPACE_ID || spaceId === SPACE_1_ID), + ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), + }, + // try to delete this object again, this time using the `force` option + { + ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, + force: true, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/get.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/get.ts index dabe174af4d4be..b554eb55b0adb1 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/get.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/get.ts @@ -28,6 +28,7 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts index 0b531a3dccc1ab..a319a73c6a98ab 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/import.ts @@ -58,6 +58,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { const group2 = [ // when overwrite=true, all of the objects in this group are created successfully, so we can check the created object attributes CASES.NEW_MULTI_NAMESPACE_OBJ, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite && (spaceId === DEFAULT_SPACE_ID || spaceId === SPACE_1_ID)), diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts index 792fe63e5932d7..b0f4f13f268c9e 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/resolve_import_errors.ts @@ -55,6 +55,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { const group1NonImportable = [{ ...CASES.HIDDEN, ...fail400() }]; const group1All = [...group1Importable, ...group1NonImportable]; const group2 = [ + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite && (spaceId === DEFAULT_SPACE_ID || spaceId === SPACE_1_ID)), diff --git a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/update.ts b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/update.ts index fec6f2b7de7154..a976ce08adb1f2 100644 --- a/x-pack/test/saved_object_api_integration/security_and_spaces/apis/update.ts +++ b/x-pack/test/saved_object_api_integration/security_and_spaces/apis/update.ts @@ -28,6 +28,7 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts index cc2c5e2e7fc005..dc340f2c2aa7cb 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_create.ts @@ -28,6 +28,7 @@ const createTestCases = (overwrite: boolean) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail409(!overwrite) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, expectedNamespaces }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, expectedNamespaces }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail409(), ...unresolvableConflict() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail409(), ...unresolvableConflict() }, @@ -35,6 +36,8 @@ const createTestCases = (overwrite: boolean) => { { ...CASES.NEW_SINGLE_NAMESPACE_OBJ, expectedNamespaces }, { ...CASES.NEW_MULTI_NAMESPACE_OBJ, expectedNamespaces }, CASES.NEW_NAMESPACE_AGNOSTIC_OBJ, + CASES.NEW_EACH_SPACE_OBJ, + CASES.NEW_ALL_SPACES_OBJ, ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail400() }]; const allTypes = normalTypes.concat(hiddenType); diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts index d305e08da1b327..96eddf1f8bd3cc 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_get.ts @@ -22,6 +22,7 @@ const createTestCases = () => { CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404() }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404() }, + CASES.MULTI_NAMESPACE_ALL_SPACES, CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail404() }, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts index 39ceb5a70d1b23..2a19c56f80ce6f 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/bulk_update.ts @@ -28,6 +28,7 @@ const createTestCases = () => { CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404() }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404() }, + CASES.MULTI_NAMESPACE_ALL_SPACES, CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail404() }, @@ -42,6 +43,7 @@ const createTestCases = () => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, namespace: DEFAULT_SPACE_ID }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, namespace: SPACE_1_ID }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespace: SPACE_1_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, namespace: DEFAULT_SPACE_ID }, // any spaceId will work (not '*') { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespace: DEFAULT_SPACE_ID }, // SPACE_1_ID would also work { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespace: SPACE_2_ID, ...fail404() }, // intentional 404 test case { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, namespace: SPACE_2_ID }, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/create.ts b/x-pack/test/saved_object_api_integration/security_only/apis/create.ts index b7c6ecef979bda..f469ffc97fbe08 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/create.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/create.ts @@ -27,6 +27,7 @@ const createTestCases = (overwrite: boolean) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail409(!overwrite) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, expectedNamespaces }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, expectedNamespaces }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail409() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail409() }, @@ -34,6 +35,8 @@ const createTestCases = (overwrite: boolean) => { { ...CASES.NEW_SINGLE_NAMESPACE_OBJ, expectedNamespaces }, { ...CASES.NEW_MULTI_NAMESPACE_OBJ, expectedNamespaces }, CASES.NEW_NAMESPACE_AGNOSTIC_OBJ, + CASES.NEW_EACH_SPACE_OBJ, + CASES.NEW_ALL_SPACES_OBJ, ]; const hiddenType = [{ ...CASES.HIDDEN, ...fail400() }]; const allTypes = normalTypes.concat(hiddenType); diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/delete.ts b/x-pack/test/saved_object_api_integration/security_only/apis/delete.ts index 4f379d5d1cbb9b..4caf112a59b272 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/delete.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/delete.ts @@ -13,7 +13,7 @@ import { DeleteTestDefinition, } from '../../common/suites/delete'; -const { fail404 } = testCaseFailures; +const { fail400, fail404 } = testCaseFailures; const createTestCases = () => { // for each permitted (non-403) outcome, if failure !== undefined then we expect @@ -22,7 +22,12 @@ const createTestCases = () => { CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404() }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404() }, - CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail400() }, + // try to delete this object again, this time using the `force` option + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, force: true }, + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail400() }, + // try to delete this object again, this time using the `force` option + { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, force: true }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail404() }, CASES.NAMESPACE_AGNOSTIC, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/get.ts b/x-pack/test/saved_object_api_integration/security_only/apis/get.ts index 0f105b939960f2..5eed2839b61722 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/get.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/get.ts @@ -22,6 +22,7 @@ const createTestCases = () => { CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404() }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404() }, + CASES.MULTI_NAMESPACE_ALL_SPACES, CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail404() }, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/import.ts b/x-pack/test/saved_object_api_integration/security_only/apis/import.ts index 34be3b7408432a..df763dba6d18ab 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/import.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/import.ts @@ -49,6 +49,7 @@ const createTestCases = (overwrite: boolean) => { const group2 = [ // when overwrite=true, all of the objects in this group are created successfully, so we can check the created object attributes CASES.NEW_MULTI_NAMESPACE_OBJ, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...destinationId() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...destinationId() }, diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts index 91134dd14bd8a3..22734e95da0b59 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/resolve_import_errors.ts @@ -43,6 +43,7 @@ const createTestCases = (overwrite: boolean) => { const group1NonImportable = [{ ...CASES.HIDDEN, ...fail400() }]; const group1All = [...group1Importable, ...group1NonImportable]; const group2 = [ + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite) }, { ...CASES.CONFLICT_1A_OBJ, ...newCopy() }, // "ambiguous source" conflict which results in a new destination ID and empty origin ID { ...CASES.CONFLICT_1B_OBJ, ...newCopy() }, // "ambiguous source" conflict which results in a new destination ID and empty origin ID diff --git a/x-pack/test/saved_object_api_integration/security_only/apis/update.ts b/x-pack/test/saved_object_api_integration/security_only/apis/update.ts index c1fd350101fd4e..d6e22abf4af246 100644 --- a/x-pack/test/saved_object_api_integration/security_only/apis/update.ts +++ b/x-pack/test/saved_object_api_integration/security_only/apis/update.ts @@ -22,6 +22,7 @@ const createTestCases = () => { CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404() }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404() }, + CASES.MULTI_NAMESPACE_ALL_SPACES, CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404() }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, ...fail404() }, diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts index ef47b09eddbc83..e0ba6839530668 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_create.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import expect from '@kbn/expect'; import { SPACES } from '../../common/lib/spaces'; import { testCaseFailures, getTestScenarios } from '../../common/lib/saved_object_test_utils'; import { FtrProviderContext } from '../../common/ftr_provider_context'; @@ -39,6 +38,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { ...fail409(!overwrite && spaceId === SPACE_2_ID), expectedNamespaces, }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite || (spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID)), @@ -59,6 +59,8 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { { ...CASES.NEW_SINGLE_NAMESPACE_OBJ, expectedNamespaces }, { ...CASES.NEW_MULTI_NAMESPACE_OBJ, expectedNamespaces }, CASES.NEW_NAMESPACE_AGNOSTIC_OBJ, + CASES.NEW_EACH_SPACE_OBJ, + CASES.NEW_ALL_SPACES_OBJ, ]; }; @@ -73,21 +75,7 @@ export default function ({ getService }: FtrProviderContext) { return createTestDefinitions(testCases, false, overwrite, { spaceId, singleRequest: true, - }).concat( - ['namespace', 'namespaces'].map((key) => ({ - title: `(bad request) when ${key} is specified on the saved object`, - request: [{ type: 'isolatedtype', id: 'some-id', [key]: 'any-value' }] as any, - responseStatusCode: 400, - responseBody: async (response: Record<string, any>) => { - expect(response.body).to.eql({ - statusCode: 400, - error: 'Bad Request', - message: `[request body.0.${key}]: definition for this key is missing`, - }); - }, - overwrite, - })) - ); + }); }; describe('_bulk_create', () => { diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts index 37bb2ec920c1e6..7eef9a95f52385 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_get.ts @@ -22,6 +22,7 @@ const createTestCases = (spaceId: string) => [ { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts index b51ec303fadf3f..e789377b93fe16 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/bulk_update.ts @@ -23,6 +23,7 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), @@ -39,6 +40,7 @@ const createTestCases = (spaceId: string) => { { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, namespace: DEFAULT_SPACE_ID }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, namespace: SPACE_1_ID }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, namespace: SPACE_1_ID, ...fail404() }, // intentional 404 test case + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, namespace: spaceId }, // any spaceId will work (not '*') { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, namespace: DEFAULT_SPACE_ID }, // SPACE_1_ID would also work { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, namespace: SPACE_2_ID, ...fail404() }, // intentional 404 test case { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_2, namespace: SPACE_2_ID }, diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts index 10e57b4db82dc7..c5013a0f0ddd3e 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/create.ts @@ -36,6 +36,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { ...fail409(!overwrite && spaceId === SPACE_2_ID), expectedNamespaces, }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite || (spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID)), @@ -47,6 +48,8 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { { ...CASES.NEW_SINGLE_NAMESPACE_OBJ, expectedNamespaces }, { ...CASES.NEW_MULTI_NAMESPACE_OBJ, expectedNamespaces }, CASES.NEW_NAMESPACE_AGNOSTIC_OBJ, + CASES.NEW_EACH_SPACE_OBJ, + CASES.NEW_ALL_SPACES_OBJ, ]; }; diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/delete.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/delete.ts index 82045a9e288ced..66309a4be44608 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/delete.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/delete.ts @@ -14,7 +14,7 @@ const { SPACE_1: { spaceId: SPACE_1_ID }, SPACE_2: { spaceId: SPACE_2_ID }, } = SPACES; -const { fail404 } = testCaseFailures; +const { fail400, fail404 } = testCaseFailures; const createTestCases = (spaceId: string) => [ // for each outcome, if failure !== undefined then we expect to receive @@ -22,8 +22,18 @@ const createTestCases = (spaceId: string) => [ { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail400() }, + // try to delete this object again, this time using the `force` option + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, force: true }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, + ...fail400(spaceId === DEFAULT_SPACE_ID || spaceId === SPACE_1_ID), + ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), + }, + // try to delete this object again, this time using the `force` option + { + ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, + force: true, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), }, { ...CASES.MULTI_NAMESPACE_ONLY_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/get.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/get.ts index b0fed3e13b9af8..a56216537a365f 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/get.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/get.ts @@ -22,6 +22,7 @@ const createTestCases = (spaceId: string) => [ { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts index a36249528540b8..3009fa0bd75a4d 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/import.ts @@ -44,6 +44,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail409(!overwrite && spaceId === SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail409(!overwrite && spaceId === SPACE_2_ID) }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite && (spaceId === DEFAULT_SPACE_ID || spaceId === SPACE_1_ID)), diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts index 1431a61b1cbe07..721a6b2bf71080 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/resolve_import_errors.ts @@ -48,6 +48,7 @@ const createTestCases = (overwrite: boolean, spaceId: string) => { : CASES.SINGLE_NAMESPACE_SPACE_2; return [ { ...singleNamespaceObject, ...fail409(!overwrite) }, + { ...CASES.MULTI_NAMESPACE_ALL_SPACES, ...fail409(!overwrite) }, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail409(!overwrite && (spaceId === DEFAULT_SPACE_ID || spaceId === SPACE_1_ID)), diff --git a/x-pack/test/saved_object_api_integration/spaces_only/apis/update.ts b/x-pack/test/saved_object_api_integration/spaces_only/apis/update.ts index 31ef6fb25b2f21..7a004290249ca6 100644 --- a/x-pack/test/saved_object_api_integration/spaces_only/apis/update.ts +++ b/x-pack/test/saved_object_api_integration/spaces_only/apis/update.ts @@ -22,6 +22,7 @@ const createTestCases = (spaceId: string) => [ { ...CASES.SINGLE_NAMESPACE_DEFAULT_SPACE, ...fail404(spaceId !== DEFAULT_SPACE_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_1, ...fail404(spaceId !== SPACE_1_ID) }, { ...CASES.SINGLE_NAMESPACE_SPACE_2, ...fail404(spaceId !== SPACE_2_ID) }, + CASES.MULTI_NAMESPACE_ALL_SPACES, { ...CASES.MULTI_NAMESPACE_DEFAULT_AND_SPACE_1, ...fail404(spaceId !== DEFAULT_SPACE_ID && spaceId !== SPACE_1_ID), diff --git a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json index 7e528c23c20a05..5ce6c0ce6b7c5b 100644 --- a/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json +++ b/x-pack/test/spaces_api_integration/common/fixtures/es_archiver/saved_objects/spaces/data.json @@ -482,7 +482,7 @@ { "type": "doc", "value": { - "id": "sharedtype:all_spaces", + "id": "sharedtype:each_space", "index": ".kibana", "source": { "sharedtype": { @@ -496,6 +496,23 @@ } } +{ + "type": "doc", + "value": { + "id": "sharedtype:all_spaces", + "index": ".kibana", + "source": { + "sharedtype": { + "title": "A shared saved-object in all spaces" + }, + "type": "sharedtype", + "namespaces": ["*"], + "updated_at": "2017-09-21T18:59:16.270Z" + }, + "type": "doc" + } +} + { "type": "doc", "value": { diff --git a/x-pack/test/spaces_api_integration/common/lib/saved_object_test_cases.ts b/x-pack/test/spaces_api_integration/common/lib/saved_object_test_cases.ts index 3b0f5f8570aa32..9b8baa7f22a2bb 100644 --- a/x-pack/test/spaces_api_integration/common/lib/saved_object_test_cases.ts +++ b/x-pack/test/spaces_api_integration/common/lib/saved_object_test_cases.ts @@ -29,9 +29,13 @@ export const MULTI_NAMESPACE_SAVED_OBJECT_TEST_CASES = Object.freeze({ id: 'space_1_and_space_2', existingNamespaces: ['space_1', 'space_2'], }), + EACH_SPACE: Object.freeze({ + id: 'each_space', + existingNamespaces: ['default', 'space_1', 'space_2'], // each individual space + }), ALL_SPACES: Object.freeze({ id: 'all_spaces', - existingNamespaces: ['default', 'space_1', 'space_2'], + existingNamespaces: ['*'], // all current and future spaces }), DOES_NOT_EXIST: Object.freeze({ id: 'does_not_exist', diff --git a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts index 26c736034501f7..ee7a2fb7316571 100644 --- a/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts +++ b/x-pack/test/spaces_api_integration/common/suites/copy_to_space.ts @@ -423,7 +423,7 @@ export function copyToSpaceTestSuiteFactory( const type = 'sharedtype'; const v4 = new RegExp(/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i); const noConflictId = `${spaceId}_only`; - const exactMatchId = 'all_spaces'; + const exactMatchId = 'each_space'; const inexactMatchId = `conflict_1_${spaceId}`; const ambiguousConflictId = `conflict_2_${spaceId}`; diff --git a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts index cb9219b1ba2ed8..eba7e2033eadf2 100644 --- a/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts +++ b/x-pack/test/spaces_api_integration/common/suites/resolve_copy_to_space_conflicts.ts @@ -310,7 +310,7 @@ export function resolveCopyToSpaceConflictsSuite( // a 403 error actually comes back as an HTTP 200 response const statusCode = outcome === 'noAccess' ? 404 : 200; const type = 'sharedtype'; - const exactMatchId = 'all_spaces'; + const exactMatchId = 'each_space'; const inexactMatchId = `conflict_1_${spaceId}`; const ambiguousConflictId = `conflict_2_${spaceId}`; diff --git a/x-pack/test/spaces_api_integration/common/suites/share_add.ts b/x-pack/test/spaces_api_integration/common/suites/share_add.ts index 219190cb280029..f2a3c69a91196a 100644 --- a/x-pack/test/spaces_api_integration/common/suites/share_add.ts +++ b/x-pack/test/spaces_api_integration/common/suites/share_add.ts @@ -26,8 +26,6 @@ export interface ShareAddTestCase { id: string; namespaces: string[]; failure?: 400 | 403 | 404; - fail400Param?: string; - fail403Param?: string; } const TYPE = 'sharedtype'; @@ -39,22 +37,16 @@ const getTestTitle = ({ id, namespaces }: ShareAddTestCase) => `{id: ${id}, namespaces: [${namespaces.join(',')}]}`; export function shareAddTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>) { + const expectForbidden = expectResponses.forbiddenTypes('share_to_space'); const expectResponseBody = (testCase: ShareAddTestCase): ExpectResponseBody => async ( response: Record<string, any> ) => { - const { id, failure, fail400Param, fail403Param } = testCase; + const { id, failure } = testCase; const object = response.body; if (failure === 403) { - await expectResponses.forbiddenTypes(fail403Param!)(TYPE)(response); - } else if (failure) { - let error: any; - if (failure === 400) { - error = SavedObjectsErrorHelpers.createBadRequestError( - `${id} already exists in the following namespace(s): ${fail400Param}` - ); - } else if (failure === 404) { - error = SavedObjectsErrorHelpers.createGenericNotFoundError(TYPE, id); - } + await expectForbidden(TYPE)(response); + } else if (failure === 404) { + const error = SavedObjectsErrorHelpers.createGenericNotFoundError(TYPE, id); expect(object.error).to.eql(error.output.payload.error); expect(object.statusCode).to.eql(error.output.payload.statusCode); } else { @@ -67,13 +59,12 @@ export function shareAddTestSuiteFactory(esArchiver: any, supertest: SuperTest<a forbidden: boolean, options?: { responseBodyOverride?: ExpectResponseBody; - fail403Param?: string; } ): ShareAddTestDefinition[] => { let cases = Array.isArray(testCases) ? testCases : [testCases]; if (forbidden) { // override the expected result in each test case - cases = cases.map((x) => ({ ...x, failure: 403, fail403Param: options?.fail403Param })); + cases = cases.map((x) => ({ ...x, failure: 403 })); } return cases.map((x) => ({ title: getTestTitle(x), diff --git a/x-pack/test/spaces_api_integration/common/suites/share_remove.ts b/x-pack/test/spaces_api_integration/common/suites/share_remove.ts index 0748aa797264cb..d318609e115701 100644 --- a/x-pack/test/spaces_api_integration/common/suites/share_remove.ts +++ b/x-pack/test/spaces_api_integration/common/suites/share_remove.ts @@ -27,7 +27,6 @@ export interface ShareRemoveTestCase { id: string; namespaces: string[]; failure?: 400 | 403 | 404; - fail400Param?: string; } const TYPE = 'sharedtype'; @@ -37,23 +36,16 @@ const createRequest = ({ id, namespaces }: ShareRemoveTestCase) => ({ }); export function shareRemoveTestSuiteFactory(esArchiver: any, supertest: SuperTest<any>) { - const expectForbidden = expectResponses.forbiddenTypes('delete'); + const expectForbidden = expectResponses.forbiddenTypes('share_to_space'); const expectResponseBody = (testCase: ShareRemoveTestCase): ExpectResponseBody => async ( response: Record<string, any> ) => { - const { id, failure, fail400Param } = testCase; + const { id, failure } = testCase; const object = response.body; if (failure === 403) { await expectForbidden(TYPE)(response); - } else if (failure) { - let error: any; - if (failure === 400) { - error = SavedObjectsErrorHelpers.createBadRequestError( - `${id} doesn't exist in the following namespace(s): ${fail400Param}` - ); - } else if (failure === 404) { - error = SavedObjectsErrorHelpers.createGenericNotFoundError(TYPE, id); - } + } else if (failure === 404) { + const error = SavedObjectsErrorHelpers.createGenericNotFoundError(TYPE, id); expect(object.error).to.eql(error.output.payload.error); expect(object.statusCode).to.eql(error.output.payload.statusCode); } else { diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_add.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_add.ts index ddd029c8d7d687..40f87cb90933f3 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_add.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_add.ts @@ -12,7 +12,11 @@ import { import { TestUser } from '../../../saved_object_api_integration/common/lib/types'; import { MULTI_NAMESPACE_SAVED_OBJECT_TEST_CASES as CASES } from '../../common/lib/saved_object_test_cases'; import { TestInvoker } from '../../common/lib/types'; -import { shareAddTestSuiteFactory, ShareAddTestDefinition } from '../../common/suites/share_add'; +import { + shareAddTestSuiteFactory, + ShareAddTestDefinition, + ShareAddTestCase, +} from '../../common/suites/share_add'; const { DEFAULT: { spaceId: DEFAULT_SPACE_ID }, @@ -33,6 +37,8 @@ const createTestCases = (spaceId: string) => { { ...CASES.SPACE_1_AND_SPACE_2, namespaces, ...fail404(spaceId === DEFAULT_SPACE_ID) }, { ...CASES.ALL_SPACES, namespaces }, { ...CASES.DOES_NOT_EXIST, namespaces, ...fail404() }, + // Test case to check adding all spaces ("*") to a saved object + { ...CASES.EACH_SPACE, namespaces: ['*'] }, // Test cases to check adding multiple namespaces to different saved objects that exist in one space // These are non-exhaustive, they only check cases for adding two additional namespaces to a saved object // More permutations are covered in the corresponding spaces_only test suite @@ -57,13 +63,24 @@ const calculateSingleSpaceAuthZ = ( testCases: ReturnType<typeof createTestCases>, spaceId: string ) => { - const targetsOtherSpace = testCases.filter( - (x) => !x.namespaces.includes(spaceId) || x.namespaces.length > 1 - ); - const tmp = testCases.filter((x) => !targetsOtherSpace.includes(x)); // doesn't target other space - const doesntExistInThisSpace = tmp.filter((x) => !x.existingNamespaces.includes(spaceId)); - const existsInThisSpace = tmp.filter((x) => x.existingNamespaces.includes(spaceId)); - return { targetsOtherSpace, doesntExistInThisSpace, existsInThisSpace }; + const targetsAllSpaces: ShareAddTestCase[] = []; + const targetsOtherSpace: ShareAddTestCase[] = []; + const doesntExistInThisSpace: ShareAddTestCase[] = []; + const existsInThisSpace: ShareAddTestCase[] = []; + + for (const testCase of testCases) { + const { namespaces, existingNamespaces } = testCase; + if (namespaces.includes('*')) { + targetsAllSpaces.push(testCase); + } else if (!namespaces.includes(spaceId) || namespaces.length > 1) { + targetsOtherSpace.push(testCase); + } else if (!existingNamespaces.includes(spaceId)) { + doesntExistInThisSpace.push(testCase); + } else { + existsInThisSpace.push(testCase); + } + } + return { targetsAllSpaces, targetsOtherSpace, doesntExistInThisSpace, existsInThisSpace }; }; // eslint-disable-next-line import/no-default-export export default function ({ getService }: TestInvoker) { @@ -77,18 +94,20 @@ export default function ({ getService }: TestInvoker) { const otherSpaceId = spaceId === DEFAULT_SPACE_ID ? SPACE_1_ID : DEFAULT_SPACE_ID; const otherSpace = calculateSingleSpaceAuthZ(testCases, otherSpaceId); return { - unauthorized: createTestDefinitions(testCases, true, { fail403Param: 'create' }), + unauthorized: createTestDefinitions(testCases, true), authorizedInSpace: [ - createTestDefinitions(thisSpace.targetsOtherSpace, true, { fail403Param: 'create' }), + createTestDefinitions(thisSpace.targetsAllSpaces, true), + createTestDefinitions(thisSpace.targetsOtherSpace, true), createTestDefinitions(thisSpace.doesntExistInThisSpace, false), createTestDefinitions(thisSpace.existsInThisSpace, false), ].flat(), authorizedInOtherSpace: [ - createTestDefinitions(otherSpace.targetsOtherSpace, true, { fail403Param: 'create' }), - // If the preflight GET request fails, it will return a 404 error; users who are authorized to create saved objects in the target - // space(s) but are not authorized to update saved objects in this space will see a 403 error instead of 404. This is a safeguard to + createTestDefinitions(thisSpace.targetsAllSpaces, true), + createTestDefinitions(otherSpace.targetsOtherSpace, true), + // If the preflight GET request fails, it will return a 404 error; users who are authorized to share saved objects in the target + // space(s) but are not authorized to share saved objects in this space will see a 403 error instead of 404. This is a safeguard to // prevent potential information disclosure of the spaces that a given saved object may exist in. - createTestDefinitions(otherSpace.doesntExistInThisSpace, true, { fail403Param: 'update' }), + createTestDefinitions(otherSpace.doesntExistInThisSpace, true), createTestDefinitions(otherSpace.existsInThisSpace, false), ].flat(), authorized: createTestDefinitions(testCases, false), diff --git a/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_remove.ts b/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_remove.ts index 4b120a71213b75..34406d3258aa41 100644 --- a/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_remove.ts +++ b/x-pack/test/spaces_api_integration/security_and_spaces/apis/share_remove.ts @@ -35,16 +35,20 @@ const createTestCases = (spaceId: string) => { { id: CASES.DEFAULT_AND_SPACE_1.id, namespaces, ...fail404(spaceId === SPACE_2_ID) }, { id: CASES.DEFAULT_AND_SPACE_2.id, namespaces, ...fail404(spaceId === SPACE_1_ID) }, { id: CASES.SPACE_1_AND_SPACE_2.id, namespaces, ...fail404(spaceId === DEFAULT_SPACE_ID) }, - { id: CASES.ALL_SPACES.id, namespaces }, + { id: CASES.EACH_SPACE.id, namespaces }, { id: CASES.DOES_NOT_EXIST.id, namespaces, ...fail404() }, ] as ShareRemoveTestCase[]; - // Test cases to check removing all three namespaces from different saved objects that exist in two spaces - // These are non-exhaustive, they only check some cases -- each object will result in a 404, either because - // it never existed in the target namespace, or it was removed in one of the test cases above - // More permutations are covered in the corresponding spaces_only test suite namespaces = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID]; const multipleSpaces = [ + // Test case to check removing all spaces from a saved object that exists in all spaces; + // It fails the second time because the object no longer exists + { ...CASES.ALL_SPACES, namespaces: ['*'] }, + { ...CASES.ALL_SPACES, namespaces: ['*'], ...fail404() }, + // Test cases to check removing all three namespaces from different saved objects that exist in two spaces + // These are non-exhaustive, they only check some cases -- each object will result in a 404, either because + // it never existed in the target namespace, or it was removed in one of the test cases above + // More permutations are covered in the corresponding spaces_only test suite { id: CASES.DEFAULT_AND_SPACE_1.id, namespaces, ...fail404() }, { id: CASES.DEFAULT_AND_SPACE_2.id, namespaces, ...fail404() }, { id: CASES.SPACE_1_AND_SPACE_2.id, namespaces, ...fail404() }, diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/share_add.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/share_add.ts index 25ba986a12fd88..8b8e449b3c323a 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/share_add.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/share_add.ts @@ -33,6 +33,7 @@ const createSingleTestCases = (spaceId: string) => { { ...CASES.DEFAULT_AND_SPACE_1, namespaces, ...fail404(spaceId === SPACE_2_ID) }, { ...CASES.DEFAULT_AND_SPACE_2, namespaces, ...fail404(spaceId === SPACE_1_ID) }, { ...CASES.SPACE_1_AND_SPACE_2, namespaces, ...fail404(spaceId === DEFAULT_SPACE_ID) }, + { ...CASES.EACH_SPACE, namespaces }, { ...CASES.ALL_SPACES, namespaces }, { ...CASES.DOES_NOT_EXIST, namespaces, ...fail404() }, ]; @@ -42,14 +43,26 @@ const createSingleTestCases = (spaceId: string) => { * These are non-exhaustive, but they check different permutations of saved objects and spaces to add */ const createMultiTestCases = () => { - const allSpaces = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID]; - let id = CASES.DEFAULT_ONLY.id; - const one = [{ id, namespaces: allSpaces }]; - id = CASES.DEFAULT_AND_SPACE_1.id; - const two = [{ id, namespaces: allSpaces }]; - id = CASES.ALL_SPACES.id; - const three = [{ id, namespaces: allSpaces }]; - return { one, two, three }; + const eachSpace = [DEFAULT_SPACE_ID, SPACE_1_ID, SPACE_2_ID]; + const allSpaces = ['*']; + // for each of the cases below, test adding each space and all spaces to the object + const one = [ + { id: CASES.DEFAULT_ONLY.id, namespaces: eachSpace }, + { id: CASES.DEFAULT_ONLY.id, namespaces: allSpaces }, + ]; + const two = [ + { id: CASES.DEFAULT_AND_SPACE_1.id, namespaces: eachSpace }, + { id: CASES.DEFAULT_AND_SPACE_1.id, namespaces: allSpaces }, + ]; + const three = [ + { id: CASES.EACH_SPACE.id, namespaces: eachSpace }, + { id: CASES.EACH_SPACE.id, namespaces: allSpaces }, + ]; + const four = [ + { id: CASES.ALL_SPACES.id, namespaces: eachSpace }, + { id: CASES.ALL_SPACES.id, namespaces: allSpaces }, + ]; + return { one, two, three, four }; }; // eslint-disable-next-line import/no-default-export @@ -68,6 +81,7 @@ export default function ({ getService }: TestInvoker) { one: createTestDefinitions(testCases.one, false), two: createTestDefinitions(testCases.two, false), three: createTestDefinitions(testCases.three, false), + four: createTestDefinitions(testCases.four, false), }; }; @@ -76,9 +90,10 @@ export default function ({ getService }: TestInvoker) { const tests = createSingleTests(spaceId); addTests(`targeting the ${spaceId} space`, { spaceId, tests }); }); - const { one, two, three } = createMultiTests(); + const { one, two, three, four } = createMultiTests(); addTests('for a saved object in the default space', { tests: one }); addTests('for a saved object in the default and space_1 spaces', { tests: two }); addTests('for a saved object in the default, space_1, and space_2 spaces', { tests: three }); + addTests('for a saved object in all spaces', { tests: four }); }); } diff --git a/x-pack/test/spaces_api_integration/spaces_only/apis/share_remove.ts b/x-pack/test/spaces_api_integration/spaces_only/apis/share_remove.ts index 2c4506b7235339..bb0e6f858cbab7 100644 --- a/x-pack/test/spaces_api_integration/spaces_only/apis/share_remove.ts +++ b/x-pack/test/spaces_api_integration/spaces_only/apis/share_remove.ts @@ -33,7 +33,7 @@ const createSingleTestCases = (spaceId: string) => { { ...CASES.DEFAULT_AND_SPACE_1, namespaces, ...fail404(spaceId === SPACE_2_ID) }, { ...CASES.DEFAULT_AND_SPACE_2, namespaces, ...fail404(spaceId === SPACE_1_ID) }, { ...CASES.SPACE_1_AND_SPACE_2, namespaces, ...fail404(spaceId === DEFAULT_SPACE_ID) }, - { ...CASES.ALL_SPACES, namespaces }, + { ...CASES.EACH_SPACE, namespaces }, { ...CASES.DOES_NOT_EXIST, namespaces, ...fail404() }, ]; }; @@ -56,7 +56,7 @@ const createMultiTestCases = () => { { id, namespaces: [DEFAULT_SPACE_ID], ...fail404() }, // this object's namespaces no longer contains DEFAULT_SPACE_ID { id, namespaces: [SPACE_1_ID], ...fail404() }, // this object's namespaces does contain SPACE_1_ID ]; - id = CASES.ALL_SPACES.id; + id = CASES.EACH_SPACE.id; const three = [ { id, namespaces: [DEFAULT_SPACE_ID, SPACE_1_ID, nonExistentSpaceId] }, // this saved object will not be found in the context of the current namespace ('default') @@ -64,7 +64,15 @@ const createMultiTestCases = () => { { id, namespaces: [SPACE_1_ID], ...fail404() }, // this object's namespaces no longer contains SPACE_1_ID { id, namespaces: [SPACE_2_ID], ...fail404() }, // this object's namespaces does contain SPACE_2_ID ]; - return { one, two, three }; + id = CASES.ALL_SPACES.id; + const four = [ + { id, namespaces: [DEFAULT_SPACE_ID, SPACE_1_ID, nonExistentSpaceId] }, + // this saved object will still be found in the context of the current namespace ('default') + { id, namespaces: ['*'] }, + // this object no longer exists + { id, namespaces: ['*'], ...fail404() }, + ]; + return { one, two, three, four }; }; // eslint-disable-next-line import/no-default-export @@ -83,6 +91,7 @@ export default function ({ getService }: TestInvoker) { one: createTestDefinitions(testCases.one, false), two: createTestDefinitions(testCases.two, false), three: createTestDefinitions(testCases.three, false), + four: createTestDefinitions(testCases.four, false), }; }; @@ -91,9 +100,10 @@ export default function ({ getService }: TestInvoker) { const tests = createSingleTests(spaceId); addTests(`targeting the ${spaceId} space`, { spaceId, tests }); }); - const { one, two, three } = createMultiTests(); + const { one, two, three, four } = createMultiTests(); addTests('for a saved object in the default space', { tests: one }); addTests('for a saved object in the default and space_1 spaces', { tests: two }); addTests('for a saved object in the default, space_1, and space_2 spaces', { tests: three }); + addTests('for a saved object in all spaces', { tests: four }); }); } From 9e1c44fcc08351070afffe87056cd86aefbe3449 Mon Sep 17 00:00:00 2001 From: Spencer <email@spalger.com> Date: Sun, 4 Oct 2020 21:33:12 -0700 Subject: [PATCH 128/128] add core-js production dependency (#79395) Co-authored-by: spalger <spalger@users.noreply.github.com> --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 30d614aa43f7bc..b2252e2bd264b2 100644 --- a/package.json +++ b/package.json @@ -153,6 +153,7 @@ "chokidar": "^3.4.2", "color": "1.0.3", "commander": "^3.0.2", + "core-js": "^3.6.5", "cypress-promise": "^1.1.0", "deep-freeze-strict": "^1.1.1", "del": "^5.1.0",