UNPKG

@grafana/runtime

Version:
1 lines • 189 kB
{"version":3,"file":"index.cjs","sources":["../../src/services/backendSrv.ts","../../src/services/dataSourceSrv.ts","../../src/services/LocationSrv.ts","../../src/services/EchoSrv.ts","../../src/services/templateSrv.ts","../../src/services/live.ts","../../src/config.ts","../../src/services/LocationService.tsx","../../src/services/appEvents.ts","../../src/services/pluginExtensions/usePluginComponent.ts","../../src/services/pluginExtensions/usePluginComponents.ts","../../src/services/pluginExtensions/usePluginLinks.ts","../../src/services/pluginExtensions/usePluginFunctions.ts","../../src/services/navigation/useHelpNavItem.ts","../../src/services/pluginExtensions/getObservablePluginLinks.ts","../../src/services/pluginExtensions/getObservablePluginComponents.ts","../../src/services/pluginExtensions/utils.tsx","../../src/services/user.ts","../../src/services/RuntimeDataSource.ts","../../src/services/ScopesContext.ts","../../src/analytics/types.ts","../../src/utils/plugin.ts","../../src/analytics/utils.ts","../../src/utils/licensing.ts","../../src/utils/logging.ts","../../src/utils/toDataQueryError.ts","../../src/utils/queryResponse.ts","../../src/utils/publicDashboardQueryHandler.ts","../../src/utils/userStorage.tsx","../../src/utils/DataSourceWithBackend.ts","../../src/components/PanelRenderer.tsx","../../src/components/PanelDataErrorView.tsx","../../src/services/QueryRunner.ts","../../src/components/PluginPage.tsx","../../src/components/DataSourcePicker.tsx","../../src/analytics/plugins/eventProperties.ts","../../src/analytics/plugins/usePluginInteractionReporter.ts","../../src/utils/returnToPrevious.ts","../../src/utils/megaMenuOpen.ts","../../src/utils/chromeHeaderHeight.ts","../../src/components/EmbeddedDashboard.tsx","../../src/utils/rbac.ts","../../src/utils/migrationHandler.ts","../../src/components/QueryEditorWithMigration.tsx","../../src/utils/useFavoriteDatasources.ts","../../src/components/FolderPicker.tsx","../../src/services/CorrelationsService.ts"],"sourcesContent":["import { Observable } from 'rxjs';\n\n/**\n * Used to initiate a remote call via the {@link BackendSrv}\n *\n * @public\n */\nexport type BackendSrvRequest = {\n /**\n * Request URL\n */\n url: string;\n\n /**\n * Number of times to retry the remote call if it fails.\n */\n retry?: number;\n\n /**\n * HTTP headers that should be passed along with the remote call.\n * Please have a look at {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API | Fetch API}\n * for supported headers.\n */\n headers?: Record<string, any>;\n\n /**\n * HTTP verb to perform in the remote call GET, POST, PUT etc.\n */\n method?: string;\n\n /**\n * Set to false an success application alert box will not be shown for successful PUT, DELETE, POST requests\n */\n showSuccessAlert?: boolean;\n\n /**\n * Set to false to not show an application alert box for request errors\n */\n showErrorAlert?: boolean;\n\n /**\n * Provided by the initiator to identify a particular remote call. An example\n * of this is when a datasource plugin triggers a query. If the request id already\n * exist the backendSrv will try to cancel and replace the previous call with the\n * new one.\n */\n requestId?: string;\n\n /**\n * Set to to true to not include call in query inspector\n */\n hideFromInspector?: boolean;\n\n /**\n * The data to send\n */\n data?: any;\n\n /**\n * Query params\n */\n params?: Record<string, any>;\n\n /**\n * Define how the response object should be parsed. See:\n *\n * https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data\n *\n * By default values are json parsed from text\n */\n responseType?: 'json' | 'text' | 'arraybuffer' | 'blob';\n\n /**\n * Used to cancel an open connection\n * https://developer.mozilla.org/en-US/docs/Web/API/AbortController\n */\n abortSignal?: AbortSignal;\n\n /**\n * The credentials read-only property of the Request interface indicates whether the user agent should send cookies from the other domain in the case of cross-origin requests.\n */\n credentials?: RequestCredentials;\n\n /**\n * @deprecated withCredentials is deprecated in favor of credentials\n */\n withCredentials?: boolean;\n\n /**\n * Set to true to validate the URL path to prevent path traversal attacks.\n * Use this when constructing URLs from user input.\n */\n validatePath?: boolean;\n};\n\n/**\n * Response for fetch function in {@link BackendSrv}\n *\n * @public\n */\nexport interface FetchResponse<T = any> {\n data: T;\n readonly status: number;\n readonly statusText: string;\n readonly ok: boolean;\n readonly headers: Headers;\n readonly redirected: boolean;\n readonly type: ResponseType;\n readonly url: string;\n readonly config: BackendSrvRequest;\n readonly traceId?: string;\n}\n\n/**\n * Error type for fetch function in {@link BackendSrv}\n *\n * @public\n */\nexport interface FetchErrorDataProps {\n message?: string;\n status?: string;\n error?: string | any;\n}\n\n/**\n * Error type for fetch function in {@link BackendSrv}\n *\n * @public\n */\nexport interface FetchError<T = any> {\n status: number;\n statusText?: string;\n data: T;\n message?: string;\n cancelled?: boolean;\n isHandled?: boolean;\n config: BackendSrvRequest;\n traceId?: string;\n}\n\nexport function isFetchError<T = any>(e: unknown): e is FetchError<T> {\n return typeof e === 'object' && e !== null && 'status' in e && 'data' in e;\n}\n\n/**\n * Used to communicate via http(s) to a remote backend such as the Grafana backend,\n * a datasource etc. The BackendSrv is using the {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API | Fetch API}\n * under the hood to handle all the communication.\n *\n * The request function can be used to perform a remote call by specifying a {@link BackendSrvRequest}.\n * To make the BackendSrv a bit easier to use we have added a couple of shorthand functions that will\n * use default values executing the request.\n *\n * @remarks\n * By default, Grafana displays an error message alert if the remote call fails. To prevent this from\n * happening `showErrorAlert = false` on the options object.\n *\n * @public\n */\nexport interface BackendSrv {\n get<T = any>(url: string, params?: any, requestId?: string, options?: Partial<BackendSrvRequest>): Promise<T>;\n delete<T = unknown>(url: string, data?: unknown, options?: Partial<BackendSrvRequest>): Promise<T>;\n post<T = any>(url: string, data?: unknown, options?: Partial<BackendSrvRequest>): Promise<T>;\n patch<T = any>(url: string, data?: unknown, options?: Partial<BackendSrvRequest>): Promise<T>;\n put<T = any>(url: string, data?: unknown, options?: Partial<BackendSrvRequest>): Promise<T>;\n\n /**\n * @deprecated Use the `.fetch()` function instead. If you prefer to work with a promise\n * wrap the Observable returned by fetch with the lastValueFrom function, or use the get|delete|post|patch|put methods.\n * This method is going to be private from Grafana 10.\n */\n request<T = unknown>(options: BackendSrvRequest): Promise<T>;\n\n /**\n * Special function used to communicate with datasources that will emit core\n * events that the Grafana QueryInspector and QueryEditor is listening for to be able\n * to display datasource query information. Can be skipped by adding `option.silent`\n * when initializing the request.\n *\n * @deprecated Use the fetch function instead\n */\n datasourceRequest<T = unknown>(options: BackendSrvRequest): Promise<FetchResponse<T>>;\n\n /**\n * Observable http request interface\n */\n fetch<T>(options: BackendSrvRequest): Observable<FetchResponse<T>>;\n\n /**\n * Observe each raw chunk in the response. This is useful when reading values from\n * a long living HTTP connection like the kubernetes WATCH command.\n *\n * Each chunk includes the full response headers and the `data` property is filled with the chunk.\n */\n chunked(options: BackendSrvRequest): Observable<FetchResponse<Uint8Array | undefined>>;\n}\n\nlet singletonInstance: BackendSrv;\n\n/**\n * Used during startup by Grafana to set the BackendSrv so it is available\n * via the {@link getBackendSrv} to the rest of the application.\n *\n * @internal\n */\nexport const setBackendSrv = (instance: BackendSrv) => {\n singletonInstance = instance;\n};\n\n/**\n * Used to retrieve the {@link BackendSrv} that can be used to communicate\n * via http(s) to a remote backend such as the Grafana backend, a datasource etc.\n *\n * @public\n */\nexport const getBackendSrv = (): BackendSrv => singletonInstance;\n","import { ScopedVars, DataSourceApi, DataSourceInstanceSettings, DataSourceRef } from '@grafana/data';\n\nimport { RuntimeDataSource } from './RuntimeDataSource';\n\n/**\n * This is the entry point for communicating with a datasource that is added as\n * a plugin (both external and internal). Via this service you will get access\n * to the {@link @grafana/data#DataSourceApi | DataSourceApi} that have a rich API for\n * communicating with the datasource.\n *\n * @public\n */\nexport interface DataSourceSrv {\n /**\n * Returns the requested dataSource. If it cannot be found it rejects the promise.\n * @param ref - The datasource identifier, it can be a name, UID or DataSourceRef (an object with UID),\n * @param scopedVars - variables used to interpolate a templated passed as name.\n */\n get(ref?: DataSourceRef | string | null, scopedVars?: ScopedVars): Promise<DataSourceApi>;\n\n /**\n * Get a list of data sources\n */\n getList(filters?: GetDataSourceListFilters): DataSourceInstanceSettings[];\n\n /**\n * Get settings and plugin metadata by name or uid\n */\n getInstanceSettings(\n ref?: DataSourceRef | string | null,\n scopedVars?: ScopedVars\n ): DataSourceInstanceSettings | undefined;\n\n /**\n * Reloads the DataSourceSrv\n */\n reload(): void;\n\n /**\n * Registers a runtime data source. Make sure your data source uid is unique.\n */\n registerRuntimeDataSource(entry: RuntimeDataSourceRegistration): void;\n}\n\nexport interface RuntimeDataSourceRegistration {\n dataSource: RuntimeDataSource;\n}\n\n/** @public */\nexport interface GetDataSourceListFilters {\n /** Include mixed data source by setting this to true */\n mixed?: boolean;\n\n /** Only return data sources that support metrics response */\n metrics?: boolean;\n\n /** Only return data sources that support tracing response */\n tracing?: boolean;\n\n /** Only return data sources that support logging response */\n logs?: boolean;\n\n /** Only return data sources that support annotations */\n annotations?: boolean;\n\n /** Only filter data sources that support alerting */\n alerting?: boolean;\n\n /**\n * By default only data sources that can be queried will be returned. Meaning they have tracing,\n * metrics, logs or annotations flag set in plugin.json file\n * */\n all?: boolean;\n\n /** Set to true to return dashboard data source */\n dashboard?: boolean;\n\n /** Set to true to return data source variables */\n variables?: boolean;\n\n /** filter list by plugin */\n pluginId?: string;\n\n /** apply a function to filter */\n filter?: (dataSource: DataSourceInstanceSettings) => boolean;\n\n /** Only returns datasources matching the specified types (ie. Loki, Prometheus) */\n type?: string | string[];\n}\n\nlet singletonInstance: DataSourceSrv;\n\n/**\n * Used during startup by Grafana to set the DataSourceSrv so it is available\n * via the {@link getDataSourceSrv} to the rest of the application.\n *\n * @internal\n */\nexport function setDataSourceSrv(instance: DataSourceSrv) {\n singletonInstance = instance;\n}\n\n/**\n * Used to retrieve the {@link DataSourceSrv} that is the entry point for communicating with\n * a datasource that is added as a plugin (both external and internal).\n *\n * @public\n */\nexport function getDataSourceSrv(): DataSourceSrv {\n return singletonInstance;\n}\n","import { UrlQueryMap } from '@grafana/data';\n\n/**\n * @public\n * @deprecated in favor of {@link locationService} and will be removed in Grafana 9\n */\nexport interface LocationUpdate {\n /**\n * Target path where you automatically wants to navigate the user.\n */\n path?: string;\n\n /**\n * Specify this value if you want to add values to the query string of the URL.\n */\n query?: UrlQueryMap;\n\n /**\n * If set to true, the query argument will be added to the existing URL.\n */\n partial?: boolean;\n\n /**\n * Used internally to sync the Redux state from Angular to make sure that the Redux location\n * state is in sync when navigating using the Angular router.\n *\n * @remarks\n * Do not change this unless you are the Angular router.\n *\n * @internal\n */\n routeParams?: UrlQueryMap;\n\n /*\n * If set to true, this will replace URL state (ie. cause no new browser history).\n */\n replace?: boolean;\n}\n\n/**\n * If you need to automatically navigate the user to a new place in the application this should\n * be done via the LocationSrv and it will make sure to update the application state accordingly.\n *\n * @public\n * @deprecated in favor of {@link locationService} and will be removed in Grafana 9\n */\nexport interface LocationSrv {\n update(options: LocationUpdate): void;\n}\n\nlet singletonInstance: LocationSrv;\n\n/**\n * Used during startup by Grafana to set the LocationSrv so it is available\n * via the {@link getLocationSrv} to the rest of the application.\n *\n * @internal\n */\nexport function setLocationSrv(instance: LocationSrv) {\n singletonInstance = instance;\n}\n\n/**\n * Used to retrieve the {@link LocationSrv} that can be used to automatically navigate\n * the user to a new place in Grafana.\n *\n * @public\n * @deprecated in favor of {@link locationService} and will be removed in Grafana 9\n */\nexport function getLocationSrv(): LocationSrv {\n return singletonInstance;\n}\n","/**\n * Describes a size with width/height\n *\n * @public\n */\nexport interface SizeMeta {\n width: number;\n height: number;\n}\n\n/**\n * Describes the meta information that are sent together with each event.\n *\n * @public\n */\nexport interface EchoMeta {\n screenSize: SizeMeta;\n windowSize: SizeMeta;\n userAgent: string;\n url?: string;\n path?: string;\n /**\n * A unique browser session\n */\n sessionId: string;\n /**\n * The current user's username used to login into Grafana e.g. email.\n */\n userLogin: string;\n /**\n * The current user's unique identifier.\n */\n userId: number;\n /**\n * True when user is logged in into Grafana.\n */\n userSignedIn: boolean;\n /**\n * Current user's role\n */\n orgRole: string | '';\n /**\n * Current user's org\n */\n orgId: number;\n /**\n * A millisecond epoch\n */\n ts: number;\n /**\n * A highres timestamp since navigation start\n */\n timeSinceNavigationStart: number;\n}\n\n/**\n * Describes echo backends that can be registered to receive of events.\n *\n * @public\n */\nexport interface EchoBackend<T extends EchoEvent = any, O = any> {\n options: O;\n supportedEvents: EchoEventType[];\n flush: () => void;\n addEvent: (event: T) => void;\n}\n\n/**\n * Describes an echo event.\n *\n * @public\n */\nexport interface EchoEvent<T extends EchoEventType = any, P = any> {\n type: EchoEventType;\n /**\n * Event payload containing event specific data.\n */\n payload: P;\n meta: EchoMeta;\n}\n\n/**\n * Supported echo event types that can be sent via the {@link EchoSrv}.\n *\n * @public\n */\nexport enum EchoEventType {\n Performance = 'performance',\n MetaAnalytics = 'meta-analytics',\n Pageview = 'pageview',\n Interaction = 'interaction',\n ExperimentView = 'experimentview',\n GrafanaJavascriptAgent = 'grafana-javascript-agent',\n}\n\n/**\n * Used to send events to all the registered backends. This should be accessed via the\n * {@link getEchoSrv} function. Will, by default, flush events to the backends every\n * 10s or when the flush function is triggered.\n *\n * @public\n */\nexport interface EchoSrv {\n /**\n * Call this to flush current events to the echo backends.\n */\n flush(): void;\n /**\n * Add a new echo backend to the list of backends that will receive events.\n */\n addBackend(backend: EchoBackend): void;\n /**\n * Call this to add event that will be sent to the echo backends upon next\n * flush.\n *\n * @param event - Object containing event information.\n * @param meta - Object that will extend/override the default meta object.\n */\n addEvent<T extends EchoEvent>(event: Omit<T, 'meta'>, meta?: {}): void;\n}\n\nlet singletonInstance: EchoSrv;\n\n/**\n * Used during startup by Grafana to set the EchoSrv so it is available\n * via the {@link getEchoSrv} to the rest of the application.\n *\n * @internal\n */\nexport function setEchoSrv(instance: EchoSrv) {\n // Check if there were any events reported to the FakeEchoSrv (before the main EchoSrv was initialized), and track them\n if (singletonInstance instanceof FakeEchoSrv) {\n for (const item of singletonInstance.buffer) {\n instance.addEvent(item.event, item.meta);\n }\n }\n\n singletonInstance = instance;\n}\n\n/**\n * Used to retrieve the {@link EchoSrv} that can be used to report events to registered\n * echo backends.\n *\n * @public\n */\nexport function getEchoSrv(): EchoSrv {\n if (!singletonInstance) {\n singletonInstance = new FakeEchoSrv();\n }\n\n return singletonInstance;\n}\n\n/**\n * Used to register echo backends that will receive Grafana echo events during application\n * runtime.\n *\n * @public\n */\nexport const registerEchoBackend = (backend: EchoBackend) => {\n getEchoSrv().addBackend(backend);\n};\n\nexport class FakeEchoSrv implements EchoSrv {\n buffer: Array<{ event: Omit<EchoEvent, 'meta'>; meta?: {} | undefined }> = [];\n\n flush(): void {\n this.buffer = [];\n }\n\n addBackend(backend: EchoBackend): void {}\n\n addEvent<T extends EchoEvent>(event: Omit<T, 'meta'>, meta?: {} | undefined): void {\n this.buffer.push({ event, meta });\n }\n}\n","import { ScopedVars, TimeRange, TypedVariableModel } from '@grafana/data';\n\n/**\n * Can be used to gain more information about an interpolation operation\n */\nexport interface VariableInterpolation {\n /** The full matched expression including, example: ${varName.field:regex} */\n match: string;\n /** In the expression ${varName.field:regex} variableName is varName */\n variableName: string;\n /** In the expression ${varName.fields[0].name:regex} the fieldPath is fields[0].name */\n fieldPath?: string;\n /** In the expression ${varName:regex} the regex part is the format */\n format?: string;\n /** The formatted value of the variable expresion. Will equal match when variable not found or scopedVar was undefined or null **/\n value: string;\n // When value === match this will be true, meaning the variable was not found\n found?: boolean;\n}\n\n/**\n * Via the TemplateSrv consumers get access to all the available template variables\n * that can be used within the current active dashboard.\n *\n * For a more in-depth description visit: https://grafana.com/docs/grafana/latest/reference/templating\n * @public\n */\nexport interface TemplateSrv {\n /**\n * List the dashboard variables\n */\n getVariables(): TypedVariableModel[];\n\n /**\n * Replace the values within the target string. See also {@link InterpolateFunction}\n *\n * Note: interpolations array is being mutated by replace function by adding information about variables that\n * have been interpolated during replacement. Variables that were specified in the target but not found in\n * the list of available variables are also added to the array. See {@link VariableInterpolation} for more details.\n *\n * @param {VariableInterpolation[]} interpolations an optional map that is updated with interpolated variables\n */\n replace(\n target?: string,\n scopedVars?: ScopedVars,\n format?: string | Function,\n interpolations?: VariableInterpolation[]\n ): string;\n\n /**\n * Checks if a target contains template variables.\n */\n containsTemplate(target?: string): boolean;\n\n /**\n * Update the current time range to be used when interpolating __from / __to variables.\n */\n updateTimeRange(timeRange: TimeRange): void;\n}\n\nlet singletonInstance: TemplateSrv;\n\n/**\n * Used during startup by Grafana to set the TemplateSrv so it is available\n * via the {@link getTemplateSrv} to the rest of the application.\n *\n * @internal\n */\nexport const setTemplateSrv = (instance: TemplateSrv) => {\n singletonInstance = instance;\n};\n\n/**\n * Used to retrieve the {@link TemplateSrv} that can be used to fetch available\n * template variables.\n *\n * @public\n */\nexport const getTemplateSrv = (): TemplateSrv => singletonInstance;\n","import { Observable } from 'rxjs';\n\nimport {\n DataFrameJSON,\n DataQueryRequest,\n DataQueryResponse,\n LiveChannelAddress,\n LiveChannelEvent,\n LiveChannelPresenceStatus,\n StreamingFrameOptions,\n} from '@grafana/data';\n\n/**\n * @alpha -- experimental\n */\nexport interface LiveDataFilter {\n fields?: string[];\n}\n\n// StreamingFrameAction and StreamingFrameOptions are now in @grafana/data\nexport { StreamingFrameAction, type StreamingFrameOptions } from '@grafana/data';\n\n/**\n * @alpha\n */\nexport interface LiveDataStreamOptions {\n addr: LiveChannelAddress;\n frame?: DataFrameJSON; // initial results\n key?: string;\n buffer?: Partial<StreamingFrameOptions>;\n filter?: LiveDataFilter;\n}\n\n/**\n * @alpha -- experimental: send a normal query request over websockt\n */\nexport interface LiveQueryDataOptions {\n request: DataQueryRequest;\n body: unknown; // processed queries, same as sent to `/api/query/ds`\n}\n\n/**\n * @alpha -- experimental\n */\nexport interface LivePublishOptions {\n /**\n * Publish the data over the websocket instead of the HTTP API.\n *\n * This is not recommended for most use cases.\n *\n * @experimental\n */\n useSocket?: boolean;\n}\n\n/**\n * @alpha -- experimental\n */\nexport interface GrafanaLiveSrv {\n /**\n * Listen for changes to the main service\n */\n getConnectionState(): Observable<boolean>;\n\n /**\n * Watch for messages in a channel\n */\n getStream<T>(address: LiveChannelAddress): Observable<LiveChannelEvent<T>>;\n\n /**\n * Connect to a channel and return results as DataFrames\n */\n getDataStream(options: LiveDataStreamOptions): Observable<DataQueryResponse>;\n\n /**\n * For channels that support presence, this will request the current state from the server.\n *\n * Join and leave messages will be sent to the open stream\n */\n getPresence(address: LiveChannelAddress): Promise<LiveChannelPresenceStatus>;\n\n /**\n * Publish into a channel\n *\n * @alpha -- experimental\n */\n publish(address: LiveChannelAddress, data: unknown, options?: LivePublishOptions): Promise<unknown>;\n}\n\nlet singletonInstance: GrafanaLiveSrv;\n\n/**\n * Used during startup by Grafana to set the GrafanaLiveSrv so it is available\n * via the {@link getGrafanaLiveSrv} to the rest of the application.\n *\n * @internal\n */\nexport const setGrafanaLiveSrv = (instance: GrafanaLiveSrv) => {\n singletonInstance = instance;\n};\n\n/**\n * Used to retrieve the GrafanaLiveSrv that allows you to subscribe to\n * server side events and streams\n *\n * @alpha -- experimental\n */\nexport const getGrafanaLiveSrv = (): GrafanaLiveSrv => singletonInstance;\n","import { merge } from 'lodash';\n\nimport {\n AppPluginConfig as AppPluginConfigGrafanaData,\n AuthSettings,\n AzureSettings as AzureSettingsGrafanaData,\n BootData,\n BuildInfo,\n DataSourceInstanceSettings,\n FeatureToggles,\n GrafanaTheme,\n GrafanaTheme2,\n LicenseInfo,\n MapLayerOptions,\n OAuthSettings,\n PanelPluginMeta,\n PreinstalledPlugin as PreinstalledPluginGrafanaData,\n systemDateFormats,\n SystemDateFormatSettings,\n getThemeById,\n AngularMeta,\n PluginLoadingStrategy,\n PluginDependencies,\n PluginExtensions,\n TimeOption,\n UnifiedAlertingConfig,\n GrafanaConfig,\n CurrentUserDTO,\n} from '@grafana/data';\n\n/**\n * @deprecated Use the type from `@grafana/data`\n */\n// TODO remove in G13\nexport interface AzureSettings {\n cloud?: string;\n clouds?: AzureCloudInfo[];\n managedIdentityEnabled: boolean;\n workloadIdentityEnabled: boolean;\n userIdentityEnabled: boolean;\n userIdentityFallbackCredentialsEnabled: boolean;\n azureEntraPasswordCredentialsEnabled: boolean;\n}\n\n/**\n * @deprecated Use the type from `@grafana/data`\n */\n// TODO remove in G13\nexport interface AzureCloudInfo {\n name: string;\n displayName: string;\n}\n\n/**\n * @deprecated Use the type from `@grafana/data`\n */\n// TODO remove in G13\nexport type AppPluginConfig = {\n id: string;\n path: string;\n version: string;\n preload: boolean;\n angular: AngularMeta;\n loadingStrategy: PluginLoadingStrategy;\n dependencies: PluginDependencies;\n extensions: PluginExtensions;\n moduleHash?: string;\n};\n\n/**\n * @deprecated Use the type from `@grafana/data`\n */\n// TODO remove in G13\nexport type PreinstalledPlugin = {\n id: string;\n version: string;\n};\n\n/**\n * Use to access Grafana config settings in application code.\n * This takes `window.grafanaBootData.settings` as input and returns a config object.\n */\nexport class GrafanaBootConfig {\n publicDashboardAccessToken?: string;\n publicDashboardsEnabled = true;\n snapshotEnabled = true;\n datasources: { [str: string]: DataSourceInstanceSettings } = {};\n panels: { [key: string]: PanelPluginMeta } = {};\n apps: Record<string, AppPluginConfigGrafanaData> = {};\n auth: AuthSettings = {};\n minRefreshInterval = '';\n appUrl = '';\n appSubUrl = '';\n namespace = 'default';\n windowTitlePrefix = 'Grafana - ';\n buildInfo: BuildInfo = {\n version: '1.0',\n commit: '1',\n env: 'production',\n } as BuildInfo;\n bootData: BootData;\n externalUserMngLinkUrl = '';\n externalUserMngLinkName = '';\n externalUserMngInfo = '';\n externalUserMngAnalytics = false;\n externalUserMngAnalyticsParams = '';\n allowOrgCreate = false;\n feedbackLinksEnabled = true;\n disableLoginForm = false;\n defaultDatasource = ''; // UID\n authProxyEnabled = false;\n exploreEnabled = false;\n queryHistoryEnabled = false;\n helpEnabled = false;\n profileEnabled = false;\n newsFeedEnabled = true;\n ldapEnabled = false;\n jwtHeaderName = '';\n jwtUrlLogin = false;\n sigV4AuthEnabled = false;\n azureAuthEnabled = false;\n secureSocksDSProxyEnabled = false;\n samlEnabled = false;\n samlName = '';\n autoAssignOrg = true;\n verifyEmailEnabled = false;\n oauth: OAuthSettings = {};\n rbacEnabled = true;\n disableUserSignUp = false;\n loginHint = '';\n passwordHint = '';\n loginError?: string;\n viewersCanEdit = false;\n disableSanitizeHtml = false;\n trustedTypesDefaultPolicyEnabled = false;\n cspReportOnlyEnabled = false;\n liveEnabled = true;\n liveMessageSizeLimit = 65536;\n /** @deprecated Use `theme2` instead. */\n theme: GrafanaTheme;\n theme2: GrafanaTheme2;\n featureToggles: FeatureToggles = {};\n anonymousEnabled = false;\n anonymousDeviceLimit?: number;\n licenseInfo: LicenseInfo = {} as LicenseInfo;\n rendererAvailable = false;\n rendererVersion = '';\n rendererDefaultImageWidth = 1000;\n rendererDefaultImageHeight = 500;\n rendererDefaultImageScale = 1;\n supportBundlesEnabled = false;\n http2Enabled = false;\n dateFormats?: SystemDateFormatSettings;\n grafanaJavascriptAgent = {\n enabled: false,\n apiKey: '',\n customEndpoint: '',\n consoleInstrumentalizationEnabled: false,\n performanceInstrumentalizationEnabled: false,\n cspInstrumentalizationEnabled: false,\n tracingInstrumentalizationEnabled: false,\n webVitalsAttribution: false,\n internalLoggerLevel: 0,\n botFilterEnabled: false,\n };\n pluginCatalogURL = 'https://grafana.com/grafana/plugins/';\n pluginAdminEnabled = true;\n pluginAdminExternalManageEnabled = false;\n pluginCatalogHiddenPlugins: string[] = [];\n pluginCatalogManagedPlugins: string[] = [];\n pluginCatalogPreinstalledPlugins: PreinstalledPluginGrafanaData[] = [];\n pluginsCDNBaseURL = '';\n expressionsEnabled = false;\n awsAllowedAuthProviders: string[] = [];\n awsAssumeRoleEnabled = false;\n azure: AzureSettingsGrafanaData = {\n managedIdentityEnabled: false,\n workloadIdentityEnabled: false,\n userIdentityEnabled: false,\n userIdentityFallbackCredentialsEnabled: false,\n azureEntraPasswordCredentialsEnabled: false,\n };\n caching = {\n enabled: false,\n };\n geomapDefaultBaseLayerConfig?: MapLayerOptions;\n geomapDisableCustomBaseLayer?: boolean;\n unifiedAlertingEnabled = false;\n unifiedAlerting: UnifiedAlertingConfig = {\n minInterval: '',\n stateHistory: {\n backend: undefined,\n primary: undefined,\n prometheusTargetDatasourceUID: undefined,\n prometheusMetricName: undefined,\n },\n recordingRulesEnabled: false,\n defaultRecordingRulesTargetDatasourceUID: undefined,\n\n // Backward compatibility fields - populated by backend\n alertStateHistoryBackend: undefined,\n alertStateHistoryPrimary: undefined,\n };\n applicationInsightsConnectionString?: string;\n applicationInsightsEndpointUrl?: string;\n recordedQueries = {\n enabled: true,\n };\n featureHighlights = {\n enabled: false,\n };\n reporting = {\n enabled: true,\n };\n analytics = {\n enabled: true,\n };\n googleAnalyticsId?: string;\n googleAnalytics4Id?: string;\n googleAnalytics4SendManualPageViews = false;\n rudderstackWriteKey?: string;\n rudderstackDataPlaneUrl?: string;\n rudderstackSdkUrl?: string;\n rudderstackConfigUrl?: string;\n rudderstackIntegrationsUrl?: string;\n analyticsConsoleReporting = false;\n dashboardPerformanceMetrics: string[] = [];\n panelSeriesLimit = 0;\n sqlConnectionLimits = {\n maxOpenConns: 100,\n maxIdleConns: 100,\n connMaxLifetime: 14400,\n };\n defaultDatasourceManageAlertsUiToggle = true;\n defaultAllowRecordingRulesTargetAlertsUiToggle = true;\n tokenExpirationDayLimit?: number;\n enableFrontendSandboxForPlugins: string[] = [];\n sharedWithMeFolderUID?: string;\n rootFolderUID?: string;\n localFileSystemAvailable?: boolean;\n cloudMigrationIsTarget?: boolean;\n cloudMigrationPollIntervalMs = 2000;\n reportingStaticContext?: Record<string, string>;\n exploreDefaultTimeOffset = '1h';\n exploreHideLogsDownload?: boolean;\n quickRanges?: TimeOption[];\n pluginRestrictedAPIsAllowList?: Record<string, string[]>;\n pluginRestrictedAPIsBlockList?: Record<string, string[]>;\n\n /**\n * Language used in Grafana's UI. This is after the user's preference (or deteceted locale) is resolved to one of\n * Grafana's supported language.\n */\n language: string | undefined;\n\n /**\n * regionalFormat used in Grafana's UI. Default to 'es-US' in the backend and overwritten when the user select a different one in SharedPreferences.\n * This is the regionalFormat that is used for date formatting and other locale-specific features.\n */\n regionalFormat: string;\n listDashboardScopesEndpoint = '';\n listScopesEndpoint = '';\n\n openFeatureContext: Record<string, unknown> = {};\n\n constructor(\n options: BootData['settings'] & {\n bootData: BootData;\n }\n ) {\n this.bootData = options.bootData;\n\n merge(this, options);\n\n if (this.dateFormats) {\n systemDateFormats.update(this.dateFormats);\n }\n\n overrideFeatureTogglesFromUrl(this);\n overrideFeatureTogglesFromLocalStorage(this);\n\n // Creating theme after applying feature toggle overrides in case we need to toggle anything\n this.theme2 = getThemeById(this.bootData.user.theme);\n this.bootData.user.lightTheme = this.theme2.isLight;\n this.theme = this.theme2.v1;\n this.regionalFormat = options.bootData.user.regionalFormat;\n }\n}\n\n// localstorage key: grafana.featureToggles\n// example value: panelEditor=1,panelInspector=1\nfunction overrideFeatureTogglesFromLocalStorage(config: GrafanaBootConfig) {\n const featureToggles = config.featureToggles;\n const localStorageKey = 'grafana.featureToggles';\n const localStorageValue = window.localStorage.getItem(localStorageKey);\n if (localStorageValue) {\n const features = localStorageValue.split(',');\n for (const feature of features) {\n const [featureName, featureValue] = feature.split('=');\n const toggleState = featureValue === 'true' || featureValue === '1';\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n featureToggles[featureName as keyof FeatureToggles] = toggleState;\n console.log(`Setting feature toggle ${featureName} = ${toggleState} via localstorage`);\n }\n }\n}\n\nfunction overrideFeatureTogglesFromUrl(config: GrafanaBootConfig) {\n if (window.location.href.indexOf('__feature') === -1) {\n return;\n }\n\n const isDevelopment = config.buildInfo.env === 'development';\n\n // Although most flags can not be changed from the URL in production,\n // some of them are safe (and useful!) to change dynamically from the browser URL\n const safeRuntimeFeatureFlags = new Set(['queryServiceFromUI', 'dashboardSceneSolo']);\n\n const params = new URLSearchParams(window.location.search);\n params.forEach((value, key) => {\n if (key.startsWith('__feature.')) {\n const featureToggles = config.featureToggles as Record<string, boolean>;\n const featureName = key.substring(10);\n\n const toggleState = value === 'true' || value === ''; // browser rewrites true as ''\n if (toggleState !== featureToggles[key]) {\n if (isDevelopment || safeRuntimeFeatureFlags.has(featureName)) {\n featureToggles[featureName] = toggleState;\n console.log(`Setting feature toggle ${featureName} = ${toggleState} via url`);\n } else {\n console.log(`Unable to change feature toggle ${featureName} via url in production.`);\n }\n }\n }\n });\n}\n\nlet bootData = window.grafanaBootData;\n\nif (!bootData) {\n if (process.env.NODE_ENV !== 'test') {\n console.error('window.grafanaBootData was not set by the time config was initialized');\n }\n\n bootData = {\n assets: {\n dark: '',\n light: '',\n },\n settings: {} as GrafanaConfig,\n user: {} as CurrentUserDTO,\n navTree: [],\n };\n}\n\n/**\n * Use this to access the {@link GrafanaBootConfig} for the current running Grafana instance.\n *\n * @public\n */\nexport const config = new GrafanaBootConfig({\n ...bootData.settings,\n // need to separately include bootData here\n // this allows people to access the user object on config.bootData.user and maintains backwards compatibility\n // TODO expose a user object (similar to `GrafanaBootConfig`) and deprecate this recursive bootData\n bootData,\n});\n","import * as H from 'history';\nimport React, { useContext } from 'react';\nimport { BehaviorSubject, Observable } from 'rxjs';\n\nimport { deprecationWarning, UrlQueryMap, urlUtil } from '@grafana/data';\nimport { attachDebugger, createLogger } from '@grafana/ui';\n\nimport { config } from '../config';\n\nimport { LocationUpdate } from './LocationSrv';\n\n/**\n * @public\n * A wrapper to help work with browser location and history\n */\nexport interface LocationService {\n partial: (query: Record<string, any>, replace?: boolean) => void;\n push: (location: H.Path | H.LocationDescriptor<any>) => void;\n replace: (location: H.Path | H.LocationDescriptor<any>) => void;\n reload: () => void;\n getLocation: () => H.Location;\n getHistory: () => H.History;\n getSearch: () => URLSearchParams;\n getSearchObject: () => UrlQueryMap;\n getLocationObservable: () => Observable<H.Location>;\n\n /**\n * This is from the old LocationSrv interface\n * @deprecated use partial, push or replace instead */\n update: (update: LocationUpdate) => void;\n}\n\n/** @internal */\nexport class HistoryWrapper implements LocationService {\n private readonly history: H.History;\n private locationObservable: BehaviorSubject<H.Location>;\n\n constructor(history?: H.History) {\n // If no history passed create an in memory one if being called from test\n this.history =\n history ||\n (process.env.NODE_ENV === 'test'\n ? H.createMemoryHistory({ initialEntries: ['/'] })\n : H.createBrowserHistory({ basename: config.appSubUrl ?? '/' }));\n\n this.locationObservable = new BehaviorSubject(this.history.location);\n\n this.history.listen((location) => {\n this.locationObservable.next(location);\n });\n\n this.partial = this.partial.bind(this);\n this.push = this.push.bind(this);\n this.replace = this.replace.bind(this);\n this.getSearch = this.getSearch.bind(this);\n this.getHistory = this.getHistory.bind(this);\n this.getLocation = this.getLocation.bind(this);\n }\n\n getLocationObservable() {\n return this.locationObservable.asObservable();\n }\n\n getHistory() {\n return this.history;\n }\n\n getSearch() {\n return new URLSearchParams(this.history.location.search);\n }\n\n partial(query: Record<string, any>, replace?: boolean) {\n const currentLocation = this.history.location;\n const newQuery = this.getSearchObject();\n\n for (const key in query) {\n // removing params with null | undefined\n if (query[key] === null || query[key] === undefined) {\n delete newQuery[key];\n } else {\n newQuery[key] = query[key];\n }\n }\n\n const updatedUrl = urlUtil.renderUrl(currentLocation.pathname, newQuery);\n\n if (replace) {\n this.history.replace(updatedUrl, this.history.location.state);\n } else {\n this.history.push(updatedUrl, this.history.location.state);\n }\n }\n\n push(location: H.Path | H.LocationDescriptor) {\n this.history.push(location);\n }\n\n replace(location: H.Path | H.LocationDescriptor) {\n this.history.replace(location);\n }\n\n reload() {\n const prevState = (this.history.location.state as any)?.routeReloadCounter;\n this.history.replace({\n ...this.history.location,\n state: { routeReloadCounter: prevState ? prevState + 1 : 1 },\n });\n }\n\n getLocation() {\n return this.history.location;\n }\n\n getSearchObject() {\n return locationSearchToObject(this.history.location.search);\n }\n\n /** @deprecated use partial, push or replace instead */\n update(options: LocationUpdate) {\n deprecationWarning('LocationSrv', 'update', 'partial, push or replace');\n if (options.partial && options.query) {\n this.partial(options.query, options.partial);\n } else {\n const newLocation: H.LocationDescriptor = {\n pathname: options.path,\n };\n if (options.query) {\n newLocation.search = urlUtil.toUrlParams(options.query);\n }\n if (options.replace) {\n this.replace(newLocation);\n } else {\n this.push(newLocation);\n }\n }\n }\n}\n\n/**\n * @public\n * Parses a location search string to an object\n * */\nexport function locationSearchToObject(search: string | number): UrlQueryMap {\n let queryString = typeof search === 'number' ? String(search) : search;\n\n if (queryString.length > 0) {\n if (queryString.startsWith('?')) {\n return urlUtil.parseKeyValue(queryString.substring(1));\n }\n return urlUtil.parseKeyValue(queryString);\n }\n\n return {};\n}\n\n/**\n * @public\n */\nexport let locationService: LocationService = new HistoryWrapper();\n\n/**\n * Used for tests only\n * @internal\n */\nexport const setLocationService = (location: LocationService) => {\n if (process.env.NODE_ENV !== 'test') {\n throw new Error('locationService can be only overriden in test environment');\n }\n locationService = location;\n};\n\nconst navigationLog = createLogger('Router');\n\n/** @internal */\nexport const navigationLogger = navigationLog.logger;\n\n// For debugging purposes the location service is attached to global _debug variable\nattachDebugger('location', locationService, navigationLog);\n\n// Simple context so the location service can be used without being a singleton\nconst LocationServiceContext = React.createContext<LocationService | undefined>(undefined);\n\nexport function useLocationService(): LocationService {\n const service = useContext(LocationServiceContext);\n if (!service) {\n throw new Error('useLocationService must be used within a LocationServiceProvider');\n }\n return service;\n}\n\nexport const LocationServiceProvider: React.FC<{ service: LocationService; children: React.ReactNode }> = ({\n service,\n children,\n}) => {\n return <LocationServiceContext.Provider value={service}>{children}</LocationServiceContext.Provider>;\n};\n","import { BusEventBase, BusEventWithPayload, EventBus, GrafanaTheme2, PanelModel, TimeRange } from '@grafana/data';\n\n/**\n * Called when a dashboard is refreshed\n *\n * @public\n */\nexport class RefreshEvent extends BusEventBase {\n static type = 'refresh';\n}\n\n/**\n * Called when the theme settings change\n *\n * @public\n */\nexport class ThemeChangedEvent extends BusEventWithPayload<GrafanaTheme2> {\n static type = 'theme-changed';\n}\n\n/**\n * Called when time range is updated\n *\n * @public\n */\nexport class TimeRangeUpdatedEvent extends BusEventWithPayload<TimeRange> {\n static type = 'time-range-updated';\n}\n\n/**\n * Called to copy a panel JSON into local storage\n *\n * @public\n */\nexport class CopyPanelEvent extends BusEventWithPayload<PanelModel> {\n static type = 'copy-panel';\n}\n\n// Internal singleton instance\nlet singletonInstance: EventBus;\n\n/**\n * Used during startup by Grafana to set the setAppEvents so it is available\n * via the {@link setAppEvents} to the rest of the application.\n *\n * @internal\n */\nexport function setAppEvents(instance: EventBus) {\n singletonInstance = instance;\n}\n\n/**\n * Used to retrieve an event bus that manages application level events\n *\n * @public\n */\nexport function getAppEvents(): EventBus {\n return singletonInstance;\n}\n","export type UsePluginComponent<Props extends object = {}> = (componentId: string) => UsePluginComponentResult<Props>;\n\nexport type UsePluginComponentResult<Props = {}> = {\n component: React.ComponentType<Props> | undefined | null;\n isLoading: boolean;\n};\n\nlet singleton: UsePluginComponent | undefined;\n\nexport function setPluginComponentHook(hook: UsePluginComponent): void {\n // We allow overriding the registry in tests\n if (singleton && process.env.NODE_ENV !== 'test') {\n throw new Error('setPluginComponentHook() function should only be called once, when Grafana is starting.');\n }\n singleton = hook;\n}\n\nexport function usePluginComponent<Props extends object = {}>(componentId: string): UsePluginComponentResult<Props> {\n if (!singleton) {\n throw new Error('setPluginComponentHook(options) can only be used after the Grafana instance has started.');\n }\n return singleton(componentId) as UsePluginComponentResult<Props>;\n}\n","import { type ComponentTypeWithExtensionMeta } from '@grafana/data';\n\nexport type UsePluginComponentsOptions = {\n extensionPointId: string;\n limitPerPlugin?: number;\n};\n\nexport type UsePluginComponentsResult<Props = {}> = {\n components: Array<ComponentTypeWithExtensionMeta<Props>>;\n isLoading: boolean;\n};\n\nexport type UsePluginComponents<Props extends object = {}> = (\n options: UsePluginComponentsOptions\n) => UsePluginComponentsResult<Props>;\n\nlet singleton: UsePluginComponents | undefined;\n\nexport function setPluginComponentsHook(hook: UsePluginComponents): void {\n // We allow overriding the hook in tests\n if (singleton && process.env.NODE_ENV !== 'test') {\n throw new Error('setPluginComponentsHook() function should only be called once, when Grafana is starting.');\n }\n singleton = hook;\n}\n\nexport function usePluginComponents<Props extends object = {}>(\n options: UsePluginComponentsOptions\n): UsePluginComponentsResult<Props> {\n if (!singleton) {\n throw new Error('setPluginComponentsHook(options) can only be used after the Grafana instance has started.');\n }\n return singleton(options) as UsePluginComponentsResult<Props>;\n}\n","import { PluginExtensionLink } from '@grafana/data';\n\nexport type UsePluginLinksOptions = {\n extensionPointId: string;\n context?: object | Record<string | symbol, unknown>;\n limitPerPlugin?: number;\n};\n\nexport type UsePluginLinksResult = {\n isLoading: boolean;\n links: PluginExtensionLink[];\n};\n\nexport type UsePluginLinks = (options: UsePluginLinksOptions) => UsePluginLinksResult;\n\nlet singleton: UsePluginLinks | undefined;\n\nexport function setPluginLinksHook(hook: UsePluginLinks): void {\n // We allow overriding the registry in tests\n if (singleton && process.env.NODE_ENV !== 'test') {\n throw new Error('setPluginLinksHook() function should only be called once, when Grafana is starting.');\n }\n singleton = hook;\n}\n\nexport function usePluginLinks(options: UsePluginLinksOptions): UsePluginLinksResult {\n if (!singleton) {\n throw new Error('setPluginLinksHook(options) can only be used after the Grafana instance has started.');\n }\n return singleton(options);\n}\n","import { PluginExtensionFunction } from '@grafana/data';\n\nexport type UsePluginFunctionsOptions = {\n extensionPointId: string;\n limitPerPlugin?: number;\n};\n\nexport type UsePluginFunctionsResult<Signature> = {\n isLoading: boolean;\n functions: Array<PluginExtensionFunction<Signature>>;\n};\n\nexport type UsePluginFunctions<T> = (options: UsePluginFunctionsOptions) => UsePluginFunctionsResult<T>;\n\nlet singleton: UsePluginFunctions<unknown> | undefined;\n\nexport function setPluginFunctionsHook(hook: UsePluginFunctions<unknown>): void {\n // We allow overriding the registry in tests\n if (singleton && process.env.NODE_ENV !== 'test') {\n throw new Error('setUsePluginFunctionsHook() function should only be called once, when Grafana is starting.');\n }\n singleton = hook;\n}\n\nexport function usePluginFunctions<T>(options: UsePluginFunctionsOptions): UsePluginFunctionsResult<T> {\n if (!singleton) {\n throw new Error('usePluginFunctions(options) can only be used after the Grafana instance has started.');\n }\n return singleton(options) as UsePluginFunctionsResult<T>;\n}\n","import { NavModelItem } from '@grafana/data';\n\nexport type UseHelpNavItem = () => NavModelItem | undefined;\n\nlet singleton: UseHelpNavItem | undefined;\n\nexport function setHelpNavItemHook(hook: UseHelpNavItem): void {\n // We allow overriding the registry in tests\n if (singleton && process.env.NODE_ENV !== 'test') {\n throw new Error('setHelpNavItemHook() function should only be called once, when Grafana is starting.');\n }\n singleton = hook;\n}\n\nexport function useHelpNavItem(): NavModelItem | undefined {\n if (!singleton) {\n throw new Error('useHelpNavItem() can only be used after the Grafana instance has started.');\n }\n return singleton();\n}\n","import { Observable } from 'rxjs';\n\nimport { PluginExtensionLink } from '@grafana/data';\n\ntype GetObservablePluginLinksOptions = {\n context?: object | Record<string | symbol, unknown>;\n extensionPointId: string;\n limitPerPlugin?: number;\n};\n\nexport type GetObservablePluginLinks = (options: GetObservablePluginLinksOptions) => Observable<PluginExtensionLink[]>;\n\nlet singleton: GetObservablePluginLinks | undefined;\n\nexport function setGetObservablePluginLinks(fn: GetObservablePluginLinks): void {\n // We allow overriding the registry in tests\n if (singleton && process.env.NODE_ENV !== 'test') {\n throw new Error('setGetObservablePluginLinks() function should only be called once, when Grafana is starting.');\n }\n\n singleton = fn;\n}\n\nexport function getObservablePluginLinks(options: GetObservablePluginLinksOptions): Observable<PluginExtensionLink[]> {\n if (!singleton) {\n throw new Error('getObservablePluginLinks() can only be used after the Grafana instance has started.');\n }\n\n return singleton(options);\n}\n","import { Observable } from 'rxjs';\n\nimport { PluginExtensionComponent } from '@grafana/data';\n\ntype GetObservablePluginComponentsOptions = {\n context?: object | Record<string, unknown>;\n extensionPointId: string;\n limitPerPlugin?: number;\n};\n\nexport type GetObservablePluginComponents = (\n options: GetObservablePluginComponentsOptions\n) => Observable<PluginExtensionComponent[]>;\n\nlet singleton: GetObservablePluginComponents | undefined;\n\nexport function setGetObservablePluginComponents(fn: GetObservablePluginComponents): void {\n // We allow overriding the registry in tests\n if (singleton && process.env.NODE_ENV !== 'test') {\n throw new Error(\n 'setGetObservablePluginComponents() function should only be called once, when Grafana is starting.'\n );\n }\n\n singleton = fn;\n}\n\nexport function getObservablePluginComponents(\n options: GetObservablePluginComponentsOptions\n): Observable<PluginExtensionComponent[]> {\n if (!singleton) {\n throw new Error('getObservablePluginComponents() can only be used after the Grafana instance has started.');\n }\n\n return singleton(options);\n}\n","import React from 'react';\n\nimport {\n ComponentTypeWithExtensionMeta,\n type PluginExtension,\n type PluginExtensionComponent,\n type PluginExtensionLink,\n PluginExtensionTypes,\n} from '@grafana/data';\n\nexport function isPluginExtensionLink(extension: PluginExtension | undefined): extension is PluginExtensionLink {\n if (!extension