From 12620214174001390cb0c8b525badad9d8c5f58c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 6 Dec 2021 09:45:12 +0000 Subject: [PATCH] Simple static location sharing (#7135) Adds maplibre as a dependency, and behind a labs flag, lets users send and receive [MSC3488](https://github.com/matrix-org/matrix-doc/blob/matthew/location/proposals/3488-location.md) style location shares - with backwards compatibility with old school `m.location` `msgtype` location shares too. For this to work, you have to define a valid maptile server and API in your config.json's `map_style_url`. --- package.json | 2 + res/css/_common.scss | 4 +- res/css/_components.scss | 2 + res/css/views/location/_LocationPicker.scss | 44 ++++ res/css/views/messages/_MLocationBody.scss | 22 ++ res/css/views/rooms/_MessageComposer.scss | 4 + .../element-icons/room/composer/location.svg | 3 + .../views/location/LocationPicker.tsx | 200 ++++++++++++++++++ .../views/location/LocationShareType.tsx | 30 +++ .../views/messages/MLocationBody.tsx | 112 ++++++++++ .../views/messages/MessageEvent.tsx | 8 + .../views/rooms/MessageComposer.tsx | 76 ++++++- src/i18n/strings/en_EN.json | 6 + src/settings/Settings.tsx | 7 + src/settings/controllers/SettingController.ts | 2 + test/setupTests.js | 2 + yarn.lock | 182 +++++++++++++++- 17 files changed, 703 insertions(+), 3 deletions(-) create mode 100644 res/css/views/location/_LocationPicker.scss create mode 100644 res/css/views/messages/_MLocationBody.scss create mode 100644 res/img/element-icons/room/composer/location.svg create mode 100644 src/components/views/location/LocationPicker.tsx create mode 100644 src/components/views/location/LocationShareType.tsx create mode 100644 src/components/views/messages/MLocationBody.tsx diff --git a/package.json b/package.json index 9329620ed7..8b41dd61f9 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@babel/runtime": "^7.12.5", "@sentry/browser": "^6.11.0", "@sentry/tracing": "^6.11.0", + "@types/geojson": "^7946.0.8", "await-lock": "^2.1.0", "blurhash": "^1.1.3", "browser-encrypt-attachment": "^0.3.0", @@ -83,6 +84,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", + "maplibre-gl": "^1.15.2", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-widget-api": "^0.1.0-beta.18", "minimist": "^1.2.5", diff --git a/res/css/_common.scss b/res/css/_common.scss index ae8a475aef..c7e21b500c 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -1,7 +1,8 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd -Copyright 2017 New Vector Ltd +Copyright 2017 - 2019 New Vector Ltd +Copyright 2019 - 2021 The Matrix.org Foundation C.I.C Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,6 +20,7 @@ limitations under the License. @import "./_font-sizes.scss"; @import "./_font-weights.scss"; @import "./_animations.scss"; +@import url("maplibre-gl/dist/maplibre-gl.css"); $hover-transition: 0.08s cubic-bezier(.46, .03, .52, .96); // quadratic diff --git a/res/css/_components.scss b/res/css/_components.scss index 3eb8b9dadf..22f22aef1d 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -173,6 +173,7 @@ @import "./views/groups/_GroupPublicityToggle.scss"; @import "./views/groups/_GroupRoomList.scss"; @import "./views/groups/_GroupUserSettings.scss"; +@import "./views/location/_LocationPicker.scss"; @import "./views/messages/_CallEvent.scss"; @import "./views/messages/_CreateEvent.scss"; @import "./views/messages/_DateSeparator.scss"; @@ -182,6 +183,7 @@ @import "./views/messages/_MImageBody.scss"; @import "./views/messages/_MImageReplyBody.scss"; @import "./views/messages/_MJitsiWidgetEvent.scss"; +@import "./views/messages/_MLocationBody.scss"; @import "./views/messages/_MNoticeBody.scss"; @import "./views/messages/_MPollBody.scss"; @import "./views/messages/_MStickerBody.scss"; diff --git a/res/css/views/location/_LocationPicker.scss b/res/css/views/location/_LocationPicker.scss new file mode 100644 index 0000000000..b386268cec --- /dev/null +++ b/res/css/views/location/_LocationPicker.scss @@ -0,0 +1,44 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_LocationPicker { + // placeholder sizing to be replaced once there's a proper design + width: 450px; + height: 500px; + + border-radius: 4px; + + display: flex; + flex-direction: column; +} + +#mx_LocationPicker_map { + height: 324px; + border-radius: 8px 8px 0px 0px; +} + +.mx_LocationPicker_footer { + margin: 24px; + + .mx_Dropdown_menu { + max-height: 140px; + } +} + +.mx_LocationPicker_error { + color: red; + margin: auto; +} diff --git a/res/css/views/messages/_MLocationBody.scss b/res/css/views/messages/_MLocationBody.scss new file mode 100644 index 0000000000..5bb90fbf7b --- /dev/null +++ b/res/css/views/messages/_MLocationBody.scss @@ -0,0 +1,22 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MLocationBody_map { + width: 450px; + height: 300px; + + border-radius: $timeline-image-border-radius; +} diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 2a49840f17..454d604572 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -256,6 +256,10 @@ limitations under the License. mask-image: url('$(res)/img/element-icons/room/composer/emoji.svg'); } +.mx_MessageComposer_location::before { + mask-image: url('$(res)/img/element-icons/room/composer/location.svg'); +} + .mx_MessageComposer_stickers::before { mask-image: url('$(res)/img/element-icons/room/composer/sticker.svg'); } diff --git a/res/img/element-icons/room/composer/location.svg b/res/img/element-icons/room/composer/location.svg new file mode 100644 index 0000000000..7118645443 --- /dev/null +++ b/res/img/element-icons/room/composer/location.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/views/location/LocationPicker.tsx b/src/components/views/location/LocationPicker.tsx new file mode 100644 index 0000000000..8d4efdfa70 --- /dev/null +++ b/src/components/views/location/LocationPicker.tsx @@ -0,0 +1,200 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import maplibregl from 'maplibre-gl'; + +import SdkConfig from '../../../SdkConfig'; +import Field from "../elements/Field"; +import DialogButtons from "../elements/DialogButtons"; +import Dropdown from "../elements/Dropdown"; +import LocationShareType from "./LocationShareType"; + +import { _t } from '../../../languageHandler'; +import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { logger } from "matrix-js-sdk/src/logger"; + +interface IDropdownProps { + value: LocationShareType; + label: string; + width?: number; + onChange(type: LocationShareType): void; +} + +const LocationShareTypeDropdown = ({ + value, + label, + width, + onChange, +}: IDropdownProps) => { + const options = [ + //
{ _t("Share custom location") }
, +
{ _t("Share my current location as a once off") }
, + //
{ _t("Share my current location for one minute") }
, + //
{ _t("Share my current location for five minutes") }
, + //
{ _t("Share my current location for thirty minutes") }
, + //
{ _t("Share my current location for one hour") }
, + //
{ _t("Share my current location for three hours") }
, + //
{ _t("Share my current location for six hours") }
, + //
{ _t("Share my current location for one day") }
, + //
{ _t("Share my current location until I disable it") }
, + ]; + + return { onChange(LocationShareType[LocationShareType[parseInt(key)]]); }} + menuWidth={width} + label={label} + value={value.toString()} + > + { options } + ; +}; + +interface IProps { + onChoose(uri: string, ts: number, type: LocationShareType, description: string): boolean; + onFinished(); +} + +interface IState { + description: string; + type: LocationShareType; + position?: GeolocationPosition; + manual: boolean; + error: Error; +} + +@replaceableComponent("views.location.LocationPicker") +class LocationPicker extends React.Component { + private map: maplibregl.Map; + private geolocate: maplibregl.GeolocateControl; + + constructor(props) { + super(props); + + this.state = { + description: _t("My location"), + type: LocationShareType.OnceOff, + position: undefined, + manual: false, + error: undefined, + }; + } + + componentDidMount() { + const config = SdkConfig.get(); + this.map = new maplibregl.Map({ + container: 'mx_LocationPicker_map', + style: config.map_style_url, + center: [0, 0], + zoom: 1, + }); + + // Add geolocate control to the map. + this.geolocate = new maplibregl.GeolocateControl({ + positionOptions: { + enableHighAccuracy: true, + }, + trackUserLocation: true, + }); + this.map.addControl(this.geolocate); + + this.map.on('error', (e)=>{ + logger.error("Failed to load map: check map_style_url in config.json has a valid URL and API key", e.error); + this.setState({ error: e.error }); + }); + + this.map.on('load', ()=>{ + this.geolocate.trigger(); + }); + + this.geolocate.on('geolocate', this.onGeolocate); + } + + componentWillUnmount() { + this.geolocate.off('geolocate', this.onGeolocate); + } + + private onGeolocate = (position) => { + this.setState({ position }); + }; + + private onDescriptionChange = (ev: React.ChangeEvent) => { + this.setState({ description: ev.target.value }); + }; + + private getGeoUri = (position) => { + return (`geo:${ position.coords.latitude },` + + position.coords.longitude + + ( position.coords.altitude != null ? + `,${ position.coords.altitude }` : '' ) + + `;u=${ position.coords.accuracy }`); + }; + + private onOk = () => { + this.props.onChoose( + this.state.position ? this.getGeoUri(this.state.position) : undefined, + this.state.position ? this.state.position.timestamp : undefined, + this.state.type, + this.state.description, + ); + this.props.onFinished(); + }; + + private onTypeChange= (type: LocationShareType) => { + this.setState({ type }); + }; + + render() { + const error = this.state.error ? +
+ { _t("Failed to load map") } +
: null; + + return ( +
+
+ { error } +
+
+ + + + + + +
+
+ ); + } +} + +export default LocationPicker; diff --git a/src/components/views/location/LocationShareType.tsx b/src/components/views/location/LocationShareType.tsx new file mode 100644 index 0000000000..957860514e --- /dev/null +++ b/src/components/views/location/LocationShareType.tsx @@ -0,0 +1,30 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +enum LocationShareType { + Custom = -1, + OnceOff = 0, + OneMine = 60, + FiveMins = 5 * 60, + ThirtyMins = 30 * 60, + OneHour = 60 * 60, + ThreeHours = 3 * 60 * 60, + SixHours = 6 * 60 * 60, + OneDay = 24 * 60 * 60, + Forever = Number.MAX_SAFE_INTEGER, +} + +export default LocationShareType; diff --git a/src/components/views/messages/MLocationBody.tsx b/src/components/views/messages/MLocationBody.tsx new file mode 100644 index 0000000000..8e096fa382 --- /dev/null +++ b/src/components/views/messages/MLocationBody.tsx @@ -0,0 +1,112 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import maplibregl from 'maplibre-gl'; +import SdkConfig from '../../../SdkConfig'; +import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { IBodyProps } from "./IBodyProps"; +import { logger } from "matrix-js-sdk/src/logger"; +import { _t } from '../../../languageHandler'; + +interface IState { + error: Error; +} + +@replaceableComponent("views.messages.MLocationBody") +export default class MLocationBody extends React.Component { + private map: maplibregl.Map; + private coords: GeolocationCoordinates; + private description: string; + + constructor(props: IBodyProps) { + super(props); + + // unfortunately we're stuck supporting legacy `content.geo_uri` + // events until the end of days, or until we figure out mutable + // events - so folks can read their old chat history correctly. + // https://github.com/matrix-org/matrix-doc/issues/3516 + const content = this.props.mxEvent.getContent(); + const uri = content['org.matrix.msc3488.location'] ? + content['org.matrix.msc3488.location'].uri : + content['geo_uri']; + + this.coords = this.parseGeoUri(uri); + this.state = { + error: undefined, + }; + + this.description = + content['org.matrix.msc3488.location']?.description ?? content['body']; + } + + private parseGeoUri = (uri: string): GeolocationCoordinates => { + const m = uri.match(/^\s*geo:(.*?)\s*$/); + if (!m) return; + const parts = m[1].split(';'); + const coords = parts[0].split(','); + let uncertainty: number; + for (const param of parts.slice(1)) { + const m = param.match(/u=(.*)/); + if (m) uncertainty = parseFloat(m[1]); + } + return { + latitude: parseFloat(coords[0]), + longitude: parseFloat(coords[1]), + altitude: parseFloat(coords[2]), + accuracy: uncertainty, + altitudeAccuracy: undefined, + heading: undefined, + speed: undefined, + }; + }; + + componentDidMount() { + const config = SdkConfig.get(); + this.map = new maplibregl.Map({ + container: this.getBodyId(), + style: config.map_style_url, + center: [this.coords.longitude, this.coords.latitude], + zoom: 13, + }); + + new maplibregl.Marker() + .setLngLat([this.coords.longitude, this.coords.latitude]) + .addTo(this.map); + + this.map.on('error', (e)=>{ + logger.error("Failed to load map: check map_style_url in config.json has a valid URL and API key", e.error); + this.setState({ error: e.error }); + }); + } + + private getBodyId = () => { + return `mx_MLocationBody_${this.props.mxEvent.getId()}`; + }; + + render() { + const error = this.state.error ? +
+ { _t("Failed to load map") } +
: null; + + return
+
+ { error } + { this.description } +
; + } +} diff --git a/src/components/views/messages/MessageEvent.tsx b/src/components/views/messages/MessageEvent.tsx index 6cffd79149..2896eafe8e 100644 --- a/src/components/views/messages/MessageEvent.tsx +++ b/src/components/views/messages/MessageEvent.tsx @@ -125,6 +125,14 @@ export default class MessageEvent extends React.Component implements IMe BodyType = sdk.getComponent('messages.MPollBody'); } } + + if ((type && type === "org.matrix.msc3488.location") || + (type && type === EventType.RoomMessage && msgtype && msgtype === MsgType.Location)) { + // TODO: tidy this up once location sharing is out of labs + if (SettingsStore.getValue("feature_location_share")) { + BodyType = sdk.getComponent('messages.MLocationBody'); + } + } } if (SettingsStore.getValue("feature_mjolnir")) { diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index aedad1848c..d64bef9d2d 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -48,6 +48,7 @@ import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInse import { Action } from "../../../dispatcher/actions"; import EditorModel from "../../../editor/model"; import EmojiPicker from '../emojipicker/EmojiPicker'; +import LocationPicker from '../location/LocationPicker'; import UIStore, { UI_EVENTS } from '../../../stores/UIStore'; import Modal from "../../../Modal"; import { RelationType } from 'matrix-js-sdk/src/@types/event'; @@ -55,6 +56,9 @@ import RoomContext from '../../../contexts/RoomContext'; import { POLL_START_EVENT_TYPE } from "../../../polls/consts"; import ErrorDialog from "../dialogs/ErrorDialog"; import PollCreateDialog from "../elements/PollCreateDialog"; +import { MsgType } from "matrix-js-sdk/src/@types/event"; +import { logger } from "matrix-js-sdk/src/logger"; +import LocationShareType from "../location/LocationShareType"; import { SettingUpdatedPayload } from "../../../dispatcher/payloads/SettingUpdatedPayload"; let instanceCount = 0; @@ -77,7 +81,7 @@ function SendButton(props: ISendButtonProps) { interface IEmojiButtonProps { addEmoji: (unicode: string) => boolean; - menuPosition: any; // TODO: Types + menuPosition: AboveLeftOf; narrowMode: boolean; } @@ -114,6 +118,46 @@ const EmojiButton: React.FC = ({ addEmoji, menuPosition, narr ; }; +interface ILocationButtonProps { + room: Room; + shareLocation: (uri: string, ts: number, type: LocationShareType, description: string) => boolean; + menuPosition: AboveLeftOf; + narrowMode: boolean; +} + +const LocationButton: React.FC = ({ shareLocation, menuPosition, narrowMode }) => { + const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu(); + + let contextMenu; + if (menuDisplayed) { + const position = menuPosition ?? aboveLeftOf(button.current.getBoundingClientRect()); + contextMenu = + + ; + } + + const className = classNames( + "mx_MessageComposer_button", + "mx_MessageComposer_location", + { + "mx_MessageComposer_button_highlight": menuDisplayed, + }, + ); + + // TODO: replace ContextMenuTooltipButton with a unified representation of + // the header buttons and the right panel buttons + return + + + { contextMenu } + ; +}; + interface IUploadButtonProps { roomId: string; relation?: IEventRelation | null; @@ -447,6 +491,25 @@ export default class MessageComposer extends React.Component { return true; }; + private shareLocation = (uri: string, ts: number, type: LocationShareType, description: string): boolean => { + if (!uri) return false; + try { + const text = `${description ? description : 'Location'} at ${uri} as of ${new Date(ts).toISOString()}`; + // noinspection ES6MissingAwait - we don't care if it fails, it'll get queued. + MatrixClientPeg.get().sendMessage(this.props.room.roomId, { + "body": text, + "msgtype": MsgType.Location, + "geo_uri": uri, + "org.matrix.msc3488.location": { uri, description }, + "org.matrix.msc3488.ts": ts, + // TODO: MSC1767 fallbacks for text & thumbnail + }); + } catch (e) { + logger.error("Error sending location:", e); + } + return true; + }; + private sendMessage = async () => { if (this.state.haveRecording && this.voiceRecordingButton.current) { // There shouldn't be any text message to send when a voice recording is active, so @@ -514,6 +577,17 @@ export default class MessageComposer extends React.Component { relation={this.props.relation} />, ); + if (SettingsStore.getValue("feature_location_share")) { + buttons.push( + , + ); + } buttons.push( , ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6f75ecb67a..2116b8cfa8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -854,6 +854,7 @@ "Show message previews for reactions in all rooms": "Show message previews for reactions in all rooms", "Offline encrypted messaging using dehydrated devices": "Offline encrypted messaging using dehydrated devices", "Polls (under active development)": "Polls (under active development)", + "Location sharing (under active development)": "Location sharing (under active development)", "Show info about bridges in room settings": "Show info about bridges in room settings", "New layout switcher (with message bubbles)": "New layout switcher (with message bubbles)", "Meta Spaces": "Meta Spaces", @@ -1649,6 +1650,7 @@ "Send message": "Send message", "Emoji picker": "Emoji picker", "Add emoji": "Add emoji", + "Share location": "Share location", "Upload file": "Upload file", "You do not have permission to start polls in this room.": "You do not have permission to start polls in this room.", "Create poll": "Create poll", @@ -2070,6 +2072,7 @@ "Declining …": "Declining …", "%(name)s wants to verify": "%(name)s wants to verify", "You sent a verification request": "You sent a verification request", + "Failed to load map": "Failed to load map", "Vote not registered": "Vote not registered", "Sorry, your vote was not registered. Please try again.": "Sorry, your vote was not registered. Please try again.", "No votes cast": "No votes cast", @@ -2100,6 +2103,9 @@ "edited": "edited", "Submit logs": "Submit logs", "Can't load this message": "Can't load this message", + "Share my current location as a once off": "Share my current location as a once off", + "My location": "My location", + "Type of location share": "Type of location share", "Failed to load group members": "Failed to load group members", "Filter community members": "Filter community members", "Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Are you sure you want to remove '%(roomName)s' from %(groupId)s?", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 61327a1df0..8d198877a6 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -307,6 +307,13 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td("Polls (under active development)"), default: false, }, + "feature_location_share": { + isFeature: true, + labsGroup: LabGroup.Messaging, + supportedLevels: LEVELS_FEATURE, + displayName: _td("Location sharing (under active development)"), + default: false, + }, "doNotDisturb": { supportedLevels: [SettingLevel.DEVICE], default: false, diff --git a/src/settings/controllers/SettingController.ts b/src/settings/controllers/SettingController.ts index ba78597da7..292b2d63e5 100644 --- a/src/settings/controllers/SettingController.ts +++ b/src/settings/controllers/SettingController.ts @@ -54,6 +54,8 @@ export default abstract class SettingController { */ public onChange(level: SettingLevel, roomId: string, newValue: any) { // do nothing by default + + // FIXME: force a fresh on the RoomView for the roomId in question } /** diff --git a/test/setupTests.js b/test/setupTests.js index e719f715c9..0b92c0c854 100644 --- a/test/setupTests.js +++ b/test/setupTests.js @@ -15,3 +15,5 @@ global.TextDecoder = TextDecoder; configure({ adapter: new Adapter() }); +// maplibre requires a createObjectURL mock +global.URL.createObjectURL = jest.fn(); diff --git a/yarn.lock b/yarn.lock index 83b24389f7..0f94f38e55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1306,6 +1306,56 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@mapbox/geojson-rewind@^0.5.0": + version "0.5.1" + resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.1.tgz#adbe16dc683eb40e90934c51a5e28c7bbf44f4e1" + integrity sha512-eL7fMmfTBKjrb+VFHXCGv9Ot0zc3C0U+CwXo1IrP+EPwDczLoXv34Tgq3y+2mPSFNVUXgU42ILWJTC7145KPTA== + dependencies: + get-stream "^6.0.1" + minimist "^1.2.5" + +"@mapbox/geojson-types@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz#9aecf642cb00eab1080a57c4f949a65b4a5846d6" + integrity sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw== + +"@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 sha1-zlblOfg1UrWNENZy6k1vya3HsjQ= + +"@mapbox/mapbox-gl-supported@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz#f60b6a55a5d8e5ee908347d2ce4250b15103dc8e" + integrity sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg== + +"@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 sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI= + +"@mapbox/tiny-sdf@^1.1.1": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@mapbox/tiny-sdf/-/tiny-sdf-1.2.5.tgz#424c620a96442b20402552be70a7f62a8407cc59" + integrity sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw== + +"@mapbox/unitbezier@^0.0.0": + version "0.0.0" + resolved "https://registry.yarnpkg.com/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz#15651bd553a67b8581fb398810c98ad86a34524e" + integrity sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4= + +"@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== + "@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz": version "3.2.3" resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz#cc332fdd25c08ef0e40f4d33fc3f822a0f98b6f4" @@ -1672,6 +1722,11 @@ "@types/fbemitter" "*" "@types/react" "*" +"@types/geojson@^7946.0.8": + version "7946.0.8" + resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca" + integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA== + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -2996,6 +3051,11 @@ css-what@^5.0.0, css-what@^5.0.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== +csscolorparser@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b" + integrity sha1-s085HupNqPPpgjHizNjfnAQfFxs= + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -3277,6 +3337,11 @@ domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0: domelementtype "^2.2.0" domhandler "^4.2.0" +earcut@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/earcut/-/earcut-2.2.3.tgz#d44ced2ff5a18859568e327dd9c7d46b16f55cf4" + integrity sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -4112,6 +4177,11 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +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-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -4150,6 +4220,11 @@ get-stream@^5.0.0: 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== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -4175,6 +4250,11 @@ gfm.css@^1.1.2: resolved "https://registry.yarnpkg.com/gfm.css/-/gfm.css-1.1.2.tgz#94acfa600672663b9dd0fd4b6ee5d11c8dbc161e" integrity sha512-KhK3rqxMj+UTLRxWnfUA5n8XZYMWfHrrcCxtWResYR2B3hWIqBM6v9FPGZSlVuX+ScLewizOvNkjYXuPs95ThQ== +gl-matrix@^3.2.1: + 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-parent@^5.0.0, glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -4256,6 +4336,11 @@ graceful-fs@^4.2.4: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== +grid-index@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/grid-index/-/grid-index-1.1.0.tgz#97f8221edec1026c8377b86446a7c71e79522ea7" + integrity sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA== + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -4465,7 +4550,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.13: +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== @@ -5638,6 +5723,11 @@ katex@^0.12.0: dependencies: commander "^2.19.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@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -5864,6 +5954,35 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +maplibre-gl@^1.15.2: + version "1.15.2" + resolved "https://registry.yarnpkg.com/maplibre-gl/-/maplibre-gl-1.15.2.tgz#7fb47868b62455af916c090903f2154394450f9c" + integrity sha512-uPeV530apb4JfX3cRFfE+awFnbcJTOnCv2QvY4mw4huiInbybElWYkNzTs324YLSADq0f4bidRoYcR81ho3aLA== + dependencies: + "@mapbox/geojson-rewind" "^0.5.0" + "@mapbox/geojson-types" "^1.0.2" + "@mapbox/jsonlint-lines-primitives" "^2.0.2" + "@mapbox/mapbox-gl-supported" "^1.5.0" + "@mapbox/point-geometry" "^0.1.0" + "@mapbox/tiny-sdf" "^1.1.1" + "@mapbox/unitbezier" "^0.0.0" + "@mapbox/vector-tile" "^1.3.1" + "@mapbox/whoots-js" "^3.1.0" + csscolorparser "~1.0.3" + earcut "^2.2.2" + geojson-vt "^3.2.1" + gl-matrix "^3.2.1" + grid-index "^1.1.0" + minimist "^1.2.5" + murmurhash-js "^1.0.0" + pbf "^3.2.1" + potpack "^1.0.1" + quickselect "^2.0.0" + rw "^1.3.3" + supercluster "^7.1.0" + tinyqueue "^2.0.3" + vt-pbf "^3.1.1" + mathml-tag-names@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" @@ -6101,6 +6220,11 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +murmurhash-js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/murmurhash-js/-/murmurhash-js-1.0.0.tgz#b06278e21fc6c37fa5313732b0412bcb6ae15f51" + integrity sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E= + nanoid@^3.1.28: version "3.1.30" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362" @@ -6570,6 +6694,14 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +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" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -6731,6 +6863,11 @@ posthog-js@1.12.2: dependencies: fflate "^0.4.1" +potpack@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/potpack/-/potpack-1.0.2.tgz#23b99e64eb74f5741ffe7656b5b5c4ddce8dfc14" + integrity sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -6790,6 +6927,11 @@ prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +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== + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -6865,6 +7007,11 @@ quick-lru@^4.0.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +quickselect@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018" + integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== + raf-schd@^4.0.2: version "4.0.3" resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a" @@ -7262,6 +7409,13 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +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" + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -7335,6 +7489,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rw@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= + rxjs@^6.5.2: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -7915,6 +8074,13 @@ sugarss@^2.0.0: dependencies: postcss "^7.0.2" +supercluster@^7.1.0: + version "7.1.4" + resolved "https://registry.yarnpkg.com/supercluster/-/supercluster-7.1.4.tgz#6762aabfd985d3390b49f13b815567d5116a828a" + integrity sha512-GhKkRM1jMR6WUwGPw05fs66pOFWhf59lXq+Q3J3SxPvhNcmgOtLRV6aVQPMRsmXdpaeFJGivt+t7QXUPL3ff4g== + dependencies: + kdbush "^3.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -8011,6 +8177,11 @@ tiny-invariant@^1.0.6: resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw== +tinyqueue@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tinyqueue/-/tinyqueue-2.0.3.tgz#64d8492ebf39e7801d7bd34062e29b45b2035f08" + integrity sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== + tmatch@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/tmatch/-/tmatch-2.0.1.tgz#0c56246f33f30da1b8d3d72895abaf16660f38cf" @@ -8406,6 +8577,15 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" +vt-pbf@^3.1.1: + 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" + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"