From 6796e66fb8be0b7528e1e64f933e1e7cff581e6c Mon Sep 17 00:00:00 2001 From: Joey <90795735+joey-grafana@users.noreply.github.com> Date: Tue, 16 Jan 2024 12:46:15 +0000 Subject: [PATCH] Pyroscope: Add standalone build (#80222) * Pyroscope standalone build * Fix for tests * Add missing packages * Remove import * Update trace to profiles * Update test --- .betterer.results | 3 + .eslintrc | 2 + .golangci.toml | 2 + package.json | 1 + .../x/GrafanaPyroscopeDataQuery_types.gen.ts | 2 +- .../api/plugins/data/expectedListResp.json | 6 +- .../standalone/datasource.go | 51 +++++++++++ .../standalone/main.go | 23 +++++ .../TraceToProfilesSettings.tsx | 20 ++--- .../SpanDetail/SpanFlameGraph.tsx | 5 +- .../app/features/plugins/built_in_plugins.ts | 2 +- .../grafana-pyroscope-datasource/CHANGELOG.md | 1 + .../datasource.ts | 3 +- .../grafana-pyroscope-datasource/package.json | 49 +++++++++++ .../grafana-pyroscope-datasource/plugin.json | 7 +- .../tsconfig.json | 4 + .../grafana-pyroscope-datasource/utils.ts | 84 +++++++++++++++++++ .../webpack.config.ts | 15 ++++ yarn.lock | 58 +++++++++++++ 19 files changed, 316 insertions(+), 22 deletions(-) create mode 100644 pkg/tsdb/grafana-pyroscope-datasource/standalone/datasource.go create mode 100644 pkg/tsdb/grafana-pyroscope-datasource/standalone/main.go create mode 100644 public/app/plugins/datasource/grafana-pyroscope-datasource/CHANGELOG.md create mode 100644 public/app/plugins/datasource/grafana-pyroscope-datasource/package.json create mode 100644 public/app/plugins/datasource/grafana-pyroscope-datasource/tsconfig.json create mode 100644 public/app/plugins/datasource/grafana-pyroscope-datasource/utils.ts create mode 100644 public/app/plugins/datasource/grafana-pyroscope-datasource/webpack.config.ts diff --git a/.betterer.results b/.betterer.results index 83dbdd6ada5..d1d169aa96e 100644 --- a/.betterer.results +++ b/.betterer.results @@ -4972,6 +4972,9 @@ exports[`better eslint`] = { [0, 0, 0, "Styles should be written using objects.", "0"], [0, 0, 0, "Styles should be written using objects.", "1"] ], + "public/app/plugins/datasource/grafana-pyroscope-datasource/utils.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"] + ], "public/app/plugins/datasource/grafana-testdata-datasource/QueryEditor.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Do not use any type assertions.", "1"], diff --git a/.eslintrc b/.eslintrc index c0854901c88..33b751f17f0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -96,6 +96,8 @@ }, { "files": [ + "public/app/plugins/datasource/grafana-pyroscope-datasource/*.{ts,tsx}", + "public/app/plugins/datasource/grafana-pyroscope-datasource/**/*.{ts,tsx}", "public/app/plugins/datasource/grafana-testdata-datasource/*.{ts,tsx}", "public/app/plugins/datasource/grafana-testdata-datasource/**/*.{ts,tsx}", "public/app/plugins/datasource/azuremonitor/*.{ts,tsx}", diff --git a/.golangci.toml b/.golangci.toml index ad6a6ecb380..67a9c683e9f 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -55,6 +55,8 @@ deny = [ { pkg = "github.com/grafana/grafana/pkg/tsdb/intervalv2", desc = "Core plugins are not allowed to depend on Grafana core packages" }, ] files = [ + "**/pkg/tsdb/grafana-pyroscope-datasource/*", + "**/pkg/tsdb/grafana-pyroscope-datasource/**/*", "**/pkg/tsdb/grafana-testdata-datasource/*", "**/pkg/tsdb/grafana-testdata-datasource/**/*", "**/pkg/tsdb/azuremonitor/*", diff --git a/package.json b/package.json index 3bbdcde63f0..ae0807e7c40 100644 --- a/package.json +++ b/package.json @@ -244,6 +244,7 @@ "@floating-ui/react": "0.26.6", "@glideapps/glide-data-grid": "^5.2.1", "@grafana-plugins/grafana-azure-monitor-datasource": "workspace:*", + "@grafana-plugins/grafana-pyroscope-datasource": "workspace:*", "@grafana-plugins/grafana-testdata-datasource": "workspace:*", "@grafana-plugins/parca": "workspace:*", "@grafana-plugins/tempo": "workspace:*", diff --git a/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts index 8860cd51842..2be0dcface6 100644 --- a/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/grafanapyroscope/dataquery/x/GrafanaPyroscopeDataQuery_types.gen.ts @@ -11,7 +11,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "10.4.0-pre"; +export const pluginVersion = "%VERSION%"; export type PyroscopeQueryType = ('metrics' | 'profile' | 'both'); diff --git a/pkg/tests/api/plugins/data/expectedListResp.json b/pkg/tests/api/plugins/data/expectedListResp.json index 5f2ceae5fdf..93c276966b7 100644 --- a/pkg/tests/api/plugins/data/expectedListResp.json +++ b/pkg/tests/api/plugins/data/expectedListResp.json @@ -160,7 +160,7 @@ "updated": "" }, "dependencies": { - "grafanaDependency": ">=10.3.0", + "grafanaDependency": "\u003e=10.3.0", "grafanaVersion": "*", "plugins": [] }, @@ -688,7 +688,7 @@ "updated": "" }, "dependencies": { - "grafanaDependency": "", + "grafanaDependency": "\u003e=10.3.0-0", "grafanaVersion": "*", "plugins": [] }, @@ -1896,4 +1896,4 @@ "signatureOrg": "", "angularDetected": false } -] \ No newline at end of file +] diff --git a/pkg/tsdb/grafana-pyroscope-datasource/standalone/datasource.go b/pkg/tsdb/grafana-pyroscope-datasource/standalone/datasource.go new file mode 100644 index 00000000000..fc00c805d31 --- /dev/null +++ b/pkg/tsdb/grafana-pyroscope-datasource/standalone/datasource.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" + pyroscope "github.com/grafana/grafana/pkg/tsdb/grafana-pyroscope-datasource" +) + +var ( + _ backend.QueryDataHandler = (*Datasource)(nil) + _ backend.CheckHealthHandler = (*Datasource)(nil) + _ backend.CallResourceHandler = (*Datasource)(nil) + _ backend.StreamHandler = (*Datasource)(nil) +) + +func NewDatasource(context.Context, backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return &Datasource{ + Service: pyroscope.ProvideService(httpclient.NewProvider()), + }, nil +} + +type Datasource struct { + Service *pyroscope.Service +} + +func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { + return d.Service.QueryData(ctx, req) +} + +func (d *Datasource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + return d.Service.CallResource(ctx, req, sender) +} + +func (d *Datasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { + return d.Service.CheckHealth(ctx, req) +} + +func (d *Datasource) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) { + return d.Service.SubscribeStream(ctx, req) +} + +func (d *Datasource) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) { + return d.Service.PublishStream(ctx, req) +} + +func (d *Datasource) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error { + return d.Service.RunStream(ctx, req, sender) +} diff --git a/pkg/tsdb/grafana-pyroscope-datasource/standalone/main.go b/pkg/tsdb/grafana-pyroscope-datasource/standalone/main.go new file mode 100644 index 00000000000..345f2493715 --- /dev/null +++ b/pkg/tsdb/grafana-pyroscope-datasource/standalone/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "os" + + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" +) + +func main() { + // Start listening to requests sent from Grafana. This call is blocking so + // it won't finish until Grafana shuts down the process or the plugin choose + // to exit by itself using os.Exit. Manage automatically manages life cycle + // of datasource instances. It accepts datasource instance factory as first + // argument. This factory will be automatically called on incoming request + // from Grafana to create different instances of SampleDatasource (per datasource + // ID). When datasource configuration changed Dispose method will be called and + // new datasource instance created using NewSampleDatasource factory. + if err := datasource.Manage("grafana-pyroscope-datasource", NewDatasource, datasource.ManageOpts{}); err != nil { + log.DefaultLogger.Error(err.Error()) + os.Exit(1) + } +} diff --git a/public/app/core/components/TraceToProfiles/TraceToProfilesSettings.tsx b/public/app/core/components/TraceToProfiles/TraceToProfilesSettings.tsx index 880da29f195..c5381ceb6c6 100644 --- a/public/app/core/components/TraceToProfiles/TraceToProfilesSettings.tsx +++ b/public/app/core/components/TraceToProfiles/TraceToProfilesSettings.tsx @@ -9,11 +9,10 @@ import { updateDatasourcePluginJsonDataOption, } from '@grafana/data'; import { ConfigDescriptionLink, ConfigSection } from '@grafana/experimental'; -import { getDataSourceSrv } from '@grafana/runtime'; +import { DataSourceWithBackend, getDataSourceSrv } from '@grafana/runtime'; import { InlineField, InlineFieldRow, Input, InlineSwitch } from '@grafana/ui'; import { DataSourcePicker } from 'app/features/datasources/components/picker/DataSourcePicker'; import { ProfileTypesCascader } from 'app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/ProfileTypesCascader'; -import { PyroscopeDataSource } from 'app/plugins/datasource/grafana-pyroscope-datasource/datasource'; import { ProfileTypeMessage } from 'app/plugins/datasource/grafana-pyroscope-datasource/types'; import { TagMappingInput } from '../TraceToLogs/TagMappingInput'; @@ -47,20 +46,19 @@ export function TraceToProfilesSettings({ options, onOptionsChange }: Props) { return await getDataSourceSrv().get(options.jsonData.tracesToProfiles?.datasourceUid); }, [options.jsonData.tracesToProfiles?.datasourceUid]); - useEffect(() => { + const { value: pTypes } = useAsync(async () => { if ( - dataSource && - dataSource instanceof PyroscopeDataSource && + dataSource instanceof DataSourceWithBackend && supportedDataSourceTypes.includes(dataSource.type) && dataSource.uid === options.jsonData.tracesToProfiles?.datasourceUid ) { - dataSource.getAllProfileTypes().then((profileTypes) => { - setProfileTypes(profileTypes); - }); - } else { - setProfileTypes([]); + return await dataSource?.getResource('profileTypes'); } - }, [dataSource, onOptionsChange, options, supportedDataSourceTypes]); + }, [dataSource]); + + useEffect(() => { + setProfileTypes(pTypes ?? []); + }, [pTypes]); return (
diff --git a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx index 7f91ea24592..631fef72849 100644 --- a/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx +++ b/public/app/features/explore/TraceView/components/TraceTimelineViewer/SpanDetail/SpanFlameGraph.tsx @@ -13,12 +13,11 @@ import { TimeZone, } from '@grafana/data'; import { FlameGraph } from '@grafana/flamegraph'; -import { config, getTemplateSrv } from '@grafana/runtime'; +import { config, DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime'; import { useStyles2 } from '@grafana/ui'; import { TraceToProfilesOptions } from 'app/core/components/TraceToProfiles/TraceToProfilesSettings'; import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; import { PyroscopeQueryType } from 'app/plugins/datasource/grafana-pyroscope-datasource/dataquery.gen'; -import { PyroscopeDataSource } from 'app/plugins/datasource/grafana-pyroscope-datasource/datasource'; import { Query } from 'app/plugins/datasource/grafana-pyroscope-datasource/types'; import { @@ -79,7 +78,7 @@ export default function SpanFlameGraph(props: SpanFlameGraphProps) { const getFlameGraphData = async (request: DataQueryRequest, datasourceUid: string) => { const ds = await getDatasourceSrv().get(datasourceUid); - if (ds instanceof PyroscopeDataSource) { + if (ds instanceof DataSourceWithBackend) { const result = await lastValueFrom(ds.query(request)); const frame = result.data.find((x: DataFrame) => { return x.name === 'response'; diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 6b08e910458..f0d9857e419 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -37,7 +37,7 @@ const tempoPlugin = async () => await import(/* webpackChunkName: "tempoPlugin" const alertmanagerPlugin = async () => await import(/* webpackChunkName: "alertmanagerPlugin" */ 'app/plugins/datasource/alertmanager/module'); const pyroscopePlugin = async () => - await import(/* webpackChunkName: "pyroscopePlugin" */ 'app/plugins/datasource/grafana-pyroscope-datasource/module'); + await import(/* webpackChunkName: "pyroscopePlugin" */ '@grafana-plugins/grafana-pyroscope-datasource/module'); const parcaPlugin = async () => await import(/* webpackChunkName: "parcaPlugin" */ '@grafana-plugins/parca/module'); import * as alertGroupsPanel from 'app/plugins/panel/alertGroups/module'; diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/CHANGELOG.md b/public/app/plugins/datasource/grafana-pyroscope-datasource/CHANGELOG.md new file mode 100644 index 00000000000..825c32f0d03 --- /dev/null +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/CHANGELOG.md @@ -0,0 +1 @@ +# Changelog diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/datasource.ts b/public/app/plugins/datasource/grafana-pyroscope-datasource/datasource.ts index efcc5cb97cb..460f32b10fe 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/datasource.ts +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/datasource.ts @@ -11,11 +11,10 @@ import { } from '@grafana/data'; import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime'; -import { extractLabelMatchers, toPromLikeExpr } from '../prometheus/language_utils'; - import { VariableSupport } from './VariableSupport'; import { defaultGrafanaPyroscope, defaultPyroscopeQueryType } from './dataquery.gen'; import { PyroscopeDataSourceOptions, Query, ProfileTypeMessage } from './types'; +import { extractLabelMatchers, toPromLikeExpr } from './utils'; export class PyroscopeDataSource extends DataSourceWithBackend { constructor( diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json new file mode 100644 index 00000000000..f47a58f03b2 --- /dev/null +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/package.json @@ -0,0 +1,49 @@ +{ + "name": "@grafana-plugins/grafana-pyroscope-datasource", + "description": "Continuous profiling for analysis of CPU and memory usage, down to the line number and throughout time. Saving infrastructure cost, improving performance, and increasing reliability.", + "private": true, + "version": "10.4.0-pre", + "dependencies": { + "@emotion/css": "11.11.2", + "@grafana/data": "10.4.0-pre", + "@grafana/runtime": "10.4.0-pre", + "@grafana/schema": "10.4.0-pre", + "@grafana/ui": "10.4.0-pre", + "fast-deep-equal": "^3.1.3", + "lodash": "4.17.21", + "monaco-editor": "0.34.0", + "prismjs": "1.29.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-use": "17.4.0", + "rxjs": "7.8.1", + "tslib": "2.6.0" + }, + "devDependencies": { + "@grafana/plugin-configs": "10.4.0-pre", + "@testing-library/jest-dom": "6.1.4", + "@testing-library/react": "14.0.0", + "@testing-library/user-event": "14.5.1", + "@types/jest": "29.5.4", + "@types/lodash": "4.14.195", + "@types/prismjs": "1.26.0", + "@types/react": "18.2.15", + "@types/react-dom": "18.2.7", + "@types/testing-library__jest-dom": "5.14.8", + "css-loader": "6.8.1", + "jest": "29.7.0", + "style-loader": "3.3.3", + "ts-node": "10.9.1", + "typescript": "5.2.2", + "webpack": "5.89.0" + }, + "peerDependencies": { + "@grafana/runtime": "*" + }, + "scripts": { + "build": "webpack -c ./webpack.config.ts --env production", + "build:commit": "webpack -c ./webpack.config.ts --env production --env commit=$(git rev-parse --short HEAD)", + "dev": "webpack -w -c ./webpack.config.ts --env development" + }, + "packageManager": "yarn@3.6.0" +} diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/plugin.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/plugin.json index ba90841a3e8..9aef1503eda 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/plugin.json +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/plugin.json @@ -2,6 +2,7 @@ "type": "datasource", "name": "Grafana Pyroscope", "id": "grafana-pyroscope-datasource", + "executable": "gpx_grafana-pyroscope-datasource", "aliasIDs": ["phlare"], "category": "profiling", @@ -29,6 +30,10 @@ "name": "GitHub Project", "url": "https://github.com/grafana/pyroscope" } - ] + ], + "version": "%VERSION%" + }, + "dependencies": { + "grafanaDependency": ">=10.3.0-0" } } diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/tsconfig.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/tsconfig.json new file mode 100644 index 00000000000..7daf2ee8aba --- /dev/null +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@grafana/plugin-configs/tsconfig.json", + "include": ["."] +} diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/utils.ts b/public/app/plugins/datasource/grafana-pyroscope-datasource/utils.ts new file mode 100644 index 00000000000..f811fbe4ebc --- /dev/null +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/utils.ts @@ -0,0 +1,84 @@ +import { invert } from 'lodash'; +import { Token } from 'prismjs'; + +import { AbstractLabelMatcher, AbstractLabelOperator, AbstractQuery } from '@grafana/data'; + +export function extractLabelMatchers(tokens: Array): AbstractLabelMatcher[] { + const labelMatchers: AbstractLabelMatcher[] = []; + + for (const token of tokens) { + if (!(token instanceof Token)) { + continue; + } + + if (token.type === 'context-labels') { + let labelKey = ''; + let labelValue = ''; + let labelOperator = ''; + + const contentTokens = Array.isArray(token.content) ? token.content : [token.content]; + + for (let currentToken of contentTokens) { + if (typeof currentToken === 'string') { + let currentStr: string; + currentStr = currentToken; + if (currentStr === '=' || currentStr === '!=' || currentStr === '=~' || currentStr === '!~') { + labelOperator = currentStr; + } + } else if (currentToken instanceof Token) { + switch (currentToken.type) { + case 'label-key': + labelKey = getMaybeTokenStringContent(currentToken); + break; + case 'label-value': + labelValue = getMaybeTokenStringContent(currentToken); + labelValue = labelValue.substring(1, labelValue.length - 1); + const labelComparator = FromPromLikeMap[labelOperator]; + if (labelComparator) { + labelMatchers.push({ name: labelKey, operator: labelComparator, value: labelValue }); + } + break; + } + } + } + } + } + + return labelMatchers; +} + +export function toPromLikeExpr(labelBasedQuery: AbstractQuery): string { + const expr = labelBasedQuery.labelMatchers + .map((selector: AbstractLabelMatcher) => { + const operator = ToPromLikeMap[selector.operator]; + if (operator) { + return `${selector.name}${operator}"${selector.value}"`; + } else { + return ''; + } + }) + .filter((e: string) => e !== '') + .join(', '); + + return expr ? `{${expr}}` : ''; +} + +function getMaybeTokenStringContent(token: Token): string { + if (typeof token.content === 'string') { + return token.content; + } + + return ''; +} + +const FromPromLikeMap: Record = { + '=': AbstractLabelOperator.Equal, + '!=': AbstractLabelOperator.NotEqual, + '=~': AbstractLabelOperator.EqualRegEx, + '!~': AbstractLabelOperator.NotEqualRegEx, +}; + +const ToPromLikeMap: Record = invert(FromPromLikeMap) as Record< + AbstractLabelOperator, + string +>; diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/webpack.config.ts b/public/app/plugins/datasource/grafana-pyroscope-datasource/webpack.config.ts new file mode 100644 index 00000000000..8e03497ddb2 --- /dev/null +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/webpack.config.ts @@ -0,0 +1,15 @@ +import config from '@grafana/plugin-configs/webpack.config'; + +const configWithFallback = async (env: Record) => { + const response = await config(env); + if (response !== undefined && response.resolve !== undefined) { + response.resolve.fallback = { + ...response.resolve.fallback, + stream: false, + string_decoder: false, + }; + } + return response; +}; + +export default configWithFallback; diff --git a/yarn.lock b/yarn.lock index 6a07b08ce0f..7b8b1b50fb0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2970,6 +2970,45 @@ __metadata: languageName: unknown linkType: soft +"@grafana-plugins/grafana-pyroscope-datasource@workspace:*, @grafana-plugins/grafana-pyroscope-datasource@workspace:public/app/plugins/datasource/grafana-pyroscope-datasource": + version: 0.0.0-use.local + resolution: "@grafana-plugins/grafana-pyroscope-datasource@workspace:public/app/plugins/datasource/grafana-pyroscope-datasource" + dependencies: + "@emotion/css": "npm:11.11.2" + "@grafana/data": "npm:10.4.0-pre" + "@grafana/plugin-configs": "npm:10.4.0-pre" + "@grafana/runtime": "npm:10.4.0-pre" + "@grafana/schema": "npm:10.4.0-pre" + "@grafana/ui": "npm:10.4.0-pre" + "@testing-library/jest-dom": "npm:6.1.4" + "@testing-library/react": "npm:14.0.0" + "@testing-library/user-event": "npm:14.5.1" + "@types/jest": "npm:29.5.4" + "@types/lodash": "npm:4.14.195" + "@types/prismjs": "npm:1.26.0" + "@types/react": "npm:18.2.15" + "@types/react-dom": "npm:18.2.7" + "@types/testing-library__jest-dom": "npm:5.14.8" + css-loader: "npm:6.8.1" + fast-deep-equal: "npm:^3.1.3" + jest: "npm:29.7.0" + lodash: "npm:4.17.21" + monaco-editor: "npm:0.34.0" + prismjs: "npm:1.29.0" + react: "npm:18.2.0" + react-dom: "npm:18.2.0" + react-use: "npm:17.4.0" + rxjs: "npm:7.8.1" + style-loader: "npm:3.3.3" + ts-node: "npm:10.9.1" + tslib: "npm:2.6.0" + typescript: "npm:5.2.2" + webpack: "npm:5.89.0" + peerDependencies: + "@grafana/runtime": "*" + languageName: unknown + linkType: soft + "@grafana-plugins/grafana-testdata-datasource@workspace:*, @grafana-plugins/grafana-testdata-datasource@workspace:public/app/plugins/datasource/grafana-testdata-datasource": version: 0.0.0-use.local resolution: "@grafana-plugins/grafana-testdata-datasource@workspace:public/app/plugins/datasource/grafana-testdata-datasource" @@ -7943,6 +7982,15 @@ __metadata: languageName: node linkType: hard +"@testing-library/user-event@npm:14.5.1": + version: 14.5.1 + resolution: "@testing-library/user-event@npm:14.5.1" + peerDependencies: + "@testing-library/dom": ">=7.21.4" + checksum: 696e1328c230b0a7063a41d82b5350c6be926696106809a4d79d446d190ff56bebb850fe564ff0952cb74ae81e59a6f10534a88ecbb3792083271a249e04e728 + languageName: node + linkType: hard + "@testing-library/user-event@npm:14.5.2": version: 14.5.2 resolution: "@testing-library/user-event@npm:14.5.2" @@ -9415,6 +9463,15 @@ __metadata: languageName: node linkType: hard +"@types/testing-library__jest-dom@npm:5.14.8": + version: 5.14.8 + resolution: "@types/testing-library__jest-dom@npm:5.14.8" + dependencies: + "@types/jest": "npm:*" + checksum: 26d768b3de5c71ecef683aa0e968ec2bb7f4626d78718132fa5a9c2064f9237836135e2551aa048c728407760d51d985a9d58a14528f2679a35d18d5ca200f08 + languageName: node + linkType: hard + "@types/testing-library__jest-dom@npm:5.14.9": version: 5.14.9 resolution: "@types/testing-library__jest-dom@npm:5.14.9" @@ -17143,6 +17200,7 @@ __metadata: "@floating-ui/react": "npm:0.26.6" "@glideapps/glide-data-grid": "npm:^5.2.1" "@grafana-plugins/grafana-azure-monitor-datasource": "workspace:*" + "@grafana-plugins/grafana-pyroscope-datasource": "workspace:*" "@grafana-plugins/grafana-testdata-datasource": "workspace:*" "@grafana-plugins/parca": "workspace:*" "@grafana-plugins/tempo": "workspace:*"