Merge pull request #31055 from secustor/feat/add-devtools-extension

feat(devtools): support custom tabs using extensions
This commit is contained in:
Fredrik Adelöw
2026-01-20 15:45:10 +01:00
committed by GitHub
28 changed files with 641 additions and 34 deletions
+9
View File
@@ -0,0 +1,9 @@
---
'@backstage/plugin-catalog-unprocessed-entities': patch
'@backstage/plugin-devtools-react': minor
'@backstage/plugin-devtools': patch
'@backstage/plugin-devtools-backend': patch
'@backstage/plugin-devtools-common': patch
---
Add support for adding `unprocessed-entities` and other tabs to `devtools` when using the New Frontend system
+5
View File
@@ -114,6 +114,11 @@ app:
# - entity-content:azure-devops/pull-requests
# - entity-content:azure-devops/git-tags
# Enable the catalog-unprocessed-entities tab in devtools
- devtools-content:catalog-unprocessed-entities: true
# Disable the catalog-unprocessed-entities element outside devtools
- page:catalog-unprocessed-entities: false
# scmAuthExtension: >-
# createScmAuthExtension({
# id: 'apis.scmAuth.addons.ghe',
+1
View File
@@ -57,6 +57,7 @@
"@backstage/plugin-catalog-import": "workspace:^",
"@backstage/plugin-catalog-react": "workspace:^",
"@backstage/plugin-catalog-unprocessed-entities": "workspace:^",
"@backstage/plugin-devtools": "workspace:^",
"@backstage/plugin-home": "workspace:^",
"@backstage/plugin-kubernetes": "workspace:^",
"@backstage/plugin-kubernetes-cluster": "workspace:^",
+9
View File
@@ -45,6 +45,8 @@ import { convertLegacyPageExtension } from '@backstage/core-compat-api';
import { convertLegacyEntityContentExtension } from '@backstage/plugin-catalog-react/alpha';
import { pluginInfoResolver } from './pluginInfoResolver';
import { appModuleNav } from './modules/appModuleNav';
import devtoolsPlugin from '@backstage/plugin-devtools/alpha';
import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha';
/*
@@ -116,6 +118,11 @@ const notFoundErrorPageModule = createFrontendModule({
extensions: [notFoundErrorPage],
});
const devtoolsPluginUnprocessed = createFrontendModule({
pluginId: 'catalog-unprocessed-entities',
extensions: [unprocessedEntitiesDevToolsContent],
});
const collectedLegacyPlugins = convertLegacyAppRoot(
<FlatRoutes>
<Route path="/catalog-import" element={<CatalogImportPage />} />
@@ -133,6 +140,8 @@ const app = createApp({
notFoundErrorPageModule,
appModuleNav,
customHomePageModule,
devtoolsPlugin,
devtoolsPluginUnprocessed,
...collectedLegacyPlugins,
],
advanced: {
+19 -1
View File
@@ -52,18 +52,36 @@ Import `catalogUnprocessedEntitiesPlugin` in your `App.tsx` and add it to your a
```typescript
import catalogUnprocessedEntitiesPlugin from '@backstage/plugin-catalog-unprocessed-entities';
import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha';
// ...
// Optionally add unprocessed entities route to devtools
const devtoolsPluginUnprocessed = createFrontendModule({
pluginId: 'catalog-unprocessed-entities',
extensions: [unprocessedEntitiesDevToolsContent],
});
export const app = createApp({
features: [
// ...
catalogUnprocessedEntitiesPlugin,
// Optionally add unprocessed entities route to devtools
devtoolsPluginUnprocessed,
devtoolsPlugin, // devtools plugin needs to be added too, if autodiscover is disabled
// ...
],
});
```
```yaml
app:
extensions:
# Enable the catalog-unprocessed-entities tab in devtools
- devtools-content:catalog-unprocessed-entities: true
# Disable the catalog-unprocessed-entities element outside devtools including the sidebar
- page:catalog-unprocessed-entities: false
```
## Customization
If you want to use the provided endpoints in a different way, you can use the ApiRef doing the following:
@@ -50,11 +50,13 @@
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/core-compat-api": "workspace:^",
"@backstage/core-components": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^",
"@backstage/plugin-devtools-react": "workspace:^",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.60",
@@ -6,6 +6,7 @@
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { DevToolsContentBlueprintParams } from '@backstage/plugin-devtools-react';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
@@ -89,5 +90,32 @@ const _default: OverridableFrontendPlugin<
>;
export default _default;
// @alpha
export const unprocessedEntitiesDevToolsContent: OverridableExtensionDefinition<{
kind: 'devtools-content';
name: undefined;
config: {
path: string | undefined;
title: string | undefined;
};
configInput: {
title?: string | undefined;
path?: string | undefined;
};
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<string, 'core.title', {}>;
inputs: {};
params: DevToolsContentBlueprintParams;
}>;
// (No @packageDocumentation comment for this package)
```
@@ -0,0 +1,36 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react';
/**
* DevTools content for catalog unprocessed entities.
*
* @alpha
*/
export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make(
{
disabled: true,
params: {
path: 'unprocessed-entities',
title: 'Unprocessed Entities',
loader: () =>
import('../components/UnprocessedEntities').then(m => (
<m.UnprocessedEntitiesContent />
)),
},
},
);
@@ -15,3 +15,4 @@
*/
export { default } from './plugin';
export { unprocessedEntitiesDevToolsContent } from './devToolsContent';
+2 -1
View File
@@ -7,7 +7,8 @@
"pluginPackages": [
"@backstage/plugin-devtools",
"@backstage/plugin-devtools-backend",
"@backstage/plugin-devtools-common"
"@backstage/plugin-devtools-common",
"@backstage/plugin-devtools-react"
]
},
"publishConfig": {
+2 -1
View File
@@ -8,7 +8,8 @@
"pluginPackages": [
"@backstage/plugin-devtools",
"@backstage/plugin-devtools-backend",
"@backstage/plugin-devtools-common"
"@backstage/plugin-devtools-common",
"@backstage/plugin-devtools-react"
]
},
"publishConfig": {
+1
View File
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
+5
View File
@@ -0,0 +1,5 @@
# @backstage/plugin-devtools-react
Welcome to the web library package for the `devtools` plugin!
_This plugin was created through the Backstage CLI_
+10
View File
@@ -0,0 +1,10 @@
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: backstage-plugin-devtools-react
title: '@backstage/plugin-devtools-react'
description: Web library for the devtools plugin
spec:
lifecycle: experimental
type: backstage-web-library
owner: maintainers
+67
View File
@@ -0,0 +1,67 @@
{
"name": "@backstage/plugin-devtools-react",
"version": "0.0.0",
"description": "Web library for the devtools plugin",
"backstage": {
"role": "web-library",
"pluginId": "devtools",
"pluginPackages": [
"@backstage/plugin-devtools",
"@backstage/plugin-devtools-backend",
"@backstage/plugin-devtools-common",
"@backstage/plugin-devtools-react"
]
},
"publishConfig": {
"access": "public",
"main": "dist/index.esm.js",
"types": "dist/index.d.ts"
},
"repository": {
"type": "git",
"url": "https://github.com/backstage/backstage",
"directory": "plugins/devtools-react"
},
"license": "Apache-2.0",
"sideEffects": false,
"main": "src/index.ts",
"types": "src/index.ts",
"files": [
"dist"
],
"scripts": {
"build": "backstage-cli package build",
"clean": "backstage-cli package clean",
"lint": "backstage-cli package lint",
"prepack": "backstage-cli package prepack",
"postpack": "backstage-cli package postpack",
"start": "backstage-cli package start",
"test": "backstage-cli package test"
},
"dependencies": {
"@backstage/core-plugin-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@material-ui/core": "^4.9.13"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.0.0",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"react-router-dom": "^6.3.0"
},
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0",
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0",
"react-router-dom": "^6.3.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
}
+50
View File
@@ -0,0 +1,50 @@
## API Report File for "@backstage/plugin-devtools-react"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { RouteRef } from '@backstage/frontend-plugin-api';
// @public
export const DevToolsContentBlueprint: ExtensionBlueprint<{
kind: 'devtools-content';
params: DevToolsContentBlueprintParams;
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>
| ExtensionDataRef<string, 'core.title', {}>;
inputs: {};
config: {
path: string | undefined;
title: string | undefined;
};
configInput: {
title?: string | undefined;
path?: string | undefined;
};
dataRefs: never;
}>;
// @public
export interface DevToolsContentBlueprintParams {
// (undocumented)
loader: () => Promise<JSX_2.Element>;
// (undocumented)
path: string;
// (undocumented)
routeRef?: RouteRef;
// (undocumented)
title: string;
}
```
@@ -0,0 +1,87 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
coreExtensionData,
createExtensionBlueprint,
ExtensionBoundary,
RouteRef,
} from '@backstage/frontend-plugin-api';
import { JSX } from 'react';
/**
* Parameters for creating a DevTools route extension
* @public
*/
export interface DevToolsContentBlueprintParams {
path: string;
title: string;
loader: () => Promise<JSX.Element>;
routeRef?: RouteRef;
}
/**
* Extension blueprint for creating DevTools content pages (appearing as tabs)
*
* @example
* ```tsx
* const myDevToolsContent = DevToolsContentBlueprint.make({
* {
* params: {
* path: 'my-dev-tools',
* title: 'My DevTools',
* loader: () =>
* import('../components/MyDevTools').then(m =>
* compatWrapper(<m.MyDevTools />),
* ),
* },
* },
* });
* ```
* @public
*/
export const DevToolsContentBlueprint = createExtensionBlueprint({
kind: 'devtools-content',
attachTo: { id: 'page:devtools', input: 'contents' },
output: [
coreExtensionData.reactElement,
coreExtensionData.routePath,
coreExtensionData.routeRef.optional(),
coreExtensionData.title,
],
config: {
schema: {
path: z => z.string().optional(),
title: z => z.string().optional(),
},
},
*factory(params: DevToolsContentBlueprintParams, { node, config }) {
const path = config.path ?? params.path;
const title = config.title ?? params.title;
yield coreExtensionData.reactElement(
ExtensionBoundary.lazy(node, params.loader),
);
yield coreExtensionData.routePath(path);
yield coreExtensionData.title(title);
if (params.routeRef) {
yield coreExtensionData.routeRef(params.routeRef);
}
},
});
+25
View File
@@ -0,0 +1,25 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Web library for the devtools plugin.
*
* @packageDocumentation
*/
export {
type DevToolsContentBlueprintParams,
DevToolsContentBlueprint,
} from './devToolsContentBlueprint';
+33
View File
@@ -166,6 +166,39 @@ You can also add tabs to show content from other plugins that fit well with the
#### Catalog Unprocessed Entities Tab
##### New Frontend System
Create an extension and/or load a 3rd party extension to add additional tabs.
```shell
yarn --cwd plugins/<your-plugin> add @backstage/plugin-devtools-react
```
```tsx
import { DevToolsContentBlueprint } from '@backstage/plugin-devtools-react';
export const unprocessedEntitiesDevToolsContent = DevToolsContentBlueprint.make(
{
disabled: true,
params: {
path: 'unprocessed-entities',
title: 'Unprocessed Entities',
loader: () =>
import('../components/UnprocessedEntities').then(m => (
<m.UnprocessedEntitiesContent />
)),
},
},
);
const appFeature = createFrontendModule({
pluginId: 'catalog-unprocessed-entities',
extensions: [unprocessedEntitiesDevToolsContent],
});
```
##### Old System
Here's how to add the Catalog Unprocessed Entities tab:
1. Install and setup the [Catalog Unprocessed Entities plugin](https://github.com/backstage/backstage/tree/master/plugins/catalog-unprocessed-entities) as per its documentation
+3 -1
View File
@@ -7,7 +7,8 @@
"pluginPackages": [
"@backstage/plugin-devtools",
"@backstage/plugin-devtools-backend",
"@backstage/plugin-devtools-common"
"@backstage/plugin-devtools-common",
"@backstage/plugin-devtools-react"
]
},
"publishConfig": {
@@ -58,6 +59,7 @@
"@backstage/errors": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/plugin-devtools-common": "workspace:^",
"@backstage/plugin-devtools-react": "workspace:^",
"@backstage/plugin-permission-react": "workspace:^",
"@material-ui/core": "^4.9.13",
"@material-ui/icons": "^4.9.1",
+22 -3
View File
@@ -6,8 +6,10 @@
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
@@ -59,8 +61,6 @@ const _default: OverridableFrontendPlugin<
};
}>;
'page:devtools': OverridableExtensionDefinition<{
kind: 'page';
name: undefined;
config: {
path: string | undefined;
};
@@ -77,7 +77,26 @@ const _default: OverridableFrontendPlugin<
optional: true;
}
>;
inputs: {};
inputs: {
contents: ExtensionInput<
| ConfigurableExtensionDataRef<string, 'core.title', {}>
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
| ConfigurableExtensionDataRef<
RouteRef_2<AnyRouteRefParams>,
'core.routing.ref',
{
optional: true;
}
>,
{
singleton: false;
optional: true;
}
>;
};
kind: 'page';
name: undefined;
params: {
defaultPath?: [Error: `Use the 'path' param instead`];
path: string;
+18 -1
View File
@@ -6,6 +6,7 @@
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { ElementType } from 'react';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import { ReactElement } from 'react';
import { ReactNode } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { TabProps } from '@material-ui/core/Tab';
@@ -28,7 +29,23 @@ export type DevToolsLayoutProps = {
};
// @public (undocumented)
export const DevToolsPage: () => JSX_2.Element;
export const DevToolsPage: ({ contents }: DevToolsPageProps) => JSX_2.Element;
// @public (undocumented)
export interface DevToolsPageContent {
// (undocumented)
children: ReactElement;
// (undocumented)
path: string;
// (undocumented)
title: string;
}
// @public (undocumented)
export interface DevToolsPageProps {
// (undocumented)
contents?: DevToolsPageContent[];
}
// @public (undocumented)
export const devToolsPlugin: BackstagePlugin<
+31 -6
View File
@@ -21,6 +21,8 @@ import {
ApiBlueprint,
PageBlueprint,
NavItemBlueprint,
createExtensionInput,
coreExtensionData,
} from '@backstage/frontend-plugin-api';
import { devToolsApiRef, DevToolsClient } from '../api';
@@ -42,12 +44,35 @@ export const devToolsApi = ApiBlueprint.make({
});
/** @alpha */
export const devToolsPage = PageBlueprint.make({
params: {
path: '/devtools',
routeRef: rootRouteRef,
loader: () =>
import('../components/DevToolsPage').then(m => <m.DevToolsPage />),
export const devToolsPage = PageBlueprint.makeWithOverrides({
inputs: {
contents: createExtensionInput(
[
coreExtensionData.reactElement,
coreExtensionData.routePath,
coreExtensionData.routeRef.optional(),
coreExtensionData.title,
],
{
optional: true,
},
),
},
factory(originalFactory, { inputs }) {
return originalFactory({
path: '/devtools',
routeRef: rootRouteRef,
loader: () => {
const contents = inputs.contents.map(content => ({
path: content.get(coreExtensionData.routePath),
title: content.get(coreExtensionData.title),
children: content.get(coreExtensionData.reactElement),
}));
return import('../components/DevToolsPage').then(m => (
<m.DevToolsPage contents={contents} />
));
},
});
},
});
@@ -18,15 +18,17 @@ import {
devToolsConfigReadPermission,
devToolsInfoReadPermission,
} from '@backstage/plugin-devtools-common';
import { ConfigContent } from '../Content';
import { devToolsTaskSchedulerReadPermission } from '@backstage/plugin-devtools-common/alpha';
import { ConfigContent } from '../Content/ConfigContent';
import { DevToolsLayout } from '../DevToolsLayout';
import { InfoContent } from '../Content/InfoContent';
import { InfoContent } from '../Content';
import { RequirePermission } from '@backstage/plugin-permission-react';
import { ScheduledTasksContent } from '../Content/ScheduledTasksContent';
import { DevToolsPageProps } from '../DevToolsPage';
/** @public */
export const DefaultDevToolsPage = () => (
export const DefaultDevToolsPage = ({ contents }: DevToolsPageProps) => (
<DevToolsLayout>
<DevToolsLayout.Route path="info" title="Info">
<RequirePermission permission={devToolsInfoReadPermission}>
@@ -43,5 +45,14 @@ export const DefaultDevToolsPage = () => (
<ScheduledTasksContent />
</RequirePermission>
</DevToolsLayout.Route>
{contents?.map((content, index) => (
<DevToolsLayout.Route
key={`extension-${index}`}
path={content.path}
title={content.title}
>
{content.children}
</DevToolsLayout.Route>
))}
</DevToolsLayout>
);
@@ -16,9 +16,26 @@
import { useOutlet } from 'react-router-dom';
import { DefaultDevToolsPage } from '../DefaultDevToolsPage';
import { ReactElement } from 'react';
export const DevToolsPage = () => {
/**
@public
*/
export interface DevToolsPageProps {
contents?: DevToolsPageContent[];
}
/**
@public
*/
export interface DevToolsPageContent {
title: string;
path: string;
children: ReactElement;
}
export const DevToolsPage = ({ contents }: DevToolsPageProps) => {
const outlet = useOutlet();
return <>{outlet || <DefaultDevToolsPage />}</>;
return <>{outlet || <DefaultDevToolsPage contents={contents} />}</>;
};
@@ -14,4 +14,8 @@
* limitations under the License.
*/
export { DevToolsPage } from './DevToolsPage';
export {
DevToolsPage,
type DevToolsPageProps,
type DevToolsPageContent,
} from './DevToolsPage';
+4
View File
@@ -16,3 +16,7 @@
export * from './Content';
export * from './DevToolsLayout';
export {
type DevToolsPageProps,
type DevToolsPageContent,
} from './DevToolsPage';
+133 -14
View File
@@ -5444,12 +5444,14 @@ __metadata:
resolution: "@backstage/plugin-catalog-unprocessed-entities@workspace:plugins/catalog-unprocessed-entities"
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/core-compat-api": "workspace:^"
"@backstage/core-components": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/dev-utils": "workspace:^"
"@backstage/errors": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/plugin-catalog-unprocessed-entities-common": "workspace:^"
"@backstage/plugin-devtools-react": "workspace:^"
"@material-ui/core": "npm:^4.9.13"
"@material-ui/icons": "npm:^4.9.1"
"@material-ui/lab": "npm:^4.0.0-alpha.60"
@@ -5611,6 +5613,32 @@ __metadata:
languageName: unknown
linkType: soft
"@backstage/plugin-devtools-react@workspace:^, @backstage/plugin-devtools-react@workspace:plugins/devtools-react":
version: 0.0.0-use.local
resolution: "@backstage/plugin-devtools-react@workspace:plugins/devtools-react"
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/test-utils": "workspace:^"
"@material-ui/core": "npm:^4.9.13"
"@testing-library/jest-dom": "npm:^6.0.0"
"@testing-library/react": "npm:^14.0.0"
"@types/react": "npm:^18.0.0"
react: "npm:^18.0.2"
react-dom: "npm:^18.0.2"
react-router-dom: "npm:^6.3.0"
peerDependencies:
"@types/react": ^17.0.0 || ^18.0.0
react: ^17.0.0 || ^18.0.0
react-dom: ^17.0.0 || ^18.0.0
react-router-dom: ^6.3.0
peerDependenciesMeta:
"@types/react":
optional: true
languageName: unknown
linkType: soft
"@backstage/plugin-devtools@workspace:^, @backstage/plugin-devtools@workspace:plugins/devtools":
version: 0.0.0-use.local
resolution: "@backstage/plugin-devtools@workspace:plugins/devtools"
@@ -5623,6 +5651,7 @@ __metadata:
"@backstage/errors": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/plugin-devtools-common": "workspace:^"
"@backstage/plugin-devtools-react": "workspace:^"
"@backstage/plugin-permission-react": "workspace:^"
"@material-ui/core": "npm:^4.9.13"
"@material-ui/icons": "npm:^4.9.1"
@@ -20169,6 +20198,22 @@ __metadata:
languageName: node
linkType: hard
"@testing-library/dom@npm:^9.0.0":
version: 9.3.4
resolution: "@testing-library/dom@npm:9.3.4"
dependencies:
"@babel/code-frame": "npm:^7.10.4"
"@babel/runtime": "npm:^7.12.5"
"@types/aria-query": "npm:^5.0.1"
aria-query: "npm:5.1.3"
chalk: "npm:^4.1.0"
dom-accessibility-api: "npm:^0.5.9"
lz-string: "npm:^1.5.0"
pretty-format: "npm:^27.0.2"
checksum: 10/510da752ea76f4a10a0a4e3a77917b0302cf03effe576cd3534cab7e796533ee2b0e9fb6fb11b911a1ebd7c70a0bb6f235bf4f816c9b82b95b8fe0cddfd10975
languageName: node
linkType: hard
"@testing-library/jest-dom@npm:^6.0.0, @testing-library/jest-dom@npm:^6.6.3":
version: 6.9.1
resolution: "@testing-library/jest-dom@npm:6.9.1"
@@ -20205,6 +20250,20 @@ __metadata:
languageName: node
linkType: hard
"@testing-library/react@npm:^14.0.0":
version: 14.3.1
resolution: "@testing-library/react@npm:14.3.1"
dependencies:
"@babel/runtime": "npm:^7.12.5"
"@testing-library/dom": "npm:^9.0.0"
"@types/react-dom": "npm:^18.0.0"
peerDependencies:
react: ^18.0.0
react-dom: ^18.0.0
checksum: 10/83359dcdf9eaf067839f34604e1a181cbc14fc09f3a07672403700fcc6a900c4b8054ad1114fc24b4b9f89d84e2a09e1b7c9afce2306b1d4b4c9e30eb1cb12de
languageName: node
linkType: hard
"@testing-library/react@npm:^16.0.0":
version: 16.3.1
resolution: "@testing-library/react@npm:16.3.1"
@@ -24713,6 +24772,15 @@ __metadata:
languageName: node
linkType: hard
"aria-query@npm:5.1.3":
version: 5.1.3
resolution: "aria-query@npm:5.1.3"
dependencies:
deep-equal: "npm:^2.0.5"
checksum: 10/e5da608a7c4954bfece2d879342b6c218b6b207e2d9e5af270b5e38ef8418f02d122afdc948b68e32649b849a38377785252059090d66fa8081da95d1609c0d2
languageName: node
linkType: hard
"aria-query@npm:5.3.0":
version: 5.3.0
resolution: "aria-query@npm:5.3.0"
@@ -24729,7 +24797,7 @@ __metadata:
languageName: node
linkType: hard
"array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2":
"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1, array-buffer-byte-length@npm:^1.0.2":
version: 1.0.2
resolution: "array-buffer-byte-length@npm:1.0.2"
dependencies:
@@ -25013,6 +25081,20 @@ __metadata:
languageName: node
linkType: hard
"async-function@npm:^1.0.0":
version: 1.0.0
resolution: "async-function@npm:1.0.0"
checksum: 10/1a09379937d846f0ce7614e75071c12826945d4e417db634156bf0e4673c495989302f52186dfa9767a1d9181794554717badd193ca2bbab046ef1da741d8efd
languageName: node
linkType: hard
"async-generator-function@npm:^1.0.0":
version: 1.0.0
resolution: "async-generator-function@npm:1.0.0"
checksum: 10/3d49e7acbeee9e84537f4cb0e0f91893df8eba976759875ae8ee9e3d3c82f6ecdebdb347c2fad9926b92596d93cdfc78ecc988bcdf407e40433e8e8e6fe5d78e
languageName: node
linkType: hard
"async-lock@npm:^1.4.1":
version: 1.4.1
resolution: "async-lock@npm:1.4.1"
@@ -28491,6 +28573,32 @@ __metadata:
languageName: node
linkType: hard
"deep-equal@npm:^2.0.5":
version: 2.2.3
resolution: "deep-equal@npm:2.2.3"
dependencies:
array-buffer-byte-length: "npm:^1.0.0"
call-bind: "npm:^1.0.5"
es-get-iterator: "npm:^1.1.3"
get-intrinsic: "npm:^1.2.2"
is-arguments: "npm:^1.1.1"
is-array-buffer: "npm:^3.0.2"
is-date-object: "npm:^1.0.5"
is-regex: "npm:^1.1.4"
is-shared-array-buffer: "npm:^1.0.2"
isarray: "npm:^2.0.5"
object-is: "npm:^1.1.5"
object-keys: "npm:^1.1.1"
object.assign: "npm:^4.1.4"
regexp.prototype.flags: "npm:^1.5.1"
side-channel: "npm:^1.0.4"
which-boxed-primitive: "npm:^1.0.2"
which-collection: "npm:^1.0.1"
which-typed-array: "npm:^1.1.13"
checksum: 10/1ce49d0b71d0f14d8ef991a742665eccd488dfc9b3cada069d4d7a86291e591c92d2589c832811dea182b4015736b210acaaebce6184be356c1060d176f5a05f
languageName: node
linkType: hard
"deep-equal@npm:~1.0.1":
version: 1.0.1
resolution: "deep-equal@npm:1.0.1"
@@ -29686,7 +29794,7 @@ __metadata:
languageName: node
linkType: hard
"es-get-iterator@npm:^1.0.2":
"es-get-iterator@npm:^1.0.2, es-get-iterator@npm:^1.1.3":
version: 1.1.3
resolution: "es-get-iterator@npm:1.1.3"
dependencies:
@@ -30700,6 +30808,7 @@ __metadata:
"@backstage/plugin-catalog-import": "workspace:^"
"@backstage/plugin-catalog-react": "workspace:^"
"@backstage/plugin-catalog-unprocessed-entities": "workspace:^"
"@backstage/plugin-devtools": "workspace:^"
"@backstage/plugin-home": "workspace:^"
"@backstage/plugin-kubernetes": "workspace:^"
"@backstage/plugin-kubernetes-cluster": "workspace:^"
@@ -32256,6 +32365,13 @@ __metadata:
languageName: node
linkType: hard
"generator-function@npm:^2.0.0":
version: 2.0.1
resolution: "generator-function@npm:2.0.1"
checksum: 10/eb7e7eb896c5433f3d40982b2ccacdb3dd990dd3499f14040e002b5d54572476513be8a2e6f9609f6e41ab29f2c4469307611ddbfc37ff4e46b765c326663805
languageName: node
linkType: hard
"generic-names@npm:^2.0.1":
version: 2.0.1
resolution: "generic-names@npm:2.0.1"
@@ -32303,21 +32419,24 @@ __metadata:
languageName: node
linkType: hard
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0":
version: 1.3.0
resolution: "get-intrinsic@npm:1.3.0"
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.3.0":
version: 1.3.1
resolution: "get-intrinsic@npm:1.3.1"
dependencies:
async-function: "npm:^1.0.0"
async-generator-function: "npm:^1.0.0"
call-bind-apply-helpers: "npm:^1.0.2"
es-define-property: "npm:^1.0.1"
es-errors: "npm:^1.3.0"
es-object-atoms: "npm:^1.1.1"
function-bind: "npm:^1.1.2"
generator-function: "npm:^2.0.0"
get-proto: "npm:^1.0.1"
gopd: "npm:^1.2.0"
has-symbols: "npm:^1.1.0"
hasown: "npm:^2.0.2"
math-intrinsics: "npm:^1.1.0"
checksum: 10/6e9dd920ff054147b6f44cb98104330e87caafae051b6d37b13384a45ba15e71af33c3baeac7cb630a0aaa23142718dcf25b45cfdd86c184c5dcb4e56d953a10
checksum: 10/bb579dda84caa4a3a41611bdd483dade7f00f246f2a7992eb143c5861155290df3fdb48a8406efa3dfb0b434e2c8fafa4eebd469e409d0439247f85fc3fa2cc1
languageName: node
linkType: hard
@@ -34407,7 +34526,7 @@ __metadata:
languageName: node
linkType: hard
"is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5":
"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5":
version: 3.0.5
resolution: "is-array-buffer@npm:3.0.5"
dependencies:
@@ -34880,7 +34999,7 @@ __metadata:
languageName: node
linkType: hard
"is-regex@npm:^1.2.1":
"is-regex@npm:^1.1.4, is-regex@npm:^1.2.1":
version: 1.2.1
resolution: "is-regex@npm:1.2.1"
dependencies:
@@ -34929,7 +35048,7 @@ __metadata:
languageName: node
linkType: hard
"is-shared-array-buffer@npm:^1.0.4":
"is-shared-array-buffer@npm:^1.0.2, is-shared-array-buffer@npm:^1.0.4":
version: 1.0.4
resolution: "is-shared-array-buffer@npm:1.0.4"
dependencies:
@@ -44711,7 +44830,7 @@ __metadata:
languageName: node
linkType: hard
"regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4":
"regexp.prototype.flags@npm:^1.5.1, regexp.prototype.flags@npm:^1.5.3, regexp.prototype.flags@npm:^1.5.4":
version: 1.5.4
resolution: "regexp.prototype.flags@npm:1.5.4"
dependencies:
@@ -46279,7 +46398,7 @@ __metadata:
languageName: node
linkType: hard
"side-channel@npm:^1.1.0":
"side-channel@npm:^1.0.4, side-channel@npm:^1.1.0":
version: 1.1.0
resolution: "side-channel@npm:1.1.0"
dependencies:
@@ -50503,7 +50622,7 @@ __metadata:
languageName: node
linkType: hard
"which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1":
"which-boxed-primitive@npm:^1.0.2, which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1":
version: 1.1.1
resolution: "which-boxed-primitive@npm:1.1.1"
dependencies:
@@ -50537,7 +50656,7 @@ __metadata:
languageName: node
linkType: hard
"which-collection@npm:^1.0.2":
"which-collection@npm:^1.0.1, which-collection@npm:^1.0.2":
version: 1.0.2
resolution: "which-collection@npm:1.0.2"
dependencies:
@@ -50559,7 +50678,7 @@ __metadata:
languageName: node
linkType: hard
"which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2":
"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.16, which-typed-array@npm:^1.1.19, which-typed-array@npm:^1.1.2":
version: 1.1.19
resolution: "which-typed-array@npm:1.1.19"
dependencies: