mirror of
https://github.com/sourcegraph/sourcegraph.git
synced 2026-02-06 14:51:44 +00:00
Svelte: Add telemetry v2 to svelte client (#63041)
* Svelte: Add telemetry v2 to svelte client * Run format * Add telemetry to the bazel config
This commit is contained in:
parent
79c912d6d9
commit
5915788ef0
@ -7,7 +7,7 @@ import { findFilters } from '@sourcegraph/shared/src/search/query/query'
|
||||
import { scanSearchQuery, succeedScan } from '@sourcegraph/shared/src/search/query/scanner'
|
||||
import type { Filter as QueryFilter } from '@sourcegraph/shared/src/search/query/token'
|
||||
import { omitFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
import { V2FilterTypes, type Filter } from '@sourcegraph/shared/src/search/stream'
|
||||
import { TELEMETRY_V2_FILTER_TYPES, type Filter } from '@sourcegraph/shared/src/search/stream'
|
||||
import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
|
||||
import type { TelemetryProps } from '@sourcegraph/shared/src/telemetry/telemetryService'
|
||||
import { Button, H1, H3, Icon, Tooltip } from '@sourcegraph/wildcard'
|
||||
@ -72,7 +72,7 @@ export const NewSearchFilters: FC<NewSearchFiltersProps> = ({
|
||||
(filter: URLQueryFilter, remove: boolean): void => {
|
||||
telemetryService.log('SearchFiltersTypeClick', { filterType: filter.label }, { filterType: filter.label })
|
||||
telemetryRecorder.recordEvent('search.filters.type', 'click', {
|
||||
metadata: { filterKind: V2FilterTypes[filter.kind] },
|
||||
metadata: { filterKind: TELEMETRY_V2_FILTER_TYPES[filter.kind] },
|
||||
})
|
||||
if (remove) {
|
||||
setSelectedFilters(
|
||||
@ -96,7 +96,7 @@ export const NewSearchFilters: FC<NewSearchFiltersProps> = ({
|
||||
setSelectedFilters(filters)
|
||||
telemetryService.log('SearchFiltersSelectFilter', { filterKind }, { filterKind })
|
||||
telemetryRecorder.recordEvent('search.filters', 'select', {
|
||||
metadata: { filterKind: V2FilterTypes[filterKind] },
|
||||
metadata: { filterKind: TELEMETRY_V2_FILTER_TYPES[filterKind] },
|
||||
})
|
||||
},
|
||||
[setSelectedFilters, telemetryService, telemetryRecorder]
|
||||
@ -303,7 +303,7 @@ const SyntheticCountFilter: FC<SyntheticCountFilterProps> = props => {
|
||||
const handleCountAllFilter = (filterKind: Filter['kind'], countFilters: URLQueryFilter[]): void => {
|
||||
telemetryService.log('SearchFiltersSelectFilter', { filterKind }, { filterKind })
|
||||
telemetryRecorder.recordEvent('search.filters', 'select', {
|
||||
metadata: { filterKind: V2FilterTypes[filterKind] },
|
||||
metadata: { filterKind: TELEMETRY_V2_FILTER_TYPES[filterKind] },
|
||||
})
|
||||
|
||||
if (countFilters.length > 0) {
|
||||
|
||||
@ -91,6 +91,19 @@ export interface SubmitSearchParameters
|
||||
searchMode?: SearchMode
|
||||
}
|
||||
|
||||
export const TELEMETRY_V2_SEARCH_SOURCE_TYPE: { [key in SubmitSearchParameters['source']]: number } = {
|
||||
home: 1,
|
||||
nav: 2,
|
||||
repo: 3,
|
||||
tree: 4,
|
||||
filter: 5,
|
||||
type: 6,
|
||||
scopePage: 7,
|
||||
communitySearchContextPage: 8,
|
||||
excludedResults: 9,
|
||||
smartSearchDisabled: 10,
|
||||
}
|
||||
|
||||
export interface SubmitSearchProps {
|
||||
submitSearch: (parameters: Partial<Omit<SubmitSearchParameters, 'query'>>) => void
|
||||
}
|
||||
|
||||
@ -284,7 +284,7 @@ export interface Filter {
|
||||
kind: 'file' | 'repo' | 'lang' | 'utility' | 'author' | 'commit date' | 'symbol type' | 'type'
|
||||
}
|
||||
|
||||
export const V2FilterTypes: { [key in Filter['kind']]: number } = {
|
||||
export const TELEMETRY_V2_FILTER_TYPES: { [key in Filter['kind']]: number } = {
|
||||
file: 1,
|
||||
repo: 2,
|
||||
lang: 3,
|
||||
|
||||
@ -86,6 +86,7 @@ BUILD_DEPS = [
|
||||
":node_modules/@sourcegraph/shared",
|
||||
":node_modules/@sourcegraph/web",
|
||||
":node_modules/@sourcegraph/wildcard",
|
||||
":node_modules/@sourcegraph/telemetry",
|
||||
":node_modules/@storybook/svelte",
|
||||
":node_modules/@sveltejs/adapter-static",
|
||||
":node_modules/@sveltejs/kit",
|
||||
|
||||
@ -75,6 +75,7 @@
|
||||
"@sourcegraph/common": "workspace:*",
|
||||
"@sourcegraph/http-client": "workspace:*",
|
||||
"@sourcegraph/shared": "workspace:*",
|
||||
"@sourcegraph/telemetry": "^0.11.0",
|
||||
"@sourcegraph/web": "workspace:*",
|
||||
"@sourcegraph/wildcard": "workspace:*",
|
||||
"@storybook/test": "^8.0.5",
|
||||
|
||||
@ -42,6 +42,8 @@
|
||||
import SymbolKindIcon from '$lib/search/SymbolKindIcon.svelte'
|
||||
import { displayRepoName, scanSearchQuery, type Filter } from '$lib/shared'
|
||||
import { SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS } from '$lib/telemetry'
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import { TELEMETRY_V2_FILTER_TYPES } from '@sourcegraph/shared/src/search/stream'
|
||||
import { delay } from '$lib/utils'
|
||||
import { Alert } from '$lib/wildcard'
|
||||
import Button from '$lib/wildcard/Button.svelte'
|
||||
@ -90,6 +92,9 @@
|
||||
|
||||
function handleFilterSelect(kind: SectionItemData['kind']): void {
|
||||
SVELTE_LOGGER.log(SVELTE_TELEMETRY_EVENTS.SelectSearchFilter, { kind }, { kind })
|
||||
TELEMETRY_V2_RECORDER.recordEvent('search.filters', 'select', {
|
||||
metadata: { filterKind: TELEMETRY_V2_FILTER_TYPES[kind] },
|
||||
})
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
|
||||
109
client/web-sveltekit/src/lib/telemetry2.ts
Normal file
109
client/web-sveltekit/src/lib/telemetry2.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import { gql } from '@urql/core'
|
||||
|
||||
import type { BillingCategory, BillingProduct } from '@sourcegraph/shared/src/telemetry'
|
||||
import { sessionTracker } from '@sourcegraph/shared/src/telemetry/web/sessionTracker'
|
||||
import { userTracker } from '@sourcegraph/shared/src/telemetry/web/userTracker'
|
||||
import {
|
||||
type MarketingTrackingProvider,
|
||||
type TelemetryEventMarketingTrackingInput,
|
||||
type TelemetryEventInput,
|
||||
type TelemetryExporter,
|
||||
MarketingTrackingTelemetryProcessor,
|
||||
TelemetryRecorderProvider as BaseTelemetryRecorderProvider,
|
||||
} from '@sourcegraph/telemetry'
|
||||
|
||||
import type { GraphQLClient } from '$lib/graphql'
|
||||
import { getGraphQLClient } from '$lib/graphql'
|
||||
import type { ExportTelemetryEventsResult } from '$lib/graphql-operations'
|
||||
|
||||
/**
|
||||
* TelemetryRecorderProvider is the default provider implementation for the
|
||||
* Sourcegraph web app.
|
||||
*/
|
||||
export class TelemetryRecorderProvider extends BaseTelemetryRecorderProvider<BillingCategory, BillingProduct> {
|
||||
constructor(
|
||||
graphQlClient: GraphQLClient,
|
||||
options: {
|
||||
/**
|
||||
* Enables buffering of events for export. Only enable if there is a
|
||||
* reliable unsubscribe mechanism available.
|
||||
*/
|
||||
enableBuffering: boolean
|
||||
}
|
||||
) {
|
||||
super(
|
||||
{
|
||||
client: getTelemetrySourceClient(),
|
||||
clientVersion: window.context.version,
|
||||
},
|
||||
new GraphQlTelemetryExporter(graphQlClient),
|
||||
[new MarketingTrackingTelemetryProcessor(new TrackingMetadataProvider())],
|
||||
{
|
||||
/**
|
||||
* Use buffer time of 100ms - some existing buffering uses
|
||||
* 1000ms, but we use a more conservative value.
|
||||
*/
|
||||
bufferTimeMs: options.enableBuffering ? 100 : 0,
|
||||
bufferMaxSize: 10,
|
||||
errorHandler: error => {
|
||||
throw new Error(error)
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function getTelemetrySourceClient(): string {
|
||||
if (window.context?.sourcegraphDotComMode) {
|
||||
return 'dotcom.svelte-web'
|
||||
}
|
||||
return 'server.svelte-web'
|
||||
}
|
||||
|
||||
/**
|
||||
* ApolloTelemetryExporter exports events via the new Sourcegraph telemetry
|
||||
* framework: https://docs-legacy.sourcegraph.com/dev/background-information/telemetry
|
||||
*/
|
||||
export class GraphQlTelemetryExporter implements TelemetryExporter {
|
||||
constructor(private client: GraphQLClient) {}
|
||||
|
||||
public async exportEvents(events: TelemetryEventInput[]): Promise<void> {
|
||||
await this.client.mutation<ExportTelemetryEventsResult>(
|
||||
gql`
|
||||
mutation ExportTelemetryEvents($events: [TelemetryEventInput!]!) {
|
||||
telemetry {
|
||||
recordEvents(events: $events) {
|
||||
alwaysNil
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
{ events }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TrackingMetadataProvider implements MarketingTrackingProvider {
|
||||
private user = userTracker
|
||||
private session = sessionTracker
|
||||
|
||||
public getMarketingTrackingMetadata(): TelemetryEventMarketingTrackingInput | null {
|
||||
if (!window.context?.sourcegraphDotComMode) {
|
||||
return null // don't report this data outside dotcom
|
||||
}
|
||||
|
||||
return {
|
||||
cohortID: this.user.cohortID,
|
||||
deviceSessionID: this.user.deviceSessionID,
|
||||
firstSourceURL: this.session.getFirstSourceURL(),
|
||||
lastSourceURL: this.session.getLastSourceURL(),
|
||||
referrer: this.session.getReferrer(),
|
||||
sessionFirstURL: this.session.getSessionFirstURL(),
|
||||
sessionReferrer: this.session.getSessionReferrer(),
|
||||
url: window.location.href,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const TELEMETRY_V2 = new TelemetryRecorderProvider(getGraphQLClient(), { enableBuffering: true })
|
||||
export const TELEMETRY_V2_RECORDER = TELEMETRY_V2.getRecorder()
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
import { createPromiseStore } from '$lib/utils'
|
||||
import { SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS } from '$lib/telemetry'
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import Readme from '$lib/repo/Readme.svelte'
|
||||
|
||||
import type { PageData } from './$types'
|
||||
@ -16,6 +17,7 @@
|
||||
|
||||
onMount(() => {
|
||||
SVELTE_LOGGER.logViewEvent(SVELTE_TELEMETRY_EVENTS.ViewRepositoryPage)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('repo', 'view')
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
// @sg EnableRollout
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import { SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS } from '$lib/telemetry'
|
||||
|
||||
import type { PageData, Snapshot } from './$types'
|
||||
@ -29,6 +30,7 @@
|
||||
|
||||
onMount(() => {
|
||||
SVELTE_LOGGER.logViewEvent(SVELTE_TELEMETRY_EVENTS.ViewBlobPage)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('blob', 'view')
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
import Permalink from '$lib/repo/Permalink.svelte'
|
||||
import { createCodeIntelAPI } from '$lib/shared'
|
||||
import { isLightTheme, settings } from '$lib/stores'
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import { codeCopiedEvent, SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS } from '$lib/telemetry'
|
||||
import { createPromiseStore, formatBytes } from '$lib/utils'
|
||||
import { Alert, Badge, MenuButton, MenuLink } from '$lib/wildcard'
|
||||
@ -126,6 +127,7 @@
|
||||
// TODO: track other blob mode
|
||||
if (event.detail === CodeViewMode.Blame) {
|
||||
SVELTE_LOGGER.log(SVELTE_TELEMETRY_EVENTS.GitBlameEnabled)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('repo.gitBlame', 'enable')
|
||||
}
|
||||
|
||||
goto(viewModeURL(event.detail), { replaceState: true, keepFocus: true })
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS } from '$lib/telemetry'
|
||||
import Tooltip from '$lib/Tooltip.svelte'
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import { getHumanNameForCodeHost } from '$lib/repo/shared/codehost'
|
||||
|
||||
import Tooltip from '$lib/Tooltip.svelte'
|
||||
import CodeHostIcon from '$lib/search/CodeHostIcon.svelte'
|
||||
import type { OpenInCodeHostAction } from './OpenInCodeHostAction.gql'
|
||||
|
||||
@ -9,6 +11,7 @@
|
||||
|
||||
function handleOpenCodeHostClick(): void {
|
||||
SVELTE_LOGGER.log(SVELTE_TELEMETRY_EVENTS.GoToCodeHost)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('repo.goToCodeHost', 'click')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -3,13 +3,16 @@
|
||||
import { mdiMagnify } from '@mdi/js'
|
||||
import { createDialog } from '@melt-ui/svelte'
|
||||
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import SearchInput from '$lib/search/input/SearchInput.svelte'
|
||||
import { QueryState, queryStateStore } from '$lib/search/state'
|
||||
import { settings } from '$lib/stores'
|
||||
import { registerHotkey } from '$lib/Hotkey'
|
||||
import { repositoryInsertText } from '$lib/shared'
|
||||
import { SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS } from '$lib/telemetry'
|
||||
import { registerHotkey } from '$lib/Hotkey'
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import { TELEMETRY_V2_SEARCH_SOURCE_TYPE } from '@sourcegraph/shared/src/search'
|
||||
import { QueryState, queryStateStore } from '$lib/search/state'
|
||||
|
||||
import Icon from '$lib/Icon.svelte'
|
||||
import SearchInput from '$lib/search/input/SearchInput.svelte'
|
||||
|
||||
export let repoName: string
|
||||
/**
|
||||
@ -35,6 +38,9 @@
|
||||
{ source: 'repo', query: state.query },
|
||||
{ source: 'repo', patternType: state.patternType }
|
||||
)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('search', 'submit', {
|
||||
metadata: { source: TELEMETRY_V2_SEARCH_SOURCE_TYPE['repo'] },
|
||||
})
|
||||
}
|
||||
|
||||
$: query = `repo:${repositoryInsertText({ repository: repoName })}${revision ? `@${revision}` : ''} `
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
import { setContext, onMount } from 'svelte'
|
||||
|
||||
import { SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS } from '$lib/telemetry'
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import { TELEMETRY_V2_SEARCH_SOURCE_TYPE } from '@sourcegraph/shared/src/search'
|
||||
import { logoLight, logoDark } from '$lib/images'
|
||||
import SearchInput from '$lib/search/input/SearchInput.svelte'
|
||||
import type { QueryStateStore, QueryState } from '$lib/search/state'
|
||||
@ -20,6 +22,7 @@
|
||||
|
||||
onMount(() => {
|
||||
SVELTE_LOGGER.logViewEvent(SVELTE_TELEMETRY_EVENTS.ViewHomePage)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('home', 'view')
|
||||
})
|
||||
|
||||
function handleSubmit(state: QueryState) {
|
||||
@ -28,6 +31,9 @@
|
||||
{ source: 'home', query: state.query },
|
||||
{ source: 'home', patternType: state.patternType }
|
||||
)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('search', 'submit', {
|
||||
metadata: { source: TELEMETRY_V2_SEARCH_SOURCE_TYPE['home'] },
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@
|
||||
type ContentMatch,
|
||||
} from '$lib/shared'
|
||||
import { SVELTE_LOGGER, SVELTE_TELEMETRY_EVENTS, codeCopiedEvent } from '$lib/telemetry'
|
||||
import { TELEMETRY_V2_RECORDER } from '$lib/telemetry2'
|
||||
import { TELEMETRY_V2_SEARCH_SOURCE_TYPE } from '@sourcegraph/shared/src/search'
|
||||
import Panel from '$lib/wildcard/resizable-panel/Panel.svelte'
|
||||
import PanelGroup from '$lib/wildcard/resizable-panel/PanelGroup.svelte'
|
||||
import PanelResizeHandle from '$lib/wildcard/resizable-panel/PanelResizeHandle.svelte'
|
||||
@ -108,6 +110,7 @@
|
||||
|
||||
onMount(() => {
|
||||
SVELTE_LOGGER.logViewEvent(SVELTE_TELEMETRY_EVENTS.ViewSearchResultsPage)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('search.results', 'view')
|
||||
})
|
||||
|
||||
function loadMore(event: { detail: boolean }) {
|
||||
@ -133,8 +136,14 @@
|
||||
SVELTE_LOGGER.log(...codeCopiedEvent('search-result'))
|
||||
}
|
||||
|
||||
function handleSearchResultClick(): void {
|
||||
function handleSearchResultClick(index: number): void {
|
||||
SVELTE_LOGGER.log(SVELTE_TELEMETRY_EVENTS.SearchResultClick)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('search.result.area', 'click', {
|
||||
metadata: {
|
||||
index,
|
||||
resultsLength: results.length,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function handleSubmit(state: QueryState) {
|
||||
@ -143,6 +152,9 @@
|
||||
{ source: 'nav', query: state.query },
|
||||
{ source: 'nav', patternType: state.patternType }
|
||||
)
|
||||
TELEMETRY_V2_RECORDER.recordEvent('search', 'submit', {
|
||||
metadata: { source: TELEMETRY_V2_SEARCH_SOURCE_TYPE['nav'] },
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -181,15 +193,21 @@
|
||||
2. A11y: Non-interactive element <ol> should not be assigned mouse
|
||||
or keyboard event listeners.
|
||||
-->
|
||||
<ol on:click={handleSearchResultClick} on:copy={handleResultCopy}>
|
||||
<ol on:copy={handleResultCopy}>
|
||||
{#each resultsToShow as result, i}
|
||||
{@const component = getSearchResultComponent(result)}
|
||||
{#if i === resultsToShow.length - 1}
|
||||
<li use:observeIntersection on:intersecting={loadMore}>
|
||||
<li
|
||||
use:observeIntersection
|
||||
on:intersecting={loadMore}
|
||||
on:click={() => handleSearchResultClick(i)}
|
||||
>
|
||||
<svelte:component this={component} {result} />
|
||||
</li>
|
||||
{:else}
|
||||
<li><svelte:component this={component} {result} /></li>
|
||||
<li on:click={() => handleSearchResultClick(i)}>
|
||||
<svelte:component this={component} {result} />
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
</ol>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { FILTERS_URL_KEY } from '@sourcegraph/branded/src/search-ui/results/filters/hooks'
|
||||
import { compatNavigate } from '@sourcegraph/common'
|
||||
import type { SubmitSearchParameters } from '@sourcegraph/shared/src/search'
|
||||
import { type SubmitSearchParameters, TELEMETRY_V2_SEARCH_SOURCE_TYPE } from '@sourcegraph/shared/src/search'
|
||||
import { appendContextFilter } from '@sourcegraph/shared/src/search/query/transformer'
|
||||
import type { TelemetryV2Props } from '@sourcegraph/shared/src/telemetry'
|
||||
import { EVENT_LOGGER } from '@sourcegraph/shared/src/telemetry/web/eventLogger'
|
||||
@ -8,19 +8,6 @@ import { buildSearchURLQuery } from '@sourcegraph/shared/src/util/url'
|
||||
|
||||
import { AGGREGATION_MODE_URL_KEY, AGGREGATION_UI_MODE_URL_KEY } from './results/components/aggregation/constants'
|
||||
|
||||
const v2SearchSourceType: { [key in SubmitSearchParameters['source']]: number } = {
|
||||
home: 1,
|
||||
nav: 2,
|
||||
repo: 3,
|
||||
tree: 4,
|
||||
filter: 5,
|
||||
type: 6,
|
||||
scopePage: 7,
|
||||
communitySearchContextPage: 8,
|
||||
excludedResults: 9,
|
||||
smartSearchDisabled: 10,
|
||||
}
|
||||
|
||||
/**
|
||||
* By default {@link submitSearch} overrides all existing query parameters.
|
||||
* This breaks all functionality that is built on top of URL query params and history
|
||||
@ -85,7 +72,7 @@ export function submitSearch({
|
||||
},
|
||||
{ source, patternType }
|
||||
)
|
||||
telemetryRecorder.recordEvent('search', 'submit', { metadata: { source: v2SearchSourceType[source] } })
|
||||
telemetryRecorder.recordEvent('search', 'submit', { metadata: { source: TELEMETRY_V2_SEARCH_SOURCE_TYPE[source] } })
|
||||
|
||||
const state = {
|
||||
...(typeof location.state === 'object' ? location.state : null),
|
||||
|
||||
@ -1548,6 +1548,9 @@ importers:
|
||||
'@sourcegraph/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../shared
|
||||
'@sourcegraph/telemetry':
|
||||
specifier: ^0.11.0
|
||||
version: 0.11.0
|
||||
'@sourcegraph/web':
|
||||
specifier: workspace:*
|
||||
version: link:../web
|
||||
|
||||
Loading…
Reference in New Issue
Block a user