@grafana/runtime
Version:
Grafana Runtime Library
1 lines • 178 kB
Source Map (JSON)
{"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/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/chromeHeaderHeight.ts","../../src/components/EmbeddedDashboard.tsx","../../src/utils/rbac.ts","../../src/utils/migrationHandler.ts","../../src/components/QueryEditorWithMigration.tsx","../../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 = true` 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 AuthSettings,\n BootData,\n BuildInfo,\n DataSourceInstanceSettings,\n FeatureToggles,\n GrafanaConfig,\n GrafanaTheme,\n GrafanaTheme2,\n LicenseInfo,\n MapLayerOptions,\n OAuthSettings,\n PanelPluginMeta,\n systemDateFormats,\n SystemDateFormatSettings,\n getThemeById,\n AngularMeta,\n PluginLoadingStrategy,\n PluginDependencies,\n PluginExtensions,\n TimeOption,\n} from '@grafana/data';\n\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\nexport interface AzureCloudInfo {\n name: string;\n displayName: string;\n}\n\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\nexport type PreinstalledPlugin = {\n id: string;\n version: string;\n};\n\nexport class GrafanaBootConfig implements GrafanaConfig {\n publicDashboardAccessToken?: string;\n publicDashboardsEnabled = true;\n snapshotEnabled = true;\n datasources: { [str: string]: DataSourceInstanceSettings } = {};\n panels: { [key: string]: PanelPluginMeta } = {};\n apps: Record<string, AppPluginConfig> = {};\n auth: AuthSettings = {};\n minRefreshInterval = '';\n appUrl = '';\n appSubUrl = '';\n namespace = 'default';\n windowTitlePrefix = '';\n buildInfo: 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 | undefined = undefined;\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 | undefined = undefined;\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 customEndpoint: '',\n apiKey: '',\n allInstrumentationsEnabled: false,\n errorInstrumentalizationEnabled: true,\n consoleInstrumentalizationEnabled: false,\n webVitalsInstrumentalizationEnabled: false,\n tracingInstrumentalizationEnabled: false,\n };\n pluginCatalogURL = 'https://grafana.com/grafana/plugins/';\n pluginAdminEnabled = true;\n pluginAdminExternalManageEnabled = false;\n pluginCatalogHiddenPlugins: string[] = [];\n pluginCatalogManagedPlugins: string[] = [];\n pluginCatalogPreinstalledPlugins: PreinstalledPlugin[] = [];\n pluginsCDNBaseURL = '';\n expressionsEnabled = false;\n awsAllowedAuthProviders: string[] = [];\n awsAssumeRoleEnabled = false;\n azure: AzureSettings = {\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 = {\n minInterval: '',\n alertStateHistoryBackend: undefined,\n alertStateHistoryPrimary: undefined,\n recordingRulesEnabled: false,\n defaultRecordingRulesTargetDatasourceUID: 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: undefined;\n googleAnalytics4Id: undefined;\n googleAnalytics4SendManualPageViews = false;\n rudderstackWriteKey: undefined;\n rudderstackDataPlaneUrl: undefined;\n rudderstackSdkUrl: undefined;\n rudderstackConfigUrl: undefined;\n rudderstackIntegrationsUrl: undefined;\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\n tokenExpirationDayLimit: undefined;\n enableFrontendSandboxForPlugins: string[] = [];\n sharedWithMeFolderUID: string | undefined;\n rootFolderUID: string | undefined;\n localFileSystemAvailable: boolean | undefined;\n cloudMigrationIsTarget: boolean | undefined;\n cloudMigrationPollIntervalMs = 2000;\n reportingStaticContext?: Record<string, string>;\n exploreDefaultTimeOffset = '1h';\n exploreHideLogsDownload: boolean | undefined;\n quickRanges?: TimeOption[];\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\n constructor(options: GrafanaBootConfig) {\n this.bootData = options.bootData;\n\n const defaults = {\n datasources: {},\n windowTitlePrefix: 'Grafana - ',\n panels: {},\n playlist_timespan: '1m',\n unsaved_changes_warning: true,\n appUrl: '',\n appSubUrl: '',\n buildInfo: {\n version: '1.0',\n commit: '1',\n env: 'production',\n },\n viewersCanEdit: false,\n disableSanitizeHtml: false,\n };\n\n merge(this, defaults, options);\n\n this.buildInfo = options.buildInfo || defaults.buildInfo;\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 geomapDefaultBaseLayer?: MapLayerOptions<any> | undefined;\n listDashboardScopesEndpoint?: string | undefined;\n listScopesEndpoint?: string | undefined;\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 as any).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 settings: {},\n user: {},\n navTree: [],\n };\n}\n\nconst options = bootData.settings;\noptions.bootData = bootData;\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(options);\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 { 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) {\n return false;\n }\n return extension.type === PluginExtensionTypes.link && ('path' in extension || 'onClick' in extension);\n}\n\nexport function isPluginExtensionComponent(\n extension: PluginExtension | undefined\n): extension is PluginExtensionComponent {\n if (!extension) {\n return false;\n }\n return extension.type === PluginExtensionTypes.component && 'component' in extension;\n}\n\nexport function getLimitedComponentsToRender<Props extends {}>({\n props,\n components,\n limit,\n pluginId,\n}: {\n props: Props;\n components: Array<ComponentTypeWithExtensionMeta<Props>>;\n limit?: number;\n pluginId?: string | string[] | RegExp;\n}) {\n if (!components.length) {\n return null;\n }\n\n const renderedComponents: Array<ComponentTypeWithExtensionMeta<Props>> = [];\n\n for (const Component of components) {\n const { meta } = Component;\n\n if (pluginId && typeof pluginId === 'string' && pluginId !== meta.pluginId) {\n continue;\n }\n\n if (pluginId && Array.isArray(pluginId) && !pluginId.includes(meta.pluginId)) {\n continue;\n }\n\n if (pluginId instanceof RegExp && !pluginId.test(meta.pluginId)) {\n continue;\n }\n\n // If no limit is provided, return all components\n if (limit === undefined) {\n renderedComponents.push(Component);\n continue;\n }\n\n // If a component does not render anything, do not count it in the limit\n if (React.createElement<Props>(Component, props) !== null) {\n renderedComponents.push(Component);\n }\n\n // Stop if we've reached the limit\n if (renderedComponents.length >= limit) {\n break;\n }\n }\n\n return renderedComponents;\n}\n\nexport function renderLimitedComponents<Props extends {}>({\n pr