} }) => {
+ setShowTooltips(event.target.checked);
+ const source = { ...selectedLayerConfig.source, showTooltips: event.target.checked };
+ setSelectedLayerConfig({ ...selectedLayerConfig, source });
+ };
+
+ const shouldTooltipSectionOpen = () => {
+ return (
+ selectedLayerConfig.source.showTooltips &&
+ selectedLayerConfig.source.tooltipFields?.length > 0
+ );
+ };
+
+ return (
+
+
+
+
+
+
+
+ {
+ const newIndexPattern = await indexPatterns.get(newIndexPatternId);
+ setIndexPattern(newIndexPattern);
+ }}
+ isClearable={false}
+ />
+
+
+
+
+ {
+ const field = indexPattern?.getFieldByName(option[0].label);
+ setSelectedField(field || null);
+ }}
+ sortMatchesBy="startsWith"
+ placeholder={i18n.translate('documentLayer.selectDataFieldPlaceholder', {
+ defaultMessage: 'Select data field',
+ })}
+ />
+
+
+
+ Number of documents
+
+
+ {hasInvalidRequestNumber && (
+
+
+
+ )}
+
+
+
+
+
+
+
+ 0}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ Tooltip Fields
+
+
+
+
+
+
+
+ );
+};
diff --git a/custom_import_map/public/components/layer_config/documents_config/document_layer_style.tsx b/custom_import_map/public/components/layer_config/documents_config/document_layer_style.tsx
new file mode 100644
index 00000000..d5b3d361
--- /dev/null
+++ b/custom_import_map/public/components/layer_config/documents_config/document_layer_style.tsx
@@ -0,0 +1,234 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useEffect, useState } from 'react';
+import {
+ EuiColorPicker,
+ useColorPickerState,
+ EuiFieldNumber,
+ EuiFormLabel,
+ EuiFormErrorText,
+ EuiFlexItem,
+ EuiSpacer,
+ EuiButtonGroup,
+ EuiPanel,
+ EuiTitle,
+ EuiFormRow,
+ EuiForm,
+} from '@elastic/eui';
+import { FormattedMessage } from '@osd/i18n/react';
+import { DocumentLayerSpecification } from '../../../model/mapLayerType';
+
+interface Props {
+ selectedLayerConfig: DocumentLayerSpecification;
+ setSelectedLayerConfig: Function;
+}
+
+export const DocumentLayerStyle = ({ setSelectedLayerConfig, selectedLayerConfig }: Props) => {
+ const [fillColor, setFillColor] = useColorPickerState(selectedLayerConfig?.style?.fillColor);
+ const [borderColor, setBorderColor] = useColorPickerState(
+ selectedLayerConfig?.style?.borderColor
+ );
+ const [borderThickness, setBorderThickness] = useState(
+ selectedLayerConfig?.style?.borderThickness
+ );
+ const [markerSize, setMarkerSize] = useState(selectedLayerConfig?.style?.markerSize);
+ const [hasInvalidThickness, setHasInvalidThickness] = useState(false);
+ const [hasInvalidSize, setHasInvalidSize] = useState(false);
+ const geoTypeToggleButtonGroupPrefix = 'geoTypeToggleButtonGroup';
+ const [toggleGeoTypeIdSelected, setToggleGeoTypeIdSelected] = useState(
+ `${geoTypeToggleButtonGroupPrefix}__Point`
+ );
+
+ useEffect(() => {
+ setSelectedLayerConfig({
+ ...selectedLayerConfig,
+ style: {
+ ...selectedLayerConfig?.style,
+ fillColor,
+ },
+ });
+ }, [fillColor]);
+
+ useEffect(() => {
+ setSelectedLayerConfig({
+ ...selectedLayerConfig,
+ style: {
+ ...selectedLayerConfig?.style,
+ borderColor,
+ },
+ });
+ }, [borderColor]);
+
+ const onBorderThicknessChange = (e: any) => {
+ setSelectedLayerConfig({
+ ...selectedLayerConfig,
+ style: {
+ ...selectedLayerConfig?.style,
+ borderThickness: Number(e.target.value),
+ },
+ });
+ setBorderThickness(Number(e.target.value));
+ };
+
+ const onMarkerSizeChange = (e: any) => {
+ setSelectedLayerConfig({
+ ...selectedLayerConfig,
+ style: {
+ ...selectedLayerConfig?.style,
+ markerSize: Number(e.target.value),
+ },
+ });
+ setMarkerSize(Number(e.target.value));
+ };
+
+ useEffect(() => {
+ if (borderThickness < 0 || borderThickness > 100) {
+ setHasInvalidThickness(true);
+ } else {
+ setHasInvalidThickness(false);
+ }
+ }, [borderThickness]);
+
+ useEffect(() => {
+ if (markerSize < 0 || markerSize > 100) {
+ setHasInvalidSize(true);
+ } else {
+ setHasInvalidSize(false);
+ }
+ }, [markerSize]);
+
+ const toggleButtonsGeoType = [
+ {
+ id: `${geoTypeToggleButtonGroupPrefix}__Point`,
+ label: 'Points',
+ },
+ {
+ id: `${geoTypeToggleButtonGroupPrefix}__Line`,
+ label: 'Lines',
+ },
+ {
+ id: `${geoTypeToggleButtonGroupPrefix}__Polygon`,
+ label: 'Polygons',
+ },
+ ];
+
+ const onChangeGeoTypeSelected = (optionId: string) => {
+ setToggleGeoTypeIdSelected(optionId);
+ };
+
+ interface ColorPickerProps {
+ color: string;
+ setColor: Function;
+ label: string;
+ }
+
+ const ColorPicker = ({ color, setColor, label }: ColorPickerProps) => {
+ return (
+
+ {}}
+ fullWidth={true}
+ />
+
+ );
+ };
+
+ interface WidthSelectorProps {
+ size: number;
+ onWidthChange: Function;
+ label: string;
+ hasInvalid: boolean;
+ }
+
+ const WidthSelector = ({ label, onWidthChange, size, hasInvalid }: WidthSelectorProps) => {
+ return (
+
+
+ px}
+ fullWidth={true}
+ />
+ {hasInvalid && (
+
+
+
+ )}
+
+
+ );
+ };
+
+ return (
+
+
+ Layer style
+
+
+ onChangeGeoTypeSelected(id)}
+ buttonSize="compressed"
+ />
+
+
+ {toggleGeoTypeIdSelected === `${geoTypeToggleButtonGroupPrefix}__Point` && (
+
+
+
+
+
+
+ )}
+ {toggleGeoTypeIdSelected === `${geoTypeToggleButtonGroupPrefix}__Line` && (
+
+
+
+
+ )}
+ {toggleGeoTypeIdSelected === `${geoTypeToggleButtonGroupPrefix}__Polygon` && (
+
+
+
+
+
+ )}
+
+
+ );
+};
diff --git a/custom_import_map/public/components/layer_config/index.ts b/custom_import_map/public/components/layer_config/index.ts
new file mode 100644
index 00000000..4d043e76
--- /dev/null
+++ b/custom_import_map/public/components/layer_config/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { LayerConfigPanel } from './layer_config_panel';
+export { BaseMapLayerConfigPanel } from './base_map_layer_config_panel';
diff --git a/custom_import_map/public/components/layer_config/layer_basic_settings.tsx b/custom_import_map/public/components/layer_config/layer_basic_settings.tsx
new file mode 100644
index 00000000..67fbfb56
--- /dev/null
+++ b/custom_import_map/public/components/layer_config/layer_basic_settings.tsx
@@ -0,0 +1,153 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useState, useEffect } from 'react';
+import {
+ EuiDualRange,
+ EuiFieldText,
+ EuiForm,
+ EuiFormRow,
+ EuiTitle,
+ EuiSpacer,
+ EuiRange,
+ EuiPanel,
+ EuiFormLabel,
+ EuiTextArea,
+} from '@elastic/eui';
+import { MapLayerSpecification } from '../../model/mapLayerType';
+import {
+ MAP_DEFAULT_MIN_ZOOM,
+ MAP_DEFAULT_MAX_ZOOM,
+ MAP_LAYER_DEFAULT_MIN_OPACITY,
+ MAP_LAYER_DEFAULT_MAX_OPACITY,
+ MAP_LAYER_DEFAULT_OPACITY_STEP,
+ MAX_LAYER_NAME_LIMIT,
+} from '../../../common';
+import { layersTypeNameMap } from '../../model/layersFunctions';
+
+interface Props {
+ selectedLayerConfig: MapLayerSpecification;
+ setSelectedLayerConfig: Function;
+ setIsUpdateDisabled: Function;
+ isLayerExists: Function;
+}
+
+export const LayerBasicSettings = ({
+ selectedLayerConfig,
+ setSelectedLayerConfig,
+ setIsUpdateDisabled,
+ isLayerExists,
+}: Props) => {
+ const [invalid, setInvalid] = useState(selectedLayerConfig.name.length === 0);
+ const [errors, setErrors] = useState([]);
+
+ const validateName = (name: string) => {
+ if (name?.length === 0) {
+ setInvalid(true);
+ setErrors(['Name cannot be empty']);
+ return;
+ }
+ if (MAX_LAYER_NAME_LIMIT < name?.length) {
+ setInvalid(true);
+ setErrors(['Name should be less than ' + MAX_LAYER_NAME_LIMIT + ' characters']);
+ return;
+ }
+ if (isLayerExists(name)) {
+ setInvalid(true);
+ setErrors(['Name already exists']);
+ return;
+ }
+ setInvalid(false);
+ return;
+ };
+
+ const { name } = selectedLayerConfig;
+
+ useEffect(() => {
+ const disableUpdate = !name || invalid;
+ setIsUpdateDisabled(disableUpdate);
+ }, [setIsUpdateDisabled, name, invalid]);
+
+ const commonUpdate = (key: string, value: any) => {
+ const newLayerConfig = { ...selectedLayerConfig, [key]: value };
+ setSelectedLayerConfig(newLayerConfig);
+ };
+ const onZoomChange = (value: number[]) => {
+ commonUpdate('zoomRange', value);
+ };
+
+ const onOpacityChange = (e: any) => {
+ commonUpdate('opacity', Number(e.target.value));
+ };
+
+ const onNameChange = (e: any) => {
+ const layerName = String(e.target.value);
+ validateName(layerName);
+ commonUpdate('name', layerName);
+ };
+
+ const onDescriptionChange = (e: any) => {
+ commonUpdate('description', String(e.target.value));
+ };
+
+ return (
+
+
+ Layer settings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %}
+ />
+
+
+
+ );
+};
diff --git a/custom_import_map/public/components/layer_config/layer_config_panel.tsx b/custom_import_map/public/components/layer_config/layer_config_panel.tsx
new file mode 100644
index 00000000..e9a15d7f
--- /dev/null
+++ b/custom_import_map/public/components/layer_config/layer_config_panel.tsx
@@ -0,0 +1,175 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useEffect, useState } from 'react';
+import { cloneDeep, isEqual } from 'lodash';
+
+import {
+ EuiButton,
+ EuiFlyout,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiFlyoutHeader,
+ EuiFlexItem,
+ EuiButtonEmpty,
+ EuiFlexGroup,
+ EuiModal,
+ EuiModalBody,
+ EuiModalFooter,
+ EuiModalHeader,
+ EuiModalHeaderTitle,
+ EuiIcon,
+} from '@elastic/eui';
+
+import { MapLayerSpecification } from '../../model/mapLayerType';
+import { BaseMapLayerConfigPanel } from './index';
+import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../../common';
+import { DocumentLayerConfigPanel } from './documents_config/document_layer_config_panel';
+import { layersTypeIconMap } from '../../model/layersFunctions';
+import { IndexPattern } from '../../../../../src/plugins/data/public';
+import { CustomMapConfigPanel } from './custom_map_config/custom_map_config_panel';
+
+interface Props {
+ closeLayerConfigPanel: Function;
+ selectedLayerConfig: MapLayerSpecification;
+ setSelectedLayerConfig: Function;
+ updateLayer: Function;
+ removeLayer: Function;
+ isNewLayer: boolean;
+ setIsNewLayer: Function;
+ layersIndexPatterns: IndexPattern[];
+ updateIndexPatterns: Function;
+ isLayerExists: Function;
+}
+
+export const LayerConfigPanel = ({
+ closeLayerConfigPanel,
+ selectedLayerConfig,
+ setSelectedLayerConfig,
+ updateLayer,
+ removeLayer,
+ isNewLayer,
+ setIsNewLayer,
+ layersIndexPatterns,
+ updateIndexPatterns,
+ isLayerExists,
+}: Props) => {
+ const [isUpdateDisabled, setIsUpdateDisabled] = useState(false);
+ const [originLayerConfig, setOriginLayerConfig] = useState(null);
+ const [warnModalVisible, setWarnModalVisible] = useState(false);
+
+ useEffect(() => {
+ setOriginLayerConfig(cloneDeep(selectedLayerConfig));
+ }, []);
+
+ const discardChanges = () => {
+ closeLayerConfigPanel(false);
+ setSelectedLayerConfig(undefined);
+ };
+ const onClose = () => {
+ if (isEqual(originLayerConfig, selectedLayerConfig)) {
+ discardChanges();
+ } else {
+ setWarnModalVisible(true);
+ }
+ if (isNewLayer) {
+ removeLayer(selectedLayerConfig.id);
+ setIsNewLayer(false);
+ }
+ };
+ const onUpdate = () => {
+ updateLayer();
+ closeLayerConfigPanel(false);
+ if (isNewLayer) {
+ setIsNewLayer(false);
+ }
+ };
+
+ const closeModal = () => {
+ setWarnModalVisible(false);
+ };
+
+ return (
+
+
+
+
+
+
+
+ {selectedLayerConfig.name}
+
+
+
+
+
+
+ {selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP && (
+
+ )}
+ {selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS && (
+
+ )}
+ {selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP && (
+
+ )}
+
+
+
+
+
+
+
+ Discard
+
+
+
+
+ Update
+
+
+
+
+ {warnModalVisible && (
+
+
+ Unsaved changes
+
+
+ Do you want to discard the changes?
+
+
+ Cancel
+
+ Discard
+
+
+
+ )}
+
+ );
+};
diff --git a/custom_import_map/public/components/layer_control_panel/index.ts b/custom_import_map/public/components/layer_control_panel/index.ts
new file mode 100644
index 00000000..30cd7379
--- /dev/null
+++ b/custom_import_map/public/components/layer_control_panel/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { LayerControlPanel } from './layer_control_panel';
diff --git a/custom_import_map/public/components/layer_control_panel/layer_control_panel.scss b/custom_import_map/public/components/layer_control_panel/layer_control_panel.scss
new file mode 100644
index 00000000..b3ad8c0c
--- /dev/null
+++ b/custom_import_map/public/components/layer_control_panel/layer_control_panel.scss
@@ -0,0 +1,32 @@
+.layerControlPanel--show {
+ pointer-events: auto;
+ width: $euiSizeL * 11;
+
+ .layerControlPanel__title {
+ padding: $euiSizeM $euiSizeM
+ }
+
+ .layerControlPanel__selected {
+ background-color: $euiColorLightShade;
+ }
+
+ .layerControlPanel__layerFunctionButton {
+ height: $euiSizeL;
+ width: $euiSizeL;
+ }
+
+ .euiListGroupItem__label {
+ width: $euiSizeL * 6;
+ }
+}
+
+.layerControlPanel--hide {
+ pointer-events: auto;
+
+ .layerControlPanel__visButton {
+ background-color: $euiColorEmptyShade;
+ color: $euiTextColor;
+ border-color: $euiColorLightShade;
+ }
+}
+
diff --git a/custom_import_map/public/components/layer_control_panel/layer_control_panel.tsx b/custom_import_map/public/components/layer_control_panel/layer_control_panel.tsx
new file mode 100644
index 00000000..14a7d9f2
--- /dev/null
+++ b/custom_import_map/public/components/layer_control_panel/layer_control_panel.tsx
@@ -0,0 +1,462 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { memo, useEffect, useState } from 'react';
+import {
+ EuiPanel,
+ EuiTitle,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiListGroupItem,
+ EuiButtonEmpty,
+ EuiHorizontalRule,
+ EuiButtonIcon,
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable,
+ EuiConfirmModal,
+ DropResult,
+} from '@elastic/eui';
+import { I18nProvider } from '@osd/i18n/react';
+import { Map as Maplibre } from 'maplibre-gl';
+import './layer_control_panel.scss';
+import { IndexPattern } from '../../../../../src/plugins/data/public';
+import { AddLayerPanel } from '../add_layer_panel';
+import { LayerConfigPanel } from '../layer_config';
+import { MapLayerSpecification } from '../../model/mapLayerType';
+import {
+ LAYER_VISIBILITY,
+ DASHBOARDS_MAPS_LAYER_TYPE,
+ LAYER_ICON_TYPE_MAP,
+ LAYER_PANEL_SHOW_LAYER_ICON,
+ LAYER_PANEL_HIDE_LAYER_ICON,
+} from '../../../common';
+import {
+ LayerActions,
+ layersFunctionMap,
+ referenceLayerTypeLookup,
+} from '../../model/layersFunctions';
+import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public';
+import { MapServices } from '../../types';
+import {
+ handleReferenceLayerRender,
+ handleDataLayerRender,
+} from '../../model/layerRenderController';
+import { MapState } from '../../model/mapState';
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+interface Props {
+ maplibreRef: MaplibreRef;
+ setLayers: (layers: MapLayerSpecification[]) => void;
+ layers: MapLayerSpecification[];
+ layersIndexPatterns: IndexPattern[];
+ setLayersIndexPatterns: (indexPatterns: IndexPattern[]) => void;
+ mapState: MapState;
+}
+
+export const LayerControlPanel = memo(
+ ({
+ maplibreRef,
+ setLayers,
+ layers,
+ layersIndexPatterns,
+ setLayersIndexPatterns,
+ mapState,
+ }: Props) => {
+ const { services } = useOpenSearchDashboards();
+ const {
+ data: { indexPatterns },
+ } = services;
+
+ const [isLayerConfigVisible, setIsLayerConfigVisible] = useState(false);
+ const [isLayerControlVisible, setIsLayerControlVisible] = useState(true);
+ const [selectedLayerConfig, setSelectedLayerConfig] = useState<
+ MapLayerSpecification | undefined
+ >();
+ const [initialLayersLoaded, setInitialLayersLoaded] = useState(false);
+ const [isUpdatingLayerRender, setIsUpdatingLayerRender] = useState(false);
+ const [isNewLayer, setIsNewLayer] = useState(false);
+ const [isDeleteLayerModalVisible, setIsDeleteLayerModalVisible] = useState(false);
+ const [selectedDeleteLayer, setSelectedDeleteLayer] = useState<
+ MapLayerSpecification | undefined
+ >();
+
+ useEffect(() => {
+ if (!isUpdatingLayerRender && initialLayersLoaded) {
+ return;
+ }
+ if (layers.length <= 0) {
+ return;
+ }
+
+ if (initialLayersLoaded) {
+ if (!selectedLayerConfig) {
+ return;
+ }
+ if (
+ selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP ||
+ selectedLayerConfig.type === DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP
+ ) {
+ handleReferenceLayerRender(selectedLayerConfig, maplibreRef, undefined);
+ } else {
+ updateIndexPatterns();
+ handleDataLayerRender(selectedLayerConfig, mapState, services, maplibreRef, undefined);
+ }
+ setSelectedLayerConfig(undefined);
+ } else {
+ layers.forEach((layer) => {
+ const beforeLayerId = getMapBeforeLayerId(layer);
+ if (referenceLayerTypeLookup[layer.type]) {
+ handleReferenceLayerRender(layer, maplibreRef, beforeLayerId);
+ } else {
+ handleDataLayerRender(layer, mapState, services, maplibreRef, beforeLayerId);
+ }
+ });
+ setInitialLayersLoaded(true);
+ }
+ setIsUpdatingLayerRender(false);
+ }, [layers]);
+
+ // Get layer id from layers that is above the selected layer
+ function getMapBeforeLayerId(selectedLayer: MapLayerSpecification): string | undefined {
+ const selectedLayerIndex = layers.findIndex((layer) => layer.id === selectedLayer.id);
+ const beforeLayers = layers.slice(selectedLayerIndex + 1);
+ if (beforeLayers.length === 0) {
+ return undefined;
+ }
+ return beforeLayers[0]?.id;
+ }
+
+ const closeLayerConfigPanel = () => {
+ setIsLayerConfigVisible(false);
+ setTimeout(() => {
+ maplibreRef.current?.resize();
+ }, 0);
+ };
+
+ const newLayerIndex = () => {
+ return layers?.length + 1;
+ };
+
+ const addLayer = (layer: MapLayerSpecification) => {
+ setLayers([...layers, layer]);
+ };
+
+ const updateLayer = () => {
+ if (!selectedLayerConfig) {
+ return;
+ }
+ const layersClone = [...layers];
+ const index = layersClone.findIndex((layer) => layer.id === selectedLayerConfig.id);
+ if (index <= -1) {
+ layersClone.push(selectedLayerConfig);
+ } else {
+ layersClone[index] = {
+ ...layersClone[index],
+ ...selectedLayerConfig,
+ };
+ }
+ setLayers(layersClone);
+ setIsUpdatingLayerRender(true);
+ };
+
+ const removeLayer = (layerId: string) => {
+ const layersClone = [...layers];
+ const index = layersClone.findIndex((layer) => layer.id === layerId);
+ if (index > -1) {
+ layersClone.splice(index, 1);
+ setLayers(layersClone);
+ }
+ };
+
+ const onClickLayerName = (layer: MapLayerSpecification) => {
+ setSelectedLayerConfig(layer);
+ setIsLayerConfigVisible(true);
+ };
+ const isLayerExists = (name: string) => {
+ return layers.findIndex((layer) => layer.name === name) > -1;
+ };
+
+ const [layerVisibility, setLayerVisibility] = useState(new Map([]));
+ layers.forEach((layer) => {
+ layerVisibility.set(layer.id, layer.visibility === LAYER_VISIBILITY.VISIBLE);
+ });
+
+ const beforeMaplibreLayerID = (source: number, destination: number) => {
+ if (source > destination) {
+ // if layer is moved below, move current layer below given destination
+ return layers[destination].id;
+ }
+ const beforeIndex = destination + 1; // if layer is moved up, move current layer above destination
+ if (beforeIndex < layers.length) {
+ return layers[beforeIndex].id;
+ }
+ return undefined;
+ };
+
+ const onDragEnd = (dropResult: DropResult) => {
+ if (!dropResult) {
+ return;
+ }
+ if (dropResult.source && dropResult.destination) {
+ // we display list in reverse order
+ const prevIndex = getLayerIndex(dropResult.source.index);
+ const newIndex = getLayerIndex(dropResult.destination.index);
+
+ const currentMaplibreLayerId = layers[prevIndex].id;
+ const beforeMaplibreLayerId = beforeMaplibreLayerID(prevIndex, newIndex);
+ LayerActions.move(maplibreRef, currentMaplibreLayerId, beforeMaplibreLayerId);
+
+ // update map layers
+ const layersClone = [...layers];
+ const oldLayer = layersClone[prevIndex];
+ layersClone.splice(prevIndex, 1);
+ layersClone.splice(newIndex, 0, oldLayer);
+ setLayers(layersClone);
+ }
+ };
+
+ const getLayerIndex = (reversedIndex: number) => {
+ return layers.length - reversedIndex - 1;
+ };
+
+ const getReverseLayers = () => {
+ const layersClone = [...layers];
+ return layersClone.reverse();
+ };
+
+ const updateIndexPatterns = async () => {
+ if (!selectedLayerConfig) {
+ return;
+ }
+ if (referenceLayerTypeLookup[selectedLayerConfig.type]) {
+ return;
+ }
+ const findIndexPattern = layersIndexPatterns.find(
+ // @ts-ignore
+ (indexPattern) => indexPattern.id === selectedLayerConfig.source.indexPatternId
+ );
+ if (!findIndexPattern) {
+ // @ts-ignore
+ const newIndexPattern = await indexPatterns.get(selectedLayerConfig.source.indexPatternId);
+ const cloneLayersIndexPatterns = [...layersIndexPatterns, newIndexPattern];
+ setLayersIndexPatterns(cloneLayersIndexPatterns);
+ }
+ };
+
+ const onLayerVisibilityChange = (layer: MapLayerSpecification) => {
+ if (layer.visibility === LAYER_VISIBILITY.VISIBLE) {
+ layer.visibility = LAYER_VISIBILITY.NONE;
+ setLayerVisibility(new Map(layerVisibility.set(layer.id, false)));
+ } else {
+ layer.visibility = LAYER_VISIBILITY.VISIBLE;
+ setLayerVisibility(new Map(layerVisibility.set(layer.id, true)));
+ }
+ layersFunctionMap[layer.type]?.hide(maplibreRef, layer);
+ };
+
+ const onDeleteLayerIconClick = (layer: MapLayerSpecification) => {
+ setSelectedDeleteLayer(layer);
+ setIsDeleteLayerModalVisible(true);
+ };
+
+ const onDeleteLayerConfirm = () => {
+ if (selectedDeleteLayer) {
+ layersFunctionMap[selectedDeleteLayer.type]?.remove(maplibreRef, selectedDeleteLayer);
+ removeLayer(selectedDeleteLayer.id);
+ setIsDeleteLayerModalVisible(false);
+ setSelectedDeleteLayer(undefined);
+ }
+ };
+
+ const onCancelDeleteLayer = () => {
+ setIsDeleteLayerModalVisible(false);
+ setSelectedDeleteLayer(undefined);
+ };
+
+ let deleteLayerModal;
+ if (isDeleteLayerModalVisible) {
+ deleteLayerModal = (
+
+
+ Do you want to delete layer {selectedDeleteLayer?.name}?
+
+
+ );
+ }
+
+ if (isLayerControlVisible) {
+ return (
+
+
+
+
+
+
+ Layer
+
+
+
+ setIsLayerControlVisible((visible) => !visible)}
+ aria-label="Hide layer control"
+ color="text"
+ className="layerControlPanel__visButton"
+ title="Collapse layers panel"
+ />
+
+
+
+
+
+ {getReverseLayers().map((layer, index) => {
+ const isLayerSelected =
+ isLayerConfigVisible &&
+ selectedLayerConfig &&
+ selectedLayerConfig.id === layer.id;
+ return (
+
+ {(provided) => (
+
+
+
+ onClickLayerName(layer)}
+ />
+
+
+
+ onLayerVisibilityChange(layer)}
+ aria-label="Hide or show layer"
+ color="text"
+ title={
+ layerVisibility.get(layer.id) ? 'Hide layer' : 'Show layer'
+ }
+ />
+
+
+ onDeleteLayerIconClick(layer)}
+ aria-label="Delete layer"
+ color={layer.id === selectedLayerConfig?.id ? 'text' : 'danger'}
+ title="Delete layer"
+ disabled={layer.id === selectedLayerConfig?.id}
+ />
+
+
+
+
+
+
+
+
+ )}
+
+ );
+ })}
+
+
+ {isLayerConfigVisible && selectedLayerConfig && (
+
+ )}
+
+ {deleteLayerModal}
+
+
+
+ );
+ }
+
+ return (
+
+ setIsLayerControlVisible((visible) => !visible)}
+ aria-label="Show layer control"
+ title="Expand layers panel"
+ />
+
+ );
+ }
+);
diff --git a/custom_import_map/public/components/map_container/index.ts b/custom_import_map/public/components/map_container/index.ts
new file mode 100644
index 00000000..b0716344
--- /dev/null
+++ b/custom_import_map/public/components/map_container/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { MapContainer } from './map_container';
diff --git a/custom_import_map/public/components/map_container/map_container.scss b/custom_import_map/public/components/map_container/map_container.scss
new file mode 100644
index 00000000..7453ca94
--- /dev/null
+++ b/custom_import_map/public/components/map_container/map_container.scss
@@ -0,0 +1,32 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+@import "maplibre-gl/dist/maplibre-gl.css";
+@import "../../variables";
+
+/* stylelint-disable no-empty-source */
+.map-container {
+ width: 100%;
+ min-height: calc(100vh - #{$mapHeaderOffset});
+}
+
+.maplibregl-ctrl-top-left {
+ left: $euiSizeS;
+ top: $euiSizeS;
+}
+
+.layerControlPanel-container {
+ z-index: 1;
+ position: absolute;
+ margin-left: $euiSizeS;
+ margin-top: $euiSizeS;
+}
+
+.zoombar {
+ z-index: 1;
+ position: absolute;
+ bottom: $euiSizeM;
+ right: $euiSizeS;
+}
diff --git a/custom_import_map/public/components/map_container/map_container.tsx b/custom_import_map/public/components/map_container/map_container.tsx
new file mode 100644
index 00000000..d2e71027
--- /dev/null
+++ b/custom_import_map/public/components/map_container/map_container.tsx
@@ -0,0 +1,153 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useEffect, useRef, useState } from 'react';
+import { EuiPanel } from '@elastic/eui';
+import { LngLat, Map as Maplibre, MapMouseEvent, NavigationControl, Popup } from 'maplibre-gl';
+import { LayerControlPanel } from '../layer_control_panel';
+import './map_container.scss';
+import { MAP_INITIAL_STATE, MAP_GLYPHS } from '../../../common';
+import { MapLayerSpecification } from '../../model/mapLayerType';
+import { IndexPattern } from '../../../../../src/plugins/data/public';
+import { MapState } from '../../model/mapState';
+import { createPopup, getPopupLngLat, isTooltipEnabledLayer } from '../tooltip/create_tooltip';
+
+interface MapContainerProps {
+ setLayers: (layers: MapLayerSpecification[]) => void;
+ layers: MapLayerSpecification[];
+ layersIndexPatterns: IndexPattern[];
+ setLayersIndexPatterns: (indexPatterns: IndexPattern[]) => void;
+ maplibreRef: React.MutableRefObject;
+ mapState: MapState;
+}
+
+export const MapContainer = ({
+ setLayers,
+ layers,
+ layersIndexPatterns,
+ setLayersIndexPatterns,
+ maplibreRef,
+ mapState,
+}: MapContainerProps) => {
+ const mapContainer = useRef(null);
+ const [mounted, setMounted] = useState(false);
+ const [zoom, setZoom] = useState(MAP_INITIAL_STATE.zoom);
+ const [coordinates, setCoordinates] = useState();
+
+ useEffect(() => {
+ if (!mapContainer.current) return;
+ const mbStyle = {
+ version: 8 as 8,
+ sources: {},
+ layers: [],
+ glyphs: MAP_GLYPHS,
+ };
+
+ maplibreRef.current = new Maplibre({
+ container: mapContainer.current!,
+ center: [MAP_INITIAL_STATE.lng, MAP_INITIAL_STATE.lat],
+ zoom,
+ style: mbStyle,
+ });
+
+ const maplibreInstance = maplibreRef.current!;
+ maplibreInstance.addControl(new NavigationControl({ showCompass: true }), 'top-right');
+ maplibreInstance.on('style.load', function () {
+ setMounted(true);
+ });
+ maplibreInstance.on('move', () => {
+ return setZoom(Number(maplibreInstance.getZoom().toFixed(2)));
+ });
+ }, []);
+
+ useEffect(() => {
+ let clickPopup: Popup | null = null;
+ let hoverPopup: Popup | null = null;
+
+ // We don't want to show layer information in the popup for the map tile layer
+ const tooltipEnabledLayers = layers.filter(isTooltipEnabledLayer);
+
+ function onClickMap(e: MapMouseEvent) {
+ // remove previous popup
+ clickPopup?.remove();
+
+ const features = maplibreRef.current?.queryRenderedFeatures(e.point);
+ if (features && maplibreRef.current) {
+ clickPopup = createPopup({ features, layers: tooltipEnabledLayers });
+ clickPopup
+ ?.setLngLat(getPopupLngLat(features[0].geometry) ?? e.lngLat)
+ .addTo(maplibreRef.current);
+ }
+ }
+
+ function onMouseMoveMap(e: MapMouseEvent) {
+ setCoordinates(e.lngLat.wrap());
+
+ // remove previous popup
+ hoverPopup?.remove();
+
+ const features = maplibreRef.current?.queryRenderedFeatures(e.point);
+ if (features && maplibreRef.current) {
+ hoverPopup = createPopup({
+ features,
+ layers: tooltipEnabledLayers,
+ showCloseButton: false,
+ showPagination: false,
+ showLayerSelection: false,
+ });
+ hoverPopup
+ ?.setLngLat(getPopupLngLat(features[0].geometry) ?? e.lngLat)
+ .addTo(maplibreRef.current);
+ }
+ }
+
+ if (maplibreRef.current) {
+ const map = maplibreRef.current;
+ map.on('click', onClickMap);
+ // reset cursor to default when user is no longer hovering over a clickable feature
+ map.on('mouseleave', () => {
+ map.getCanvas().style.cursor = '';
+ hoverPopup?.remove();
+ });
+ map.on('mouseenter', () => {
+ map.getCanvas().style.cursor = 'pointer';
+ });
+ // add tooltip when users mouse move over a point
+ map.on('mousemove', onMouseMoveMap);
+ }
+
+ return () => {
+ if (maplibreRef.current) {
+ maplibreRef.current.off('click', onClickMap);
+ maplibreRef.current.off('mousemove', onMouseMoveMap);
+ }
+ };
+ }, [layers]);
+
+ return (
+
+
+
+ {coordinates &&
+ `lat: ${coordinates.lat.toFixed(4)}, lon: ${coordinates.lng.toFixed(4)}, `}
+ zoom: {zoom}
+
+
+
+ {mounted && (
+
+ )}
+
+
+
+ );
+};
diff --git a/custom_import_map/public/components/map_page/index.ts b/custom_import_map/public/components/map_page/index.ts
new file mode 100644
index 00000000..a79e0689
--- /dev/null
+++ b/custom_import_map/public/components/map_page/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { MapPage } from './map_page';
diff --git a/custom_import_map/public/components/map_page/map_page.tsx b/custom_import_map/public/components/map_page/map_page.tsx
new file mode 100644
index 00000000..d8dc8d3d
--- /dev/null
+++ b/custom_import_map/public/components/map_page/map_page.tsx
@@ -0,0 +1,86 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useEffect, useRef, useState } from 'react';
+import { useParams } from 'react-router-dom';
+import { SimpleSavedObject } from 'opensearch-dashboards/public';
+import { Map as Maplibre } from 'maplibre-gl';
+import { MapContainer } from '../map_container';
+import { MapTopNavMenu } from '../map_top_nav';
+import { MapLayerSpecification } from '../../model/mapLayerType';
+import { MapServices } from '../../types';
+import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public';
+import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attributes';
+import {
+ DASHBOARDS_MAPS_LAYER_TYPE,
+ MAP_LAYER_DEFAULT_NAME,
+ OPENSEARCH_MAP_LAYER,
+} from '../../../common';
+import { getLayerConfigMap, getInitialMapState } from '../../utils/getIntialConfig';
+import { IndexPattern } from '../../../../../src/plugins/data/public';
+import { MapState } from '../../model/mapState';
+
+export const MapPage = () => {
+ const { services } = useOpenSearchDashboards();
+ const {
+ savedObjects: { client: savedObjectsClient },
+ } = services;
+ const [layers, setLayers] = useState([]);
+ const { id: mapIdFromUrl } = useParams<{ id: string }>();
+ const [savedMapObject, setSavedMapObject] =
+ useState | null>();
+ const [layersIndexPatterns, setLayersIndexPatterns] = useState([]);
+ const maplibreRef = useRef(null);
+ const [mapState, setMapState] = useState(getInitialMapState());
+
+ useEffect(() => {
+ if (mapIdFromUrl) {
+ savedObjectsClient.get('map', mapIdFromUrl).then((res) => {
+ setSavedMapObject(res);
+ const layerList: MapLayerSpecification[] = JSON.parse(res.attributes.layerList as string);
+ const savedMapState: MapState = JSON.parse(res.attributes.mapState as string);
+ setMapState(savedMapState);
+ setLayers(layerList);
+ const savedIndexPatterns: IndexPattern[] = [];
+ layerList.forEach(async (layer: MapLayerSpecification) => {
+ if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) {
+ const indexPatternId = layer.source.indexPatternId;
+ const indexPattern = await services.data.indexPatterns.get(indexPatternId);
+ savedIndexPatterns.push(indexPattern);
+ }
+ });
+ setLayersIndexPatterns(savedIndexPatterns);
+ });
+ } else {
+ const initialDefaultLayer: MapLayerSpecification = getLayerConfigMap()[
+ OPENSEARCH_MAP_LAYER.type
+ ];
+ initialDefaultLayer.name = MAP_LAYER_DEFAULT_NAME;
+ setLayers([initialDefaultLayer]);
+ }
+ }, []);
+
+ return (
+
+
+
+
+ );
+};
diff --git a/custom_import_map/public/components/map_top_nav/get_top_nav_config.tsx b/custom_import_map/public/components/map_top_nav/get_top_nav_config.tsx
new file mode 100644
index 00000000..4269c356
--- /dev/null
+++ b/custom_import_map/public/components/map_top_nav/get_top_nav_config.tsx
@@ -0,0 +1,133 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React from 'react';
+import { i18n } from '@osd/i18n';
+import { TopNavMenuData } from '../../../../../src/plugins/navigation/public';
+import {
+ OnSaveProps,
+ SavedObjectSaveModalOrigin,
+ showSaveModal,
+ checkForDuplicateTitle,
+} from '../../../../../src/plugins/saved_objects/public';
+import { MapServices } from '../../types';
+import { MapState } from '../../model/mapState';
+
+const SAVED_OBJECT_TYPE = 'map';
+
+interface GetTopNavConfigParams {
+ mapIdFromUrl: string;
+ layers: any;
+ title: string;
+ description: string;
+ setTitle: (title: string) => void;
+ setDescription: (description: string) => void;
+ mapState: MapState;
+}
+
+export const getTopNavConfig = (
+ {
+ notifications: { toasts },
+ i18n: { Context: I18nContext },
+ savedObjects: { client: savedObjectsClient },
+ history,
+ overlays,
+ }: MapServices,
+ {
+ mapIdFromUrl,
+ layers,
+ title,
+ description,
+ setTitle,
+ setDescription,
+ mapState,
+ }: GetTopNavConfigParams
+) => {
+ const topNavConfig: TopNavMenuData[] = [
+ {
+ iconType: 'save',
+ emphasize: true,
+ id: 'save',
+ label: i18n.translate('maps.topNav.saveMapButtonLabel', {
+ defaultMessage: `Save`,
+ }),
+ run: (_anchorElement) => {
+ const onModalSave = async ({ newTitle, newDescription, onTitleDuplicate }: OnSaveProps) => {
+ let newlySavedMap;
+ const saveAttributes = {
+ title: newTitle,
+ description: newDescription,
+ layerList: JSON.stringify(layers),
+ mapState: JSON.stringify(mapState),
+ };
+ try {
+ await checkForDuplicateTitle(
+ {
+ title: newTitle,
+ lastSavedTitle: title,
+ copyOnSave: false,
+ getDisplayName: () => SAVED_OBJECT_TYPE,
+ getOpenSearchType: () => SAVED_OBJECT_TYPE,
+ },
+ false,
+ onTitleDuplicate,
+ {
+ savedObjectsClient,
+ overlays,
+ }
+ );
+ } catch (_error) {
+ return {};
+ }
+ if (mapIdFromUrl) {
+ // edit existing map
+ newlySavedMap = await savedObjectsClient.update(
+ SAVED_OBJECT_TYPE,
+ mapIdFromUrl,
+ saveAttributes
+ );
+ } else {
+ // save new map
+ newlySavedMap = await savedObjectsClient.create(SAVED_OBJECT_TYPE, saveAttributes);
+ }
+ const id = newlySavedMap.id;
+ if (id) {
+ history.push({
+ ...history.location,
+ pathname: `${id}`,
+ });
+ setTitle(newTitle);
+ setDescription(newDescription);
+ toasts.addSuccess({
+ title: i18n.translate('map.topNavMenu.saveMap.successNotificationText', {
+ defaultMessage: `Saved ${newTitle}`,
+ values: {
+ visTitle: newTitle,
+ },
+ }),
+ });
+ }
+ return { id };
+ };
+
+ const documentInfo = {
+ title,
+ description,
+ };
+
+ const saveModal = (
+ {}}
+ />
+ );
+ showSaveModal(saveModal, I18nContext);
+ },
+ },
+ ];
+ return topNavConfig;
+};
diff --git a/custom_import_map/public/components/map_top_nav/index.ts b/custom_import_map/public/components/map_top_nav/index.ts
new file mode 100644
index 00000000..c732aa00
--- /dev/null
+++ b/custom_import_map/public/components/map_top_nav/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { MapTopNavMenu } from './top_nav_menu';
diff --git a/custom_import_map/public/components/map_top_nav/top_nav_menu.tsx b/custom_import_map/public/components/map_top_nav/top_nav_menu.tsx
new file mode 100644
index 00000000..15b2fbe3
--- /dev/null
+++ b/custom_import_map/public/components/map_top_nav/top_nav_menu.tsx
@@ -0,0 +1,139 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useCallback, useEffect, useState } from 'react';
+import { SimpleSavedObject } from 'opensearch-dashboards/public';
+import { IndexPattern, Query, TimeRange } from '../../../../../src/plugins/data/public';
+import { DASHBOARDS_MAPS_LAYER_TYPE, PLUGIN_NAVIGATION_BAR_ID } from '../../../common';
+import { getTopNavConfig } from './get_top_nav_config';
+import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public';
+import { MapServices } from '../../types';
+import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attributes';
+import { getSavedMapBreadcrumbs } from '../../utils/breadcrumbs';
+import { handleDataLayerRender } from '../../model/layerRenderController';
+import { MapLayerSpecification } from '../../model/mapLayerType';
+import { MapState } from '../../model/mapState';
+
+interface MapTopNavMenuProps {
+ mapIdFromUrl: string;
+ layers: MapLayerSpecification[];
+ savedMapObject: SimpleSavedObject | null | undefined;
+ layersIndexPatterns: IndexPattern[];
+ maplibreRef: any;
+ mapState: MapState;
+ setMapState: (mapState: MapState) => void;
+}
+
+export const MapTopNavMenu = ({
+ mapIdFromUrl,
+ savedMapObject,
+ layers,
+ layersIndexPatterns,
+ maplibreRef,
+ mapState,
+ setMapState,
+}: MapTopNavMenuProps) => {
+ const { services } = useOpenSearchDashboards();
+ const {
+ setHeaderActionMenu,
+ navigation: {
+ ui: { TopNavMenu },
+ },
+ chrome,
+ application: { navigateToApp },
+ } = services;
+
+ const [title, setTitle] = useState('');
+ const [description, setDescription] = useState('');
+ const [dateFrom, setDateFrom] = useState('');
+ const [dateTo, setDateTo] = useState('');
+ const [queryConfig, setQueryConfig] = useState({ query: '', language: 'kuery' });
+ const [refreshIntervalValue, setRefreshIntervalValue] = useState(60000);
+ const [isRefreshPaused, setIsRefreshPaused] = useState(false);
+ const changeTitle = useCallback(
+ (newTitle: string) => {
+ chrome.setBreadcrumbs(getSavedMapBreadcrumbs(newTitle, navigateToApp));
+ chrome.docTitle.change(newTitle);
+ },
+ [chrome, navigateToApp]
+ );
+
+ useEffect(() => {
+ if (savedMapObject) {
+ setTitle(savedMapObject.attributes.title);
+ setDescription(savedMapObject.attributes.description!);
+ }
+ }, [savedMapObject, mapIdFromUrl]);
+
+ useEffect(() => {
+ changeTitle(title || 'Create');
+ }, [title, changeTitle]);
+
+ const refreshDataLayerRender = () => {
+ layers.forEach((layer: MapLayerSpecification) => {
+ if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) {
+ return;
+ }
+ handleDataLayerRender(layer, mapState, services, maplibreRef, undefined);
+ });
+ };
+
+ const handleQuerySubmit = ({ query, dateRange }: { query?: Query; dateRange: TimeRange }) => {
+ if (query) {
+ setMapState({ ...mapState, query });
+ }
+ if (dateRange) {
+ setMapState({ ...mapState, timeRange: dateRange });
+ }
+ };
+
+ useEffect(() => {
+ setDateFrom(mapState.timeRange.from);
+ setDateTo(mapState.timeRange.to);
+ setQueryConfig(mapState.query);
+ setIsRefreshPaused(mapState.refreshInterval.pause);
+ setRefreshIntervalValue(mapState.refreshInterval.value);
+ refreshDataLayerRender();
+ }, [mapState]);
+
+ const onRefreshChange = useCallback(
+ ({ isPaused, refreshInterval }: { isPaused: boolean; refreshInterval: number }) => {
+ setIsRefreshPaused(isPaused);
+ setRefreshIntervalValue(refreshInterval);
+ },
+ []
+ );
+
+ return (
+
+ );
+};
diff --git a/custom_import_map/public/components/maps_list/index.ts b/custom_import_map/public/components/maps_list/index.ts
new file mode 100644
index 00000000..11ecaf3e
--- /dev/null
+++ b/custom_import_map/public/components/maps_list/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { MapsList } from './maps_list';
diff --git a/custom_import_map/public/components/maps_list/maps_list.tsx b/custom_import_map/public/components/maps_list/maps_list.tsx
new file mode 100644
index 00000000..3dbc5c9d
--- /dev/null
+++ b/custom_import_map/public/components/maps_list/maps_list.tsx
@@ -0,0 +1,151 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { i18n } from '@osd/i18n';
+import React, { useCallback, useEffect } from 'react';
+import { I18nProvider } from '@osd/i18n/react';
+import {
+ EuiPage,
+ EuiPageBody,
+ EuiPageContentBody,
+ EuiLink,
+ EuiButton,
+ EuiPageHeader,
+} from '@elastic/eui';
+import {
+ TableListView,
+ useOpenSearchDashboards,
+} from '../../../../../src/plugins/opensearch_dashboards_react/public';
+import { MapSavedObjectAttributes } from '../../../common/map_saved_object_attributes';
+import { MapServices } from '../../types';
+import { getMapsLandingBreadcrumbs } from '../../utils/breadcrumbs';
+import { APP_PATH, PLUGIN_NAVIGATION_BAR_ID } from '../../../common';
+
+export const MapsList = () => {
+ const {
+ services: {
+ notifications: { toasts },
+ savedObjects: { client: savedObjectsClient },
+ application: { navigateToApp },
+ chrome: { docTitle, setBreadcrumbs },
+ },
+ } = useOpenSearchDashboards();
+
+ useEffect(() => {
+ setBreadcrumbs(getMapsLandingBreadcrumbs(navigateToApp));
+ docTitle.change(i18n.translate('maps.listing.pageTitle', { defaultMessage: 'Maps' }));
+ }, [docTitle, navigateToApp, setBreadcrumbs]);
+
+ const navigateToSavedMapPage = (id: string) => {
+ navigateToApp(PLUGIN_NAVIGATION_BAR_ID, { path: `/${id}` });
+ };
+
+ const tableColumns = [
+ {
+ field: 'attributes.title',
+ name: i18n.translate('maps.listing.table.titleColumnName', {
+ defaultMessage: 'Title',
+ }),
+ sortable: true,
+ render: (title: string, record: any) => (
+ navigateToSavedMapPage(record.id)}>{title}
+ ),
+ },
+ {
+ field: 'attributes.description',
+ name: i18n.translate('maps.listing.table.descriptionColumnName', {
+ defaultMessage: 'Description',
+ }),
+ sortable: true,
+ },
+ {
+ field: 'updated_at',
+ name: i18n.translate('maps.listing.table.updatedTimeColumnName', {
+ defaultMessage: 'Last updated',
+ }),
+ sortable: true,
+ },
+ ];
+
+ const navigateToCreateMapPage = () => {
+ navigateToApp(PLUGIN_NAVIGATION_BAR_ID, { path: APP_PATH.CREATE_MAP });
+ };
+
+ const fetchMaps = useCallback(async (): Promise<{
+ total: number;
+ hits: object[];
+ }> => {
+ const res = await savedObjectsClient.find({
+ type: 'map',
+ fields: ['description', 'title'],
+ });
+ return {
+ total: res.total,
+ hits: res.savedObjects,
+ };
+ }, [savedObjectsClient]);
+
+ const deleteMaps = useCallback(
+ async (selectedItems: object[]) => {
+ await Promise.all(
+ selectedItems.map((item: any) => savedObjectsClient.delete(item.type, item.id))
+ ).catch((error) => {
+ toasts.addError(error, {
+ title: i18n.translate('map.mapListingDeleteErrorTitle', {
+ defaultMessage: 'Error deleting map',
+ }),
+ });
+ });
+ },
+ [savedObjectsClient, toasts]
+ );
+
+ const noMapItem = (
+
+ Create map
+ ,
+ ]}
+ />
+ );
+
+ // Render the map list DOM.
+ return (
+
+ <>
+
+
+
+
+
+
+
+ >
+
+ );
+};
diff --git a/custom_import_map/public/components/tooltip/create_tooltip.tsx b/custom_import_map/public/components/tooltip/create_tooltip.tsx
new file mode 100644
index 00000000..e77bd0ca
--- /dev/null
+++ b/custom_import_map/public/components/tooltip/create_tooltip.tsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { Popup, MapGeoJSONFeature } from 'maplibre-gl';
+
+import { MapLayerSpecification, DocumentLayerSpecification } from '../../model/mapLayerType';
+import { FeatureGroupItem, TooltipContainer } from './tooltipContainer';
+
+type Options = {
+ features: MapGeoJSONFeature[];
+ layers: DocumentLayerSpecification[];
+ showCloseButton?: boolean;
+ showPagination?: boolean;
+ showLayerSelection?: boolean;
+};
+
+export function isTooltipEnabledLayer(
+ layer: MapLayerSpecification
+): layer is DocumentLayerSpecification {
+ return layer.type !== 'opensearch_vector_tile_map' && layer.source.showTooltips === true;
+}
+
+export function groupFeaturesByLayers(
+ features: MapGeoJSONFeature[],
+ layers: DocumentLayerSpecification[]
+) {
+ const featureGroups: FeatureGroupItem[] = [];
+ if (layers.length > 0) {
+ layers.forEach((layer) => {
+ const layerFeatures = features.filter((f) => f.layer.source === layer.id);
+ if (layerFeatures.length > 0) {
+ featureGroups.push({ features: layerFeatures, layer });
+ }
+ });
+ }
+ return featureGroups;
+}
+
+export function getPopupLngLat(geometry: GeoJSON.Geometry) {
+ // geometry.coordinates is different for different geometry.type, here we use the geometry.coordinates
+ // of a Point as the position of the popup. For other types, such as Polygon, MultiPolygon, etc,
+ // use mouse position should be better
+ if (geometry.type === 'Point') {
+ return [geometry.coordinates[0], geometry.coordinates[1]] as [number, number];
+ } else {
+ return null;
+ }
+}
+
+export function createPopup({
+ features,
+ layers,
+ showCloseButton = true,
+ showPagination = true,
+ showLayerSelection = true,
+}: Options) {
+ const popup = new Popup({
+ closeButton: false,
+ closeOnClick: false,
+ maxWidth: 'max-content',
+ });
+
+ const featureGroup = groupFeaturesByLayers(features, layers);
+
+ // Don't show popup if no feature
+ if (featureGroup.length === 0) {
+ return null;
+ }
+
+ const div = document.createElement('div');
+ ReactDOM.render(
+ ,
+ div
+ );
+
+ return popup.setDOMContent(div);
+}
diff --git a/custom_import_map/public/components/tooltip/tooltipContainer.tsx b/custom_import_map/public/components/tooltip/tooltipContainer.tsx
new file mode 100644
index 00000000..236546ba
--- /dev/null
+++ b/custom_import_map/public/components/tooltip/tooltipContainer.tsx
@@ -0,0 +1,96 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import React, { useMemo, useState } from 'react';
+
+import { EuiFlexItem, EuiFlexGroup, EuiPanel, EuiText, EuiHorizontalRule } from '@elastic/eui';
+import { MapGeoJSONFeature } from 'maplibre-gl';
+import { TooltipHeaderContent } from './tooltipHeaderContent';
+import { ALL_LAYERS, PageData, TableData, TooltipTable } from './tooltipTable';
+import { DocumentLayerSpecification } from '../../model/mapLayerType';
+
+export type FeatureGroupItem = {
+ features: MapGeoJSONFeature[];
+ layer: DocumentLayerSpecification;
+};
+
+interface TooltipProps {
+ featureGroup: FeatureGroupItem[];
+ onClose: () => void;
+ showCloseButton?: boolean;
+ showPagination?: boolean;
+ showLayerSelection?: boolean;
+}
+
+function featureToTableRow(properties: Record) {
+ const row: PageData = [];
+ for (const [k, v] of Object.entries(properties)) {
+ row.push({
+ key: k,
+ value: `${v}`,
+ });
+ }
+ return row;
+}
+
+function toTable(featureGroupItem: FeatureGroupItem) {
+ const table: TableData = [];
+ for (const feature of featureGroupItem.features) {
+ if (feature?.properties) {
+ table.push(featureToTableRow(feature.properties));
+ }
+ }
+ return { table, layer: featureGroupItem.layer.name };
+}
+
+function createTableData(featureGroups: FeatureGroupItem[]) {
+ return featureGroups.map(toTable);
+}
+
+export function TooltipContainer({
+ featureGroup,
+ onClose,
+ showCloseButton = true,
+ showPagination = true,
+ showLayerSelection = true,
+}: TooltipProps) {
+ const [selectedLayerIndexes, setSelectedLayerIndexes] = useState([0]);
+ const tables = useMemo(() => createTableData(featureGroup), [featureGroup]);
+
+ const title = useMemo(() => {
+ if (selectedLayerIndexes.includes(ALL_LAYERS)) {
+ return 'All layers';
+ }
+ if (selectedLayerIndexes.length === 1) {
+ return tables[selectedLayerIndexes[0]].layer;
+ }
+ return '';
+ }, [selectedLayerIndexes, tables]);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/custom_import_map/public/components/tooltip/tooltipHeaderContent.tsx b/custom_import_map/public/components/tooltip/tooltipHeaderContent.tsx
new file mode 100644
index 00000000..7373b38f
--- /dev/null
+++ b/custom_import_map/public/components/tooltip/tooltipHeaderContent.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiTextColor } from '@elastic/eui';
+import { i18n } from '@osd/i18n';
+import React from 'react';
+
+interface Props {
+ title: string;
+ showCloseButton: boolean;
+ onClose: Function;
+}
+
+const TooltipHeaderContent = (props: Props) => {
+ return (
+
+
+
+
+ {props.title}
+
+
+
+ {props.showCloseButton && (
+
+ {
+ return props.onClose();
+ }}
+ iconType="cross"
+ aria-label={i18n.translate('maps.tooltip.closeLabel', {
+ defaultMessage: 'Close tooltip',
+ })}
+ data-test-subj="featureTooltipCloseButton"
+ />
+
+ )}
+
+ );
+};
+
+export { TooltipHeaderContent };
diff --git a/custom_import_map/public/components/tooltip/tooltipTable.tsx b/custom_import_map/public/components/tooltip/tooltipTable.tsx
new file mode 100644
index 00000000..4ca7a571
--- /dev/null
+++ b/custom_import_map/public/components/tooltip/tooltipTable.tsx
@@ -0,0 +1,200 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ EuiBasicTable,
+ EuiComboBox,
+ EuiComboBoxOptionOption,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiPagination,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import React, { useState, Fragment, useCallback, useEffect, useMemo } from 'react';
+
+export type RowData = {
+ key: string;
+ value: string;
+};
+export type PageData = RowData[];
+export type TableData = PageData[];
+type Table = { table: TableData; layer: string };
+
+export const ALL_LAYERS = -1;
+
+interface Props {
+ tables: Table[];
+ onLayerChange?: (layerIndexes: number[]) => void;
+ showPagination?: boolean;
+ showLayerSelection?: boolean;
+}
+
+function mergeTables(tables: Table[], selectedIndex: number[]) {
+ const merged: TableData = [];
+ const allSelected = selectedIndex.includes(ALL_LAYERS);
+
+ if (!allSelected) {
+ for (const index of selectedIndex) {
+ merged.push(...tables[index].table);
+ }
+ return merged;
+ }
+ for (let i = 0; i < tables.length; i++) {
+ const features: PageData[] = [];
+ tables[i].table.map((feature) => {
+ // Add layer name to every feature as first field
+ features.push(
+ [
+ {
+ key: 'Layer name',
+ value: tables[i].layer,
+ },
+ ].concat(feature.slice(0))
+ );
+ });
+ merged.push(...features);
+ }
+ return merged;
+}
+
+const TooltipTable = ({
+ tables,
+ onLayerChange,
+ showPagination = true,
+ showLayerSelection = true,
+}: Props) => {
+ const [selectedLayers, setSelectedLayers] = useState[]>([
+ {
+ label: tables[0]?.layer ?? '',
+ value: 0,
+ key: '0',
+ },
+ ]);
+ const [activePage, setActivePage] = useState(0);
+ const columns = [
+ {
+ field: 'key',
+ name: 'Field Name',
+ width: '25%',
+ truncateText: false,
+ },
+ {
+ field: 'value',
+ name: 'Field Value',
+ width: '75%',
+ truncateText: true,
+ },
+ ];
+
+ useEffect(() => {
+ // When selected layer changed, reset the active page to the first page
+ setActivePage(0);
+ }, [selectedLayers]);
+
+ const getRowProps = (item) => {
+ const { id } = item;
+ return {
+ 'data-test-subj': `row-${id}`,
+ className: 'customRowClass',
+ };
+ };
+
+ const handleLayerChange = useCallback(
+ (layerSelections: EuiComboBoxOptionOption[]) => {
+ if (tables.length === 0) {
+ return;
+ }
+
+ const selections = layerSelections;
+
+ setSelectedLayers(selections);
+ if (onLayerChange) {
+ onLayerChange(selections.map((s) => s.value ?? 0));
+ }
+ },
+ [tables]
+ );
+
+ const options = useMemo(() => {
+ const layerOptions = [];
+ if (tables.length > 1) {
+ layerOptions.push({ label: 'All layers', value: ALL_LAYERS, key: '-1' });
+ }
+ tables.forEach(({ layer }, i) => {
+ layerOptions.push({ label: layer, value: i, key: `${i}` });
+ });
+ return layerOptions;
+ }, [tables]);
+
+ const tableItems = useMemo(
+ () =>
+ mergeTables(
+ tables,
+ selectedLayers.map((l) => l.value ?? 0)
+ ),
+ [tables, selectedLayers]
+ );
+ const pageItems = tableItems[activePage];
+
+ const getCellProps = (item, column) => {
+ const { id } = item;
+ const { field } = column;
+ return {
+ 'data-test-subj': `cell-${id}-${field}`,
+ className: 'customCellClass',
+ textOnly: true,
+ };
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {showLayerSelection && options?.length > 1 && (
+
+
+ placeholder="Select a layer"
+ selectedOptions={selectedLayers}
+ singleSelection={{ asPlainText: true }}
+ options={options}
+ onChange={handleLayerChange}
+ isClearable={false}
+ />
+
+ )}
+
+ {showPagination ? (
+
+ ) : (
+
+ {1} of {tableItems.length}
+
+ )}
+
+
+
+ );
+};
+
+export { TooltipTable };
diff --git a/custom_import_map/public/index.scss b/custom_import_map/public/index.scss
new file mode 100644
index 00000000..a850c169
--- /dev/null
+++ b/custom_import_map/public/index.scss
@@ -0,0 +1,4 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
diff --git a/custom_import_map/public/index.ts b/custom_import_map/public/index.ts
index 6fe617d6..c0461f64 100644
--- a/custom_import_map/public/index.ts
+++ b/custom_import_map/public/index.ts
@@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import './index.scss';
+
import { CustomImportMapPlugin } from './plugin';
// This exports static code and TypeScript types,
diff --git a/custom_import_map/public/model/OSMLayerFunctions.ts b/custom_import_map/public/model/OSMLayerFunctions.ts
new file mode 100644
index 00000000..ca879121
--- /dev/null
+++ b/custom_import_map/public/model/OSMLayerFunctions.ts
@@ -0,0 +1,123 @@
+import { Map as Maplibre, LayerSpecification } from 'maplibre-gl';
+import { OSMLayerSpecification } from './mapLayerType';
+import { getMaplibreBeforeLayerId, layerExistInMbSource } from './layersFunctions';
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+// Fetch style layers from OpenSearch vector tile service
+const fetchStyleLayers = (url: string) => {
+ return fetch(url)
+ .then((res) => res.json())
+ .then((json) => json.layers)
+ .catch((error) => {
+ // eslint-disable-next-line no-console
+ console.log('error', error);
+ });
+};
+
+const getCurrentStyleLayers = (maplibreRef: MaplibreRef) => {
+ return maplibreRef.current?.getStyle().layers || [];
+};
+
+const handleStyleLayers = (layerConfig: OSMLayerSpecification, maplibreRef: MaplibreRef) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((mbLayer) => {
+ if (mbLayer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.setLayerZoomRange(
+ mbLayer.id,
+ layerConfig.zoomRange[0],
+ layerConfig.zoomRange[1]
+ );
+ // TODO: figure out error reason
+ if (mbLayer.type === 'symbol') {
+ return;
+ }
+ maplibreRef.current?.setPaintProperty(
+ mbLayer.id,
+ `${mbLayer.type}-opacity`,
+ layerConfig.opacity / 100
+ );
+ }
+ });
+};
+
+const updateLayerConfig = (layerConfig: OSMLayerSpecification, maplibreRef: MaplibreRef) => {
+ if (maplibreRef.current) {
+ handleStyleLayers(layerConfig, maplibreRef);
+ }
+};
+
+const addNewLayer = (
+ layerConfig: OSMLayerSpecification,
+ maplibreRef: MaplibreRef,
+ beforeLayerId: string | undefined
+) => {
+ if (maplibreRef.current) {
+ const layerSource = layerConfig?.source;
+ const layerStyle = layerConfig?.style;
+ maplibreRef.current.addSource(layerConfig.id, {
+ type: 'vector',
+ url: layerSource?.dataURL,
+ });
+ fetchStyleLayers(layerStyle?.styleURL).then((styleLayers: LayerSpecification[]) => {
+ const beforeMbLayerId = getMaplibreBeforeLayerId(layerConfig, maplibreRef, beforeLayerId);
+ styleLayers.forEach((styleLayer) => {
+ styleLayer.id = styleLayer.id + '_' + layerConfig.id;
+ if (styleLayer.type !== 'background') {
+ styleLayer.source = layerConfig.id;
+ }
+ maplibreRef.current?.addLayer(styleLayer, beforeMbLayerId);
+ maplibreRef.current?.setLayoutProperty(styleLayer.id, 'visibility', layerConfig.visibility);
+ maplibreRef.current?.setLayerZoomRange(
+ styleLayer.id,
+ layerConfig.zoomRange[0],
+ layerConfig.zoomRange[1]
+ );
+ // TODO: figure out error reason
+ if (styleLayer.type === 'symbol') {
+ return;
+ }
+ maplibreRef.current?.setPaintProperty(
+ styleLayer.id,
+ `${styleLayer.type}-opacity`,
+ layerConfig.opacity / 100
+ );
+ });
+ });
+ }
+};
+
+// Functions for OpenSearch maps vector tile layer
+export const OSMLayerFunctions = {
+ render: (
+ maplibreRef: MaplibreRef,
+ layerConfig: OSMLayerSpecification,
+ beforeLayerId: string | undefined
+ ) => {
+ // If layer already exist in maplibre source, update layer config
+ // else add new layer.
+ if (layerExistInMbSource(layerConfig.id, maplibreRef)) {
+ updateLayerConfig(layerConfig, maplibreRef);
+ } else {
+ addNewLayer(layerConfig, maplibreRef, beforeLayerId);
+ }
+ },
+ remove: (maplibreRef: MaplibreRef, layerConfig: OSMLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((mbLayer: { id: any }) => {
+ if (mbLayer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.removeLayer(mbLayer.id);
+ }
+ });
+ },
+ hide: (maplibreRef: MaplibreRef, layerConfig: OSMLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((mbLayer: { id: any }) => {
+ if (mbLayer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.setLayoutProperty(mbLayer.id, 'visibility', layerConfig.visibility);
+ }
+ });
+ },
+};
diff --git a/custom_import_map/public/model/customLayerFunctions.ts b/custom_import_map/public/model/customLayerFunctions.ts
new file mode 100644
index 00000000..66647317
--- /dev/null
+++ b/custom_import_map/public/model/customLayerFunctions.ts
@@ -0,0 +1,117 @@
+import { Map as Maplibre, AttributionControl, RasterSourceSpecification } from 'maplibre-gl';
+import { CustomLayerSpecification, OSMLayerSpecification } from './mapLayerType';
+import { getMaplibreBeforeLayerId, layerExistInMbSource } from './layersFunctions';
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+const getCurrentStyleLayers = (maplibreRef: MaplibreRef) => {
+ return maplibreRef.current?.getStyle().layers || [];
+};
+
+const updateLayerConfig = (layerConfig: CustomLayerSpecification, maplibreRef: MaplibreRef) => {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const customLauer = maplibreInstance.getLayer(layerConfig.id);
+ if (customLauer) {
+ maplibreInstance.setPaintProperty(
+ layerConfig.id,
+ 'raster-opacity',
+ layerConfig.opacity / 100
+ );
+ maplibreInstance.setLayerZoomRange(
+ layerConfig.id,
+ layerConfig.zoomRange[0],
+ layerConfig.zoomRange[1]
+ );
+ const rasterLayerSource = maplibreInstance.getSource(
+ layerConfig.id
+ )! as RasterSourceSpecification;
+ if (rasterLayerSource.attribution !== layerConfig.source?.attribution) {
+ rasterLayerSource.attribution = layerConfig?.source?.attribution;
+ maplibreInstance._controls.forEach((control) => {
+ if (control instanceof AttributionControl) {
+ control._updateAttributions();
+ }
+ });
+ }
+ const tilesURL = getCustomMapURL(layerConfig);
+ if (rasterLayerSource.tiles![0] !== tilesURL) {
+ rasterLayerSource.tiles = [layerConfig?.source?.url];
+ maplibreInstance.style.sourceCaches[layerConfig.id].clearTiles();
+ maplibreInstance.style.sourceCaches[layerConfig.id].update(maplibreInstance.transform);
+ maplibreInstance.triggerRepaint();
+ }
+ }
+ }
+};
+
+const addNewLayer = (
+ layerConfig: CustomLayerSpecification,
+ maplibreRef: MaplibreRef,
+ beforeLayerId: string | undefined
+) => {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const tilesURL = getCustomMapURL(layerConfig);
+ const layerSource = layerConfig?.source;
+ maplibreInstance.addSource(layerConfig.id, {
+ type: 'raster',
+ tiles: [tilesURL],
+ tileSize: 256,
+ attribution: layerSource?.attribution,
+ });
+ const beforeMbLayerId = getMaplibreBeforeLayerId(layerConfig, maplibreRef, beforeLayerId);
+ maplibreInstance.addLayer(
+ {
+ id: layerConfig.id,
+ type: 'raster',
+ source: layerConfig.id,
+ },
+ beforeMbLayerId
+ );
+ }
+};
+
+const getCustomMapURL = (layerConfig: CustomLayerSpecification) => {
+ const layerSource = layerConfig?.source;
+ if (layerSource?.customType === 'tms') {
+ return layerSource?.url;
+ } else if (layerSource?.customType === 'wms') {
+ const referenceSystemName = layerSource.version === '1.3.0' ? 'crs' : 'srs';
+ return `${layerSource?.url}?service=WMS&version=${layerSource.version}&request=GetMap&format=${layerSource.format}&transparent=true&layers=${layerSource?.layers}&styles=${layerSource.styles}&${referenceSystemName}=${layerSource.crs}&width=256&height=256&bbox={bbox-epsg-3857}`;
+ } else {
+ return '';
+ }
+};
+
+export const CustomLayerFunctions = {
+ render: (
+ maplibreRef: MaplibreRef,
+ layerConfig: CustomLayerSpecification,
+ beforeLayerId: string | undefined
+ ) => {
+ if (layerExistInMbSource(layerConfig.id, maplibreRef)) {
+ updateLayerConfig(layerConfig, maplibreRef);
+ } else {
+ addNewLayer(layerConfig, maplibreRef, beforeLayerId);
+ }
+ },
+ remove: (maplibreRef: MaplibreRef, layerConfig: OSMLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((mbLayer: { id: any }) => {
+ if (mbLayer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.removeLayer(mbLayer.id);
+ }
+ });
+ },
+ hide: (maplibreRef: MaplibreRef, layerConfig: OSMLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((mbLayer: { id: any }) => {
+ if (mbLayer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.setLayoutProperty(mbLayer.id, 'visibility', layerConfig.visibility);
+ }
+ });
+ },
+};
diff --git a/custom_import_map/public/model/documentLayerFunctions.ts b/custom_import_map/public/model/documentLayerFunctions.ts
new file mode 100644
index 00000000..c39dce4c
--- /dev/null
+++ b/custom_import_map/public/model/documentLayerFunctions.ts
@@ -0,0 +1,379 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Map as Maplibre, Popup, MapGeoJSONFeature } from 'maplibre-gl';
+import { createPopup, getPopupLngLat } from '../components/tooltip/create_tooltip';
+import { DocumentLayerSpecification } from './mapLayerType';
+import { convertGeoPointToGeoJSON, isGeoJSON } from '../utils/geo_formater';
+import { getMaplibreBeforeLayerId, layerExistInMbSource } from './layersFunctions';
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+// https://opensearch.org/docs/1.3/opensearch/supported-field-types/geo-shape
+const openSearchGeoJSONMap = new Map([
+ ['point', 'Point'],
+ ['linestring', 'LineString'],
+ ['polygon', 'Polygon'],
+ ['multipoint', 'MultiPoint'],
+ ['multilinestring', 'MultiLineString'],
+ ['multipolygon', 'MultiPolygon'],
+ ['geometrycollection', 'GeometryCollection'],
+]);
+
+const GeoJSONMaplibreMap = new Map([
+ ['Point', 'circle'],
+ ['LineString', 'line'],
+ ['Polygon', 'fill'],
+]);
+
+const getFieldValue = (data: any, name: string) => {
+ if (!name) {
+ return null;
+ }
+ const keys = name.split('.');
+ return keys.reduce((pre, cur) => {
+ return pre?.[cur];
+ }, data);
+};
+
+const getCurrentStyleLayers = (maplibreRef: MaplibreRef) => {
+ return maplibreRef.current?.getStyle().layers || [];
+};
+
+const getGeoFieldType = (layerConfig: DocumentLayerSpecification) => {
+ return layerConfig?.source?.geoFieldType;
+};
+
+const getGeoFieldName = (layerConfig: DocumentLayerSpecification) => {
+ return layerConfig?.source?.geoFieldName;
+};
+
+const buildGeometry = (fieldType: string, location: any) => {
+ if (isGeoJSON(location)) {
+ return {
+ type: openSearchGeoJSONMap.get(location.type?.toLowerCase()),
+ coordinates: location.coordinates,
+ };
+ }
+ if (fieldType === 'geo_point') {
+ // convert other supported formats to GeoJSON
+ return convertGeoPointToGeoJSON(location);
+ }
+ // We don't support non-geo-json format for geo_shape yet
+ return undefined;
+};
+
+const buildProperties = (document: any, fields: string[]) => {
+ const property: { [name: string]: any } = {};
+ if (!fields) {
+ return property;
+ }
+ fields.forEach((field) => {
+ const fieldValue = getFieldValue(document._source, field);
+ if (fieldValue) {
+ property[field] = fieldValue;
+ }
+ });
+ return property;
+};
+
+const getLayerSource = (data: any, layerConfig: DocumentLayerSpecification) => {
+ const geoFieldName = getGeoFieldName(layerConfig);
+ const geoFieldType = getGeoFieldType(layerConfig);
+ const featureList: any = [];
+ data.forEach((item: any) => {
+ const geoFieldValue = getFieldValue(item._source, geoFieldName);
+ const geometry = buildGeometry(geoFieldType, geoFieldValue);
+ if (geometry) {
+ const feature = {
+ geometry,
+ properties: buildProperties(item, layerConfig.source.tooltipFields),
+ };
+ featureList.push(feature);
+ }
+ });
+ return {
+ type: 'FeatureCollection',
+ features: featureList,
+ };
+};
+
+const addNewLayer = (
+ layerConfig: DocumentLayerSpecification,
+ maplibreRef: MaplibreRef,
+ data: any,
+ beforeLayerId: string | undefined
+) => {
+ const maplibreInstance = maplibreRef.current;
+ const mbLayerBeforeId = getMaplibreBeforeLayerId(layerConfig, maplibreRef, beforeLayerId);
+ const addGeoPointLayer = () => {
+ maplibreInstance?.addLayer(
+ {
+ id: layerConfig.id,
+ type: 'circle',
+ source: layerConfig.id,
+ paint: {
+ 'circle-radius': layerConfig.style?.markerSize,
+ 'circle-color': layerConfig.style?.fillColor,
+ 'circle-opacity': layerConfig.opacity / 100,
+ 'circle-stroke-width': layerConfig.style?.borderThickness,
+ 'circle-stroke-color': layerConfig.style?.borderColor,
+ },
+ },
+ mbLayerBeforeId
+ );
+ maplibreInstance?.setLayoutProperty(layerConfig.id, 'visibility', layerConfig.visibility);
+ };
+
+ const addGeoShapeLayer = (source: any) => {
+ source.features.map((feature: any, index: number) => {
+ const mbType = GeoJSONMaplibreMap.get(feature.geometry.type);
+ const mbLayerId = `${layerConfig.id}-${index}`;
+ if (mbType === 'circle') {
+ maplibreInstance?.addLayer(
+ {
+ id: mbLayerId,
+ type: 'circle',
+ source: layerConfig.id,
+ filter: ['==', '$type', 'Point'],
+ paint: {
+ 'circle-radius': layerConfig.style?.markerSize,
+ 'circle-color': layerConfig.style?.fillColor,
+ 'circle-opacity': layerConfig.opacity / 100,
+ 'circle-stroke-width': layerConfig.style?.borderThickness,
+ 'circle-stroke-color': layerConfig.style?.borderColor,
+ },
+ },
+ mbLayerBeforeId
+ );
+ maplibreInstance?.setLayoutProperty(mbLayerId, 'visibility', layerConfig.visibility);
+ } else if (mbType === 'line') {
+ maplibreInstance?.addLayer(
+ {
+ id: mbLayerId,
+ type: 'line',
+ source: layerConfig.id,
+ filter: ['==', '$type', 'LineString'],
+ paint: {
+ 'line-color': layerConfig.style?.fillColor,
+ 'line-opacity': layerConfig.opacity / 100,
+ 'line-width': layerConfig.style?.borderThickness,
+ },
+ },
+ mbLayerBeforeId
+ );
+ maplibreInstance?.setLayoutProperty(mbLayerId, 'visibility', layerConfig.visibility);
+ } else if (mbType === 'fill') {
+ const polygonBorderLayerId = `${mbLayerId}-border`;
+ maplibreInstance?.addLayer(
+ {
+ id: mbLayerId,
+ type: 'fill',
+ source: layerConfig.id,
+ filter: ['==', '$type', 'Polygon'],
+ paint: {
+ 'fill-color': layerConfig.style?.fillColor,
+ 'fill-opacity': layerConfig.opacity / 100,
+ 'fill-outline-color': layerConfig.style?.borderColor,
+ },
+ },
+ mbLayerBeforeId
+ );
+ maplibreInstance?.setLayoutProperty(mbLayerId, 'visibility', layerConfig.visibility);
+ // Add boarder for polygon
+ maplibreInstance?.addLayer(
+ {
+ id: polygonBorderLayerId,
+ type: 'line',
+ source: layerConfig.id,
+ filter: ['==', '$type', 'Polygon'],
+ paint: {
+ 'line-color': layerConfig.style?.borderColor,
+ 'line-opacity': layerConfig.opacity / 100,
+ 'line-width': layerConfig.style?.borderThickness,
+ },
+ },
+ mbLayerBeforeId
+ );
+ maplibreInstance?.setLayoutProperty(
+ polygonBorderLayerId,
+ 'visibility',
+ layerConfig.visibility
+ );
+ }
+ });
+ };
+ if (maplibreInstance) {
+ const source = getLayerSource(data, layerConfig);
+ maplibreInstance.addSource(layerConfig.id, {
+ type: 'geojson',
+ data: source,
+ });
+ const geoFieldType = getGeoFieldType(layerConfig);
+ if (geoFieldType === 'geo_point') {
+ addGeoPointLayer();
+ } else {
+ addGeoShapeLayer(source);
+ }
+ }
+};
+
+const updateLayerConfig = (
+ layerConfig: DocumentLayerSpecification,
+ maplibreRef: MaplibreRef,
+ data: any
+) => {
+ const maplibreInstance = maplibreRef.current;
+ if (maplibreInstance) {
+ const dataSource = maplibreInstance?.getSource(layerConfig.id);
+ if (dataSource) {
+ // @ts-ignore
+ dataSource.setData(getLayerSource(data, layerConfig));
+ }
+ const geoFieldType = getGeoFieldType(layerConfig);
+ if (geoFieldType === 'geo_point') {
+ maplibreInstance?.setLayerZoomRange(
+ layerConfig.id,
+ layerConfig.zoomRange[0],
+ layerConfig.zoomRange[1]
+ );
+ maplibreInstance?.setPaintProperty(
+ layerConfig.id,
+ 'circle-opacity',
+ layerConfig.opacity / 100
+ );
+ maplibreInstance?.setPaintProperty(
+ layerConfig.id,
+ 'circle-color',
+ layerConfig.style?.fillColor
+ );
+ maplibreInstance?.setPaintProperty(
+ layerConfig.id,
+ 'circle-stroke-color',
+ layerConfig.style?.borderColor
+ );
+ maplibreInstance?.setPaintProperty(
+ layerConfig.id,
+ 'circle-stroke-width',
+ layerConfig.style?.borderThickness
+ );
+ maplibreInstance?.setPaintProperty(
+ layerConfig.id,
+ 'circle-radius',
+ layerConfig.style?.markerSize
+ );
+ } else {
+ getCurrentStyleLayers(maplibreRef).forEach((layer) => {
+ if (layer.id.includes(layerConfig.id)) {
+ maplibreInstance.setLayerZoomRange(
+ layer.id,
+ layerConfig.zoomRange[0],
+ layerConfig.zoomRange[1]
+ );
+ if (layer.type === 'circle') {
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'circle-opacity',
+ layerConfig.opacity / 100
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'circle-color',
+ layerConfig.style?.fillColor
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'circle-stroke-color',
+ layerConfig.style?.borderColor
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'circle-stroke-width',
+ layerConfig.style?.borderThickness
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'circle-radius',
+ layerConfig.style?.markerSize
+ );
+ } else if (layer.type === 'line') {
+ if (layer.id.includes('border')) {
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'line-color',
+ layerConfig.style?.borderColor
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'line-width',
+ layerConfig.style?.borderThickness
+ );
+ } else {
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'line-opacity',
+ layerConfig.opacity / 100
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'line-color',
+ layerConfig.style?.fillColor
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'line-width',
+ layerConfig.style?.borderThickness
+ );
+ }
+ } else if (layer.type === 'fill') {
+ maplibreInstance?.setPaintProperty(layer.id, 'fill-opacity', layerConfig.opacity / 100);
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'fill-color',
+ layerConfig.style?.fillColor
+ );
+ maplibreInstance?.setPaintProperty(
+ layer.id,
+ 'fill-outline-color',
+ layerConfig.style?.borderColor
+ );
+ }
+ }
+ });
+ }
+ }
+};
+
+export const DocumentLayerFunctions = {
+ render: (
+ maplibreRef: MaplibreRef,
+ layerConfig: DocumentLayerSpecification,
+ data: any,
+ beforeLayerId: string | undefined
+ ) => {
+ if (layerExistInMbSource(layerConfig.id, maplibreRef)) {
+ updateLayerConfig(layerConfig, maplibreRef, data);
+ } else {
+ addNewLayer(layerConfig, maplibreRef, data, beforeLayerId);
+ }
+ },
+ remove: (maplibreRef: MaplibreRef, layerConfig: DocumentLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((layer: { id: any }) => {
+ if (layer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.removeLayer(layer.id);
+ }
+ });
+ },
+ hide: (maplibreRef: MaplibreRef, layerConfig: DocumentLayerSpecification) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ layers.forEach((layer) => {
+ if (layer.id.includes(layerConfig.id)) {
+ maplibreRef.current?.setLayoutProperty(layer.id, 'visibility', layerConfig.visibility);
+ }
+ });
+ },
+};
diff --git a/custom_import_map/public/model/layerRenderController.ts b/custom_import_map/public/model/layerRenderController.ts
new file mode 100644
index 00000000..5e710d17
--- /dev/null
+++ b/custom_import_map/public/model/layerRenderController.ts
@@ -0,0 +1,102 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Map as Maplibre } from 'maplibre-gl';
+import { MapLayerSpecification } from './mapLayerType';
+import { DASHBOARDS_MAPS_LAYER_TYPE } from '../../common';
+import {
+ buildOpenSearchQuery,
+ getTime,
+ IOpenSearchDashboardsSearchResponse,
+ isCompleteResponse,
+} from '../../../../src/plugins/data/common';
+import { layersFunctionMap } from './layersFunctions';
+import { MapServices } from '../types';
+import { MapState } from './mapState';
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+export const prepareDataLayerSource = (
+ layer: MapLayerSpecification,
+ mapState: MapState,
+ { data, notifications }: MapServices
+): Promise => {
+ return new Promise(async (resolve, reject) => {
+ if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) {
+ const sourceConfig = layer.source;
+ const indexPattern = await data.indexPatterns.get(sourceConfig.indexPatternId);
+ const indexPatternRefName = sourceConfig?.indexPatternRefName;
+ const geoField = sourceConfig.geoFieldName;
+ const sourceFields: string[] = [geoField];
+ if (sourceConfig.showTooltips && sourceConfig.tooltipFields.length > 0) {
+ sourceFields.push(...sourceConfig.tooltipFields);
+ }
+ let buildQuery;
+ if (indexPattern) {
+ const timeFilters = getTime(indexPattern, mapState.timeRange);
+ buildQuery = buildOpenSearchQuery(
+ indexPattern,
+ [],
+ [
+ ...(layer.source.filters ? layer.source.filters : []),
+ ...(timeFilters ? [timeFilters] : []),
+ ]
+ );
+ }
+ const request = {
+ params: {
+ index: indexPatternRefName,
+ size: layer.source.documentRequestNumber,
+ body: {
+ _source: sourceFields,
+ query: buildQuery,
+ },
+ },
+ };
+
+ const search$ = data.search.search(request).subscribe({
+ next: (response: IOpenSearchDashboardsSearchResponse) => {
+ if (isCompleteResponse(response)) {
+ const dataSource: object = response.rawResponse.hits.hits;
+ search$.unsubscribe();
+ resolve({ dataSource, layer });
+ } else {
+ notifications.toasts.addWarning('An error has occurred when query dataSource');
+ search$.unsubscribe();
+ reject();
+ }
+ },
+ error: (e: Error) => {
+ data.search.showError(e);
+ },
+ });
+ }
+ });
+};
+
+export const handleDataLayerRender = (
+ mapLayer: MapLayerSpecification,
+ mapState: MapState,
+ services: MapServices,
+ maplibreRef: MaplibreRef,
+ beforeLayerId: string | undefined
+) => {
+ return prepareDataLayerSource(mapLayer, mapState, services).then((result) => {
+ const { layer, dataSource } = result;
+ if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) {
+ layersFunctionMap[layer.type].render(maplibreRef, layer, dataSource, beforeLayerId);
+ }
+ });
+};
+
+export const handleReferenceLayerRender = (
+ layer: MapLayerSpecification,
+ maplibreRef: MaplibreRef,
+ beforeLayerId: string | undefined
+) => {
+ layersFunctionMap[layer.type].render(maplibreRef, layer, beforeLayerId);
+};
diff --git a/custom_import_map/public/model/layersFunctions.ts b/custom_import_map/public/model/layersFunctions.ts
new file mode 100644
index 00000000..68634816
--- /dev/null
+++ b/custom_import_map/public/model/layersFunctions.ts
@@ -0,0 +1,107 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Map as Maplibre } from 'maplibre-gl';
+import {
+ DASHBOARDS_MAPS_LAYER_ICON,
+ DASHBOARDS_MAPS_LAYER_NAME,
+ DASHBOARDS_MAPS_LAYER_TYPE,
+} from '../../common';
+import { OSMLayerFunctions } from './OSMLayerFunctions';
+import { DocumentLayerFunctions } from './documentLayerFunctions';
+import { MapLayerSpecification } from './mapLayerType';
+import { CustomLayerFunctions } from './customLayerFunctions';
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+interface MaplibreRef {
+ current: Maplibre | null;
+}
+
+const getAllMaplibreLayersIncludesId = (maplibreRef: MaplibreRef, layerId?: string) => {
+ if (!layerId && !maplibreRef) {
+ return [];
+ }
+ return (
+ maplibreRef.current
+ ?.getStyle()
+ .layers.filter((layer) => layer.id?.includes(String(layerId)) === true) || []
+ );
+};
+
+export const LayerActions = {
+ move: (maplibreRef: MaplibreRef, sourceId: string, beforeId?: string) => {
+ const sourceMaplibreLayers = getAllMaplibreLayersIncludesId(maplibreRef, sourceId);
+ if (!sourceMaplibreLayers) {
+ return;
+ }
+ const beforeMaplibreLayers = getAllMaplibreLayersIncludesId(maplibreRef, beforeId);
+ if (!beforeMaplibreLayers || beforeMaplibreLayers.length < 1) {
+ // move to top
+ sourceMaplibreLayers.forEach((layer) => maplibreRef.current?.moveLayer(layer.id));
+ return;
+ }
+ const topOfBeforeLayer = beforeMaplibreLayers[0];
+ sourceMaplibreLayers.forEach((layer) =>
+ maplibreRef.current?.moveLayer(layer.id, topOfBeforeLayer.id)
+ );
+ return;
+ },
+};
+
+export const layersFunctionMap: { [key: string]: any } = {
+ [DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: OSMLayerFunctions,
+ [DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: DocumentLayerFunctions,
+ [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: CustomLayerFunctions,
+};
+
+export const layersTypeNameMap: { [key: string]: string } = {
+ [DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: DASHBOARDS_MAPS_LAYER_NAME.OPENSEARCH_MAP,
+ [DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: DASHBOARDS_MAPS_LAYER_NAME.DOCUMENTS,
+ [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: DASHBOARDS_MAPS_LAYER_NAME.CUSTOM_MAP,
+};
+
+const getCurrentStyleLayers = (maplibreRef: MaplibreRef) => {
+ return maplibreRef.current?.getStyle().layers || [];
+};
+
+export const getMaplibreBeforeLayerId = (
+ selectedLayer: MapLayerSpecification,
+ maplibreRef: MaplibreRef,
+ beforeLayerId: string | undefined
+): string | undefined => {
+ const currentLoadedMbLayers = getCurrentStyleLayers(maplibreRef);
+ if (beforeLayerId) {
+ const beforeMbLayer = currentLoadedMbLayers.find((mbLayer) =>
+ mbLayer.id.includes(beforeLayerId)
+ );
+ return beforeMbLayer?.id;
+ }
+ return undefined;
+};
+
+export const layerExistInMbSource = (layerConfigId: string, maplibreRef: MaplibreRef) => {
+ const layers = getCurrentStyleLayers(maplibreRef);
+ for (const layer in layers) {
+ if (layers[layer].id.includes(layerConfigId)) {
+ return true;
+ }
+ }
+ return false;
+};
+
+export const layersTypeIconMap: { [key: string]: string } = {
+ [DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: DASHBOARDS_MAPS_LAYER_ICON.OPENSEARCH_MAP,
+ [DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: DASHBOARDS_MAPS_LAYER_ICON.DOCUMENTS,
+ [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: DASHBOARDS_MAPS_LAYER_ICON.CUSTOM_MAP,
+};
+
+export const referenceLayerTypeLookup = {
+ [DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP]: true,
+ [DASHBOARDS_MAPS_LAYER_TYPE.CUSTOM_MAP]: true,
+ [DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS]: false,
+};
diff --git a/custom_import_map/public/model/mapLayerType.ts b/custom_import_map/public/model/mapLayerType.ts
new file mode 100644
index 00000000..e792c2f4
--- /dev/null
+++ b/custom_import_map/public/model/mapLayerType.ts
@@ -0,0 +1,92 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Filter } from '../../../../src/plugins/data/public';
+
+/* eslint @typescript-eslint/consistent-type-definitions: ["error", "type"] */
+export type MapLayerSpecification =
+ | OSMLayerSpecification
+ | DocumentLayerSpecification
+ | CustomLayerSpecification;
+
+export type OSMLayerSpecification = {
+ name: string;
+ id: string;
+ type: 'opensearch_vector_tile_map';
+ description: string;
+ zoomRange: number[];
+ opacity: number;
+ visibility: string;
+ source: {
+ dataURL: string;
+ };
+ style: {
+ styleURL: string;
+ };
+};
+
+export type DocumentLayerSpecification = {
+ name: string;
+ id: string;
+ type: 'documents';
+ description: string;
+ zoomRange: number[];
+ opacity: number;
+ visibility: string;
+ source: {
+ indexPatternRefName: string;
+ indexPatternId: string;
+ geoFieldType: 'geo_point' | 'geo_shape';
+ geoFieldName: string;
+ documentRequestNumber: number;
+ showTooltips: boolean;
+ tooltipFields: string[];
+ filters: Filter[];
+ };
+ style: {
+ fillColor: string;
+ borderColor: string;
+ borderThickness: number;
+ markerSize: number;
+ };
+};
+
+export type CustomLayerSpecification = CustomTMSLayerSpecification | CustomWMSLayerSpecification;
+
+export type CustomTMSLayerSpecification = {
+ name: string;
+ id: string;
+ type: 'custom_map';
+ description: string;
+ zoomRange: number[];
+ opacity: number;
+ visibility: string;
+ source: {
+ url: string;
+ customType: 'tms';
+ attribution: string;
+ };
+};
+
+export type CustomWMSLayerSpecification = {
+ name: string;
+ id: string;
+ type: 'custom_map';
+ description: string;
+ zoomRange: number[];
+ opacity: number;
+ visibility: string;
+ source: {
+ url: string;
+ customType: 'wms';
+ attribution: string;
+ layers: string;
+ styles: string;
+ version: string;
+ format: string;
+ crs: string;
+ bbox: string;
+ };
+};
diff --git a/custom_import_map/public/model/mapState.ts b/custom_import_map/public/model/mapState.ts
new file mode 100644
index 00000000..4d4ceddc
--- /dev/null
+++ b/custom_import_map/public/model/mapState.ts
@@ -0,0 +1,10 @@
+import { Query, TimeRange } from '../../../../src/plugins/data/common';
+
+export interface MapState {
+ timeRange: TimeRange;
+ query: Query;
+ refreshInterval: {
+ pause: boolean;
+ value: number;
+ };
+}
diff --git a/custom_import_map/public/plugin.tsx b/custom_import_map/public/plugin.tsx
index da0b291d..3fd3d89b 100644
--- a/custom_import_map/public/plugin.tsx
+++ b/custom_import_map/public/plugin.tsx
@@ -3,14 +3,22 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React from 'react';
import { i18n } from '@osd/i18n';
-import { CoreSetup, CoreStart, Plugin } from '../../../src/core/public';
+import React from 'react';
+import {
+ AppMountParameters,
+ CoreSetup,
+ CoreStart,
+ DEFAULT_APP_CATEGORIES,
+ Plugin,
+} from '../../../src/core/public';
import {
- CustomImportMapPluginSetup,
- CustomImportMapPluginStart,
- AppPluginSetupDependencies,
+ AppPluginStartDependencies,
+ MapServices, CustomImportMapPluginSetup, CustomImportMapPluginStart,
} from './types';
+import {PLUGIN_NAME, PLUGIN_NAVIGATION_BAR_ID, PLUGIN_NAVIGATION_BAR_TILE} from '../common/constants/shared';
+
+import { AppPluginSetupDependencies } from './types';
import { RegionMapVisualizationDependencies } from '../../../src/plugins/region_map/public';
import { VectorUploadOptions } from './components/vector_upload_options';
@@ -20,6 +28,39 @@ export class CustomImportMapPlugin
core: CoreSetup,
{ regionMap }: AppPluginSetupDependencies
): CustomImportMapPluginSetup {
+ // Register an application into the side navigation menu
+ core.application.register({
+ id: PLUGIN_NAVIGATION_BAR_ID,
+ title: PLUGIN_NAVIGATION_BAR_TILE,
+ category: DEFAULT_APP_CATEGORIES.opensearchDashboards,
+ async mount(params: AppMountParameters) {
+ // Load application bundle
+ const { renderApp } = await import('./application');
+ // Get start services as specified in opensearch_dashboards.json
+ const [coreStart, depsStart] = await core.getStartServices();
+ const { navigation, data } = depsStart as AppPluginStartDependencies;
+
+ // make sure the index pattern list is up to date
+ data.indexPatterns.clearCache();
+ // make sure a default index pattern exists
+ // if not, the page will be redirected to management and maps won't be rendered
+ await data.indexPatterns.ensureDefaultIndexPattern();
+
+ const services: MapServices = {
+ ...coreStart,
+ setHeaderActionMenu: params.setHeaderActionMenu,
+ appBasePath: params.history,
+ element: params.element,
+ navigation,
+ toastNotifications: coreStart.notifications.toasts,
+ history: params.history,
+ data,
+ };
+ // Render the application
+ return renderApp(params, services);
+ },
+ });
+
regionMap.addOptionTab({
name: 'controls',
title: i18n.translate('regionMap.mapVis.regionMapEditorConfig.controlTabs.controlsTitle', {
@@ -29,7 +70,16 @@ export class CustomImportMapPlugin
});
// Return methods that should be available to other plugins
- return {};
+ return {
+ getGreeting() {
+ return i18n.translate('mapsDashboards.greetingText', {
+ defaultMessage: 'Hello from {name}!',
+ values: {
+ name: PLUGIN_NAME,
+ },
+ });
+ },
+ };
}
public start(core: CoreStart): CustomImportMapPluginStart {
diff --git a/custom_import_map/public/types.ts b/custom_import_map/public/types.ts
index 791b872f..d34f7273 100644
--- a/custom_import_map/public/types.ts
+++ b/custom_import_map/public/types.ts
@@ -3,11 +3,37 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import {
+ AppMountParameters,
+ CoreStart,
+ SavedObjectsClient,
+ ToastsStart,
+} from 'opensearch-dashboards/public';
import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/public';
+import { DataPublicPluginStart } from '../../../src/plugins/data/public';
+
import { RegionMapPluginSetup } from '../../../src/plugins/region_map/public';
+export interface AppPluginStartDependencies {
+ navigation: NavigationPublicPluginStart;
+ savedObjects: SavedObjectsClient;
+ data: DataPublicPluginStart;
+}
+
+export interface MapServices extends CoreStart {
+ setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
+ appBasePath: AppMountParameters['history'];
+ element: AppMountParameters['element'];
+ navigation: NavigationPublicPluginStart;
+ toastNotifications: ToastsStart;
+ history: AppMountParameters['history'];
+ data: DataPublicPluginStart;
+}
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
-export interface CustomImportMapPluginSetup {}
+export interface CustomImportMapPluginSetup {
+ getGreeting: () => string;
+}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface CustomImportMapPluginStart {}
diff --git a/custom_import_map/public/utils/breadcrumbs.ts b/custom_import_map/public/utils/breadcrumbs.ts
new file mode 100644
index 00000000..daa69ea2
--- /dev/null
+++ b/custom_import_map/public/utils/breadcrumbs.ts
@@ -0,0 +1,38 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { i18n } from '@osd/i18n';
+import {PLUGIN_NAVIGATION_BAR_ID} from '../../common';
+
+export function getMapsLandingBreadcrumbs(navigateToApp: any) {
+ return [
+ {
+ text: i18n.translate('maps.listing.breadcrumb', {
+ defaultMessage: 'Maps',
+ }),
+ onClick: () => navigateToApp(PLUGIN_NAVIGATION_BAR_ID),
+ },
+ ];
+}
+
+export function getCreateBreadcrumbs(navigateToApp: any) {
+ return [
+ ...getMapsLandingBreadcrumbs(navigateToApp),
+ {
+ text: i18n.translate('maps.create.breadcrumb', {
+ defaultMessage: 'Create',
+ }),
+ },
+ ];
+}
+
+export function getSavedMapBreadcrumbs(text: string, navigateToApp: any) {
+ return [
+ ...getMapsLandingBreadcrumbs(navigateToApp),
+ {
+ text,
+ },
+ ];
+}
diff --git a/custom_import_map/public/utils/geo_formater.ts b/custom_import_map/public/utils/geo_formater.ts
new file mode 100644
index 00000000..2af1933c
--- /dev/null
+++ b/custom_import_map/public/utils/geo_formater.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+const geoJSONTypes: string[] = [
+ 'point',
+ 'linestring',
+ 'polygon',
+ 'multipoint',
+ 'multilinestring',
+ 'multipolygon',
+ 'geometrycollection',
+];
+
+export function isGeoJSON(value: { type: any; coordinates: any }) {
+ if (!value) return false;
+ if (!value.type || !value.coordinates) {
+ return false;
+ }
+ const geoJSONType = value.type;
+ if (geoJSONTypes.includes(geoJSONType.toLowerCase())) {
+ return true;
+ }
+ return false;
+}
+
+function buildGeoJSONOfTypePoint(lon: number, lat: number) {
+ return {
+ type: 'Point',
+ coordinates: [lon, lat],
+ };
+}
+
+export function convertGeoPointToGeoJSON(location: any) {
+ // An object with 'lat' and 'lon' properties
+ if (location?.lat && location?.lon) {
+ return buildGeoJSONOfTypePoint(location?.lon, location?.lat);
+ }
+ // Geopoint as an array && support either (lon/lat) or (lon/lat/z)
+ if (Array.isArray(location) && (location.length === 2 || location.length === 3)) {
+ return buildGeoJSONOfTypePoint(location[0], location[1]);
+ }
+
+ if (typeof location !== 'string') {
+ return undefined;
+ }
+ // Geopoint as a string && support either (lat,lon) or (lat, lon, z)
+ const values = location.trim().split(',');
+ if (values && (values.length === 2 || values.length === 3)) {
+ return buildGeoJSONOfTypePoint(parseFloat(values[1].trim()), parseFloat(values[0].trim()));
+ }
+ // TODO Geopoint as geohash & WKT Format
+ return undefined;
+}
diff --git a/custom_import_map/public/utils/getIntialConfig.ts b/custom_import_map/public/utils/getIntialConfig.ts
new file mode 100644
index 00000000..6a05c892
--- /dev/null
+++ b/custom_import_map/public/utils/getIntialConfig.ts
@@ -0,0 +1,117 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { v4 as uuidv4 } from 'uuid';
+import {
+ DOCUMENTS,
+ DOCUMENTS_DEFAULT_MARKER_SIZE,
+ DOCUMENTS_DEFAULT_REQUEST_NUMBER,
+ DOCUMENTS_DEFAULT_SHOW_TOOLTIPS,
+ DOCUMENTS_DEFAULT_TOOLTIPS,
+ LAYER_VISIBILITY,
+ MAP_DATA_LAYER_DEFAULT_OPACITY,
+ MAP_DEFAULT_MAX_ZOOM,
+ MAP_DEFAULT_MIN_ZOOM,
+ MAP_LAYER_DEFAULT_BORDER_THICKNESS,
+ MAP_REFERENCE_LAYER_DEFAULT_OPACITY,
+ MAP_VECTOR_TILE_BASIC_STYLE,
+ MAP_VECTOR_TILE_DATA_SOURCE,
+ OPENSEARCH_MAP_LAYER,
+ CUSTOM_MAP,
+} from '../../common';
+import { MapState } from '../model/mapState';
+
+export const getLayerConfigMap = () => ({
+ [OPENSEARCH_MAP_LAYER.type]: {
+ name: '',
+ description: '',
+ type: OPENSEARCH_MAP_LAYER.type,
+ id: uuidv4(),
+ zoomRange: [MAP_DEFAULT_MIN_ZOOM, MAP_DEFAULT_MAX_ZOOM],
+ opacity: MAP_REFERENCE_LAYER_DEFAULT_OPACITY,
+ visibility: LAYER_VISIBILITY.VISIBLE,
+ source: {
+ dataURL: MAP_VECTOR_TILE_DATA_SOURCE,
+ },
+ style: {
+ styleURL: MAP_VECTOR_TILE_BASIC_STYLE,
+ },
+ },
+ [DOCUMENTS.type]: {
+ name: '',
+ description: '',
+ type: DOCUMENTS.type,
+ id: uuidv4(),
+ zoomRange: [MAP_DEFAULT_MIN_ZOOM, MAP_DEFAULT_MAX_ZOOM],
+ opacity: MAP_DATA_LAYER_DEFAULT_OPACITY,
+ visibility: LAYER_VISIBILITY.VISIBLE,
+ source: {
+ indexPatternRefName: undefined,
+ geoFieldType: undefined,
+ geoFieldName: undefined,
+ documentRequestNumber: DOCUMENTS_DEFAULT_REQUEST_NUMBER,
+ tooltipFields: DOCUMENTS_DEFAULT_TOOLTIPS,
+ showTooltips: DOCUMENTS_DEFAULT_SHOW_TOOLTIPS,
+ },
+ style: {
+ ...getStyleColor(),
+ borderThickness: MAP_LAYER_DEFAULT_BORDER_THICKNESS,
+ markerSize: DOCUMENTS_DEFAULT_MARKER_SIZE,
+ },
+ },
+ [CUSTOM_MAP.type]: {
+ name: '',
+ description: '',
+ type: CUSTOM_MAP.type,
+ id: uuidv4(),
+ zoomRange: [MAP_DEFAULT_MIN_ZOOM, MAP_DEFAULT_MAX_ZOOM],
+ opacity: MAP_REFERENCE_LAYER_DEFAULT_OPACITY,
+ visibility: LAYER_VISIBILITY.VISIBLE,
+ source: {
+ url: '',
+ customType: 'wms',
+ attribution: '',
+ layers: '',
+ styles: '',
+ version: '',
+ format: '',
+ crs: '',
+ bbox: '',
+ },
+ },
+});
+
+const getInitialColor = () => {
+ const colorCode = (Math.random() * 0xfffff * 1000000).toString(16);
+ return '#' + colorCode.slice(0, 6);
+};
+
+export const getStyleColor = () => {
+ const initialColor = getInitialColor();
+ return {
+ fillColor: initialColor,
+ borderColor: initialColor,
+ };
+};
+
+export const getInitialMapState = (): MapState => {
+ const timeRange = {
+ from: 'now-15m',
+ to: 'now',
+ };
+ const query = {
+ query: '',
+ language: 'kuery',
+ };
+ const refreshInterval = {
+ pause: true,
+ value: 12000,
+ };
+ return {
+ timeRange,
+ query,
+ refreshInterval,
+ };
+};
diff --git a/custom_import_map/server/plugin.ts b/custom_import_map/server/plugin.ts
index d92f3650..0be30228 100644
--- a/custom_import_map/server/plugin.ts
+++ b/custom_import_map/server/plugin.ts
@@ -16,6 +16,8 @@ import { CustomImportMapPluginSetup, CustomImportMapPluginStart } from './types'
import { createGeospatialCluster } from './clusters';
import { GeospatialService, OpensearchService } from './services';
import { geospatial, opensearch } from '../server/routes';
+import { mapSavedObjectsType } from './saved_objects';
+import { capabilitiesProvider } from './saved_objects/capabilities_provider';
export class CustomImportMapPlugin
implements Plugin {
@@ -29,6 +31,7 @@ export class CustomImportMapPlugin
public async setup(core: CoreSetup) {
this.logger.debug('customImportMap: Setup');
+ // @ts-ignore
const globalConfig = await this.globalConfig$.pipe(first()).toPromise();
const geospatialClient = createGeospatialCluster(core, globalConfig);
@@ -41,6 +44,12 @@ export class CustomImportMapPlugin
geospatial(geospatialService, router);
opensearch(opensearchService, router);
+ // Register saved object types
+ core.savedObjects.registerType(mapSavedObjectsType);
+
+ // Register capabilities
+ core.capabilities.registerProvider(capabilitiesProvider);
+
return {};
}
diff --git a/custom_import_map/server/saved_objects/capabilities_provider.ts b/custom_import_map/server/saved_objects/capabilities_provider.ts
new file mode 100644
index 00000000..dde86a4f
--- /dev/null
+++ b/custom_import_map/server/saved_objects/capabilities_provider.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const capabilitiesProvider = () => ({
+ map: {
+ // TODO: investigate which capabilities we need to provide
+ // createNew: true,
+ // createShortUrl: true,
+ // delete: true,
+ show: true,
+ // showWriteControls: true,
+ // save: true,
+ // saveQuery: true,
+ },
+});
diff --git a/custom_import_map/server/saved_objects/index.ts b/custom_import_map/server/saved_objects/index.ts
new file mode 100644
index 00000000..416ff0ab
--- /dev/null
+++ b/custom_import_map/server/saved_objects/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { mapSavedObjectsType } from './map_saved_object';
diff --git a/custom_import_map/server/saved_objects/map_saved_object.ts b/custom_import_map/server/saved_objects/map_saved_object.ts
new file mode 100644
index 00000000..ca38099a
--- /dev/null
+++ b/custom_import_map/server/saved_objects/map_saved_object.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright OpenSearch Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { SavedObjectsType } from 'opensearch-dashboards/server';
+
+export const mapSavedObjectsType: SavedObjectsType = {
+ name: 'map',
+ hidden: false,
+ namespaceType: 'agnostic',
+ management: {
+ defaultSearchField: 'title',
+ importableAndExportable: true,
+ getTitle(obj) {
+ return obj.attributes.title;
+ },
+ getInAppUrl(obj) {
+ return {
+ path: `/app/maps-dashboards#/${encodeURIComponent(obj.id)}`,
+ uiCapabilitiesPath: 'map.show',
+ };
+ },
+ getEditUrl(obj) {
+ return `/management/opensearch-dashboards/objects/map/${encodeURIComponent(obj.id)}`;
+ },
+ },
+ mappings: {
+ properties: {
+ title: { type: 'text' },
+ description: { type: 'text' },
+ layerList: { type: 'text', index: false },
+ uiState: { type: 'text', index: false },
+ mapState: { type: 'text', index: false },
+ version: { type: 'integer' },
+ // Need to add a kibanaSavedObjectMeta attribute here to follow the current saved object flow
+ // When we save a saved object, the saved object plugin will extract the search source into two parts
+ // Some information will be put into kibanaSavedObjectMeta while others will be created as a reference object and pushed to the reference array
+ kibanaSavedObjectMeta: {
+ properties: { searchSourceJSON: { type: 'text', index: false } },
+ },
+ },
+ },
+ migrations: {},
+};
diff --git a/custom_import_map/server/services/index.js b/custom_import_map/server/services/index.ts
similarity index 100%
rename from custom_import_map/server/services/index.js
rename to custom_import_map/server/services/index.ts
diff --git a/custom_import_map/translations/ja-JP.json b/custom_import_map/translations/ja-JP.json
new file mode 100644
index 00000000..d00fb9fb
--- /dev/null
+++ b/custom_import_map/translations/ja-JP.json
@@ -0,0 +1,81 @@
+{
+ "formats": {
+ "number": {
+ "currency": {
+ "style": "currency"
+ },
+ "percent": {
+ "style": "percent"
+ }
+ },
+ "date": {
+ "short": {
+ "month": "numeric",
+ "day": "numeric",
+ "year": "2-digit"
+ },
+ "medium": {
+ "month": "short",
+ "day": "numeric",
+ "year": "numeric"
+ },
+ "long": {
+ "month": "long",
+ "day": "numeric",
+ "year": "numeric"
+ },
+ "full": {
+ "weekday": "long",
+ "month": "long",
+ "day": "numeric",
+ "year": "numeric"
+ }
+ },
+ "time": {
+ "short": {
+ "hour": "numeric",
+ "minute": "numeric"
+ },
+ "medium": {
+ "hour": "numeric",
+ "minute": "numeric",
+ "second": "numeric"
+ },
+ "long": {
+ "hour": "numeric",
+ "minute": "numeric",
+ "second": "numeric",
+ "timeZoneName": "short"
+ },
+ "full": {
+ "hour": "numeric",
+ "minute": "numeric",
+ "second": "numeric",
+ "timeZoneName": "short"
+ }
+ },
+ "relative": {
+ "years": {
+ "units": "year"
+ },
+ "months": {
+ "units": "month"
+ },
+ "days": {
+ "units": "day"
+ },
+ "hours": {
+ "units": "hour"
+ },
+ "minutes": {
+ "units": "minute"
+ },
+ "seconds": {
+ "units": "second"
+ }
+ }
+ },
+ "messages": {
+ "mapsDashboards.buttonText": "Translate me to Japanese"
+ }
+}
diff --git a/custom_import_map/yarn.lock b/custom_import_map/yarn.lock
new file mode 100644
index 00000000..a1ab629b
--- /dev/null
+++ b/custom_import_map/yarn.lock
@@ -0,0 +1,1369 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@colors/colors@1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
+ integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+
+"@cypress/request@^2.88.10":
+ version "2.88.10"
+ resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce"
+ integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.8.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.6"
+ extend "~3.0.2"
+ forever-agent "~0.6.1"
+ form-data "~2.3.2"
+ http-signature "~1.3.6"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.19"
+ performance-now "^2.1.0"
+ qs "~6.5.2"
+ safe-buffer "^5.1.2"
+ tough-cookie "~2.5.0"
+ tunnel-agent "^0.6.0"
+ uuid "^8.3.2"
+
+"@cypress/skip-test@^2.6.1":
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/@cypress/skip-test/-/skip-test-2.6.1.tgz#44a4bc4c2b2e369a7661177c9b38e50d417a36ea"
+ integrity sha512-X+ibefBiuOmC5gKG91wRIT0/OqXeETYvu7zXktjZ3yLeO186Y8ia0K7/gQUpAwuUi28DuqMd1+7tBQVtPkzbPA==
+
+"@cypress/xvfb@^1.2.4":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a"
+ integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==
+ dependencies:
+ debug "^3.1.0"
+ lodash.once "^4.1.1"
+
+"@mapbox/geojson-rewind@^0.5.2":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz#591a5d71a9cd1da1a0bf3420b3bea31b0fc7946a"
+ integrity sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==
+ dependencies:
+ get-stream "^6.0.1"
+ minimist "^1.2.6"
+
+"@mapbox/jsonlint-lines-primitives@^2.0.2":
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234"
+ integrity sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==
+
+"@mapbox/mapbox-gl-supported@^2.0.1":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz#c15367178d8bfe4765e6b47b542fe821ce259c7b"
+ integrity sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==
+
+"@mapbox/point-geometry@0.1.0", "@mapbox/point-geometry@^0.1.0", "@mapbox/point-geometry@~0.1.0":
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz#8a83f9335c7860effa2eeeca254332aa0aeed8f2"
+ integrity sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==
+
+"@mapbox/tiny-sdf@^2.0.5":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-2.0.5.tgz#cdba698d3d65087643130f9af43a2b622ce0b372"
+ integrity sha512-OhXt2lS//WpLdkqrzo/KwB7SRD8AiNTFFzuo9n14IBupzIMa67yGItcK7I2W9D8Ghpa4T04Sw9FWsKCJG50Bxw==
+
+"@mapbox/unitbezier@^0.0.1":
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz#d32deb66c7177e9e9dfc3bbd697083e2e657ff01"
+ integrity sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==
+
+"@mapbox/vector-tile@^1.3.1":
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz#d3a74c90402d06e89ec66de49ec817ff53409666"
+ integrity sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==
+ dependencies:
+ "@mapbox/point-geometry" "~0.1.0"
+
+"@mapbox/whoots-js@^3.1.0":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe"
+ integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==
+
+"@opensearch-dashboards-test/opensearch-dashboards-test-library@git+https://github.com/opensearch-project/opensearch-dashboards-test-library.git#main":
+ version "1.0.4"
+ resolved "git+https://github.com/opensearch-project/opensearch-dashboards-test-library.git#9c0adaa6d492accd9f737aa905b2a2ca3be6840f"
+
+"@types/geojson@*", "@types/geojson@^7946.0.10":
+ version "7946.0.10"
+ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.10.tgz#6dfbf5ea17142f7f9a043809f1cd4c448cb68249"
+ integrity sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==
+
+"@types/mapbox__point-geometry@*", "@types/mapbox__point-geometry@^0.1.2":
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz#488a9b76e8457d6792ea2504cdd4ecdd9860a27e"
+ integrity sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA==
+
+"@types/mapbox__vector-tile@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz#8fa1379dbaead1e1b639b8d96cfd174404c379d6"
+ integrity sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==
+ dependencies:
+ "@types/geojson" "*"
+ "@types/mapbox__point-geometry" "*"
+ "@types/pbf" "*"
+
+"@types/node@*":
+ version "18.11.18"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.18.tgz#8dfb97f0da23c2293e554c5a50d61ef134d7697f"
+ integrity sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==
+
+"@types/node@^14.14.31":
+ version "14.18.36"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.36.tgz#c414052cb9d43fab67d679d5f3c641be911f5835"
+ integrity sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==
+
+"@types/pbf@*", "@types/pbf@^3.0.2":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@types/pbf/-/pbf-3.0.2.tgz#8d291ad68b4b8c533e96c174a2e3e6399a59ed61"
+ integrity sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ==
+
+"@types/sinonjs__fake-timers@8.1.1":
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3"
+ integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==
+
+"@types/sizzle@^2.3.2":
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.3.tgz#ff5e2f1902969d305225a047c8a0fd5c915cebef"
+ integrity sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==
+
+"@types/yauzl@^2.9.1":
+ version "2.10.0"
+ resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599"
+ integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==
+ dependencies:
+ "@types/node" "*"
+
+aggregate-error@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
+ integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
+ dependencies:
+ clean-stack "^2.0.0"
+ indent-string "^4.0.0"
+
+ansi-colors@^4.1.1:
+ version "4.1.3"
+ resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
+ integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
+
+ansi-escapes@^4.3.0:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+ integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+ dependencies:
+ type-fest "^0.21.3"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+arch@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
+ integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
+
+asn1@~0.2.3:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
+ integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
+ dependencies:
+ safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+ integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
+
+astral-regex@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+ integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
+async@^3.2.0:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
+ integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+at-least-node@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
+ integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
+
+aws-sign2@~0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+ integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==
+
+aws4@^1.8.0:
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
+ integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+base64-js@^1.3.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+ integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==
+ dependencies:
+ tweetnacl "^0.14.3"
+
+blob-util@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb"
+ integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==
+
+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==
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+buffer-crc32@~0.2.3:
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+ integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
+
+buffer@^5.6.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+ integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.1.13"
+
+cachedir@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8"
+ integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+ integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
+
+chalk@^4.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+check-more-types@^2.24.0:
+ version "2.24.0"
+ resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
+ integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==
+
+ci-info@^3.2.0:
+ version "3.7.1"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f"
+ integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==
+
+clean-stack@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
+ integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
+
+cli-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+ integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+ dependencies:
+ restore-cursor "^3.1.0"
+
+cli-table3@~0.6.1:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2"
+ integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==
+ dependencies:
+ string-width "^4.2.0"
+ optionalDependencies:
+ "@colors/colors" "1.5.0"
+
+cli-truncate@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
+ integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
+ dependencies:
+ slice-ansi "^3.0.0"
+ string-width "^4.2.0"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+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==
+
+colorette@^2.0.16:
+ version "2.0.19"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+ integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
+
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commander@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
+ integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
+
+common-tags@^1.8.0:
+ version "1.8.2"
+ resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6"
+ integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+core-util-is@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+ integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==
+
+cross-spawn@^7.0.0:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+csscolorparser@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b"
+ integrity sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==
+
+cypress-file-upload@^5.0.8:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1"
+ integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==
+
+cypress-multi-reporters@^1.5.0:
+ version "1.6.2"
+ resolved "https://registry.yarnpkg.com/cypress-multi-reporters/-/cypress-multi-reporters-1.6.2.tgz#129dfeffa00d4deca3e9f58d84570b9962c28c2b"
+ integrity sha512-lvwGwHqZG5CwGxBJ6UJXWaxlWGkJgxBjP0h+IVLrrwRlJpT4coSwwt+UzMdeqEMrzT4IDfhbtmUNOiDleisOYA==
+ dependencies:
+ debug "^4.1.1"
+ lodash "^4.17.15"
+
+cypress@9.5.4:
+ version "9.5.4"
+ resolved "https://registry.yarnpkg.com/cypress/-/cypress-9.5.4.tgz#49d9272f62eba12f2314faf29c2a865610e87550"
+ integrity sha512-6AyJAD8phe7IMvOL4oBsI9puRNOWxZjl8z1lgixJMcgJ85JJmyKeP6uqNA0dI1z14lmJ7Qklf2MOgP/xdAqJ/Q==
+ dependencies:
+ "@cypress/request" "^2.88.10"
+ "@cypress/xvfb" "^1.2.4"
+ "@types/node" "^14.14.31"
+ "@types/sinonjs__fake-timers" "8.1.1"
+ "@types/sizzle" "^2.3.2"
+ arch "^2.2.0"
+ blob-util "^2.0.2"
+ bluebird "^3.7.2"
+ buffer "^5.6.0"
+ cachedir "^2.3.0"
+ chalk "^4.1.0"
+ check-more-types "^2.24.0"
+ cli-cursor "^3.1.0"
+ cli-table3 "~0.6.1"
+ commander "^5.1.0"
+ common-tags "^1.8.0"
+ dayjs "^1.10.4"
+ debug "^4.3.2"
+ enquirer "^2.3.6"
+ eventemitter2 "^6.4.3"
+ execa "4.1.0"
+ executable "^4.1.1"
+ extract-zip "2.0.1"
+ figures "^3.2.0"
+ fs-extra "^9.1.0"
+ getos "^3.2.1"
+ is-ci "^3.0.0"
+ is-installed-globally "~0.4.0"
+ lazy-ass "^1.6.0"
+ listr2 "^3.8.3"
+ lodash "^4.17.21"
+ log-symbols "^4.0.0"
+ minimist "^1.2.6"
+ ospath "^1.2.2"
+ pretty-bytes "^5.6.0"
+ proxy-from-env "1.0.0"
+ request-progress "^3.0.0"
+ semver "^7.3.2"
+ supports-color "^8.1.1"
+ tmp "~0.2.1"
+ untildify "^4.0.0"
+ yauzl "^2.10.0"
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==
+ dependencies:
+ assert-plus "^1.0.0"
+
+dayjs@^1.10.4:
+ version "1.11.7"
+ resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
+ integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
+
+debug@^3.1.0:
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+ integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
+ dependencies:
+ ms "^2.1.1"
+
+debug@^4.1.1, debug@^4.3.2:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+earcut@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.4.tgz#6d02fd4d68160c114825d06890a92ecaae60343a"
+ integrity sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==
+
+ecc-jsbn@~0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+ integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==
+ dependencies:
+ jsbn "~0.1.0"
+ safer-buffer "^2.1.0"
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+end-of-stream@^1.1.0:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+ integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+ dependencies:
+ once "^1.4.0"
+
+enquirer@^2.3.6:
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
+ integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
+ dependencies:
+ ansi-colors "^4.1.1"
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+eventemitter2@^6.4.3:
+ version "6.4.9"
+ resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.9.tgz#41f2750781b4230ed58827bc119d293471ecb125"
+ integrity sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==
+
+execa@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
+ integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
+ 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"
+
+executable@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
+ integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==
+ dependencies:
+ pify "^2.2.0"
+
+extend@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extract-zip@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
+ integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
+ dependencies:
+ debug "^4.1.1"
+ get-stream "^5.1.0"
+ yauzl "^2.10.0"
+ optionalDependencies:
+ "@types/yauzl" "^2.9.1"
+
+extsprintf@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+ integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==
+
+extsprintf@^1.2.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
+ integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
+
+fd-slicer@~1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
+ integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==
+ dependencies:
+ pend "~1.2.0"
+
+figures@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
+ integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
+ dependencies:
+ escape-string-regexp "^1.0.5"
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+ integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==
+
+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"
+
+fs-extra@^9.1.0:
+ version "9.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
+ integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
+ dependencies:
+ at-least-node "^1.0.0"
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+geojson-vt@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/geojson-vt/-/geojson-vt-3.2.1.tgz#f8adb614d2c1d3f6ee7c4265cad4bbf3ad60c8b7"
+ integrity sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==
+
+get-stream@^5.0.0, get-stream@^5.1.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+ integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
+ dependencies:
+ pump "^3.0.0"
+
+get-stream@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+ integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
+getos@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5"
+ integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==
+ dependencies:
+ async "^3.2.0"
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==
+ dependencies:
+ assert-plus "^1.0.0"
+
+gl-matrix@^3.4.3:
+ version "3.4.3"
+ resolved "https://registry.yarnpkg.com/gl-matrix/-/gl-matrix-3.4.3.tgz#fc1191e8320009fd4d20e9339595c6041ddc22c9"
+ integrity sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==
+
+glob@^7.1.3:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+global-dirs@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485"
+ integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==
+ dependencies:
+ ini "2.0.0"
+
+global-prefix@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
+ integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
+ dependencies:
+ ini "^1.3.5"
+ kind-of "^6.0.2"
+ which "^1.3.1"
+
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+ version "4.2.10"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+http-signature@~1.3.6:
+ version "1.3.6"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9"
+ integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==
+ dependencies:
+ assert-plus "^1.0.0"
+ jsprim "^2.0.2"
+ sshpk "^1.14.1"
+
+human-signals@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
+ integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
+
+ieee754@^1.1.12, ieee754@^1.1.13:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+indent-string@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
+ integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+ini@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
+ integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
+
+ini@^1.3.5:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
+ integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
+
+is-ci@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867"
+ integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==
+ dependencies:
+ ci-info "^3.2.0"
+
+is-fullwidth-code-point@^3.0.0:
+ version "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-installed-globally@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
+ integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==
+ dependencies:
+ global-dirs "^3.0.0"
+ is-path-inside "^3.0.2"
+
+is-path-inside@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+ integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
+is-stream@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+ integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+ integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
+
+is-unicode-supported@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+ integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+ integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+ integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
+
+json-schema@0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
+ integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
+
+json-stringify-safe@~5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
+
+jsonfile@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+ integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+ dependencies:
+ universalify "^2.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jsprim@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d"
+ integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.3.0"
+ json-schema "0.4.0"
+ verror "1.10.0"
+
+kdbush@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0"
+ integrity sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==
+
+kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+lazy-ass@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
+ integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==
+
+listr2@^3.8.3:
+ version "3.14.0"
+ resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e"
+ integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==
+ dependencies:
+ cli-truncate "^2.1.0"
+ colorette "^2.0.16"
+ log-update "^4.0.0"
+ p-map "^4.0.0"
+ rfdc "^1.3.0"
+ rxjs "^7.5.1"
+ through "^2.3.8"
+ wrap-ansi "^7.0.0"
+
+lodash.once@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
+ integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
+
+lodash@^4.17.15, lodash@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-symbols@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+ integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+ dependencies:
+ chalk "^4.1.0"
+ is-unicode-supported "^0.1.0"
+
+log-update@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+ integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
+ dependencies:
+ ansi-escapes "^4.3.0"
+ cli-cursor "^3.1.0"
+ slice-ansi "^4.0.0"
+ wrap-ansi "^6.2.0"
+
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
+maplibre-gl@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-2.4.0.tgz#2b53dbf526626bf4ee92ad4f33f13ef09e5af182"
+ integrity sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==
+ dependencies:
+ "@mapbox/geojson-rewind" "^0.5.2"
+ "@mapbox/jsonlint-lines-primitives" "^2.0.2"
+ "@mapbox/mapbox-gl-supported" "^2.0.1"
+ "@mapbox/point-geometry" "^0.1.0"
+ "@mapbox/tiny-sdf" "^2.0.5"
+ "@mapbox/unitbezier" "^0.0.1"
+ "@mapbox/vector-tile" "^1.3.1"
+ "@mapbox/whoots-js" "^3.1.0"
+ "@types/geojson" "^7946.0.10"
+ "@types/mapbox__point-geometry" "^0.1.2"
+ "@types/mapbox__vector-tile" "^1.3.0"
+ "@types/pbf" "^3.0.2"
+ csscolorparser "~1.0.3"
+ earcut "^2.2.4"
+ geojson-vt "^3.2.1"
+ gl-matrix "^3.4.3"
+ global-prefix "^3.0.0"
+ murmurhash-js "^1.0.0"
+ pbf "^3.2.1"
+ potpack "^1.0.2"
+ quickselect "^2.0.0"
+ supercluster "^7.1.5"
+ tinyqueue "^2.0.3"
+ vt-pbf "^3.1.3"
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+minimatch@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@^1.2.6:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
+ integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@^2.1.1:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+murmurhash-js@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51"
+ integrity sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==
+
+npm-run-path@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+ integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+ dependencies:
+ path-key "^3.0.0"
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+onetime@^5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
+ospath@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b"
+ integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==
+
+p-map@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
+ integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
+ dependencies:
+ aggregate-error "^3.0.0"
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.0.0, path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+pbf@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/pbf/-/pbf-3.2.1.tgz#b4c1b9e72af966cd82c6531691115cc0409ffe2a"
+ integrity sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==
+ dependencies:
+ ieee754 "^1.1.12"
+ resolve-protobuf-schema "^2.1.0"
+
+pend@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
+ integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
+
+performance-now@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+ integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
+
+pify@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
+ integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
+
+potpack@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14"
+ integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==
+
+prettier@^2.1.1:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.1.tgz#4e1fd11c34e2421bc1da9aea9bd8127cd0a35efc"
+ integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==
+
+pretty-bytes@^5.6.0:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
+ integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
+
+protocol-buffers-schema@^3.3.1:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03"
+ integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==
+
+proxy-from-env@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
+ integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==
+
+psl@^1.1.28:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
+ integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
+
+pump@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+ integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+punycode@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@~6.5.2:
+ version "6.5.3"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
+ integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
+
+quickselect@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018"
+ integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==
+
+request-progress@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe"
+ integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==
+ dependencies:
+ throttleit "^1.0.0"
+
+resolve-protobuf-schema@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz#9ca9a9e69cf192bbdaf1006ec1973948aa4a3758"
+ integrity sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==
+ dependencies:
+ protocol-buffers-schema "^3.3.1"
+
+restore-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+ integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+ dependencies:
+ onetime "^5.1.0"
+ signal-exit "^3.0.2"
+
+rfdc@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
+ integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
+
+rimraf@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+rxjs@^7.5.1:
+ version "7.8.0"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4"
+ integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==
+ dependencies:
+ tslib "^2.1.0"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.2:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+semver@^7.3.2:
+ version "7.3.8"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
+ integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
+ dependencies:
+ lru-cache "^6.0.0"
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+signal-exit@^3.0.2:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+slice-ansi@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
+ integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
+slice-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+ integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
+sshpk@^1.14.1:
+ version "1.17.0"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5"
+ integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ bcrypt-pbkdf "^1.0.0"
+ dashdash "^1.12.0"
+ ecc-jsbn "~0.1.1"
+ getpass "^0.1.1"
+ jsbn "~0.1.0"
+ safer-buffer "^2.0.2"
+ tweetnacl "~0.14.0"
+
+string-width@^4.1.0, string-width@^4.2.0:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-final-newline@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+ integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+
+supercluster@^7.1.5:
+ version "7.1.5"
+ resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.5.tgz#65a6ce4a037a972767740614c19051b64b8be5a3"
+ integrity sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==
+ dependencies:
+ kdbush "^3.0.0"
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-color@^8.1.1:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+throttleit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"
+ integrity sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==
+
+through@^2.3.8:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+ integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+
+tinyqueue@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08"
+ integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==
+
+tmp@~0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
+ integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
+ dependencies:
+ rimraf "^3.0.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==
+ dependencies:
+ psl "^1.1.28"
+ punycode "^2.1.1"
+
+tslib@^2.1.0:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
+ integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+ integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==
+
+type-fest@^0.21.3:
+ version "0.21.3"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+ integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
+universalify@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
+ integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
+
+untildify@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
+ integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
+
+uuid@3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
+ integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
+
+uuid@^8.3.2:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
+verror@1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+ integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==
+ dependencies:
+ assert-plus "^1.0.0"
+ core-util-is "1.0.2"
+ extsprintf "^1.2.0"
+
+vt-pbf@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/vt-pbf/-/vt-pbf-3.1.3.tgz#68fd150756465e2edae1cc5c048e063916dcfaac"
+ integrity sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==
+ dependencies:
+ "@mapbox/point-geometry" "0.1.0"
+ "@mapbox/vector-tile" "^1.3.1"
+ pbf "^3.2.1"
+
+which@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+wrap-ansi@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+ integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yauzl@^2.10.0:
+ version "2.10.0"
+ resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
+ integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
+ dependencies:
+ buffer-crc32 "~0.2.3"
+ fd-slicer "~1.1.0"