diff --git a/workspaces/marketplace/.changeset/unlucky-hornets-cross.md b/workspaces/marketplace/.changeset/unlucky-hornets-cross.md
new file mode 100644
index 0000000000..36e7f15389
--- /dev/null
+++ b/workspaces/marketplace/.changeset/unlucky-hornets-cross.md
@@ -0,0 +1,15 @@
+---
+'@red-hat-developer-hub/backstage-plugin-marketplace-backend': minor
+'@red-hat-developer-hub/backstage-plugin-marketplace': minor
+---
+
+Integrate plugins-info plugin and add `Installed packages` tab with enhanced UI.
+
+BREAKING: The deprecated `InstallationContextProvider` export behavior changed.
+
+- We now export a null component `InstallationContextProvider` from `plugin.ts` solely for backward compatibility. It no longer provides context and will be removed in a future release.
+- Migration: There is no replacement API; this was internal-only. Please upgrade to the latest RHDH where features no longer rely on this provider.
+
+Also:
+
+- New `Installed packages` tab with dual-source mapping and client-side filtering/pagination.
diff --git a/workspaces/marketplace/package.json b/workspaces/marketplace/package.json
index 6e56ee5509..b1d8cdc032 100644
--- a/workspaces/marketplace/package.json
+++ b/workspaces/marketplace/package.json
@@ -50,7 +50,8 @@
"minimatch": "3",
"node-gyp": "^9.0.0",
"prettier": "^3.4.2",
- "typescript": "~5.3.0"
+ "typescript": "~5.3.0",
+ "yaml": "^2.8.1"
},
"resolutions": {
"@types/react": "^18",
diff --git a/workspaces/marketplace/packages/app/src/App.tsx b/workspaces/marketplace/packages/app/src/App.tsx
index ac136baa77..289cbafdda 100644
--- a/workspaces/marketplace/packages/app/src/App.tsx
+++ b/workspaces/marketplace/packages/app/src/App.tsx
@@ -52,10 +52,7 @@ import { githubAuthApiRef } from '@backstage/core-plugin-api';
import { getAllThemes } from '@red-hat-developer-hub/backstage-plugin-theme';
-import {
- InstallationContextProvider,
- DynamicMarketplacePluginRouter as Marketplace,
-} from '@red-hat-developer-hub/backstage-plugin-marketplace';
+import { DynamicMarketplacePluginRouter as Marketplace } from '@red-hat-developer-hub/backstage-plugin-marketplace';
import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
@@ -136,14 +133,7 @@ const routes = (
} />
} />
-
-
-
- }
- />
+ } />
);
diff --git a/workspaces/marketplace/plugins/marketplace-backend/package.json b/workspaces/marketplace/plugins/marketplace-backend/package.json
index 4c23eafe0c..3bc2969230 100644
--- a/workspaces/marketplace/plugins/marketplace-backend/package.json
+++ b/workspaces/marketplace/plugins/marketplace-backend/package.json
@@ -34,6 +34,7 @@
},
"dependencies": {
"@backstage/backend-defaults": "^0.11.1",
+ "@backstage/backend-dynamic-feature-service": "^0.7.3",
"@backstage/backend-plugin-api": "^1.4.1",
"@backstage/catalog-client": "^1.10.2",
"@backstage/catalog-model": "^1.7.5",
@@ -44,7 +45,6 @@
"@red-hat-developer-hub/backstage-plugin-marketplace-common": "workspace:^",
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
- "yaml": "^2.7.1",
"zod": "^3.22.4"
},
"devDependencies": {
@@ -56,7 +56,8 @@
"@types/express": "*",
"@types/supertest": "^2.0.12",
"msw": "^1.0.0",
- "supertest": "^6.2.4"
+ "supertest": "^6.2.4",
+ "yaml": "^2.7.1"
},
"files": [
"dist"
diff --git a/workspaces/marketplace/plugins/marketplace-backend/src/plugin.ts b/workspaces/marketplace/plugins/marketplace-backend/src/plugin.ts
index 1de979e175..c2d6c41556 100644
--- a/workspaces/marketplace/plugins/marketplace-backend/src/plugin.ts
+++ b/workspaces/marketplace/plugins/marketplace-backend/src/plugin.ts
@@ -19,6 +19,7 @@ import {
createBackendPlugin,
} from '@backstage/backend-plugin-api';
import { CatalogClient } from '@backstage/catalog-client';
+import { dynamicPluginsServiceRef } from '@backstage/backend-dynamic-feature-service';
import {
MarketplaceApi,
@@ -45,8 +46,10 @@ export const marketplacePlugin = createBackendPlugin({
discovery: coreServices.discovery,
logger: coreServices.logger,
permissions: coreServices.permissions,
+ pluginProvider: dynamicPluginsServiceRef,
},
async init({
+ pluginProvider,
auth,
config,
httpAuth,
@@ -75,6 +78,8 @@ export const marketplacePlugin = createBackendPlugin({
installationDataService,
marketplaceApi,
permissions,
+ pluginProvider,
+ logger,
config,
}),
);
diff --git a/workspaces/marketplace/plugins/marketplace-backend/src/router.test.ts b/workspaces/marketplace/plugins/marketplace-backend/src/router.test.ts
index dbf26a9a24..160fde45cd 100644
--- a/workspaces/marketplace/plugins/marketplace-backend/src/router.test.ts
+++ b/workspaces/marketplace/plugins/marketplace-backend/src/router.test.ts
@@ -20,8 +20,16 @@ import request from 'supertest';
import { stringify } from 'yaml';
import { ExtendedHttpServer } from '@backstage/backend-defaults/rootHttpRouter';
-import { BackendFeature } from '@backstage/backend-plugin-api';
+import {
+ BackendFeature,
+ createServiceFactory,
+} from '@backstage/backend-plugin-api';
import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
+import {
+ DynamicPluginProvider,
+ BaseDynamicPlugin,
+ dynamicPluginsServiceRef,
+} from '@backstage/backend-dynamic-feature-service';
import type { JsonObject } from '@backstage/types';
import {
AuthorizeResult,
@@ -67,6 +75,43 @@ const BASE_CONFIG = {
},
};
+// Mock dynamic plugins data for testing
+const mockDynamicPluginsData: BaseDynamicPlugin[] = [
+ {
+ name: '@backstage/plugin-catalog-backend',
+ version: '1.0.0',
+ role: 'backend',
+ platform: 'node',
+ },
+ {
+ name: '@backstage/plugin-catalog',
+ version: '1.0.0',
+ role: 'frontend',
+ platform: 'web',
+ },
+ {
+ name: '@red-hat-developer-hub/backstage-plugin-marketplace',
+ version: '1.0.0',
+ role: 'frontend',
+ platform: 'web',
+ },
+];
+
+// Mock DynamicPluginProvider
+const mockDynamicPluginProvider: DynamicPluginProvider = {
+ plugins: () => mockDynamicPluginsData as any,
+ getScannedPackage: () => ({}) as any,
+ frontendPlugins: () => [],
+ backendPlugins: () => [],
+};
+
+// Create mock service factory for dynamicPluginsServiceRef
+const mockDynamicPluginsServiceFactory = createServiceFactory({
+ service: dynamicPluginsServiceRef,
+ deps: {},
+ factory: () => mockDynamicPluginProvider,
+});
+
const FILE_INSTALL_CONFIG = {
extensions: {
installation: {
@@ -141,6 +186,7 @@ async function startBackendServer(
mockServices.rootConfig.factory({
data: { ...BASE_CONFIG, ...(config ?? {}) },
}),
+ mockDynamicPluginsServiceFactory,
];
if (authorizeResult) {
@@ -881,4 +927,31 @@ describe('createRouter', () => {
},
);
});
+
+ describe('GET /loaded-plugins', () => {
+ it('should return the list of loaded dynamic plugins', async () => {
+ const { backendServer } = await setupTestWithMockCatalog({
+ mockData: {},
+ });
+
+ const response = await request(backendServer).get(
+ '/api/extensions/loaded-plugins',
+ );
+
+ expect(response.status).toBe(200);
+ expect(Array.isArray(response.body)).toBe(true);
+ // The response should be an array of dynamic plugin info objects
+ // Each plugin should have the expected structure
+ response.body.forEach((plugin: any) => {
+ expect(plugin).toEqual(
+ expect.objectContaining({
+ name: expect.any(String),
+ version: expect.any(String),
+ role: expect.any(String),
+ platform: expect.any(String),
+ }),
+ );
+ });
+ });
+ });
});
diff --git a/workspaces/marketplace/plugins/marketplace-backend/src/router.ts b/workspaces/marketplace/plugins/marketplace-backend/src/router.ts
index d5118de128..4aefb0ffc4 100644
--- a/workspaces/marketplace/plugins/marketplace-backend/src/router.ts
+++ b/workspaces/marketplace/plugins/marketplace-backend/src/router.ts
@@ -22,6 +22,7 @@ import type { Config } from '@backstage/config';
import {
HttpAuthService,
PermissionsService,
+ LoggerService,
} from '@backstage/backend-plugin-api';
import {
AuthorizeResult,
@@ -48,11 +49,19 @@ import { matches } from './utils/permissionUtils';
import { InstallationDataService } from './installation/InstallationDataService';
import { ConfigFormatError } from './errors/ConfigFormatError';
+import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter';
+import {
+ BaseDynamicPlugin,
+ DynamicPluginProvider,
+} from '@backstage/backend-dynamic-feature-service';
+
export type MarketplaceRouterOptions = {
httpAuth: HttpAuthService;
marketplaceApi: MarketplaceApi;
permissions: PermissionsService;
installationDataService: InstallationDataService;
+ pluginProvider: DynamicPluginProvider;
+ logger: LoggerService;
config: Config;
};
@@ -64,6 +73,8 @@ export async function createRouter(
marketplaceApi,
permissions,
installationDataService,
+ pluginProvider,
+ logger,
config,
} = options;
@@ -457,5 +468,22 @@ export async function createRouter(
res.json(packages);
});
+ const plugins = pluginProvider.plugins();
+ const dynamicPlugins = plugins.map(p => {
+ // Remove the installer details for the dynamic backend plugins
+ if (p.platform === 'node') {
+ const { installer, ...rest } = p;
+ return rest as BaseDynamicPlugin;
+ }
+ return p as BaseDynamicPlugin;
+ });
+ router.get('/loaded-plugins', async (req, response) => {
+ await httpAuth.credentials(req, { allow: ['user', 'service'] });
+ response.send(dynamicPlugins);
+ });
+ const middleware = MiddlewareFactory.create({ logger, config });
+
+ router.use(middleware.error());
+
return router;
}
diff --git a/workspaces/marketplace/plugins/marketplace/knip-report.md b/workspaces/marketplace/plugins/marketplace/knip-report.md
index c2ad928652..0c88afc9c3 100644
--- a/workspaces/marketplace/plugins/marketplace/knip-report.md
+++ b/workspaces/marketplace/plugins/marketplace/knip-report.md
@@ -6,4 +6,3 @@
| :-------------------------- | :---------------- | :------- |
| @testing-library/user-event | package.json:64:6 | error |
| msw | package.json:65:6 | error |
-
diff --git a/workspaces/marketplace/plugins/marketplace/package.json b/workspaces/marketplace/plugins/marketplace/package.json
index a8e0d580d0..fa0e44fdf8 100644
--- a/workspaces/marketplace/plugins/marketplace/package.json
+++ b/workspaces/marketplace/plugins/marketplace/package.json
@@ -41,6 +41,7 @@
"@backstage/plugin-permission-react": "^0.4.36",
"@backstage/theme": "^0.6.7",
"@backstage/types": "^1.2.1",
+ "@material-table/core": "^6.4.4",
"@material-ui/core": "^4.12.2",
"@monaco-editor/react": "^4.7.0",
"@mui/icons-material": "^5.16.7",
diff --git a/workspaces/marketplace/plugins/marketplace/report.api.md b/workspaces/marketplace/plugins/marketplace/report.api.md
index e0f2209e30..216cfee04e 100644
--- a/workspaces/marketplace/plugins/marketplace/report.api.md
+++ b/workspaces/marketplace/plugins/marketplace/report.api.md
@@ -4,27 +4,21 @@
```ts
-///
-
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { JSX as JSX_2 } from 'react/jsx-runtime';
-import { JSXElementConstructor } from 'react';
import { PathParams } from '@backstage/core-plugin-api';
-import { ReactElement } from 'react';
import { RouteRef } from '@backstage/core-plugin-api';
import { SubRouteRef } from '@backstage/core-plugin-api';
// @public (undocumented)
export const DynamicMarketplacePluginContent: () => JSX_2.Element;
-// @public (undocumented)
+// @public
export const DynamicMarketplacePluginRouter: () => JSX_2.Element;
-// @public (undocumented)
-export const InstallationContextProvider: ({ children, }: {
- children: ReactElement>;
-}) => JSX_2.Element;
+// @public @deprecated (undocumented)
+export const InstallationContextProvider: () => null;
// @public
export const MarketplaceFullPageRouter: () => JSX_2.Element;
@@ -43,11 +37,16 @@ packageRouteRef: SubRouteRef>;
packageInstallRouteRef: SubRouteRef>;
collectionsRouteRef: SubRouteRef;
collectionRouteRef: SubRouteRef>;
+catalogTabRouteRef: SubRouteRef;
+installedTabRouteRef: SubRouteRef;
}, {}, {}>;
// @public
export const MarketplaceTabbedPageRouter: () => JSX_2.Element;
+// @public (undocumented)
+export const PluginsIcon: IconComponent;
+
// (No @packageDocumentation comment for this package)
```
diff --git a/workspaces/marketplace/plugins/marketplace/src/api/DynamicPluginsInfoClient.ts b/workspaces/marketplace/plugins/marketplace/src/api/DynamicPluginsInfoClient.ts
new file mode 100644
index 0000000000..671e086c7a
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/api/DynamicPluginsInfoClient.ts
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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 { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
+import { DynamicPluginInfo, DynamicPluginsInfoApi } from '.';
+
+export interface DynamicPluginsInfoClientOptions {
+ discoveryApi: DiscoveryApi;
+ fetchApi: FetchApi;
+}
+
+const loadedPluginsEndpoint = '/loaded-plugins';
+
+export class DynamicPluginsInfoClient implements DynamicPluginsInfoApi {
+ private readonly discoveryApi: DiscoveryApi;
+ private readonly fetchApi: FetchApi;
+
+ constructor(options: DynamicPluginsInfoClientOptions) {
+ this.discoveryApi = options.discoveryApi;
+ this.fetchApi = options.fetchApi;
+ }
+ async listLoadedPlugins(): Promise {
+ const baseUrl = await this.discoveryApi.getBaseUrl('extensions');
+ const targetUrl = `${baseUrl}${loadedPluginsEndpoint}`;
+ const response = await this.fetchApi.fetch(targetUrl);
+ const data = await response.json();
+ if (!response.ok) {
+ const message = data.error?.message || data.message || data.toString?.();
+ throw new Error(`Failed to load dynamic plugin info: ${message}`);
+ }
+ return data;
+ }
+}
diff --git a/workspaces/marketplace/plugins/marketplace/src/api/index.ts b/workspaces/marketplace/plugins/marketplace/src/api/index.ts
index 7d9f79ceef..7d3c712353 100644
--- a/workspaces/marketplace/plugins/marketplace/src/api/index.ts
+++ b/workspaces/marketplace/plugins/marketplace/src/api/index.ts
@@ -21,3 +21,17 @@ import { MarketplaceApi } from '@red-hat-developer-hub/backstage-plugin-marketpl
export const marketplaceApiRef = createApiRef({
id: 'plugin.extensions.api-ref',
});
+
+export type DynamicPluginInfo = {
+ name: string;
+ version: string;
+ role: string;
+ platform: string;
+};
+export interface DynamicPluginsInfoApi {
+ listLoadedPlugins(): Promise;
+}
+
+export const dynamicPluginsInfoApiRef = createApiRef({
+ id: 'plugin.extensions.dynamic-plugins-info',
+});
diff --git a/workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.test.tsx b/workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.test.tsx
new file mode 100644
index 0000000000..946a9fff15
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.test.tsx
@@ -0,0 +1,194 @@
+/*
+ * Copyright 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 { BrowserRouter } from 'react-router-dom';
+import { fireEvent, render, screen, waitFor } from '@testing-library/react';
+import { mockApis, MockErrorApi, TestApiProvider } from '@backstage/test-utils';
+import { QueryClientProvider } from '@tanstack/react-query';
+
+import { queryClient } from '../../queryclient';
+import { marketplaceApiRef } from '../../api';
+import { dynamicPluginsInfoApiRef } from '../../api';
+import { InstalledPackagesTable } from './InstalledPackagesTable';
+import { errorApiRef } from '@backstage/core-plugin-api';
+import { translationApiRef } from '@backstage/core-plugin-api/alpha';
+
+describe('InstalledPackagesTable', () => {
+ const renderWithProviders = (apis: any) =>
+ render(
+
+
+
+
+
+
+ ,
+ );
+
+ beforeEach(() => {
+ queryClient.clear();
+ queryClient.setDefaultOptions({ queries: { retry: false } });
+ });
+
+ it('renders rows for all dynamic-plugins-info entries and maps names when entity exists', async () => {
+ const dynamicPlugins = [
+ {
+ name: '@scope/pkg-a-dynamic',
+ version: '1.0.0',
+ role: 'frontend-plugin',
+ platform: 'fe',
+ },
+ {
+ name: 'pkg-b-dynamic',
+ version: '2.0.0',
+ role: 'backend-plugin',
+ platform: 'be',
+ },
+ ];
+
+ const entities = {
+ items: [
+ {
+ apiVersion: 'extensions.backstage.io/v1alpha1',
+ kind: 'Package',
+ metadata: {
+ namespace: 'rhdh',
+ name: 'scope-pkg-a',
+ title: 'Package A',
+ },
+ spec: { packageName: '@scope/pkg-a', version: '1.0.0' },
+ },
+ ],
+ totalItems: 1,
+ pageInfo: {},
+ };
+
+ const apis = [
+ [
+ dynamicPluginsInfoApiRef,
+ { listLoadedPlugins: jest.fn().mockResolvedValue(dynamicPlugins) },
+ ],
+ [
+ marketplaceApiRef,
+ { getPackages: jest.fn().mockResolvedValue(entities) },
+ ],
+ ] as const;
+
+ renderWithProviders(apis);
+
+ await waitFor(() =>
+ expect(screen.getByText('Installed packages (2)')).toBeInTheDocument(),
+ );
+
+ // Name column shows mapped entity title for first row
+ expect(screen.getByText('Package A')).toBeInTheDocument();
+ // Second row has no entity -> uses readable fallback (should include raw package name minus suffixes)
+ expect(screen.getByText(/pkg-b/i)).toBeInTheDocument();
+
+ // npm package name column shows the dynamic plugin record name directly
+ expect(screen.getByText('@scope/pkg-a-dynamic')).toBeInTheDocument();
+ expect(screen.getByText('pkg-b-dynamic')).toBeInTheDocument();
+
+ // Role and Version columns
+ expect(
+ screen.getByText('Frontend plugin'.replace('plugin', 'plugin')),
+ ).toBeInTheDocument();
+ expect(screen.getByText('1.0.0')).toBeInTheDocument();
+ expect(screen.getByText('2.0.0')).toBeInTheDocument();
+ });
+
+ it('disables actions and shows tooltip when entity is missing', async () => {
+ const dynamicPlugins = [
+ {
+ name: 'no-entity-dynamic',
+ version: '1.0.0',
+ role: 'frontend-plugin',
+ platform: 'fe',
+ },
+ ];
+
+ const entities = { items: [], totalItems: 0, pageInfo: {} };
+
+ const apis = [
+ [
+ dynamicPluginsInfoApiRef,
+ { listLoadedPlugins: jest.fn().mockResolvedValue(dynamicPlugins) },
+ ],
+ [
+ marketplaceApiRef,
+ { getPackages: jest.fn().mockResolvedValue(entities) },
+ ],
+ ] as const;
+
+ renderWithProviders(apis);
+
+ await waitFor(() =>
+ expect(screen.getByText('Installed packages (1)')).toBeInTheDocument(),
+ );
+
+ const disabledButtons = screen.getAllByRole('button', { hidden: true });
+ // There are three disabled action buttons rendered wrapped in span
+ expect(disabledButtons.length).toBeGreaterThanOrEqual(3);
+ });
+
+ it('filters by search query', async () => {
+ const dynamicPlugins = [
+ {
+ name: 'alpha-dynamic',
+ version: '1.0.0',
+ role: 'frontend-plugin',
+ platform: 'fe',
+ },
+ {
+ name: 'beta-dynamic',
+ version: '1.0.0',
+ role: 'backend-plugin',
+ platform: 'be',
+ },
+ ];
+ const entities = { items: [], totalItems: 0, pageInfo: {} };
+ const apis = [
+ [
+ dynamicPluginsInfoApiRef,
+ { listLoadedPlugins: jest.fn().mockResolvedValue(dynamicPlugins) },
+ ],
+ [
+ marketplaceApiRef,
+ { getPackages: jest.fn().mockResolvedValue(entities) },
+ ],
+ ] as const;
+
+ renderWithProviders(apis);
+
+ await waitFor(() =>
+ expect(screen.getByText('Installed packages (2)')).toBeInTheDocument(),
+ );
+
+ const input = screen.getByRole('textbox');
+ fireEvent.change(input, { target: { value: 'alpha' } });
+
+ await waitFor(() =>
+ expect(screen.getByText('Installed packages (1)')).toBeInTheDocument(),
+ );
+ expect(screen.queryByText('beta')).not.toBeInTheDocument();
+ });
+});
diff --git a/workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.tsx b/workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.tsx
new file mode 100644
index 0000000000..42db2eeb0b
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/components/InstalledPackages/InstalledPackagesTable.tsx
@@ -0,0 +1,365 @@
+/*
+ * Copyright 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 { useMemo, useState } from 'react';
+
+import {
+ ResponseErrorPanel,
+ Table,
+ TableColumn,
+} from '@backstage/core-components';
+import { useApi } from '@backstage/core-plugin-api';
+import { Link } from '@backstage/core-components';
+import { useLocation } from 'react-router-dom';
+
+import { Query, QueryResult } from '@material-table/core';
+import { useQuery } from '@tanstack/react-query';
+
+import { dynamicPluginsInfoApiRef } from '../../api';
+import EditIcon from '@mui/icons-material/Edit';
+import DeleteIcon from '@mui/icons-material/Delete';
+import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
+import Box from '@mui/material/Box';
+import IconButton from '@mui/material/IconButton';
+import Tooltip from '@mui/material/Tooltip';
+
+import { useMarketplaceApi } from '../../hooks/useMarketplaceApi';
+import { getReadableName } from '../../utils/pluginProcessing';
+import { useQueryFullTextSearch } from '../../hooks/useQueryFullTextSearch';
+import { SearchTextField } from '../../shared-components/SearchTextField';
+
+type InstalledPackageRow = {
+ displayName: string;
+ packageName: string;
+ role?: string;
+ version?: string;
+ hasEntity: boolean;
+ namespace?: string;
+ name?: string;
+};
+
+export const InstalledPackagesTable = () => {
+ const [error, setError] = useState(undefined);
+ const [filteredCount, setFilteredCount] = useState(0);
+ const dynamicPluginInfo = useApi(dynamicPluginsInfoApiRef);
+ const marketplaceApi = useMarketplaceApi();
+ const fullTextSearch = useQueryFullTextSearch();
+ const location = useLocation();
+
+ // Fetch once and cache
+ const installedQuery = useQuery({
+ queryKey: ['dynamic-plugins-info', 'installed'],
+ queryFn: () => dynamicPluginInfo.listLoadedPlugins(),
+ staleTime: 5 * 60 * 1000,
+ refetchOnWindowFocus: false,
+ });
+
+ const packagesQuery = useQuery({
+ queryKey: ['marketplace', 'packages'],
+ queryFn: () => marketplaceApi.getPackages({}),
+ staleTime: 5 * 60 * 1000,
+ refetchOnWindowFocus: false,
+ });
+ const columns: TableColumn[] = useMemo(
+ () => [
+ {
+ title: 'Name',
+ field: 'displayName',
+ align: 'left',
+ width: '30ch',
+ defaultSort: 'asc',
+ headerStyle: {
+ textAlign: 'left',
+ },
+ cellStyle: {
+ textAlign: 'left',
+ },
+ render: (row: InstalledPackageRow) => {
+ if (row.hasEntity && row.namespace && row.name) {
+ const params = new URLSearchParams(location.search);
+ params.set('package', `${row.namespace}/${row.name}`);
+ const to = `${location.pathname}?${params.toString()}`;
+ return (
+ e.stopPropagation()}>
+ {row.displayName}
+
+ );
+ }
+ return row.displayName;
+ },
+ },
+ {
+ title: 'npm package name',
+ field: 'packageName',
+ width: '54ch',
+ align: 'left',
+ headerStyle: {
+ textAlign: 'left',
+ },
+ cellStyle: {
+ textAlign: 'left',
+ },
+ },
+ {
+ title: 'Role',
+ field: 'role',
+ width: '24ch',
+ align: 'left',
+ headerStyle: {
+ textAlign: 'left',
+ },
+ cellStyle: {
+ textAlign: 'left',
+ },
+ },
+ {
+ title: 'Version',
+ field: 'version',
+ width: '24ch',
+ align: 'left',
+ headerStyle: {
+ textAlign: 'left',
+ },
+ cellStyle: {
+ textAlign: 'left',
+ },
+ },
+ {
+ title: 'Actions',
+ align: 'right',
+ width: '78px',
+ headerStyle: {
+ textAlign: 'left',
+ },
+ cellStyle: {
+ textAlign: 'left',
+ },
+ render: (row: InstalledPackageRow) => {
+ const disabled = !row.hasEntity;
+ const tooltipTitle =
+ 'To enable actions, add a catalog entity for this package';
+ return (
+
+ {disabled ? (
+
+
+ theme.palette.action.disabled }}
+ >
+
+
+
+
+ ) : (
+ theme.palette.text.primary }}
+ onClick={() => {
+ // TODO: Implement edit functionality
+ }}
+ >
+
+
+ )}
+ {disabled ? (
+
+
+ theme.palette.action.disabled }}
+ >
+
+
+
+
+ ) : (
+ theme.palette.text.primary }}
+ onClick={() => {
+ // TODO: Implement delete functionality
+ }}
+ >
+
+
+ )}
+ {disabled ? (
+
+
+ theme.palette.action.disabled }}
+ >
+
+
+
+
+ ) : (
+ theme.palette.text.primary }}
+ onClick={() => {
+ // TODO: Implement download functionality
+ }}
+ >
+
+
+ )}
+
+ );
+ },
+ sorting: false,
+ },
+ ],
+ [location.pathname, location.search],
+ );
+ const fetchData = async (
+ query: Query,
+ ): Promise> => {
+ const { page = 0, pageSize = 5 } = query || {};
+
+ try {
+ // Ensure data available; avoid re-fetching on search or pagination
+ const installed = installedQuery.data ?? [];
+ const packagesResponse = packagesQuery.data ?? { items: [] as any[] };
+
+ const entitiesByName = new Map(
+ (packagesResponse.items ?? []).map(entity => [
+ (entity.metadata?.name ?? '').toLowerCase(),
+ entity,
+ ]),
+ );
+
+ // Build rows for ALL installed items; if no matching entity, mark missing
+ const rows: InstalledPackageRow[] = installed.map(p => {
+ const normalized = p.name
+ .replace(/[@/]/g, '-')
+ .replace(/-dynamic$/, '')
+ .replace(/^-+/, '')
+ .toLowerCase();
+ const entity = entitiesByName.get(normalized) as any | undefined;
+ const rawName = entity
+ ? (entity.metadata?.title as string) ||
+ (entity.metadata?.name as string)
+ : getReadableName(p.name);
+ const cleanedName = rawName.replace(/\s+(frontend|backend)$/i, '');
+ return {
+ displayName: cleanedName,
+ // Show the npm package name directly from dynamic-plugins-info record
+ packageName: p.name,
+ // Humanized role from dynamic-plugins-info
+ role: (p as any).role
+ ? (() => {
+ const raw = ((p as any).role as string).replace(/-/g, ' ');
+ return raw.charAt(0).toUpperCase() + raw.slice(1).toLowerCase();
+ })()
+ : undefined,
+ // Prefer dynamic-plugins-info version, then fallback to entity spec.version
+ version:
+ (p.version as string | undefined) ??
+ (entity?.spec?.version as string | undefined) ??
+ undefined,
+ hasEntity: !!entity,
+ namespace: entity?.metadata?.namespace ?? 'default',
+ name: entity?.metadata?.name,
+ } as InstalledPackageRow;
+ });
+
+ const sortField =
+ ((query as any)?.orderBy?.field as string) || 'displayName';
+ const sortDirection = (((query as any)?.orderDirection as
+ | 'asc'
+ | 'desc') || 'asc') as 'asc' | 'desc';
+
+ const filteredRows = [...rows]
+ .filter(row => {
+ const term = fullTextSearch.current?.toLowerCase().trim() ?? '';
+ return row.displayName.toLowerCase().trim().includes(term);
+ })
+ .sort((a, b) => {
+ const aVal = ((a as any)[sortField] ?? '').toString();
+ const bVal = ((b as any)[sortField] ?? '').toString();
+ const cmp = aVal.localeCompare(bVal);
+ return sortDirection === 'desc' ? -cmp : cmp;
+ });
+
+ const totalCount = filteredRows.length;
+ setFilteredCount(totalCount);
+ const lastPage = Math.max(0, Math.ceil(totalCount / pageSize) - 1);
+ const effectivePage = Math.min(page, lastPage);
+ const start = Math.max(0, effectivePage * pageSize);
+ const end = Math.min(totalCount, start + pageSize);
+ return {
+ data: filteredRows.slice(start, end),
+ page: effectivePage,
+ totalCount,
+ };
+ } catch (loadingError) {
+ // eslint-disable-next-line no-console
+ console.error('Failed to load plugins', loadingError);
+ setError(loadingError as Error);
+ return { data: [], totalCount: 0, page: 0 };
+ }
+ };
+ if (error) {
+ return ;
+ }
+
+ // Conditional empty message based on search state
+ const emptyMessage = (fullTextSearch.current || '').trim()
+ ? 'No results found. Try a different search term.'
+ : 'No records to display';
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
diff --git a/workspaces/marketplace/plugins/marketplace/src/components/Markdown.tsx b/workspaces/marketplace/plugins/marketplace/src/components/Markdown.tsx
index e4ffb76b6a..c2fc9aa038 100644
--- a/workspaces/marketplace/plugins/marketplace/src/components/Markdown.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/components/Markdown.tsx
@@ -56,7 +56,6 @@ export const Markdown = (props: MarkdownProps) => {
// TODO load images from marketplace assets endpoint ???
const transformImageUri = (href: string): string => {
- // console.log('Markdown transformImageUri href', href);
return href;
};
diff --git a/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageContent.tsx b/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageContent.tsx
index 00c673917d..40005e558b 100644
--- a/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageContent.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageContent.tsx
@@ -18,6 +18,8 @@ import type { ReactNode } from 'react';
import { Content, ErrorPage, LinkButton } from '@backstage/core-components';
import { useRouteRef, useRouteRefParams } from '@backstage/core-plugin-api';
+import { useLocation } from 'react-router-dom';
+import { useSearchParams } from 'react-router-dom';
import Skeleton from '@mui/material/Skeleton';
import Stack from '@mui/material/Stack';
@@ -96,6 +98,16 @@ const MarketplacePackageContentSkeleton = () => {
const MarketplacePackageContent = ({ pkg }: { pkg: MarketplacePackage }) => {
const getInstallPath = useRouteRef(packageInstallRouteRef);
+ const location = useLocation();
+ const installBase = getInstallPath({
+ namespace: pkg.metadata.namespace!,
+ name: pkg.metadata.name,
+ });
+ const preservedParams = new URLSearchParams(location.search);
+ preservedParams.delete('package');
+ const installTo = preservedParams.size
+ ? `${installBase}?${preservedParams.toString()}`
+ : installBase;
return (
@@ -110,14 +122,7 @@ const MarketplacePackageContent = ({ pkg }: { pkg: MarketplacePackage }) => {
-
+
{
mapPackageInstallStatusToButton[
pkg.spec?.installStatus ??
@@ -157,7 +162,13 @@ const MarketplacePackageContent = ({ pkg }: { pkg: MarketplacePackage }) => {
export const MarketplacePackageContentLoader = () => {
const params = useRouteRefParams(packageInstallRouteRef);
- const pkg = usePackage(params.namespace, params.name);
+ const [searchParams] = useSearchParams();
+ const qp = searchParams.get('package');
+ const qpNs = qp?.split('/')[0];
+ const qpName = qp?.split('/')[1];
+ const namespace = qpNs || params.namespace;
+ const name = qpName || params.name;
+ const pkg = usePackage(namespace, name);
if (pkg.isLoading) {
return ;
@@ -167,8 +178,6 @@ export const MarketplacePackageContentLoader = () => {
return ;
}
return (
-
+
);
};
diff --git a/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageDrawer.tsx b/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageDrawer.tsx
index fc6bf9bc46..7ee3fc3363 100644
--- a/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageDrawer.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageDrawer.tsx
@@ -15,9 +15,8 @@
*/
import { useState, useLayoutEffect } from 'react';
-import { useNavigate, useSearchParams } from 'react-router-dom';
+import { useNavigate, useSearchParams, useLocation } from 'react-router-dom';
-import { useRouteRef } from '@backstage/core-plugin-api';
import { ErrorBoundary } from '@backstage/core-components';
import Box from '@mui/material/Box';
@@ -26,25 +25,26 @@ import IconButton from '@mui/material/IconButton';
import CloseIcon from '@mui/icons-material/Close';
import { useTheme } from '@mui/material/styles';
-import { packagesRouteRef } from '../routes';
-
import { MarketplacePackageContentLoader } from './MarketplacePackageContent';
export const MarketplacePackageDrawer = () => {
const [open, setOpen] = useState(false);
+ const [searchParams] = useSearchParams();
+ const location = useLocation();
useLayoutEffect(() => {
- setOpen(true);
- }, []);
+ setOpen(!!searchParams.get('package'));
+ }, [searchParams]);
const navigate = useNavigate();
- const [searchParams] = useSearchParams();
- const packagesPath = `${useRouteRef(packagesRouteRef)()}${searchParams.size > 0 ? '?' : ''}${searchParams}`;
const theme = useTheme();
const handleClose = () => {
- // TODO analytics?
setOpen(false);
+ const params = new URLSearchParams(location.search);
+ params.delete('package');
+ const qs = params.toString();
+ const target = qs ? `${location.pathname}?${qs}` : location.pathname;
setTimeout(
- () => navigate(packagesPath),
+ () => navigate(target),
theme.transitions.duration.leavingScreen,
);
};
diff --git a/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageEditContent.tsx b/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageEditContent.tsx
new file mode 100644
index 0000000000..0b8e00ca4d
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageEditContent.tsx
@@ -0,0 +1,440 @@
+/*
+ * Copyright 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 type { SetStateAction } from 'react';
+
+import { useCallback, useEffect, useState } from 'react';
+
+import { ErrorPage, Progress } from '@backstage/core-components';
+import { useRouteRefParams } from '@backstage/core-plugin-api';
+
+import { useLocation, useNavigate } from 'react-router-dom';
+
+import {
+ MarketplacePackage,
+ ExtensionsPackageAppConfigExamples,
+} from '@red-hat-developer-hub/backstage-plugin-marketplace-common';
+
+import Box from '@mui/material/Box';
+import Grid from '@mui/material/Grid';
+import Card from '@mui/material/Card';
+import CardContent from '@mui/material/CardContent';
+import CardHeader from '@mui/material/CardHeader';
+import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
+import Tabs from '@mui/material/Tabs';
+import Tab from '@mui/material/Tab';
+import Button from '@mui/material/Button';
+import Typography from '@mui/material/Typography';
+import Alert from '@mui/material/Alert';
+import CircularProgress from '@mui/material/CircularProgress';
+import { useQueryClient } from '@tanstack/react-query';
+
+import { packageInstallRouteRef } from '../routes';
+
+import { CodeEditorContextProvider, useCodeEditor } from './CodeEditor';
+import { useInstallPackage } from '../hooks/useInstallPackage';
+import { usePackage } from '../hooks/usePackage';
+import { usePackageConfig } from '../hooks/usePackageConfig';
+import { CodeEditorCard } from './CodeEditorCard';
+import { TabPanel } from './TabPanel';
+import { useInstallationContext } from './InstallationContext';
+
+interface TabItem {
+ label: string;
+ content: string | ExtensionsPackageAppConfigExamples[];
+ key: string;
+ others?: { [key: string]: any };
+}
+
+export const MarketplacePackageEditContent = ({
+ pkg,
+}: {
+ pkg: MarketplacePackage;
+}) => {
+ const { mutateAsync: installPackage } = useInstallPackage();
+ const [hasGlobalHeader, setHasGlobalHeader] = useState(false);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const [saveError, setSaveError] = useState(null);
+
+ useEffect(() => {
+ const header = document.querySelector('nav#global-header');
+ setHasGlobalHeader(Boolean(header));
+ }, []);
+
+ const dynamicHeight = hasGlobalHeader
+ ? 'calc(100vh - 220px)'
+ : 'calc(100vh - 160px)';
+
+ const codeEditor = useCodeEditor();
+ const params = useRouteRefParams(packageInstallRouteRef);
+ const pkgConfig = usePackageConfig(params.namespace, params.name);
+
+ // Seed editor when the Monaco editor mounts (avoids race when config arrives before editor)
+ const onLoaded = useCallback(() => {
+ const existing = pkgConfig.data?.configYaml;
+ if (existing) {
+ // Wrap backend single-map YAML under plugins list using simple string ops
+ const lines = (existing || '').split('\n');
+ const first = lines[0] ?? '';
+ const isAlreadyItem = /^\s*-\s+/.test(first);
+ const wrapped = isAlreadyItem
+ ? ['plugins:', ...lines.map(l => (l ? ` ${l}` : l))]
+ : [
+ 'plugins:',
+ `- ${first}`,
+ ...lines.slice(1).map(l => (l ? ` ${l}` : l)),
+ ];
+ codeEditor.setValue(wrapped.filter(l => l !== undefined).join('\n'));
+ return;
+ }
+ if (pkgConfig.isSuccess && !existing) {
+ const path = pkg.spec?.dynamicArtifact ?? './dynamic-plugins/dist/....';
+ const yamlContent = `plugins:\n - package: ${JSON.stringify(path)}\n disabled: false\n`;
+ codeEditor.setValue(yamlContent);
+ }
+ }, [
+ codeEditor,
+ pkgConfig.data?.configYaml,
+ pkgConfig.isSuccess,
+ pkg.spec?.dynamicArtifact,
+ ]);
+
+ const navigate = useNavigate();
+ const location = useLocation();
+ const { installedPlugins, setInstalledPlugins } = useInstallationContext();
+ const queryClient = useQueryClient();
+
+ // Populate editor from backend config when available; otherwise set default template once
+ useEffect(() => {
+ const existing = pkgConfig.data?.configYaml;
+ const isLoadingConfig = (pkgConfig as any)?.isLoading;
+ if (!existing && !isLoadingConfig) {
+ // Seed default only if editor is empty to avoid overwriting existing content
+ const current = codeEditor.getValue();
+ if (!current || current.trim() === '') {
+ const path = pkg.spec?.dynamicArtifact ?? './dynamic-plugins/dist/....';
+ const yamlContent = `plugins:\n - package: ${JSON.stringify(path)}\n disabled: false\n`;
+ codeEditor.setValue(yamlContent);
+ }
+ return;
+ }
+ if (existing) {
+ // Wrap backend single-map YAML under plugins list using simple string ops
+ const lines = (existing || '').split('\n');
+ const first = lines[0] ?? '';
+ const isAlreadyItem = /^\s*-\s+/.test(first);
+ const wrapped = isAlreadyItem
+ ? ['plugins:', ...lines.map(l => (l ? ` ${l}` : l))]
+ : [
+ 'plugins:',
+ `- ${first}`,
+ ...lines.slice(1).map(l => (l ? ` ${l}` : l)),
+ ];
+ codeEditor.setValue(wrapped.filter(l => l !== undefined).join('\n'));
+ }
+ }, [
+ pkgConfig,
+ pkgConfig.data?.configYaml,
+ params.namespace,
+ params.name,
+ codeEditor,
+ pkg.spec?.dynamicArtifact,
+ ]);
+
+ const examples = [
+ {
+ [`${pkg.metadata.name}`]: pkg.spec?.appConfigExamples,
+ },
+ ];
+ const packageDynamicArtifacts = {
+ [`${pkg.metadata.name}`]: pkg.spec?.dynamicArtifact,
+ };
+ const availableTabs = [
+ !!Object.values(examples[0])[0] && {
+ label: 'Examples',
+ content: examples,
+ key: 'examples',
+ others: { packageNames: packageDynamicArtifacts },
+ },
+ ].filter(tab => tab) as TabItem[];
+
+ const showRightCard = examples;
+ const [tabIndex, setTabIndex] = useState(0);
+
+ const handleTabChange = (_: any, newValue: SetStateAction) => {
+ setTabIndex(newValue);
+ };
+
+ const handleSave = async () => {
+ try {
+ setSaveError(null);
+ setIsSubmitting(true);
+ const raw = codeEditor.getValue() ?? '';
+
+ // Extract the first package item under plugins and convert to a single-map YAML (no leading dash)
+ const lines = raw.split('\n');
+ const pluginsIdx = lines.findIndex(l => /^\s*plugins\s*:/i.test(l));
+ if (pluginsIdx === -1)
+ throw new Error("Invalid editor content: missing 'plugins' list");
+ let i = pluginsIdx + 1;
+ // Skip blank lines
+ while (i < lines.length && lines[i].trim() === '') i += 1;
+ const startLine = lines[i] || '';
+ const startMatch = startLine.match(/^(\s*)-\s+/);
+ if (!startMatch)
+ throw new Error('Invalid editor content: missing package item');
+ const itemIndent = startMatch[1].length; // indent before '- '
+ const firstLine = startLine.replace(/^(\s*)-\s+/, '');
+ const pkgLines: string[] = [firstLine];
+ // Subsequent lines that are part of this list item must be indented at least itemIndent+2
+ for (let j = i + 1; j < lines.length; j += 1) {
+ const line = lines[j] ?? '';
+ // If we hit another sibling list item at the same indent, stop
+ const siblingMatch = line.match(/^(\s*)-\s+/);
+ if (siblingMatch && siblingMatch[1].length === itemIndent) break;
+ // Remove exactly one indent level (itemIndent + 2 spaces) if present
+ const removeIndent = new RegExp(`^\\s{${itemIndent + 2}}`);
+ pkgLines.push(line.replace(removeIndent, ''));
+ }
+ // Finalize single-map YAML
+ const packageYamlString = pkgLines
+ .join('\n')
+ .replace(/^\s*[-]\s*/m, '')
+ .replace(/\r/g, '')
+ .replace(/^\s*$/gm, '')
+ .trim();
+ // Validate minimal shape before POST
+ if (!/^package\s*:/m.test(packageYamlString)) {
+ setIsSubmitting(false);
+ setSaveError("Invalid editor content: 'package' field missing in item");
+ return;
+ }
+
+ const res = await installPackage({
+ namespace: pkg.metadata.namespace ?? params.namespace,
+ name: pkg.metadata.name,
+ configYaml: packageYamlString.trim(),
+ });
+
+ if ((res as any)?.status === 'OK') {
+ const updated = {
+ ...installedPlugins,
+ [pkg.metadata.title ?? pkg.metadata.name]: 'Package updated',
+ };
+ setInstalledPlugins(updated);
+ queryClient.invalidateQueries({
+ queryKey: [
+ 'marketplaceApi',
+ 'getPackageConfigByName',
+ pkg.metadata.namespace ?? params.namespace,
+ pkg.metadata.name,
+ ],
+ });
+ const ns = pkg.metadata.namespace ?? params.namespace;
+ const name = pkg.metadata.name;
+ const preserved = new URLSearchParams(location.search);
+ preserved.set('package', `${ns}/${name}`);
+ navigate(`/extensions/installed-packages?${preserved.toString()}`);
+ } else {
+ setSaveError((res as any)?.error?.message ?? 'Failed to save');
+ setIsSubmitting(false);
+ }
+ } catch (e: any) {
+ setSaveError(e?.error?.message ?? 'Failed to save');
+ setIsSubmitting(false);
+ }
+ };
+
+ return (
+
+
+
+
+ {showRightCard && (
+
+
+ Edit instructions}
+ action={
+
+
+ Download
+
+ }
+ sx={{ pb: 0 }}
+ />
+
+
+
+ {availableTabs.map((tab, index) => (
+
+ ))}
+
+
+
+ {availableTabs.map(
+ (tab, index) =>
+ tabIndex === index && (
+
+ ),
+ )}
+
+
+
+
+ )}
+
+
+ {saveError && (
+
+ {saveError}
+
+ )}
+
+
+
+ ) : undefined
+ }
+ >
+ Save
+
+
+
+
+
+ );
+};
+
+export const MarketplacePackageEditContentLoader = () => {
+ const params = useRouteRefParams(packageInstallRouteRef);
+
+ const pkg = usePackage(params.namespace, params.name);
+
+ if (pkg.isLoading) {
+ return ;
+ } else if (pkg.data) {
+ return (
+
+
+
+ );
+ } else if (pkg.error) {
+ return ;
+ }
+ return (
+
+ );
+};
diff --git a/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageInstallContent.tsx b/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageInstallContent.tsx
deleted file mode 100644
index 35ad36ee2f..0000000000
--- a/workspaces/marketplace/plugins/marketplace/src/components/MarketplacePackageInstallContent.tsx
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright 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 type { SetStateAction } from 'react';
-
-import { useCallback, useEffect, useState } from 'react';
-
-import { ErrorPage, Progress } from '@backstage/core-components';
-import { useRouteRef, useRouteRefParams } from '@backstage/core-plugin-api';
-
-import yaml from 'yaml';
-import { useNavigate } from 'react-router-dom';
-
-import {
- MarketplacePackage,
- ExtensionsPackageAppConfigExamples,
-} from '@red-hat-developer-hub/backstage-plugin-marketplace-common';
-
-import Box from '@mui/material/Box';
-import Grid from '@mui/material/Grid';
-import Card from '@mui/material/Card';
-import CardContent from '@mui/material/CardContent';
-import CardHeader from '@mui/material/CardHeader';
-import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
-import Tabs from '@mui/material/Tabs';
-import Tab from '@mui/material/Tab';
-import Button from '@mui/material/Button';
-import Typography from '@mui/material/Typography';
-
-import {
- packageInstallRouteRef,
- pluginInstallRouteRef,
- pluginRouteRef,
-} from '../routes';
-
-import { CodeEditorContextProvider, useCodeEditor } from './CodeEditor';
-import { usePackage } from '../hooks/usePackage';
-import { CodeEditorCard } from './CodeEditorCard';
-import { TabPanel } from './TabPanel';
-
-interface TabItem {
- label: string;
- content: string | ExtensionsPackageAppConfigExamples[];
- key: string;
- others?: { [key: string]: any };
-}
-
-export const MarketplacePackageInstallContent = ({
- pkg,
-}: {
- pkg: MarketplacePackage;
-}) => {
- const [hasGlobalHeader, setHasGlobalHeader] = useState(false);
-
- useEffect(() => {
- const header = document.querySelector('nav#global-header');
- setHasGlobalHeader(Boolean(header));
- }, []);
-
- const dynamicHeight = hasGlobalHeader
- ? 'calc(100vh - 220px)'
- : 'calc(100vh - 160px)';
-
- const codeEditor = useCodeEditor();
- const params = useRouteRefParams(pluginInstallRouteRef);
-
- const pluginLink = useRouteRef(pluginRouteRef)({
- namespace: params.namespace,
- name: params.name,
- });
-
- const onLoaded = useCallback(() => {
- const dynamicPluginYaml = {
- plugins: [
- {
- package: pkg.spec?.dynamicArtifact ?? './dynamic-plugins/dist/....',
- disabled: false,
- },
- ],
- };
- codeEditor.setValue(yaml.stringify(dynamicPluginYaml));
- }, [codeEditor, pkg]);
-
- const navigate = useNavigate();
- const examples = [
- {
- [`${pkg.metadata.name}`]: pkg.spec?.appConfigExamples,
- },
- ];
- const packageDynamicArtifacts = {
- [`${pkg.metadata.name}`]: pkg.spec?.dynamicArtifact,
- };
- const availableTabs = [
- !!Object.values(examples[0])[0] && {
- label: 'Examples',
- content: examples,
- key: 'examples',
- others: { packageNames: packageDynamicArtifacts },
- },
- ].filter(tab => tab) as TabItem[];
-
- const showRightCard = examples;
- const [tabIndex, setTabIndex] = useState(0);
-
- const handleTabChange = (_: any, newValue: SetStateAction) => {
- setTabIndex(newValue);
- };
-
- return (
-
-
-
-
- {showRightCard && (
-
-
-
- Installation instructions
-
- }
- action={
-
-
- Download
-
- }
- sx={{ pb: 0 }}
- />
-
-
-
- {availableTabs.map((tab, index) => (
-
- ))}
-
-
-
- {availableTabs.map(
- (tab, index) =>
- tabIndex === index && (
-
- ),
- )}
-
-
-
-
- )}
-
-
-
-
-
-
-
-
- );
-};
-
-export const MarketplacePackageInstallContentLoader = () => {
- const params = useRouteRefParams(packageInstallRouteRef);
-
- const pkg = usePackage(params.namespace, params.name);
-
- if (pkg.isLoading) {
- return ;
- } else if (pkg.data) {
- return (
-
-
-
- );
- } else if (pkg.error) {
- return ;
- }
- return (
-
- );
-};
diff --git a/workspaces/marketplace/plugins/marketplace/src/hooks/useInstallPackage.ts b/workspaces/marketplace/plugins/marketplace/src/hooks/useInstallPackage.ts
new file mode 100644
index 0000000000..da8ad24b37
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/hooks/useInstallPackage.ts
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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 { useMutation } from '@tanstack/react-query';
+import { useMarketplaceApi } from './useMarketplaceApi';
+
+export const useInstallPackage = () => {
+ const marketplaceApi = useMarketplaceApi();
+
+ return useMutation({
+ mutationFn: async ({
+ namespace,
+ name,
+ configYaml,
+ }: {
+ namespace: string;
+ name: string;
+ configYaml: string;
+ }) => await marketplaceApi.installPackage?.(namespace, name, configYaml),
+ });
+};
diff --git a/workspaces/marketplace/plugins/marketplace/src/hooks/useInstalledPackagesCount.test.tsx b/workspaces/marketplace/plugins/marketplace/src/hooks/useInstalledPackagesCount.test.tsx
new file mode 100644
index 0000000000..1a26412a72
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/hooks/useInstalledPackagesCount.test.tsx
@@ -0,0 +1,67 @@
+/*
+ * Copyright 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 { renderHook, waitFor } from '@testing-library/react';
+import { TestApiProvider } from '@backstage/test-utils';
+
+import { dynamicPluginsInfoApiRef } from '../api';
+import { useInstalledPackagesCount } from './useInstalledPackagesCount';
+
+describe('useInstalledPackagesCount', () => {
+ const createWrapper =
+ (apis: any) =>
+ ({ children }: { children?: React.ReactNode }) => (
+ {children}
+ );
+
+ it('returns count from dynamic-plugins-info', async () => {
+ const mockApi = {
+ listLoadedPlugins: jest.fn().mockResolvedValue([
+ {
+ name: 'a',
+ version: '1.0.0',
+ role: 'frontend-plugin',
+ platform: 'fe',
+ },
+ { name: 'b', version: '1.0.0', role: 'backend-plugin', platform: 'be' },
+ ]),
+ };
+
+ const { result } = renderHook(() => useInstalledPackagesCount(), {
+ wrapper: createWrapper([[dynamicPluginsInfoApiRef, mockApi]]),
+ });
+
+ await waitFor(() => expect(result.current.loading).toBe(false));
+ expect(mockApi.listLoadedPlugins).toHaveBeenCalledTimes(1);
+ expect(result.current.count).toBe(2);
+ expect(result.current.error).toBeUndefined();
+ });
+
+ it('returns 0 and sets error on failure', async () => {
+ const mockApi = {
+ listLoadedPlugins: jest.fn().mockRejectedValue(new Error('boom')),
+ };
+
+ const { result } = renderHook(() => useInstalledPackagesCount(), {
+ wrapper: createWrapper([[dynamicPluginsInfoApiRef, mockApi]]),
+ });
+
+ await waitFor(() => expect(result.current.loading).toBe(false));
+ expect(mockApi.listLoadedPlugins).toHaveBeenCalledTimes(1);
+ expect(result.current.count).toBe(0);
+ expect(result.current.error).toBeInstanceOf(Error);
+ });
+});
diff --git a/workspaces/marketplace/plugins/marketplace/src/hooks/useInstalledPackagesCount.ts b/workspaces/marketplace/plugins/marketplace/src/hooks/useInstalledPackagesCount.ts
new file mode 100644
index 0000000000..5e8b278664
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/hooks/useInstalledPackagesCount.ts
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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 { useState, useEffect } from 'react';
+import { useApi } from '@backstage/core-plugin-api';
+import { dynamicPluginsInfoApiRef } from '../api';
+// Count should reflect all records from dynamic-plugins-info
+
+export const useInstalledPackagesCount = () => {
+ const [count, setCount] = useState(0);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(undefined);
+ const dynamicPluginInfo = useApi(dynamicPluginsInfoApiRef);
+
+ useEffect(() => {
+ const fetchCount = async () => {
+ try {
+ setLoading(true);
+ setError(undefined);
+ const plugins = await dynamicPluginInfo.listLoadedPlugins();
+ setCount(plugins.length);
+ } catch (err) {
+ setError(err as Error);
+ setCount(0);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchCount();
+ }, [dynamicPluginInfo]);
+
+ return { count, loading, error };
+};
diff --git a/workspaces/marketplace/plugins/marketplace/src/hooks/usePackage.ts b/workspaces/marketplace/plugins/marketplace/src/hooks/usePackage.ts
index a936a99f8d..11929c9cb5 100644
--- a/workspaces/marketplace/plugins/marketplace/src/hooks/usePackage.ts
+++ b/workspaces/marketplace/plugins/marketplace/src/hooks/usePackage.ts
@@ -18,10 +18,11 @@ import { useQuery } from '@tanstack/react-query';
import { useMarketplaceApi } from './useMarketplaceApi';
-export const usePackage = (namespace: string, name: string) => {
+export const usePackage = (namespace?: string, name?: string) => {
const marketplaceApi = useMarketplaceApi();
return useQuery({
queryKey: ['marketplaceApi', 'getPackageByName', namespace, name],
- queryFn: () => marketplaceApi.getPackageByName(namespace, name),
+ queryFn: () => marketplaceApi.getPackageByName(namespace!, name!),
+ enabled: Boolean(namespace && name),
});
};
diff --git a/workspaces/marketplace/plugins/marketplace/src/hooks/usePackageConfig.ts b/workspaces/marketplace/plugins/marketplace/src/hooks/usePackageConfig.ts
new file mode 100644
index 0000000000..565a981117
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/hooks/usePackageConfig.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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 { useQuery } from '@tanstack/react-query';
+import { useMarketplaceApi } from './useMarketplaceApi';
+
+export const usePackageConfig = (namespace: string, name: string) => {
+ const marketplaceApi = useMarketplaceApi();
+ return useQuery({
+ queryKey: ['marketplaceApi', 'getPackageConfigByName', namespace, name],
+ queryFn: () => marketplaceApi.getPackageConfigByName?.(namespace, name),
+ staleTime: 0,
+ refetchOnMount: 'always',
+ refetchOnWindowFocus: false,
+ });
+};
diff --git a/workspaces/marketplace/plugins/marketplace/src/pages/DynamicMarketplacePluginRouter.tsx b/workspaces/marketplace/plugins/marketplace/src/pages/DynamicMarketplacePluginRouter.tsx
index e13a240182..6a01f1ecb0 100644
--- a/workspaces/marketplace/plugins/marketplace/src/pages/DynamicMarketplacePluginRouter.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/pages/DynamicMarketplacePluginRouter.tsx
@@ -14,110 +14,130 @@
* limitations under the License.
*/
-import type { ComponentType } from 'react';
-import { Routes, Route } from 'react-router-dom';
-
+import {
+ Routes,
+ Route,
+ useParams,
+ useLocation,
+ Navigate,
+} from 'react-router-dom';
import {
Page,
Header,
- TabbedLayout,
ErrorBoundary,
+ TabbedLayout,
} from '@backstage/core-components';
-
-import { useScalprum } from '@scalprum/react-core';
+import Alert from '@mui/material/Alert';
+import AlertTitle from '@mui/material/AlertTitle';
+import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined';
+import CategoryOutlinedIcon from '@mui/icons-material/CategoryOutlined';
+import Typography from '@mui/material/Typography';
import { themeId } from '../consts';
-
import { ReactQueryProvider } from '../components/ReactQueryProvider';
-
import { MarketplaceCatalogContent } from '../components/MarketplaceCatalogContent';
-
-// import { MarketplaceCollectionsGrid } from '../components/MarketplaceCollectionsGrid';
+import { InstalledPackagesTable } from '../components/InstalledPackages/InstalledPackagesTable';
+import { useInstalledPackagesCount } from '../hooks/useInstalledPackagesCount';
import { MarketplaceCollectionPage } from './MarketplaceCollectionPage';
-
-// import { MarketplacePluginsTable } from '../components/MarketplacePluginsTable';
import { MarketplacePluginDrawer } from '../components/MarketplacePluginDrawer';
import { MarketplacePluginInstallPage } from './MarketplacePluginInstallPage';
-
-// import { MarketplacePackagesTable } from '../components/MarketplacePackagesTable';
import { MarketplacePackageDrawer } from '../components/MarketplacePackageDrawer';
-import { MarketplacePackageInstallPage } from './MarketplacePackageInstallPage';
+import { MarketplacePackageEditPage } from './MarketplacePackageEditPage';
+import { InstallationContextProvider } from '../components/InstallationContext';
+import { useInstallationContext } from '../components/InstallationContext';
+
+// Constants for consistent styling
+const TAB_ICON_STYLE = {
+ display: 'inline-flex',
+ alignItems: 'center',
+ gap: '4px',
+ fontSize: '14px',
+} as const;
+
+const ICON_PROPS = {
+ fontSize: 'small' as const,
+ sx: { pr: '2px' },
+};
-export interface PluginTab {
- Component: ComponentType;
- config: {
- path: string;
- title: string;
- };
-}
+// Helper component for tab labels with icons
+const TabLabel = ({
+ icon,
+ children,
+}: {
+ icon: React.ReactElement;
+ children: React.ReactNode;
+}) => (
+
+ {icon} {children}
+
+);
-export interface ScalprumState {
- api?: {
- dynamicRootConfig?: {
- mountPoints?: {
- 'internal.plugins/tab': PluginTab[];
- };
- };
- };
-}
+const PackageDeepLinkRedirect = () => {
+ const { namespace, name } = useParams();
+ const location = useLocation();
+ const params = new URLSearchParams(location.search);
+ if (namespace && name) {
+ params.set('package', `${namespace}/${name}`);
+ }
+ return ;
+};
-const Tabs = () => {
- const scalprum = useScalprum();
+const MarketplacePage = () => {
+ const { count: installedPluginsCount, loading } = useInstalledPackagesCount();
+ const { installedPlugins } = useInstallationContext();
+ const restartCount = Object.entries(installedPlugins)?.length ?? 0;
- const tabs = scalprum.api?.dynamicRootConfig?.mountPoints?.[
- 'internal.plugins/tab'
- ] ?? [
- {
- Component: MarketplaceCatalogContent,
- config: {
- path: '',
- title: 'Catalog',
- },
- },
- ];
+ const installedPluginsTitle = loading
+ ? 'Installed packages'
+ : `Installed packages (${installedPluginsCount})`;
return (
<>
- {/*
+ }>
+ Catalog
+
+ ),
+ }}
+ >
- */}
-
- {tabs.map(({ Component, config }) => (
-
-
-
-
-
- ))}
-
- {/*
-
-
-
-
-
-
-
-
-
-
+
+ }>
+ {installedPluginsTitle}
+
+ ),
+ }}
+ >
-
+ {restartCount > 0 && (
+
+ Backend restart required
+ To finish the package modifications, restart your backend
+ system.
+
+ )}
+
+
- */}
+
{
/>
>
@@ -133,23 +153,26 @@ const Tabs = () => {
};
export const DynamicMarketplacePluginRouter = () => (
-
-
-
-
-
-
-
-
+
+
+
+
+
+ {/* Use existing install route as the edit page */}
+
+
+
+
+
);
export const DynamicMarketplacePluginContent = () => (
diff --git a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceFullPageRouter.tsx b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceFullPageRouter.tsx
index c0e9a0dd97..bca0e339a0 100644
--- a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceFullPageRouter.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceFullPageRouter.tsx
@@ -26,7 +26,7 @@ import { MarketplacePluginInstallPage } from './MarketplacePluginInstallPage';
import { MarketplacePackagesPage } from './MarketplacePackagesPage';
import { MarketplacePackagePage } from './MarketplacePackagePage';
-import { MarketplacePackageInstallPage } from './MarketplacePackageInstallPage';
+import { MarketplacePackageEditPage } from './MarketplacePackageEditPage';
import { MarketplaceCollectionsPage } from './MarketplaceCollectionsPage';
import { MarketplaceCollectionPage } from './MarketplaceCollectionPage';
@@ -60,7 +60,7 @@ export const MarketplaceFullPageRouter = () => {
/>
diff --git a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePackageInstallPage.tsx b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePackageEditPage.tsx
similarity index 54%
rename from workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePackageInstallPage.tsx
rename to workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePackageEditPage.tsx
index 33450d2f49..793a1fbd4c 100644
--- a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePackageInstallPage.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePackageEditPage.tsx
@@ -15,6 +15,7 @@
*/
import { useRouteRef, useRouteRefParams } from '@backstage/core-plugin-api';
+import { useLocation } from 'react-router-dom';
import {
Page,
Header,
@@ -26,32 +27,41 @@ import { themeId } from '../consts';
import { packageInstallRouteRef, packageRouteRef } from '../routes';
import { ReactQueryProvider } from '../components/ReactQueryProvider';
import { usePackage } from '../hooks/usePackage';
-import { MarketplacePackageInstallContentLoader } from '../components/MarketplacePackageInstallContent';
+import { MarketplacePackageEditContentLoader } from '../components/MarketplacePackageEditContent';
-const PackageInstallHeader = () => {
+const PackageEditHeader = () => {
const params = useRouteRefParams(packageInstallRouteRef);
+ const location = useLocation();
const pkg = usePackage(params.namespace, params.name);
const displayName = pkg.data?.metadata.title ?? params.name;
- const title = `Install ${displayName}`;
- const packageLink = useRouteRef(packageRouteRef)({
+ const title = `Edit ${displayName}`;
+ const baseLink = useRouteRef(packageRouteRef)({
namespace: params.namespace,
name: params.name,
});
+ const preserved = new URLSearchParams(location.search);
+ const packageLink = preserved.size ? `${baseLink}?${preserved}` : baseLink;
- return ;
+ return ;
};
-export const MarketplacePackageInstallPage = () => (
-
-
-
-
-
-
-
-
-
-
-);
+export const MarketplacePackageEditPage = () => {
+ const location = useLocation();
+ return (
+
+
+
+
+
+ {/* Force remount on navigation within same route to reseed editor */}
+
+
+
+
+
+ );
+};
diff --git a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePluginInstallPage.tsx b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePluginInstallPage.tsx
index a899503f99..ce1a9ffd38 100644
--- a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePluginInstallPage.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplacePluginInstallPage.tsx
@@ -75,7 +75,7 @@ const PluginInstallHeader = () => {
},
}}
>
-
+
);
};
diff --git a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceTabbedPageRouter.tsx b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceTabbedPageRouter.tsx
index ab34c5795f..262bb221d5 100644
--- a/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceTabbedPageRouter.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/pages/MarketplaceTabbedPageRouter.tsx
@@ -37,7 +37,7 @@ import { InstallationContextProvider } from '../components/InstallationContext';
import { useCollections } from '../hooks/useCollections';
-import { MarketplacePackageInstallPage } from './MarketplacePackageInstallPage';
+import { MarketplacePackageEditPage } from './MarketplacePackageEditPage';
const Tabs = () => {
const showCollections = !!useCollections({}).data?.items?.length;
@@ -91,7 +91,7 @@ export const MarketplaceTabbedPageRouter = () => (
/>
diff --git a/workspaces/marketplace/plugins/marketplace/src/plugin.ts b/workspaces/marketplace/plugins/marketplace/src/plugin.ts
index e39742b9ea..764e5ab859 100644
--- a/workspaces/marketplace/plugins/marketplace/src/plugin.ts
+++ b/workspaces/marketplace/plugins/marketplace/src/plugin.ts
@@ -27,10 +27,12 @@ import {
} from '@backstage/core-plugin-api';
import MUIMarketplaceIcon from '@mui/icons-material/ShoppingBasketOutlined';
+import MUIPluginsIcon from '@mui/icons-material/PowerOutlined';
import { MarketplaceBackendClient } from '@red-hat-developer-hub/backstage-plugin-marketplace-common';
-import { marketplaceApiRef } from './api';
+import { marketplaceApiRef, dynamicPluginsInfoApiRef } from './api';
+import { DynamicPluginsInfoClient } from './api/DynamicPluginsInfoClient';
import { allRoutes } from './routes';
/**
@@ -57,6 +59,18 @@ export const marketplacePlugin = createPlugin({
configApi,
}),
}),
+ createApiFactory({
+ api: dynamicPluginsInfoApiRef,
+ deps: {
+ discoveryApi: discoveryApiRef,
+ fetchApi: fetchApiRef,
+ },
+ factory: ({ discoveryApi, fetchApi }) =>
+ new DynamicPluginsInfoClient({
+ discoveryApi,
+ fetchApi,
+ }),
+ }),
],
});
@@ -91,6 +105,7 @@ export const MarketplaceTabbedPageRouter = marketplacePlugin.provide(
);
/**
+ * Main marketplace plugin router with tabs and sub-routes.
* @public
*/
export const DynamicMarketplacePluginRouter = marketplacePlugin.provide(
@@ -122,19 +137,22 @@ export const DynamicMarketplacePluginContent = marketplacePlugin.provide(
/**
* @public
*/
+export const MarketplaceIcon: IconComponent = MUIMarketplaceIcon;
+
+/**
+ * @public
+ */
+export const PluginsIcon: IconComponent = MUIPluginsIcon;
+
+/**
+ * @public
+ * @deprecated Use the latest RHDH. This no-op export remains only for backward compatibility and will be removed in a future major release.
+ */
export const InstallationContextProvider = marketplacePlugin.provide(
createComponentExtension({
name: 'InstallationContextProvider',
component: {
- lazy: () =>
- import('./components/InstallationContext').then(
- m => m.InstallationContextProvider,
- ),
+ sync: () => null,
},
}),
);
-
-/**
- * @public
- */
-export const MarketplaceIcon: IconComponent = MUIMarketplaceIcon;
diff --git a/workspaces/marketplace/plugins/marketplace/src/routes.ts b/workspaces/marketplace/plugins/marketplace/src/routes.ts
index f8112e850d..a4d480088d 100644
--- a/workspaces/marketplace/plugins/marketplace/src/routes.ts
+++ b/workspaces/marketplace/plugins/marketplace/src/routes.ts
@@ -68,6 +68,18 @@ export const collectionRouteRef = createSubRouteRef({
parent: rootRouteRef,
});
+export const catalogTabRouteRef = createSubRouteRef({
+ id: 'extensions/catalog',
+ path: '/catalog',
+ parent: rootRouteRef,
+});
+
+export const installedTabRouteRef = createSubRouteRef({
+ id: 'extensions/installed',
+ path: '/installed',
+ parent: rootRouteRef,
+});
+
export const allRoutes = {
rootRouteRef,
pluginsRouteRef,
@@ -78,4 +90,6 @@ export const allRoutes = {
packageInstallRouteRef,
collectionsRouteRef,
collectionRouteRef,
+ catalogTabRouteRef,
+ installedTabRouteRef,
};
diff --git a/workspaces/marketplace/plugins/marketplace/src/shared-components/SearchTextField.tsx b/workspaces/marketplace/plugins/marketplace/src/shared-components/SearchTextField.tsx
index 161d0f2cf4..acc137a642 100644
--- a/workspaces/marketplace/plugins/marketplace/src/shared-components/SearchTextField.tsx
+++ b/workspaces/marketplace/plugins/marketplace/src/shared-components/SearchTextField.tsx
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+import { useCallback, useEffect, useRef, useState } from 'react';
import FormControl from '@mui/material/FormControl';
import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment';
@@ -32,6 +33,24 @@ export interface SearchTextFieldProps {
export const SearchTextField = (props: SearchTextFieldProps) => {
const fullTextSearch = useQueryFullTextSearch();
+ const timerRef = useRef(undefined);
+ const [inputValue, setInputValue] = useState(
+ fullTextSearch.current || '',
+ );
+
+ // Keep local input in sync if URL param changes externally (e.g., back/forward)
+ useEffect(() => {
+ setInputValue(fullTextSearch.current || '');
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [fullTextSearch]);
+
+ useEffect(() => {
+ return () => {
+ if (timerRef.current) {
+ window.clearTimeout(timerRef.current);
+ }
+ };
+ }, []);
const options =
props.variant === 'search'
@@ -52,8 +71,20 @@ export const SearchTextField = (props: SearchTextFieldProps) => {
variant="standard"
hiddenLabel
placeholder={options.placeholder}
- value={fullTextSearch.current}
- onChange={fullTextSearch.onChange}
+ value={inputValue}
+ onChange={useCallback(
+ (e: React.ChangeEvent) => {
+ const value = e.target.value;
+ setInputValue(value);
+ if (timerRef.current) {
+ window.clearTimeout(timerRef.current);
+ }
+ timerRef.current = window.setTimeout(() => {
+ fullTextSearch.onChange({ target: { value } } as any);
+ }, 300) as unknown as number;
+ },
+ [fullTextSearch],
+ )}
InputProps={{
startAdornment: (
@@ -66,7 +97,13 @@ export const SearchTextField = (props: SearchTextFieldProps) => {
{
+ if (timerRef.current) {
+ window.clearTimeout(timerRef.current);
+ }
+ setInputValue('');
+ fullTextSearch.clear();
+ }}
aria-label={options.clear}
>
diff --git a/workspaces/marketplace/plugins/marketplace/src/utils/pluginProcessing.test.ts b/workspaces/marketplace/plugins/marketplace/src/utils/pluginProcessing.test.ts
new file mode 100644
index 0000000000..496ae955f4
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/utils/pluginProcessing.test.ts
@@ -0,0 +1,290 @@
+/*
+ * Copyright 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 { DynamicPluginInfo } from '../api';
+import {
+ getReadableName,
+ getBasePluginName,
+ processPluginsForDisplay,
+ getUniquePluginsCount,
+} from './pluginProcessing';
+
+describe('pluginProcessing', () => {
+ describe('getReadableName', () => {
+ it('should handle Red Hat Developer Hub plugins', () => {
+ expect(
+ getReadableName(
+ '@red-hat-developer-hub/backstage-plugin-marketplace-dynamic',
+ ),
+ ).toBe('Marketplace');
+ expect(
+ getReadableName(
+ '@red-hat-developer-hub/backstage-plugin-global-header-dynamic',
+ ),
+ ).toBe('Global Header');
+ expect(
+ getReadableName(
+ '@red-hat-developer-hub/backstage-plugin-quickstart-dynamic',
+ ),
+ ).toBe('Quickstart');
+ });
+
+ it('should handle Red Hat Developer Hub plugins with backend suffix', () => {
+ expect(
+ getReadableName(
+ '@red-hat-developer-hub/backstage-plugin-marketplace-backend-dynamic',
+ ),
+ ).toBe('Marketplace');
+ });
+
+ it('should handle Red Hat Developer Hub display names with spaces', () => {
+ expect(
+ getReadableName('@Red Hat Developer Hub/Backstage Plugin Marketplace'),
+ ).toBe('Marketplace');
+ expect(
+ getReadableName('@Red Hat Developer Hub/Backstage Plugin Quickstart'),
+ ).toBe('Quickstart');
+ expect(
+ getReadableName(
+ '@Red Hat Developer Hub/Backstage Plugin Dynamic Home Page',
+ ),
+ ).toBe('Dynamic Home Page');
+ });
+
+ it('should handle community plugins', () => {
+ expect(getReadableName('@backstage-community/plugin-acr-dynamic')).toBe(
+ 'Acr',
+ );
+ expect(
+ getReadableName(
+ 'backstage-community-plugin-analytics-provider-segment',
+ ),
+ ).toBe('Analytics Provider Segment');
+ });
+
+ it('should handle official Backstage plugins', () => {
+ expect(getReadableName('@backstage/plugin-catalog-backend')).toBe(
+ 'Catalog',
+ );
+ expect(getReadableName('backstage-plugin-kubernetes')).toBe('Kubernetes');
+ });
+
+ it('should remove various suffixes', () => {
+ expect(getReadableName('some-plugin-dynamic')).toBe('Some Plugin');
+ expect(getReadableName('some-plugin-backend')).toBe('Some Plugin');
+ expect(getReadableName('some-plugin-frontend')).toBe('Some Plugin');
+ });
+
+ it('should handle edge cases', () => {
+ expect(getReadableName('')).toBe('');
+ expect(getReadableName('simple-name')).toBe('Simple Name');
+ expect(getReadableName('already-capitalized-name')).toBe(
+ 'Already Capitalized Name',
+ );
+ });
+ });
+
+ describe('getBasePluginName', () => {
+ it('should normalize Red Hat Developer Hub plugins for deduplication', () => {
+ expect(
+ getBasePluginName(
+ '@red-hat-developer-hub/backstage-plugin-marketplace-dynamic',
+ ),
+ ).toBe('marketplace');
+ expect(
+ getBasePluginName(
+ '@red-hat-developer-hub/backstage-plugin-marketplace-backend-dynamic',
+ ),
+ ).toBe('marketplace');
+ expect(
+ getBasePluginName(
+ '@red-hat-developer-hub/backstage-plugin-global-header-dynamic',
+ ),
+ ).toBe('global-header');
+ });
+
+ it('should normalize display names to base names', () => {
+ expect(
+ getBasePluginName(
+ '@Red Hat Developer Hub/Backstage Plugin Marketplace',
+ ),
+ ).toBe('marketplace');
+ expect(
+ getBasePluginName(
+ '@Red Hat Developer Hub/Backstage Plugin Dynamic Home Page',
+ ),
+ ).toBe('dynamic-home-page');
+ });
+
+ it('should handle community plugins', () => {
+ expect(getBasePluginName('@backstage-community/plugin-acr-dynamic')).toBe(
+ 'acr',
+ );
+ expect(
+ getBasePluginName(
+ 'backstage-community-plugin-analytics-provider-segment',
+ ),
+ ).toBe('analytics-provider-segment');
+ });
+
+ it('should remove frontend/backend suffixes for proper grouping', () => {
+ expect(getBasePluginName('some-plugin-frontend')).toBe('some-plugin');
+ expect(getBasePluginName('some-plugin-backend')).toBe('some-plugin');
+ expect(getBasePluginName('some-plugin-dynamic')).toBe('some-plugin');
+ });
+ });
+
+ describe('processPluginsForDisplay', () => {
+ const mockPlugins: DynamicPluginInfo[] = [
+ {
+ name: '@red-hat-developer-hub/backstage-plugin-marketplace-dynamic',
+ version: '0.9.2',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ {
+ name: '@red-hat-developer-hub/backstage-plugin-marketplace-backend-dynamic',
+ version: '0.8.0',
+ role: 'backend-plugin',
+ platform: 'node',
+ },
+ {
+ name: '@red-hat-developer-hub/backstage-plugin-global-header-dynamic',
+ version: '1.15.1',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ {
+ name: '@backstage-community/plugin-acr-dynamic',
+ version: '1.12.1',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ ];
+
+ it('should deduplicate plugins with frontend/backend variants', () => {
+ const result = processPluginsForDisplay(mockPlugins);
+
+ // Should have 3 unique plugins (marketplace deduped, global-header, acr)
+ expect(result).toHaveLength(3);
+
+ // Check that marketplace is present (should prefer frontend)
+ const marketplacePlugin = result.find(p => p.name === 'Marketplace');
+ expect(marketplacePlugin).toBeDefined();
+ expect(marketplacePlugin?.role).toBe('frontend-plugin');
+ expect(marketplacePlugin?.version).toBe('0.9.2'); // Frontend version
+ });
+
+ it('should prefer frontend plugins over backend when deduplicating', () => {
+ const result = processPluginsForDisplay(mockPlugins);
+ const marketplacePlugin = result.find(p => p.name === 'Marketplace');
+
+ expect(marketplacePlugin?.role).toBe('frontend-plugin');
+ expect(marketplacePlugin?.platform).toBe('web');
+ });
+
+ it('should preserve single plugins without duplicates', () => {
+ const result = processPluginsForDisplay(mockPlugins);
+
+ const globalHeaderPlugin = result.find(p => p.name === 'Global Header');
+ expect(globalHeaderPlugin).toBeDefined();
+ expect(globalHeaderPlugin?.version).toBe('1.15.1');
+
+ const acrPlugin = result.find(p => p.name === 'Acr');
+ expect(acrPlugin).toBeDefined();
+ expect(acrPlugin?.version).toBe('1.12.1');
+ });
+
+ it('should handle empty plugin list', () => {
+ const result = processPluginsForDisplay([]);
+ expect(result).toHaveLength(0);
+ });
+
+ it('should handle plugins with same base name but both frontend', () => {
+ const plugins: DynamicPluginInfo[] = [
+ {
+ name: 'plugin-test-frontend',
+ version: '1.0.0',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ {
+ name: 'plugin-test-frontend-v2',
+ version: '2.0.0',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ ];
+
+ const result = processPluginsForDisplay(plugins);
+ // Should keep both since they have different base names
+ expect(result).toHaveLength(2);
+ });
+ });
+
+ describe('getUniquePluginsCount', () => {
+ const mockPlugins: DynamicPluginInfo[] = [
+ {
+ name: '@red-hat-developer-hub/backstage-plugin-marketplace-dynamic',
+ version: '0.9.2',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ {
+ name: '@red-hat-developer-hub/backstage-plugin-marketplace-backend-dynamic',
+ version: '0.8.0',
+ role: 'backend-plugin',
+ platform: 'node',
+ },
+ {
+ name: '@red-hat-developer-hub/backstage-plugin-global-header-dynamic',
+ version: '1.15.1',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ ];
+
+ it('should return correct count after deduplication', () => {
+ const count = getUniquePluginsCount(mockPlugins);
+ expect(count).toBe(2); // marketplace (deduped) + global-header
+ });
+
+ it('should return 0 for empty plugin list', () => {
+ const count = getUniquePluginsCount([]);
+ expect(count).toBe(0);
+ });
+
+ it('should return correct count for plugins without duplicates', () => {
+ const uniquePlugins: DynamicPluginInfo[] = [
+ {
+ name: 'plugin-a',
+ version: '1.0.0',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ {
+ name: 'plugin-b',
+ version: '1.0.0',
+ role: 'frontend-plugin',
+ platform: 'web',
+ },
+ ];
+
+ const count = getUniquePluginsCount(uniquePlugins);
+ expect(count).toBe(2);
+ });
+ });
+});
diff --git a/workspaces/marketplace/plugins/marketplace/src/utils/pluginProcessing.ts b/workspaces/marketplace/plugins/marketplace/src/utils/pluginProcessing.ts
new file mode 100644
index 0000000000..3811be7351
--- /dev/null
+++ b/workspaces/marketplace/plugins/marketplace/src/utils/pluginProcessing.ts
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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 { DynamicPluginInfo } from '../api';
+
+// Helper function to convert package name to readable display name
+export const getReadableName = (packageName: string): string => {
+ // Remove common prefixes and convert to readable format
+ const readableName = packageName
+ .replace(/^@Red Hat Developer Hub\/Backstage Plugin\s*/, '') // Red Hat plugins (display names)
+ .replace(/^@red-hat-developer-hub\/backstage-plugin-/, '') // Red Hat plugins (package names)
+ .replace(/^red-hat-developer-hub-backstage-plugin-/, '') // Red Hat kebab-case
+ .replace(/^@backstage-community\/plugin-/, '') // Community plugins
+ .replace(/^backstage-community-plugin-/, '') // Community plugins alt
+ .replace(/^@backstage\/plugin-/, '') // Official Backstage plugins
+ .replace(/^backstage-plugin-/, '') // Generic backstage plugins
+ .replace(/-dynamic$/, '') // Remove -dynamic suffix
+ .replace(/-backend$/, '') // Remove -backend suffix
+ .replace(/-frontend$/, '') // Remove -frontend suffix
+ .replace(/^[\s-]+/, '') // Remove leading spaces or dashes
+ .replace(/-/g, ' ') // Convert remaining dashes to spaces
+ .replace(/\b\w/g, l => l.toUpperCase()); // Capitalize first letter of each word
+
+ return readableName;
+};
+
+// Helper function to extract base plugin name (without frontend/backend suffix)
+export const getBasePluginName = (packageName: string): string => {
+ const baseName = packageName
+ .replace(/^@Red Hat Developer Hub\/Backstage Plugin\s*/, '') // Red Hat plugins (display names)
+ .replace(/^@red-hat-developer-hub\/backstage-plugin-/, '') // Red Hat plugins (package names)
+ .replace(/^red-hat-developer-hub-backstage-plugin-/, '') // Red Hat kebab-case
+ .replace(/^@backstage-community\/plugin-/, '') // Community plugins
+ .replace(/^backstage-community-plugin-/, '') // Community plugins alt
+ .replace(/^@backstage\/plugin-/, '') // Official Backstage plugins
+ .replace(/^backstage-plugin-/, '') // Generic backstage plugins
+ .replace(/-dynamic$/, '') // Remove -dynamic suffix
+ .replace(/-frontend$/, '') // Remove -frontend suffix
+ .replace(/-backend$/, '') // Remove -backend suffix
+ .replace(/^[\s-]+/, '') // Remove leading spaces or dashes
+ .toLowerCase() // Normalize to lowercase for consistent comparison
+ .replace(/\s+/g, '-'); // Convert spaces back to dashes for base name
+
+ return baseName;
+};
+
+// Process plugins to create single rows with readable names
+export const processPluginsForDisplay = (
+ plugins: DynamicPluginInfo[],
+): DynamicPluginInfo[] => {
+ const pluginMap = new Map();
+
+ plugins.forEach(plugin => {
+ const basePluginName = getBasePluginName(plugin.name);
+ const existingPlugin = pluginMap.get(basePluginName);
+
+ if (
+ !existingPlugin ||
+ (plugin.role === 'frontend-plugin' &&
+ existingPlugin.role !== 'frontend-plugin')
+ ) {
+ // First occurrence of this plugin OR prefer frontend over backend
+ pluginMap.set(basePluginName, {
+ ...plugin,
+ name: getReadableName(plugin.name),
+ });
+ }
+ // If both are frontend or both are backend, keep the first one
+ });
+
+ return Array.from(pluginMap.values());
+};
+
+// Get the count of unique plugins (after deduplication)
+export const getUniquePluginsCount = (plugins: DynamicPluginInfo[]): number => {
+ return processPluginsForDisplay(plugins).length;
+};
diff --git a/workspaces/marketplace/rbac-policy.csv b/workspaces/marketplace/rbac-policy.csv
new file mode 100644
index 0000000000..4ee475637a
--- /dev/null
+++ b/workspaces/marketplace/rbac-policy.csv
@@ -0,0 +1,14 @@
+g, user:default/ciiay, role:default/admins
+p, role:default/admins, catalog-entity, read, allow
+p, role:default/admins, catalog.entity.create, create, allow
+p, role:default/admins, catalog.location.read , read, allow
+p, role:default/admins, catalog.location.create , create, allow
+p, role:default/admins, policy.entity.read, read, allow
+p, role:default/admins, policy.entity.delete, delete, allow
+p, role:default/admins, policy.entity.update, update, allow
+p, role:default/admins, policy.entity.create, create, allow
+p, role:default/admins, kubernetes.clusters.read, read, allow
+p, role:default/admins, kubernetes.resources.read, read, allow
+
+p, role:default/admins, extensions-plugin, read, allow
+p, role:default/admins, extensions-plugin, create, allow
diff --git a/workspaces/marketplace/yarn.lock b/workspaces/marketplace/yarn.lock
index 8ad412d478..5d6cdbb00c 100644
--- a/workspaces/marketplace/yarn.lock
+++ b/workspaces/marketplace/yarn.lock
@@ -33,14 +33,14 @@ __metadata:
languageName: node
linkType: hard
-"@apidevtools/json-schema-ref-parser@npm:^11.7.0":
- version: 11.7.2
- resolution: "@apidevtools/json-schema-ref-parser@npm:11.7.2"
+"@apidevtools/json-schema-ref-parser@npm:^14.0.3":
+ version: 14.2.0
+ resolution: "@apidevtools/json-schema-ref-parser@npm:14.2.0"
dependencies:
- "@jsdevtools/ono": ^7.1.3
- "@types/json-schema": ^7.0.15
js-yaml: ^4.1.0
- checksum: 44096e5cd5a03b17ee5eb0a3b9e9a4db85d87da8ae2abda264eae615f2a43e3e6ba5ca208e1161d4d946755b121c10a9550e88792a725951f2c4cff6df0d8a19
+ peerDependencies:
+ "@types/json-schema": ^7.0.15
+ checksum: fff43815d0957718b0e66c066944396a9f30e312516d60a0ce3eb4d8f6f8833bdeca059565b0e1a35da89645e4123bd5357249effe04997dc40b4e8b3a0f6981
languageName: node
linkType: hard
@@ -2755,10 +2755,10 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
- version: 7.27.1
- resolution: "@babel/runtime@npm:7.27.1"
- checksum: 11339838a54783e5b14e04d94d7a4d032e9965c5823f3f687e41556fa40344ae7aeb57c535720b7a74ab3e8217def7834a6f1a665ee55bbb3befede141419913
+"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.19.0, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.6, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.28.3, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
+ version: 7.28.3
+ resolution: "@babel/runtime@npm:7.28.3"
+ checksum: dd22662b9e02b6e66cfb061d6f9730eb0aa3b3a390a7bd70fe9a64116d86a3704df6d54ab978cb4acc13b58dbf63a3d7dd4616b0b87030eb14a22835e0aa602d
languageName: node
linkType: hard
@@ -2902,14 +2902,14 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/backend-app-api@npm:^1.2.2, @backstage/backend-app-api@npm:^1.2.5":
- version: 1.2.5
- resolution: "@backstage/backend-app-api@npm:1.2.5"
+"@backstage/backend-app-api@npm:^1.2.2, @backstage/backend-app-api@npm:^1.2.5, @backstage/backend-app-api@npm:^1.2.6":
+ version: 1.2.6
+ resolution: "@backstage/backend-app-api@npm:1.2.6"
dependencies:
- "@backstage/backend-plugin-api": ^1.4.1
+ "@backstage/backend-plugin-api": ^1.4.2
"@backstage/config": ^1.3.3
"@backstage/errors": ^1.2.7
- checksum: b0c43dbe6671f736ae5fd405262b3221e7a908b5c341ca816f7a828f9cb8a74e6275b2a77ef98e37d6a34b3d78a72af2a85b7c9e8a36b8fa96856fb515ecaac3
+ checksum: 9e15ab6e7125663d24d2697091f1f70fb873d41549ed4b05c283ea1e86f38af8aa51939e4a366729204697effd4f0a10c9c6db586fa62e2b6d7ce163dcbd2d55
languageName: node
linkType: hard
@@ -3073,6 +3073,90 @@ __metadata:
languageName: node
linkType: hard
+"@backstage/backend-defaults@npm:^0.12.0":
+ version: 0.12.0
+ resolution: "@backstage/backend-defaults@npm:0.12.0"
+ dependencies:
+ "@aws-sdk/abort-controller": ^3.347.0
+ "@aws-sdk/client-codecommit": ^3.350.0
+ "@aws-sdk/client-s3": ^3.350.0
+ "@aws-sdk/credential-providers": ^3.350.0
+ "@aws-sdk/types": ^3.347.0
+ "@azure/storage-blob": ^12.5.0
+ "@backstage/backend-app-api": ^1.2.6
+ "@backstage/backend-dev-utils": ^0.1.5
+ "@backstage/backend-plugin-api": ^1.4.2
+ "@backstage/cli-node": ^0.2.14
+ "@backstage/config": ^1.3.3
+ "@backstage/config-loader": ^1.10.2
+ "@backstage/errors": ^1.2.7
+ "@backstage/integration": ^1.17.1
+ "@backstage/integration-aws-node": ^0.1.17
+ "@backstage/plugin-auth-node": ^0.6.6
+ "@backstage/plugin-events-node": ^0.4.14
+ "@backstage/plugin-permission-node": ^0.10.3
+ "@backstage/types": ^1.2.1
+ "@google-cloud/storage": ^7.0.0
+ "@keyv/memcache": ^2.0.1
+ "@keyv/redis": ^4.0.1
+ "@keyv/valkey": ^1.0.1
+ "@manypkg/get-packages": ^1.1.3
+ "@octokit/rest": ^19.0.3
+ "@opentelemetry/api": ^1.9.0
+ "@types/cors": ^2.8.6
+ "@types/express": ^4.17.6
+ archiver: ^7.0.0
+ base64-stream: ^1.0.0
+ better-sqlite3: ^12.0.0
+ compression: ^1.7.4
+ concat-stream: ^2.0.0
+ cookie: ^0.7.0
+ cors: ^2.8.5
+ cron: ^3.0.0
+ express: ^4.17.1
+ express-promise-router: ^4.1.0
+ express-rate-limit: ^7.5.0
+ fs-extra: ^11.2.0
+ git-url-parse: ^15.0.0
+ helmet: ^6.0.0
+ infinispan: ^0.12.0
+ is-glob: ^4.0.3
+ jose: ^5.0.0
+ keyv: ^5.2.1
+ knex: ^3.0.0
+ lodash: ^4.17.21
+ logform: ^2.3.2
+ luxon: ^3.0.0
+ minimatch: ^9.0.0
+ mysql2: ^3.0.0
+ node-fetch: ^2.7.0
+ node-forge: ^1.3.1
+ p-limit: ^3.1.0
+ path-to-regexp: ^8.0.0
+ pg: ^8.11.3
+ pg-connection-string: ^2.3.0
+ pg-format: ^1.0.4
+ rate-limit-redis: ^4.2.0
+ raw-body: ^2.4.1
+ selfsigned: ^2.0.0
+ tar: ^6.1.12
+ triple-beam: ^1.4.1
+ uuid: ^11.0.0
+ winston: ^3.2.1
+ winston-transport: ^4.5.0
+ yauzl: ^3.0.0
+ yn: ^4.0.0
+ zod: ^3.22.4
+ zod-to-json-schema: ^3.20.4
+ peerDependencies:
+ "@google-cloud/cloud-sql-connector": ^1.4.0
+ peerDependenciesMeta:
+ "@google-cloud/cloud-sql-connector":
+ optional: true
+ checksum: bacb5c88246eb9aa7731bb0f46ee43c3b90faf5eca24735a4d2fe3cd7575a0404441158667e77a7acaf9fcd3755722de9066792c3e718bee39c8c480dfb78209
+ languageName: node
+ linkType: hard
+
"@backstage/backend-defaults@npm:^0.9.0":
version: 0.9.0
resolution: "@backstage/backend-defaults@npm:0.9.0"
@@ -3158,27 +3242,27 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/backend-dynamic-feature-service@npm:^0.7.2":
- version: 0.7.2
- resolution: "@backstage/backend-dynamic-feature-service@npm:0.7.2"
+"@backstage/backend-dynamic-feature-service@npm:^0.7.2, @backstage/backend-dynamic-feature-service@npm:^0.7.3":
+ version: 0.7.3
+ resolution: "@backstage/backend-dynamic-feature-service@npm:0.7.3"
dependencies:
- "@backstage/backend-defaults": ^0.11.1
- "@backstage/backend-openapi-utils": ^0.5.5
- "@backstage/backend-plugin-api": ^1.4.1
+ "@backstage/backend-defaults": ^0.12.0
+ "@backstage/backend-openapi-utils": ^0.6.0
+ "@backstage/backend-plugin-api": ^1.4.2
"@backstage/cli-common": ^0.1.15
- "@backstage/cli-node": ^0.2.13
+ "@backstage/cli-node": ^0.2.14
"@backstage/config": ^1.3.3
"@backstage/config-loader": ^1.10.2
"@backstage/errors": ^1.2.7
- "@backstage/plugin-app-node": ^0.1.35
- "@backstage/plugin-auth-node": ^0.6.5
- "@backstage/plugin-catalog-backend": ^3.0.0
- "@backstage/plugin-events-backend": ^0.5.4
- "@backstage/plugin-events-node": ^0.4.13
+ "@backstage/plugin-app-node": ^0.1.36
+ "@backstage/plugin-auth-node": ^0.6.6
+ "@backstage/plugin-catalog-backend": ^3.0.1
+ "@backstage/plugin-events-backend": ^0.5.5
+ "@backstage/plugin-events-node": ^0.4.14
"@backstage/plugin-permission-common": ^0.9.1
- "@backstage/plugin-permission-node": ^0.10.2
- "@backstage/plugin-scaffolder-node": ^0.10.0
- "@backstage/plugin-search-backend-node": ^1.3.13
+ "@backstage/plugin-permission-node": ^0.10.3
+ "@backstage/plugin-scaffolder-node": ^0.11.0
+ "@backstage/plugin-search-backend-node": ^1.3.14
"@backstage/plugin-search-common": ^1.2.19
"@backstage/types": ^1.2.1
"@manypkg/get-packages": ^1.1.3
@@ -3189,7 +3273,7 @@ __metadata:
fs-extra: ^11.2.0
lodash: ^4.17.21
winston: ^3.2.1
- checksum: f0e725c05d5de64fb0ca1371ba06d32abefc2cc430604b7a6b82d7e4e5f014c3b9234c4663862387cea8728cbf2c5e294d06e84d9823e23351bc7b606d6b6536
+ checksum: 8c4ff098fd3510b905b4347ba2e38476edd2b8630032787dbaf42787f28e2133270bde6f88bc6ccc1d116410025633844d3b4b6b479c295861d27ba8d9fb5ae3
languageName: node
linkType: hard
@@ -3217,16 +3301,40 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/backend-plugin-api@npm:^1.0.0, @backstage/backend-plugin-api@npm:^1.1.1, @backstage/backend-plugin-api@npm:^1.3.0, @backstage/backend-plugin-api@npm:^1.4.1":
- version: 1.4.1
- resolution: "@backstage/backend-plugin-api@npm:1.4.1"
+"@backstage/backend-openapi-utils@npm:^0.6.0":
+ version: 0.6.0
+ resolution: "@backstage/backend-openapi-utils@npm:0.6.0"
+ dependencies:
+ "@apidevtools/swagger-parser": ^10.1.0
+ "@backstage/backend-plugin-api": ^1.4.2
+ "@backstage/errors": ^1.2.7
+ "@backstage/types": ^1.2.1
+ "@types/express": ^4.17.6
+ "@types/express-serve-static-core": ^4.17.5
+ ajv: ^8.16.0
+ express: ^4.17.1
+ express-openapi-validator: ^5.5.8
+ express-promise-router: ^4.1.0
+ get-port: ^5.1.1
+ json-schema-to-ts: ^3.0.0
+ lodash: ^4.17.21
+ mockttp: ^3.13.0
+ openapi-merge: ^1.3.2
+ openapi3-ts: ^3.1.2
+ checksum: 81dddb23261cacb8b1a2e08d788dd96942610d4f2c094ffbd20de88933c142237c86776f73a0c30c040e0e5c58b882220af374cd9f662e17e966a97d8b7cc64b
+ languageName: node
+ linkType: hard
+
+"@backstage/backend-plugin-api@npm:^1.0.0, @backstage/backend-plugin-api@npm:^1.1.1, @backstage/backend-plugin-api@npm:^1.3.0, @backstage/backend-plugin-api@npm:^1.4.1, @backstage/backend-plugin-api@npm:^1.4.2":
+ version: 1.4.2
+ resolution: "@backstage/backend-plugin-api@npm:1.4.2"
dependencies:
"@backstage/cli-common": ^0.1.15
"@backstage/config": ^1.3.3
"@backstage/errors": ^1.2.7
- "@backstage/plugin-auth-node": ^0.6.5
+ "@backstage/plugin-auth-node": ^0.6.6
"@backstage/plugin-permission-common": ^0.9.1
- "@backstage/plugin-permission-node": ^0.10.2
+ "@backstage/plugin-permission-node": ^0.10.3
"@backstage/types": ^1.2.1
"@types/express": ^4.17.6
"@types/json-schema": ^7.0.6
@@ -3235,7 +3343,7 @@ __metadata:
knex: ^3.0.0
luxon: ^3.0.0
zod: ^3.22.4
- checksum: 5adb68945bbdc8323dff2e8bae6ecaba73c4ee02b1f1afbc3b5ad53374f3e2c4473ed79f0aac951205334aee56f2b98eb1bcc091b8d28c390a0288a25d72feb6
+ checksum: 4aa4ff08a1166f21ff3abc58036ac240b4ca1ebb61132d724119e2f98a76d53a296bb786d3abfbc70511817de7cb6eb08f5060f13b8b0254b6464ce337812f5c
languageName: node
linkType: hard
@@ -3278,15 +3386,15 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/catalog-client@npm:^1.10.2, @backstage/catalog-client@npm:^1.9.1":
- version: 1.10.2
- resolution: "@backstage/catalog-client@npm:1.10.2"
+"@backstage/catalog-client@npm:^1.10.2, @backstage/catalog-client@npm:^1.11.0, @backstage/catalog-client@npm:^1.9.1":
+ version: 1.11.0
+ resolution: "@backstage/catalog-client@npm:1.11.0"
dependencies:
"@backstage/catalog-model": ^1.7.5
"@backstage/errors": ^1.2.7
cross-fetch: ^4.0.0
uri-template: ^2.0.0
- checksum: 43745b13c70b2fc0b8f82a4e0074a1af7efc32b1671b3fb8e7024e7aea016f156737a567efa5505d03117f71a618ad5243fa73005cf8bc1fe0aec11f19b4bf3b
+ checksum: 8288c6136992ba037b831b89683ac210157efc2241e877eebc0a37fc9a108cf0e1bcc629e48eeca79749a7f34b531fd1138a68c5b292c0a5e3f5b5490b2d9406
languageName: node
linkType: hard
@@ -3309,9 +3417,9 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/cli-node@npm:^0.2.13":
- version: 0.2.13
- resolution: "@backstage/cli-node@npm:0.2.13"
+"@backstage/cli-node@npm:^0.2.13, @backstage/cli-node@npm:^0.2.14":
+ version: 0.2.14
+ resolution: "@backstage/cli-node@npm:0.2.14"
dependencies:
"@backstage/cli-common": ^0.1.15
"@backstage/errors": ^1.2.7
@@ -3321,7 +3429,7 @@ __metadata:
fs-extra: ^11.2.0
semver: ^7.5.3
zod: ^3.22.4
- checksum: d1ce09a728b181db1eae0931ffee81795cd177d32f319edcab1eee8e624820a5bf23c28a9fc70e9883522fc7ded52be232718cc18e418d9febe34ef998737c72
+ checksum: a5523f8cfe37a851bb29040d4421d76b21dc17a665584edb99fa483a5f19adfd18765cf5dd2bfce3363356749b67710daff933012f040fe2dd33e0045fc6d44b
languageName: node
linkType: hard
@@ -3945,16 +4053,16 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-app-node@npm:^0.1.35":
- version: 0.1.35
- resolution: "@backstage/plugin-app-node@npm:0.1.35"
+"@backstage/plugin-app-node@npm:^0.1.35, @backstage/plugin-app-node@npm:^0.1.36":
+ version: 0.1.36
+ resolution: "@backstage/plugin-app-node@npm:0.1.36"
dependencies:
- "@backstage/backend-plugin-api": ^1.4.1
+ "@backstage/backend-plugin-api": ^1.4.2
"@backstage/config-loader": ^1.10.2
"@types/express": ^4.17.6
express: ^4.17.1
fs-extra: ^11.2.0
- checksum: b54d93835dcba6f7fc79f894c3de201b5b77a76eb5d9d25fc2f21ed6650dd0ae8d46ce57b90dbe8f86abd998af3788a66c0c1c4cab02b451ab9310a73a539a84
+ checksum: ffb490e422ab6e371a15e4e9f00d92b71942dfe59cc9abc57bacfd479c6c4a493d4d7b7efa5f5b3b7fe375dc6deb1d47667b281e668b93c745a3586b617834d6
languageName: node
linkType: hard
@@ -4063,12 +4171,12 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-auth-node@npm:^0.6.2, @backstage/plugin-auth-node@npm:^0.6.5":
- version: 0.6.5
- resolution: "@backstage/plugin-auth-node@npm:0.6.5"
+"@backstage/plugin-auth-node@npm:^0.6.2, @backstage/plugin-auth-node@npm:^0.6.5, @backstage/plugin-auth-node@npm:^0.6.6":
+ version: 0.6.6
+ resolution: "@backstage/plugin-auth-node@npm:0.6.6"
dependencies:
- "@backstage/backend-plugin-api": ^1.4.1
- "@backstage/catalog-client": ^1.10.2
+ "@backstage/backend-plugin-api": ^1.4.2
+ "@backstage/catalog-client": ^1.11.0
"@backstage/catalog-model": ^1.7.5
"@backstage/config": ^1.3.3
"@backstage/errors": ^1.2.7
@@ -4082,7 +4190,7 @@ __metadata:
zod: ^3.22.4
zod-to-json-schema: ^3.21.4
zod-validation-error: ^3.4.0
- checksum: 04c3140ef4d40579eda27069a23516f13156614a2d71a15b66eb99c240df8b9e992dda8f026236becdbec366196bcd3a068527e70b5aa71c4d20a92d59f5b9b2
+ checksum: cb3e69133b2d27bd0d10a5bd65a0a2f23caf4211de8137713d0fd398e93e808768d5e45150782b3f8c562cf0921f67ecb52e336bf3fb33bbf5b74bce03e25370
languageName: node
linkType: hard
@@ -4141,22 +4249,22 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-catalog-backend@npm:^3.0.0":
- version: 3.0.0
- resolution: "@backstage/plugin-catalog-backend@npm:3.0.0"
+"@backstage/plugin-catalog-backend@npm:^3.0.0, @backstage/plugin-catalog-backend@npm:^3.0.1":
+ version: 3.0.1
+ resolution: "@backstage/plugin-catalog-backend@npm:3.0.1"
dependencies:
- "@backstage/backend-openapi-utils": ^0.5.5
- "@backstage/backend-plugin-api": ^1.4.1
- "@backstage/catalog-client": ^1.10.2
+ "@backstage/backend-openapi-utils": ^0.6.0
+ "@backstage/backend-plugin-api": ^1.4.2
+ "@backstage/catalog-client": ^1.11.0
"@backstage/catalog-model": ^1.7.5
"@backstage/config": ^1.3.3
"@backstage/errors": ^1.2.7
"@backstage/integration": ^1.17.1
"@backstage/plugin-catalog-common": ^1.1.5
- "@backstage/plugin-catalog-node": ^1.17.2
- "@backstage/plugin-events-node": ^0.4.13
+ "@backstage/plugin-catalog-node": ^1.18.0
+ "@backstage/plugin-events-node": ^0.4.14
"@backstage/plugin-permission-common": ^0.9.1
- "@backstage/plugin-permission-node": ^0.10.2
+ "@backstage/plugin-permission-node": ^0.10.3
"@backstage/types": ^1.2.1
"@opentelemetry/api": ^1.9.0
codeowners-utils: ^1.0.2
@@ -4176,7 +4284,7 @@ __metadata:
yaml: ^2.0.0
yn: ^4.0.0
zod: ^3.22.4
- checksum: 263a66e85da2defa074e856a186ed679a0b20766d26581f6de96fe870e022b10d40ccc00da01dfb6abc04f1626cdbf1cf79d339f8b342ec358780d375690d709
+ checksum: 5d8dcbbe8acccded9dfd1af0a453262b74c8be667e36ece55959121353f19cbf83fc7611f72cb7f726fd3eea5526e48e28a7b728a4b067f3e0d42f8c5cff66ff
languageName: node
linkType: hard
@@ -4261,21 +4369,21 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-catalog-node@npm:^1.17.2":
- version: 1.17.2
- resolution: "@backstage/plugin-catalog-node@npm:1.17.2"
+"@backstage/plugin-catalog-node@npm:^1.17.2, @backstage/plugin-catalog-node@npm:^1.18.0":
+ version: 1.18.0
+ resolution: "@backstage/plugin-catalog-node@npm:1.18.0"
dependencies:
- "@backstage/backend-plugin-api": ^1.4.1
- "@backstage/catalog-client": ^1.10.2
+ "@backstage/backend-plugin-api": ^1.4.2
+ "@backstage/catalog-client": ^1.11.0
"@backstage/catalog-model": ^1.7.5
"@backstage/errors": ^1.2.7
"@backstage/plugin-catalog-common": ^1.1.5
"@backstage/plugin-permission-common": ^0.9.1
- "@backstage/plugin-permission-node": ^0.10.2
+ "@backstage/plugin-permission-node": ^0.10.3
"@backstage/types": ^1.2.1
lodash: ^4.17.21
yaml: ^2.0.0
- checksum: 09fafd3af78d92aeb1c698202ef3a29eb0d8c7a97f3ad449ab7fd05f8a2e5dc61425398c5f72f25f747e996929c41d96c3ad259bc52febc8e7ad49b94c6625e1
+ checksum: 3688cf7b7e7b47a2f2cc02836bd6f41d35d9de88f3072d3993eb9364709485763bd35f4142b8a3fcb45b6fa36327a2b47b6bfd27eeeb61caf1c2cb517909ed50
languageName: node
linkType: hard
@@ -4366,30 +4474,30 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-events-backend@npm:^0.5.4":
- version: 0.5.4
- resolution: "@backstage/plugin-events-backend@npm:0.5.4"
+"@backstage/plugin-events-backend@npm:^0.5.5":
+ version: 0.5.5
+ resolution: "@backstage/plugin-events-backend@npm:0.5.5"
dependencies:
- "@backstage/backend-openapi-utils": ^0.5.5
- "@backstage/backend-plugin-api": ^1.4.1
+ "@backstage/backend-openapi-utils": ^0.6.0
+ "@backstage/backend-plugin-api": ^1.4.2
"@backstage/config": ^1.3.3
"@backstage/errors": ^1.2.7
- "@backstage/plugin-events-node": ^0.4.13
+ "@backstage/plugin-events-node": ^0.4.14
"@backstage/types": ^1.2.1
"@types/express": ^4.17.6
content-type: ^1.0.5
express: ^4.17.1
express-promise-router: ^4.1.0
knex: ^3.0.0
- checksum: 1c3cea46b2d1b50ba462cd9aba5b378f9f39589ddf002d1b673e77b2b355a3fc1f5e0351d7e9abbcd282920b3d55e4af36d452bc85fd8f2f073e5367e3b1c1a9
+ checksum: 729062e47df8ec5f97cb604f87c7fe6bc5ac767fdf95904b6d917fe095c398c8cc7ce9e6723e2243dabcaff330cc75995cc8572ab181a7e088f643649f413d64
languageName: node
linkType: hard
-"@backstage/plugin-events-node@npm:^0.4.10, @backstage/plugin-events-node@npm:^0.4.13":
- version: 0.4.13
- resolution: "@backstage/plugin-events-node@npm:0.4.13"
+"@backstage/plugin-events-node@npm:^0.4.10, @backstage/plugin-events-node@npm:^0.4.13, @backstage/plugin-events-node@npm:^0.4.14":
+ version: 0.4.14
+ resolution: "@backstage/plugin-events-node@npm:0.4.14"
dependencies:
- "@backstage/backend-plugin-api": ^1.4.1
+ "@backstage/backend-plugin-api": ^1.4.2
"@backstage/errors": ^1.2.7
"@backstage/types": ^1.2.1
"@types/content-type": ^1.1.8
@@ -4398,7 +4506,7 @@ __metadata:
cross-fetch: ^4.0.0
express: ^4.17.1
uri-template: ^2.0.0
- checksum: 842a18c1398302aa90e40bdfdb47577cc79133aa8b618518f0056a4108d22f26c9a868f2d4384728500cd8e66ed7ca58eb024fc0e3bbbaf45549999e1b5ddee9
+ checksum: 618f9c286501b11824ed166fc9e39738c83d31bd31f70d31461bdd17a7268ff9807eb5ee4b7fcfeca7036a109763777e7ac64931bf7a8b2bfe3589efbc5ea7a5
languageName: node
linkType: hard
@@ -4667,21 +4775,21 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-permission-node@npm:^0.10.2":
- version: 0.10.2
- resolution: "@backstage/plugin-permission-node@npm:0.10.2"
+"@backstage/plugin-permission-node@npm:^0.10.2, @backstage/plugin-permission-node@npm:^0.10.3":
+ version: 0.10.3
+ resolution: "@backstage/plugin-permission-node@npm:0.10.3"
dependencies:
- "@backstage/backend-plugin-api": ^1.4.1
+ "@backstage/backend-plugin-api": ^1.4.2
"@backstage/config": ^1.3.3
"@backstage/errors": ^1.2.7
- "@backstage/plugin-auth-node": ^0.6.5
+ "@backstage/plugin-auth-node": ^0.6.6
"@backstage/plugin-permission-common": ^0.9.1
"@types/express": ^4.17.6
express: ^4.17.1
express-promise-router: ^4.1.0
zod: ^3.22.4
zod-to-json-schema: ^3.20.4
- checksum: 70edf66456a0c1e798e8da6735c3d089dda61b8d1a7c73e1bd58517b2c2bd4ccb2734cbf36a0010f87dc5781bc0bd824c31a9ed1323f053c80b71ef966656d10
+ checksum: 2697d47e9d973acb992fd8fb8288304de9234eb1b3963c2c735dbb7d70143997c051cde21f2ae9b4d9c506b5c2abad96fd9c0a11ae6127bbe92d38bff1ea6610
languageName: node
linkType: hard
@@ -4939,14 +5047,22 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-scaffolder-common@npm:^1.6.0":
- version: 1.6.0
- resolution: "@backstage/plugin-scaffolder-common@npm:1.6.0"
+"@backstage/plugin-scaffolder-common@npm:^1.6.0, @backstage/plugin-scaffolder-common@npm:^1.7.0":
+ version: 1.7.0
+ resolution: "@backstage/plugin-scaffolder-common@npm:1.7.0"
dependencies:
"@backstage/catalog-model": ^1.7.5
+ "@backstage/errors": ^1.2.7
+ "@backstage/integration": ^1.17.1
"@backstage/plugin-permission-common": ^0.9.1
"@backstage/types": ^1.2.1
- checksum: 2d43d62df1398f305911287bc8c4d13abed39a799aa851871810f022830d54e8a171f8381310157868a4e942ce1706aff7991d57bfc4985a412f1a291db02815
+ "@microsoft/fetch-event-source": ^2.0.1
+ "@types/json-schema": ^7.0.9
+ cross-fetch: ^4.0.0
+ json-schema: ^0.4.0
+ uri-template: ^2.0.0
+ zen-observable: ^0.10.0
+ checksum: 1f2feeee8ed53451a344968c24c25cf577ba129f4b7710cff4d958af91b96576ab3dc6646252e85532b4b89845f82b695496c8023a5c8ba35d4086e7bebf0c96
languageName: node
linkType: hard
@@ -4978,6 +5094,34 @@ __metadata:
languageName: node
linkType: hard
+"@backstage/plugin-scaffolder-node@npm:^0.11.0":
+ version: 0.11.0
+ resolution: "@backstage/plugin-scaffolder-node@npm:0.11.0"
+ dependencies:
+ "@backstage/backend-plugin-api": ^1.4.2
+ "@backstage/catalog-model": ^1.7.5
+ "@backstage/errors": ^1.2.7
+ "@backstage/integration": ^1.17.1
+ "@backstage/plugin-permission-common": ^0.9.1
+ "@backstage/plugin-scaffolder-common": ^1.7.0
+ "@backstage/types": ^1.2.1
+ "@isomorphic-git/pgp-plugin": ^0.0.7
+ concat-stream: ^2.0.0
+ fs-extra: ^11.2.0
+ globby: ^11.0.0
+ isomorphic-git: ^1.23.0
+ jsonschema: ^1.5.0
+ lodash: ^4.17.21
+ p-limit: ^3.1.0
+ tar: ^6.1.12
+ winston: ^3.2.1
+ winston-transport: ^4.7.0
+ zod: ^3.22.4
+ zod-to-json-schema: ^3.20.4
+ checksum: 55b56f41d764e226b42fb2b069b4c211c82f82695614ec1a44241d274d70f3ec3b6c39c665ff45583e0c181a394644029e49a277d1b6d9bbed98841e114941b3
+ languageName: node
+ linkType: hard
+
"@backstage/plugin-scaffolder-react@npm:^1.18.0":
version: 1.18.0
resolution: "@backstage/plugin-scaffolder-react@npm:1.18.0"
@@ -5144,11 +5288,11 @@ __metadata:
languageName: node
linkType: hard
-"@backstage/plugin-search-backend-node@npm:^1.3.13":
- version: 1.3.13
- resolution: "@backstage/plugin-search-backend-node@npm:1.3.13"
+"@backstage/plugin-search-backend-node@npm:^1.3.13, @backstage/plugin-search-backend-node@npm:^1.3.14":
+ version: 1.3.14
+ resolution: "@backstage/plugin-search-backend-node@npm:1.3.14"
dependencies:
- "@backstage/backend-plugin-api": ^1.4.1
+ "@backstage/backend-plugin-api": ^1.4.2
"@backstage/config": ^1.3.3
"@backstage/errors": ^1.2.7
"@backstage/plugin-permission-common": ^0.9.1
@@ -5158,7 +5302,7 @@ __metadata:
lunr: ^2.3.9
ndjson: ^2.0.0
uuid: ^11.0.0
- checksum: faa9249d48b5697967f962cb47bf5211c47dd4dec4db9197b269c1777e3cccebd606e51e802198097b16662912f3e9e112ae5d15c6c0702d1f59d7a78046dcd1
+ checksum: e52ad8141e8ddaec7f2e7185fc85049c14f6f22c55999c1f67e4407a8b176b9dab640fa2a7ef10754e2c1399f6fd2b13f0f6f12faa104f7a3d212b9c5c323993
languageName: node
linkType: hard
@@ -6095,6 +6239,13 @@ __metadata:
languageName: node
linkType: hard
+"@date-io/core@npm:^3.0.0, @date-io/core@npm:^3.2.0":
+ version: 3.2.0
+ resolution: "@date-io/core@npm:3.2.0"
+ checksum: 711e43b449bc29f5f1aaf49e34e722a9fe42238b1ee20e81e3a11692118002872d915c2a289f1ed24e53f88bd220ce3888d7ec0a1705c131e305478f534a3450
+ languageName: node
+ linkType: hard
+
"@date-io/date-fns@npm:^1.3.13":
version: 1.3.13
resolution: "@date-io/date-fns@npm:1.3.13"
@@ -6106,6 +6257,20 @@ __metadata:
languageName: node
linkType: hard
+"@date-io/date-fns@npm:^3.0.0":
+ version: 3.2.1
+ resolution: "@date-io/date-fns@npm:3.2.1"
+ dependencies:
+ "@date-io/core": ^3.2.0
+ peerDependencies:
+ date-fns: ^3.2.0 || ^4.1.0
+ peerDependenciesMeta:
+ date-fns:
+ optional: true
+ checksum: 3c8ab25d604c36b47e8309d50bdb9c0052cb3697a391be030839605449eaff4527e0a9c1bc99e917f40268a6bbff8d8548daaa3bbc864cdcbe79702b53c649db
+ languageName: node
+ linkType: hard
+
"@electric-sql/pglite@npm:^0.2.14":
version: 0.2.17
resolution: "@electric-sql/pglite@npm:0.2.17"
@@ -6120,26 +6285,26 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/babel-plugin@npm:^11.12.0":
- version: 11.12.0
- resolution: "@emotion/babel-plugin@npm:11.12.0"
+"@emotion/babel-plugin@npm:^11.13.5":
+ version: 11.13.5
+ resolution: "@emotion/babel-plugin@npm:11.13.5"
dependencies:
"@babel/helper-module-imports": ^7.16.7
"@babel/runtime": ^7.18.3
"@emotion/hash": ^0.9.2
"@emotion/memoize": ^0.9.0
- "@emotion/serialize": ^1.2.0
+ "@emotion/serialize": ^1.3.3
babel-plugin-macros: ^3.1.0
convert-source-map: ^1.5.0
escape-string-regexp: ^4.0.0
find-root: ^1.1.0
source-map: ^0.5.7
stylis: 4.2.0
- checksum: b5d4b3dfe97e6763794a42b5c3a027a560caa1aa6dcaf05c18e5969691368dd08245c077bad7397dcc720b53d29caeaaec1888121e68cfd9ab02ff52f6fef662
+ checksum: c41df7e6c19520e76d1939f884be878bf88b5ba00bd3de9d05c5b6c5baa5051686ab124d7317a0645de1b017b574d8139ae1d6390ec267fbe8e85a5252afb542
languageName: node
linkType: hard
-"@emotion/cache@npm:^11.13.0, @emotion/cache@npm:^11.13.5":
+"@emotion/cache@npm:^11.13.5, @emotion/cache@npm:^11.14.0":
version: 11.14.0
resolution: "@emotion/cache@npm:11.14.0"
dependencies:
@@ -6152,6 +6317,13 @@ __metadata:
languageName: node
linkType: hard
+"@emotion/core@npm:^11.0.0":
+ version: 11.0.0
+ resolution: "@emotion/core@npm:11.0.0"
+ checksum: d9beeff0c120ec4d3cd89aa74e1ee3ff658976f27d14820e4e8f8e9981b4461b4a78c9e9c0853397ba09dff36b4e19357858ac8773ee5eb72bfd4b185926b5f3
+ languageName: node
+ linkType: hard
+
"@emotion/hash@npm:^0.8.0":
version: 0.8.0
resolution: "@emotion/hash@npm:0.8.0"
@@ -6198,16 +6370,16 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/react@npm:^11.10.5":
- version: 11.13.3
- resolution: "@emotion/react@npm:11.13.3"
+"@emotion/react@npm:^11.10.4, @emotion/react@npm:^11.10.5":
+ version: 11.14.0
+ resolution: "@emotion/react@npm:11.14.0"
dependencies:
"@babel/runtime": ^7.18.3
- "@emotion/babel-plugin": ^11.12.0
- "@emotion/cache": ^11.13.0
- "@emotion/serialize": ^1.3.1
- "@emotion/use-insertion-effect-with-fallbacks": ^1.1.0
- "@emotion/utils": ^1.4.0
+ "@emotion/babel-plugin": ^11.13.5
+ "@emotion/cache": ^11.14.0
+ "@emotion/serialize": ^1.3.3
+ "@emotion/use-insertion-effect-with-fallbacks": ^1.2.0
+ "@emotion/utils": ^1.4.2
"@emotion/weak-memoize": ^0.4.0
hoist-non-react-statics: ^3.3.1
peerDependencies:
@@ -6215,20 +6387,20 @@ __metadata:
peerDependenciesMeta:
"@types/react":
optional: true
- checksum: 0b58374bf28de914b49881f0060acfb908989869ebab63a2287773fc5e91a39f15552632b03d376c3e9835c5b4f23a5ebac8b0963b29af164d46c0a77ac928f0
+ checksum: 3cf023b11d132b56168713764d6fced8e5a1f0687dfe0caa2782dfd428c8f9e30f9826a919965a311d87b523cd196722aaf75919cd0f6bd0fd57f8a6a0281500
languageName: node
linkType: hard
-"@emotion/serialize@npm:^1.2.0, @emotion/serialize@npm:^1.3.0, @emotion/serialize@npm:^1.3.1":
- version: 1.3.2
- resolution: "@emotion/serialize@npm:1.3.2"
+"@emotion/serialize@npm:^1.3.3":
+ version: 1.3.3
+ resolution: "@emotion/serialize@npm:1.3.3"
dependencies:
"@emotion/hash": ^0.9.2
"@emotion/memoize": ^0.9.0
"@emotion/unitless": ^0.10.0
- "@emotion/utils": ^1.4.1
+ "@emotion/utils": ^1.4.2
csstype: ^3.0.2
- checksum: 8051bafe32459e1aecf716cdb66a22b090060806104cca89d4e664893b56878d3e9bb94a4657df9b7b3fd183700a9be72f7144c959ddcbd3cf7b330206919237
+ checksum: 510331233767ae4e09e925287ca2c7269b320fa1d737ea86db5b3c861a734483ea832394c0c1fe5b21468fe335624a75e72818831d303ba38125f54f44ba02e7
languageName: node
linkType: hard
@@ -6239,23 +6411,23 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/styled@npm:^11.10.5":
- version: 11.13.0
- resolution: "@emotion/styled@npm:11.13.0"
+"@emotion/styled@npm:^11.10.4, @emotion/styled@npm:^11.10.5":
+ version: 11.14.1
+ resolution: "@emotion/styled@npm:11.14.1"
dependencies:
"@babel/runtime": ^7.18.3
- "@emotion/babel-plugin": ^11.12.0
+ "@emotion/babel-plugin": ^11.13.5
"@emotion/is-prop-valid": ^1.3.0
- "@emotion/serialize": ^1.3.0
- "@emotion/use-insertion-effect-with-fallbacks": ^1.1.0
- "@emotion/utils": ^1.4.0
+ "@emotion/serialize": ^1.3.3
+ "@emotion/use-insertion-effect-with-fallbacks": ^1.2.0
+ "@emotion/utils": ^1.4.2
peerDependencies:
"@emotion/react": ^11.0.0-rc.0
react: ">=16.8.0"
peerDependenciesMeta:
"@types/react":
optional: true
- checksum: f5b951059418f57bc8ea32b238afb25965ece3314f2ffd1b14ce049ba3c066a424990dfbfabbf57bb88e044eaa80bf19f620ac988adda3d2fc483177be6da05e
+ checksum: 86238d9f5c41232a73499e441fa9112e855545519d6772f489fa885634bb8f31b422a9ba9d1e8bc0b4ad66132f9d398b1c309d92c19c5ee21356b41671ec984a
languageName: node
linkType: hard
@@ -6266,16 +6438,16 @@ __metadata:
languageName: node
linkType: hard
-"@emotion/use-insertion-effect-with-fallbacks@npm:^1.1.0":
- version: 1.1.0
- resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.1.0"
+"@emotion/use-insertion-effect-with-fallbacks@npm:^1.2.0":
+ version: 1.2.0
+ resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.2.0"
peerDependencies:
react: ">=16.8.0"
- checksum: 63665191773b27de66807c53b90091ef0d10d5161381f62726cfceecfe1d8c944f18594b8021805fc81575b64246fd5ab9c75d60efabec92f940c1c410530949
+ checksum: 8ff6aec7f2924526ff8c8f8f93d4b8236376e2e12c435314a18c9a373016e24dfdf984e82bbc83712b8e90ff4783cd765eb39fc7050d1a43245e5728740ddd71
languageName: node
linkType: hard
-"@emotion/utils@npm:^1.4.0, @emotion/utils@npm:^1.4.1, @emotion/utils@npm:^1.4.2":
+"@emotion/utils@npm:^1.4.2":
version: 1.4.2
resolution: "@emotion/utils@npm:1.4.2"
checksum: 04cf76849c6401205c058b82689fd0ec5bf501aed6974880fe9681a1d61543efb97e848f4c38664ac4a9068c7ad2d1cb84f73bde6cf95f1208aa3c28e0190321
@@ -6674,41 +6846,41 @@ __metadata:
languageName: node
linkType: hard
-"@floating-ui/core@npm:^1.6.0":
- version: 1.6.8
- resolution: "@floating-ui/core@npm:1.6.8"
+"@floating-ui/core@npm:^1.7.3":
+ version: 1.7.3
+ resolution: "@floating-ui/core@npm:1.7.3"
dependencies:
- "@floating-ui/utils": ^0.2.8
- checksum: 82faa6ea9d57e466779324e51308d6d49c098fb9d184a08d9bb7f4fad83f08cc070fc491f8d56f0cad44a16215fb43f9f829524288413e6c33afcb17303698de
+ "@floating-ui/utils": ^0.2.10
+ checksum: 5adfb28ddfa1776ec83516439256b9026e5d62b5413f62ae51e50a870cf0df4bea9abf72aacc0610ee84bc00e85883d0d32f2a0976ee7fa89728a717a7494f27
languageName: node
linkType: hard
-"@floating-ui/dom@npm:^1.0.0":
- version: 1.6.12
- resolution: "@floating-ui/dom@npm:1.6.12"
+"@floating-ui/dom@npm:^1.7.4":
+ version: 1.7.4
+ resolution: "@floating-ui/dom@npm:1.7.4"
dependencies:
- "@floating-ui/core": ^1.6.0
- "@floating-ui/utils": ^0.2.8
- checksum: 956514ed100c0c853e73ace9e3c877b7e535444d7c31326f687a7690d49cb1e59ef457e9c93b76141aea0d280e83ed5a983bb852718b62eea581f755454660f6
+ "@floating-ui/core": ^1.7.3
+ "@floating-ui/utils": ^0.2.10
+ checksum: 806923e6f5b09e024c366070f2115a4db6e8ad28462bac29cd075170a6f7d900497da3ee542439bd0770b8e2fff12b636cc30873d1c82e9ec4a487870b080643
languageName: node
linkType: hard
-"@floating-ui/react-dom@npm:^2.0.0":
- version: 2.1.2
- resolution: "@floating-ui/react-dom@npm:2.1.2"
+"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.0.8":
+ version: 2.1.6
+ resolution: "@floating-ui/react-dom@npm:2.1.6"
dependencies:
- "@floating-ui/dom": ^1.0.0
+ "@floating-ui/dom": ^1.7.4
peerDependencies:
react: ">=16.8.0"
react-dom: ">=16.8.0"
- checksum: 25bb031686e23062ed4222a8946e76b3f9021d40a48437bd747233c4964a766204b8a55f34fa8b259839af96e60db7c6e3714d81f1de06914294f90e86ffbc48
+ checksum: 24ff266806cd4cba6ad066f0eda7b99583f68af877f41df0b2a8d10a392692e3a1c1d666ebb75571a060818ede940bae59d833aa517ed538f7dba9dddd9991ae
languageName: node
linkType: hard
-"@floating-ui/utils@npm:^0.2.8":
- version: 0.2.8
- resolution: "@floating-ui/utils@npm:0.2.8"
- checksum: deb98bba017c4e073c7ad5740d4dec33a4d3e0942d412e677ac0504f3dade15a68fc6fd164d43c93c0bb0bcc5dc5015c1f4080dfb1a6161140fe660624f7c875
+"@floating-ui/utils@npm:^0.2.10":
+ version: 0.2.10
+ resolution: "@floating-ui/utils@npm:0.2.10"
+ checksum: ffc4c24a46a665cfd0337e9aaf7de8415b572f8a0f323af39175e4b575582aed13d172e7f049eedeece9eaf022bad019c140a2d192580451984ae529bdf1285c
languageName: node
linkType: hard
@@ -7213,6 +7385,24 @@ __metadata:
languageName: node
linkType: hard
+"@hello-pangea/dnd@npm:^16.0.0":
+ version: 16.6.0
+ resolution: "@hello-pangea/dnd@npm:16.6.0"
+ dependencies:
+ "@babel/runtime": ^7.24.1
+ css-box-model: ^1.2.1
+ memoize-one: ^6.0.0
+ raf-schd: ^4.0.3
+ react-redux: ^8.1.3
+ redux: ^4.2.1
+ use-memo-one: ^1.1.3
+ peerDependencies:
+ react: ^16.8.5 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
+ checksum: 00dcf9c2a7632e56c13e4a38c888081f65cec6bac0640b975b6cd17f2afa8995e8df02735db6f17de3a7ade3779386fc8375d7cedc884f8704e2e6c2977ebcec
+ languageName: node
+ linkType: hard
+
"@httptoolkit/httpolyglot@npm:^2.2.1":
version: 2.2.2
resolution: "@httptoolkit/httpolyglot@npm:2.2.2"
@@ -7518,6 +7708,7 @@ __metadata:
node-gyp: ^9.0.0
prettier: ^3.4.2
typescript: ~5.3.0
+ yaml: ^2.8.1
languageName: unknown
linkType: soft
@@ -8322,6 +8513,36 @@ __metadata:
languageName: node
linkType: hard
+"@material-table/core@npm:^6.4.4":
+ version: 6.4.4
+ resolution: "@material-table/core@npm:6.4.4"
+ dependencies:
+ "@babel/runtime": ^7.19.0
+ "@date-io/core": ^3.0.0
+ "@date-io/date-fns": ^3.0.0
+ "@emotion/core": ^11.0.0
+ "@emotion/react": ^11.10.4
+ "@emotion/styled": ^11.10.4
+ "@hello-pangea/dnd": ^16.0.0
+ "@mui/icons-material": ">=5.10.6"
+ "@mui/material": ">=5.11.12"
+ "@mui/x-date-pickers": ^6.19.0
+ classnames: ^2.3.2
+ date-fns: ^3.2.0
+ debounce: ^1.2.1
+ deep-eql: ^4.1.1
+ deepmerge: ^4.2.2
+ prop-types: ^15.8.1
+ uuid: ^9.0.0
+ zustand: ^4.3.0
+ peerDependencies:
+ "@mui/system": ">=5.15.5"
+ react: ">=18.0.0"
+ react-dom: ">=18.0.0"
+ checksum: 1f4c38951981c9112e3223bd31e5a8b0f486e635f727cd269b6d8d7fec0522c4bf7f3fe02c72be3281ff98e633bd4cb0ec83692c925ef4663dae56863857563e
+ languageName: node
+ linkType: hard
+
"@material-ui/core@npm:^4.12.2, @material-ui/core@npm:^4.12.4, @material-ui/core@npm:^4.9.13":
version: 4.12.4
resolution: "@material-ui/core@npm:4.12.4"
@@ -8894,6 +9115,28 @@ __metadata:
languageName: node
linkType: hard
+"@mui/base@npm:^5.0.0-beta.22":
+ version: 5.0.0-dev.20240529-082515-213b5e33ab
+ resolution: "@mui/base@npm:5.0.0-dev.20240529-082515-213b5e33ab"
+ dependencies:
+ "@babel/runtime": ^7.24.6
+ "@floating-ui/react-dom": ^2.0.8
+ "@mui/types": ^7.2.14-dev.20240529-082515-213b5e33ab
+ "@mui/utils": ^6.0.0-dev.20240529-082515-213b5e33ab
+ "@popperjs/core": ^2.11.8
+ clsx: ^2.1.1
+ prop-types: ^15.8.1
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 7ca5d369c5f4dfa2bb36fe49d721f3dc28a6d067bd74492e8f4531671ff9debe33bade579d0cdcb7d5f899bfc5003c8f710b1f9422edd22f06d805dbc68c9f4e
+ languageName: node
+ linkType: hard
+
"@mui/core-downloads-tracker@npm:^5.17.1":
version: 5.17.1
resolution: "@mui/core-downloads-tracker@npm:5.17.1"
@@ -8901,6 +9144,13 @@ __metadata:
languageName: node
linkType: hard
+"@mui/core-downloads-tracker@npm:^7.3.2":
+ version: 7.3.2
+ resolution: "@mui/core-downloads-tracker@npm:7.3.2"
+ checksum: 01c0976e5fb6a8f0b59ebd58019af5452d7761e97daf0f2ef121bc3323508edf2fea1b13ea1a54b0098d6ee5eef70041c9722fe43291b237b989b6813a24b71e
+ languageName: node
+ linkType: hard
+
"@mui/icons-material@npm:5.15.17":
version: 5.15.17
resolution: "@mui/icons-material@npm:5.15.17"
@@ -8933,6 +9183,22 @@ __metadata:
languageName: node
linkType: hard
+"@mui/icons-material@npm:>=5.10.6":
+ version: 7.3.2
+ resolution: "@mui/icons-material@npm:7.3.2"
+ dependencies:
+ "@babel/runtime": ^7.28.3
+ peerDependencies:
+ "@mui/material": ^7.3.2
+ "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 30d31b05510c9da3bd5ba97960a0210298c101abf58efc77b2b6647b1edaf0d4b81aa671a9f5790f17977970e8cdca9139da8cfac4de31be618908754e9a3d7f
+ languageName: node
+ linkType: hard
+
"@mui/icons-material@npm:^5.16.7, @mui/icons-material@npm:^5.17.1":
version: 5.17.1
resolution: "@mui/icons-material@npm:5.17.1"
@@ -8949,6 +9215,42 @@ __metadata:
languageName: node
linkType: hard
+"@mui/material@npm:>=5.11.12":
+ version: 7.3.2
+ resolution: "@mui/material@npm:7.3.2"
+ dependencies:
+ "@babel/runtime": ^7.28.3
+ "@mui/core-downloads-tracker": ^7.3.2
+ "@mui/system": ^7.3.2
+ "@mui/types": ^7.4.6
+ "@mui/utils": ^7.3.2
+ "@popperjs/core": ^2.11.8
+ "@types/react-transition-group": ^4.4.12
+ clsx: ^2.1.1
+ csstype: ^3.1.3
+ prop-types: ^15.8.1
+ react-is: ^19.1.1
+ react-transition-group: ^4.4.5
+ peerDependencies:
+ "@emotion/react": ^11.5.0
+ "@emotion/styled": ^11.3.0
+ "@mui/material-pigment-css": ^7.3.2
+ "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ "@mui/material-pigment-css":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: 993e0c33ed5d180cdac13fadfa067bd753ceb3a01940a777b6dcdbd347324ae0d0c000ef567f6cc358e065033e131ce0e9bc8790a29502146b805978938c5c0f
+ languageName: node
+ linkType: hard
+
"@mui/material@npm:^5.12.2, @mui/material@npm:^5.14.18, @mui/material@npm:^5.15.17":
version: 5.17.1
resolution: "@mui/material@npm:5.17.1"
@@ -9016,6 +9318,23 @@ __metadata:
languageName: node
linkType: hard
+"@mui/private-theming@npm:^7.3.2":
+ version: 7.3.2
+ resolution: "@mui/private-theming@npm:7.3.2"
+ dependencies:
+ "@babel/runtime": ^7.28.3
+ "@mui/utils": ^7.3.2
+ prop-types: ^15.8.1
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 443f898565cbe4a99742a84d68c6e18c2ab306a4279a00313d42a07a59cf75e8cde9356bfe9e1fd6e99d0eb08e0fa9797de34d2df12004ee3185c025feb2a7c5
+ languageName: node
+ linkType: hard
+
"@mui/styled-engine@npm:^5.16.14":
version: 5.16.14
resolution: "@mui/styled-engine@npm:5.16.14"
@@ -9037,6 +9356,29 @@ __metadata:
languageName: node
linkType: hard
+"@mui/styled-engine@npm:^7.3.2":
+ version: 7.3.2
+ resolution: "@mui/styled-engine@npm:7.3.2"
+ dependencies:
+ "@babel/runtime": ^7.28.3
+ "@emotion/cache": ^11.14.0
+ "@emotion/serialize": ^1.3.3
+ "@emotion/sheet": ^1.4.0
+ csstype: ^3.1.3
+ prop-types: ^15.8.1
+ peerDependencies:
+ "@emotion/react": ^11.4.1
+ "@emotion/styled": ^11.3.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ checksum: 452ab8bfa9a4e06b6b9a227f06465d356602459dac92fe3197eed6b5d278ec178b902405217071cc2490e515fe56b9993bb7610db75204a7eb58fd8ab840c167
+ languageName: node
+ linkType: hard
+
"@mui/styles@npm:5.18.0":
version: 5.18.0
resolution: "@mui/styles@npm:5.18.0"
@@ -9127,6 +9469,48 @@ __metadata:
languageName: node
linkType: hard
+"@mui/system@npm:^7.3.2":
+ version: 7.3.2
+ resolution: "@mui/system@npm:7.3.2"
+ dependencies:
+ "@babel/runtime": ^7.28.3
+ "@mui/private-theming": ^7.3.2
+ "@mui/styled-engine": ^7.3.2
+ "@mui/types": ^7.4.6
+ "@mui/utils": ^7.3.2
+ clsx: ^2.1.1
+ csstype: ^3.1.3
+ prop-types: ^15.8.1
+ peerDependencies:
+ "@emotion/react": ^11.5.0
+ "@emotion/styled": ^11.3.0
+ "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ "@types/react":
+ optional: true
+ checksum: 9a7b97e22991959dcdb60b4b0d60b359df4dce3735235db4fa51a214cf515f584ff84fd10f15849c91aeda7b8d39658eb9378253d3fac2a6db0b9baca693e3a7
+ languageName: node
+ linkType: hard
+
+"@mui/types@npm:^7.2.14-dev.20240529-082515-213b5e33ab, @mui/types@npm:^7.4.6":
+ version: 7.4.6
+ resolution: "@mui/types@npm:7.4.6"
+ dependencies:
+ "@babel/runtime": ^7.28.3
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 7e4b4ae06066468e8a8211376d0a08250325f4b92f6d2ea24576df37dfcd0f683602567debdf15889e0e4510eb48d677aa0d71a2ed9423c16dbd4b4ecfbccbb9
+ languageName: node
+ linkType: hard
+
"@mui/types@npm:~7.2.15, @mui/types@npm:~7.2.24":
version: 7.2.24
resolution: "@mui/types@npm:7.2.24"
@@ -9139,7 +9523,7 @@ __metadata:
languageName: node
linkType: hard
-"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.17.1":
+"@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.14.16, @mui/utils@npm:^5.17.1":
version: 5.17.1
resolution: "@mui/utils@npm:5.17.1"
dependencies:
@@ -9159,7 +9543,7 @@ __metadata:
languageName: node
linkType: hard
-"@mui/utils@npm:^6.4.9":
+"@mui/utils@npm:^6.0.0-dev.20240529-082515-213b5e33ab, @mui/utils@npm:^6.4.9":
version: 6.4.9
resolution: "@mui/utils@npm:6.4.9"
dependencies:
@@ -9179,6 +9563,74 @@ __metadata:
languageName: node
linkType: hard
+"@mui/utils@npm:^7.3.2":
+ version: 7.3.2
+ resolution: "@mui/utils@npm:7.3.2"
+ dependencies:
+ "@babel/runtime": ^7.28.3
+ "@mui/types": ^7.4.6
+ "@types/prop-types": ^15.7.15
+ clsx: ^2.1.1
+ prop-types: ^15.8.1
+ react-is: ^19.1.1
+ peerDependencies:
+ "@types/react": ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 201fb1f49434c3ee12de3a177660af04cb6411a68f8c13dc62ea828c522758b53040046daf2341b38da3b0a8a455ef4454a49cca4fa4e7b4d345d05e3ca61ecc
+ languageName: node
+ linkType: hard
+
+"@mui/x-date-pickers@npm:^6.19.0":
+ version: 6.20.2
+ resolution: "@mui/x-date-pickers@npm:6.20.2"
+ dependencies:
+ "@babel/runtime": ^7.23.2
+ "@mui/base": ^5.0.0-beta.22
+ "@mui/utils": ^5.14.16
+ "@types/react-transition-group": ^4.4.8
+ clsx: ^2.0.0
+ prop-types: ^15.8.1
+ react-transition-group: ^4.4.5
+ peerDependencies:
+ "@emotion/react": ^11.9.0
+ "@emotion/styled": ^11.8.1
+ "@mui/material": ^5.8.6
+ "@mui/system": ^5.8.0
+ date-fns: ^2.25.0 || ^3.2.0
+ date-fns-jalali: ^2.13.0-0
+ dayjs: ^1.10.7
+ luxon: ^3.0.2
+ moment: ^2.29.4
+ moment-hijri: ^2.1.2
+ moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0
+ react: ^17.0.0 || ^18.0.0
+ react-dom: ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ "@emotion/react":
+ optional: true
+ "@emotion/styled":
+ optional: true
+ date-fns:
+ optional: true
+ date-fns-jalali:
+ optional: true
+ dayjs:
+ optional: true
+ luxon:
+ optional: true
+ moment:
+ optional: true
+ moment-hijri:
+ optional: true
+ moment-jalaali:
+ optional: true
+ checksum: 0447b911ea0d78d4ee2080827bc075d8c1ed4764bd289d6bf65ee2ff870ac8ef72daef8a1858ccf27aad6c296cfece5455f6834a2d18a2c8e719518cd5464a0b
+ languageName: node
+ linkType: hard
+
"@n1ru4l/push-pull-async-iterable-iterator@npm:^3.1.0":
version: 3.2.0
resolution: "@n1ru4l/push-pull-async-iterable-iterator@npm:3.2.0"
@@ -10754,6 +11206,7 @@ __metadata:
resolution: "@red-hat-developer-hub/backstage-plugin-marketplace-backend@workspace:plugins/marketplace-backend"
dependencies:
"@backstage/backend-defaults": ^0.11.1
+ "@backstage/backend-dynamic-feature-service": ^0.7.3
"@backstage/backend-plugin-api": ^1.4.1
"@backstage/backend-test-utils": ^1.7.0
"@backstage/catalog-client": ^1.10.2
@@ -10809,6 +11262,7 @@ __metadata:
"@backstage/test-utils": ^1.7.10
"@backstage/theme": ^0.6.7
"@backstage/types": ^1.2.1
+ "@material-table/core": ^6.4.4
"@material-ui/core": ^4.12.2
"@monaco-editor/react": ^4.7.0
"@mui/icons-material": ^5.16.7
@@ -13828,7 +14282,7 @@ __metadata:
languageName: node
linkType: hard
-"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.7, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9":
+"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.7, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9":
version: 7.0.15
resolution: "@types/json-schema@npm:7.0.15"
checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98
@@ -14019,10 +14473,10 @@ __metadata:
languageName: node
linkType: hard
-"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.12, @types/prop-types@npm:^15.7.14, @types/prop-types@npm:^15.7.3":
- version: 15.7.14
- resolution: "@types/prop-types@npm:15.7.14"
- checksum: d0c5407b9ccc3dd5fae0ccf9b1007e7622ba5e6f1c18399b4f24dff33619d469da4b9fa918a374f19dc0d9fe6a013362aab0b844b606cfc10676efba3f5f736d
+"@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.12, @types/prop-types@npm:^15.7.14, @types/prop-types@npm:^15.7.15, @types/prop-types@npm:^15.7.3":
+ version: 15.7.15
+ resolution: "@types/prop-types@npm:15.7.15"
+ checksum: 31aa2f59b28f24da6fb4f1d70807dae2aedfce090ec63eaf9ea01727a9533ef6eaf017de5bff99fbccad7d1c9e644f52c6c2ba30869465dd22b1a7221c29f356
languageName: node
linkType: hard
@@ -14088,12 +14542,12 @@ __metadata:
languageName: node
linkType: hard
-"@types/react-transition-group@npm:^4.2.0, @types/react-transition-group@npm:^4.4.10":
- version: 4.4.11
- resolution: "@types/react-transition-group@npm:4.4.11"
- dependencies:
+"@types/react-transition-group@npm:^4.2.0, @types/react-transition-group@npm:^4.4.10, @types/react-transition-group@npm:^4.4.12, @types/react-transition-group@npm:^4.4.8":
+ version: 4.4.12
+ resolution: "@types/react-transition-group@npm:4.4.12"
+ peerDependencies:
"@types/react": "*"
- checksum: a6e3b2e4363cb019e256ae4f19dadf9d7eb199da1a5e4109bbbf6a132821884044d332e9c74b520b1e5321a7f545502443fd1ce0b18649c8b510fa4220b0e5c2
+ checksum: 13d36396cae4d3c316b03d4a0ba299f0d039c59368ba65e04b0c3dc06fd0a16f59d2c669c3e32d6d525a95423f156b84e550d26bff0bdd8df285f305f8f3a0ed
languageName: node
linkType: hard
@@ -15067,7 +15521,7 @@ __metadata:
languageName: node
linkType: hard
-"ajv-formats@npm:~3.0.1":
+"ajv-formats@npm:^3.0.1, ajv-formats@npm:~3.0.1":
version: 3.0.1
resolution: "ajv-formats@npm:3.0.1"
dependencies:
@@ -16193,6 +16647,17 @@ __metadata:
languageName: node
linkType: hard
+"better-sqlite3@npm:^12.0.0":
+ version: 12.2.0
+ resolution: "better-sqlite3@npm:12.2.0"
+ dependencies:
+ bindings: ^1.5.0
+ node-gyp: latest
+ prebuild-install: ^7.1.1
+ checksum: b752119ea306c5a70d5bacdf5607fd69b39142b7c8c30d72f530047fedad0b651195b37e8a8067f106d3c56d6913dc0077f1d658916f07bcdad6841384d2bb1b
+ languageName: node
+ linkType: hard
+
"better-sqlite3@npm:^9.0.0":
version: 9.6.0
resolution: "better-sqlite3@npm:9.6.0"
@@ -16557,6 +17022,15 @@ __metadata:
languageName: node
linkType: hard
+"buffer-xor@npm:^2.0.2":
+ version: 2.0.2
+ resolution: "buffer-xor@npm:2.0.2"
+ dependencies:
+ safe-buffer: ^5.1.1
+ checksum: 78226fcae9f4a0b4adec69dffc049f26f6bab240dfdd1b3f6fe07c4eb6b90da202ea5c363f98af676156ee39450a06405fddd9e8965f68a5327edcc89dcbe5d0
+ languageName: node
+ linkType: hard
+
"buffer@npm:5.6.0":
version: 5.6.0
resolution: "buffer@npm:5.6.0"
@@ -16610,7 +17084,7 @@ __metadata:
languageName: node
linkType: hard
-"busboy@npm:^1.0.0, busboy@npm:^1.6.0":
+"busboy@npm:^1.6.0":
version: 1.6.0
resolution: "busboy@npm:1.6.0"
dependencies:
@@ -17082,7 +17556,7 @@ __metadata:
languageName: node
linkType: hard
-"clsx@npm:^2.1.0, clsx@npm:^2.1.1":
+"clsx@npm:^2.0.0, clsx@npm:^2.1.0, clsx@npm:^2.1.1":
version: 2.1.1
resolution: "clsx@npm:2.1.1"
checksum: acd3e1ab9d8a433ecb3cc2f6a05ab95fe50b4a3cfc5ba47abb6cbf3754585fcb87b84e90c822a1f256c4198e3b41c7f6c391577ffc8678ad587fc0976b24fd57
@@ -17452,18 +17926,6 @@ __metadata:
languageName: node
linkType: hard
-"concat-stream@npm:^1.5.2":
- version: 1.6.2
- resolution: "concat-stream@npm:1.6.2"
- dependencies:
- buffer-from: ^1.0.0
- inherits: ^2.0.3
- readable-stream: ^2.2.2
- typedarray: ^0.0.6
- checksum: 1ef77032cb4459dcd5187bd710d6fc962b067b64ec6a505810de3d2b8cc0605638551b42f8ec91edf6fcd26141b32ef19ad749239b58fae3aba99187adc32285
- languageName: node
- linkType: hard
-
"concat-stream@npm:^2.0.0":
version: 2.0.0
resolution: "concat-stream@npm:2.0.0"
@@ -17990,7 +18452,7 @@ __metadata:
languageName: node
linkType: hard
-"css-box-model@npm:^1.2.0":
+"css-box-model@npm:^1.2.0, css-box-model@npm:^1.2.1":
version: 1.2.1
resolution: "css-box-model@npm:1.2.1"
dependencies:
@@ -18419,6 +18881,13 @@ __metadata:
languageName: node
linkType: hard
+"date-fns@npm:^3.2.0":
+ version: 3.6.0
+ resolution: "date-fns@npm:3.6.0"
+ checksum: 0daa1e9a436cf99f9f2ae9232b55e11f3dd46132bee10987164f3eebd29f245b2e066d7d7db40782627411ecf18551d8f4c9fcdf2226e48bb66545407d448ab7
+ languageName: node
+ linkType: hard
+
"date-format@npm:^4.0.14":
version: 4.0.14
resolution: "date-format@npm:4.0.14"
@@ -18447,7 +18916,7 @@ __metadata:
languageName: node
linkType: hard
-"debounce@npm:^1.2.0":
+"debounce@npm:^1.2.0, debounce@npm:^1.2.1":
version: 1.2.1
resolution: "debounce@npm:1.2.1"
checksum: 682a89506d9e54fb109526f4da255c5546102fbb8e3ae75eef3b04effaf5d4853756aee97475cd4650641869794e44f410eeb20ace2b18ea592287ab2038519e
@@ -18533,6 +19002,15 @@ __metadata:
languageName: node
linkType: hard
+"deep-eql@npm:^4.1.1":
+ version: 4.1.4
+ resolution: "deep-eql@npm:4.1.4"
+ dependencies:
+ type-detect: ^4.0.0
+ checksum: 01c3ca78ff40d79003621b157054871411f94228ceb9b2cab78da913c606631c46e8aa79efc4aa0faf3ace3092acd5221255aab3ef0e8e7b438834f0ca9a16c7
+ languageName: node
+ linkType: hard
+
"deep-equal@npm:^2.0.5":
version: 2.2.3
resolution: "deep-equal@npm:2.2.3"
@@ -18611,6 +19089,15 @@ __metadata:
languageName: node
linkType: hard
+"default-user-agent@npm:^1.0.0":
+ version: 1.0.0
+ resolution: "default-user-agent@npm:1.0.0"
+ dependencies:
+ os-name: ~1.0.3
+ checksum: b1ef07c8e7de846a66e1e120d7ba11969faa36c8db4af2317f9b64d30e7507d129e3f721c7cc3f531a1719c1ab463d830bf426fbcda87b11defe23689f4d2b60
+ languageName: node
+ linkType: hard
+
"defaults@npm:^1.0.3":
version: 1.0.4
resolution: "defaults@npm:1.0.4"
@@ -18846,6 +19333,13 @@ __metadata:
languageName: node
linkType: hard
+"digest-header@npm:^1.0.0":
+ version: 1.1.0
+ resolution: "digest-header@npm:1.1.0"
+ checksum: fadbdda75e1cc650e460c8fe2064f74c43cc005d0eab66cc390dd1ae2678cfb41f69f151323fbd3e059e28c941f1b9adc6ea4dbd9c918cb246f34a5eb8e103f0
+ languageName: node
+ linkType: hard
+
"dir-glob@npm:^3.0.1":
version: 3.0.1
resolution: "dir-glob@npm:3.0.1"
@@ -20281,26 +20775,27 @@ __metadata:
languageName: node
linkType: hard
-"express-openapi-validator@npm:^5.0.4":
- version: 5.3.9
- resolution: "express-openapi-validator@npm:5.3.9"
+"express-openapi-validator@npm:^5.0.4, express-openapi-validator@npm:^5.5.8":
+ version: 5.5.8
+ resolution: "express-openapi-validator@npm:5.5.8"
dependencies:
- "@apidevtools/json-schema-ref-parser": ^11.7.0
+ "@apidevtools/json-schema-ref-parser": ^14.0.3
"@types/multer": ^1.4.12
ajv: ^8.17.1
ajv-draft-04: ^1.0.0
- ajv-formats: ^2.1.1
+ ajv-formats: ^3.0.1
content-type: ^1.0.5
json-schema-traverse: ^1.0.0
lodash.clonedeep: ^4.5.0
lodash.get: ^4.4.2
media-typer: ^1.1.0
- multer: ^1.4.5-lts.1
+ multer: ^2.0.2
ono: ^7.1.3
- path-to-regexp: ^8.1.0
+ path-to-regexp: ^8.2.0
+ qs: ^6.14.0
peerDependencies:
express: "*"
- checksum: 0469b383b769f070dfad9c9148b18857c5aaa9aa14c596e1ada155dac63e1c355e455d42f3c81af62c4aefd3db4ad8b892d34a432e170e4eb7af73d7dbd2f644
+ checksum: f76ef56748a28c4ed338ec509eb359bcf992505a565a1a59c0c6ef02c369b03f7d306a94d51b5de4a1d1c69618529e5a7a3aa3c5e3c1dfc0437e6f789a4ab381
languageName: node
linkType: hard
@@ -20907,6 +21402,13 @@ __metadata:
languageName: node
linkType: hard
+"form-data-encoder@npm:^1.7.2":
+ version: 1.9.0
+ resolution: "form-data-encoder@npm:1.9.0"
+ checksum: a73f617976f91b594dbd777ec5147abdb0c52d707475130f8cefc8ae9102ccf51be154b929f7c18323729c2763ac25b16055f5034bc188834e9febeb0d971d7f
+ languageName: node
+ linkType: hard
+
"form-data@npm:^2.3.2, form-data@npm:^2.5.0":
version: 2.5.2
resolution: "form-data@npm:2.5.2"
@@ -20948,6 +21450,16 @@ __metadata:
languageName: node
linkType: hard
+"formdata-node@npm:^4.3.3":
+ version: 4.4.1
+ resolution: "formdata-node@npm:4.4.1"
+ dependencies:
+ node-domexception: 1.0.0
+ web-streams-polyfill: 4.0.0-beta.3
+ checksum: d91d4f667cfed74827fc281594102c0dabddd03c9f8b426fc97123eedbf73f5060ee43205d89284d6854e2fc5827e030cd352ef68b93beda8decc2d72128c576
+ languageName: node
+ linkType: hard
+
"formidable@npm:^2.1.2":
version: 2.1.2
resolution: "formidable@npm:2.1.2"
@@ -20978,6 +21490,18 @@ __metadata:
languageName: node
linkType: hard
+"formstream@npm:^1.1.1":
+ version: 1.5.2
+ resolution: "formstream@npm:1.5.2"
+ dependencies:
+ destroy: ^1.0.4
+ mime: ^2.5.2
+ node-hex: ^1.0.1
+ pause-stream: ~0.0.11
+ checksum: ed04f7ee02633608237a5ed9000d8a7c19d7ed2a69254f5d47cf81d799ffbbecf52bf2440ffa940cb1150f9f80b7b83bf17aac914be11c0fb14bd480226424e7
+ languageName: node
+ linkType: hard
+
"forwarded@npm:0.2.0":
version: 0.2.0
resolution: "forwarded@npm:0.2.0"
@@ -22567,6 +23091,19 @@ __metadata:
languageName: node
linkType: hard
+"infinispan@npm:^0.12.0":
+ version: 0.12.0
+ resolution: "infinispan@npm:0.12.0"
+ dependencies:
+ buffer-xor: ^2.0.2
+ log4js: ^6.4.6
+ protobufjs: ^7.0.0
+ underscore: ^1.13.3
+ urllib: ^3.23.0
+ checksum: 28e490dcadd2325677bd231c9e4637a90bb54ce9056cdde9e592041ba6303cbde143b7724e32c9a6e44725e52bac546727e4ff49cdd5a565fba03090863b7137
+ languageName: node
+ linkType: hard
+
"inflight@npm:^1.0.4":
version: 1.0.6
resolution: "inflight@npm:1.0.6"
@@ -25283,7 +25820,7 @@ __metadata:
languageName: node
linkType: hard
-"log4js@npm:6.9.1":
+"log4js@npm:6.9.1, log4js@npm:^6.4.6":
version: 6.9.1
resolution: "log4js@npm:6.9.1"
dependencies:
@@ -25883,6 +26420,13 @@ __metadata:
languageName: node
linkType: hard
+"memoize-one@npm:^6.0.0":
+ version: 6.0.0
+ resolution: "memoize-one@npm:6.0.0"
+ checksum: f185ea69f7cceae5d1cb596266dcffccf545e8e7b4106ec6aa93b71ab9d16460dd118ac8b12982c55f6d6322fcc1485de139df07eacffaae94888b9b3ad7675f
+ languageName: node
+ linkType: hard
+
"merge-descriptors@npm:1.0.3":
version: 1.0.3
resolution: "merge-descriptors@npm:1.0.3"
@@ -26288,7 +26832,7 @@ __metadata:
languageName: node
linkType: hard
-"mime-types@npm:^2.1.12, mime-types@npm:^2.1.18, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
+"mime-types@npm:^2.1.12, mime-types@npm:^2.1.18, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:^2.1.35, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
version: 2.1.35
resolution: "mime-types@npm:2.1.35"
dependencies:
@@ -26306,7 +26850,7 @@ __metadata:
languageName: node
linkType: hard
-"mime@npm:2.6.0":
+"mime@npm:2.6.0, mime@npm:^2.5.2":
version: 2.6.0
resolution: "mime@npm:2.6.0"
bin:
@@ -26443,7 +26987,7 @@ __metadata:
languageName: node
linkType: hard
-"minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8":
+"minimist@npm:^1.1.0, minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.8":
version: 1.2.8
resolution: "minimist@npm:1.2.8"
checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0
@@ -26591,7 +27135,7 @@ __metadata:
languageName: node
linkType: hard
-"mkdirp@npm:^0.5.4, mkdirp@npm:^0.5.6":
+"mkdirp@npm:^0.5.6":
version: 0.5.6
resolution: "mkdirp@npm:0.5.6"
dependencies:
@@ -26768,18 +27312,18 @@ __metadata:
languageName: node
linkType: hard
-"multer@npm:^1.4.5-lts.1":
- version: 1.4.5-lts.1
- resolution: "multer@npm:1.4.5-lts.1"
+"multer@npm:^2.0.2":
+ version: 2.0.2
+ resolution: "multer@npm:2.0.2"
dependencies:
append-field: ^1.0.0
- busboy: ^1.0.0
- concat-stream: ^1.5.2
- mkdirp: ^0.5.4
+ busboy: ^1.6.0
+ concat-stream: ^2.0.0
+ mkdirp: ^0.5.6
object-assign: ^4.1.1
- type-is: ^1.6.4
- xtend: ^4.0.0
- checksum: d6dfa78a6ec592b74890412f8962da8a87a3dcfe20f612e039b735b8e0faa72c735516c447f7de694ee0d981eb0a1b892fb9e2402a0348dc6091d18c38d89ecc
+ type-is: ^1.6.18
+ xtend: ^4.0.2
+ checksum: 187f3539b3d5b7f80a289288fc21d3e4116ab9ed21ac9b9ceb25272c7344d215e9572f7e7ed01edb876afddf24661b06578f2c0fc67f36655ebb51a13ccf83dc
languageName: node
linkType: hard
@@ -27023,7 +27567,7 @@ __metadata:
languageName: node
linkType: hard
-"node-domexception@npm:^1.0.0":
+"node-domexception@npm:1.0.0, node-domexception@npm:^1.0.0":
version: 1.0.0
resolution: "node-domexception@npm:1.0.0"
checksum: ee1d37dd2a4eb26a8a92cd6b64dfc29caec72bff5e1ed9aba80c294f57a31ba4895a60fd48347cf17dd6e766da0ae87d75657dfd1f384ebfa60462c2283f5c7f
@@ -27116,6 +27660,13 @@ __metadata:
languageName: node
linkType: hard
+"node-hex@npm:^1.0.1":
+ version: 1.0.1
+ resolution: "node-hex@npm:1.0.1"
+ checksum: 9053d532859ee7e9653972af77ac7b73edc4f13b9b53d0b96e4045e3ac78ac4460571d4b72ad31e9095be5f7d01e6fd71f268f02ad6029091f8cabae1d4ce4df
+ languageName: node
+ linkType: hard
+
"node-int64@npm:^0.4.0":
version: 0.4.0
resolution: "node-int64@npm:0.4.0"
@@ -27726,6 +28277,18 @@ __metadata:
languageName: node
linkType: hard
+"os-name@npm:~1.0.3":
+ version: 1.0.3
+ resolution: "os-name@npm:1.0.3"
+ dependencies:
+ osx-release: ^1.0.0
+ win-release: ^1.0.0
+ bin:
+ os-name: cli.js
+ checksum: 2fc86cc199f8b4992bb00041401c5ab0407e3069e05981f3aa3e5a44cee9b7f22c2b0f5db2c0c1d55656c519884272b5e1e55517358c2e5f728b37dd38f5af78
+ languageName: node
+ linkType: hard
+
"os-tmpdir@npm:~1.0.2":
version: 1.0.2
resolution: "os-tmpdir@npm:1.0.2"
@@ -27733,6 +28296,17 @@ __metadata:
languageName: node
linkType: hard
+"osx-release@npm:^1.0.0":
+ version: 1.1.0
+ resolution: "osx-release@npm:1.1.0"
+ dependencies:
+ minimist: ^1.1.0
+ bin:
+ osx-release: cli.js
+ checksum: abd437ef21dbfb04f098acc90112cc92ef10c17213e3fd75f8eba45931bd85f6d564ecade0642fac51acff2015597194a76a11773009a90baeb35a03b1c36b06
+ languageName: node
+ linkType: hard
+
"outdent@npm:^0.5.0":
version: 0.5.0
resolution: "outdent@npm:0.5.0"
@@ -28163,10 +28737,10 @@ __metadata:
languageName: node
linkType: hard
-"path-to-regexp@npm:^8.0.0, path-to-regexp@npm:^8.1.0":
- version: 8.2.0
- resolution: "path-to-regexp@npm:8.2.0"
- checksum: 56e13e45962e776e9e7cd72e87a441cfe41f33fd539d097237ceb16adc922281136ca12f5a742962e33d8dda9569f630ba594de56d8b7b6e49adf31803c5e771
+"path-to-regexp@npm:^8.0.0, path-to-regexp@npm:^8.2.0":
+ version: 8.3.0
+ resolution: "path-to-regexp@npm:8.3.0"
+ checksum: 73e0d3db449f9899692b10be8480bbcfa294fd575be2d09bce3e63f2f708d1fccd3aaa8591709f8b82062c528df116e118ff9df8f5c52ccc4c2443a90be73e10
languageName: node
linkType: hard
@@ -28177,6 +28751,15 @@ __metadata:
languageName: node
linkType: hard
+"pause-stream@npm:~0.0.11":
+ version: 0.0.11
+ resolution: "pause-stream@npm:0.0.11"
+ dependencies:
+ through: ~2.3
+ checksum: 3c4a14052a638b92e0c96eb00c0d7977df7f79ea28395250c525d197f1fc02d34ce1165d5362e2e6ebbb251524b94a76f3f0d4abc39ab8b016d97449fe15583c
+ languageName: node
+ linkType: hard
+
"pause@npm:0.0.1":
version: 0.0.1
resolution: "pause@npm:0.0.1"
@@ -29200,9 +29783,9 @@ __metadata:
languageName: node
linkType: hard
-"protobufjs@npm:^7.2.5, protobufjs@npm:^7.2.6, protobufjs@npm:^7.3.2, protobufjs@npm:^7.4.0":
- version: 7.4.0
- resolution: "protobufjs@npm:7.4.0"
+"protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.5, protobufjs@npm:^7.2.6, protobufjs@npm:^7.3.2, protobufjs@npm:^7.4.0":
+ version: 7.5.4
+ resolution: "protobufjs@npm:7.5.4"
dependencies:
"@protobufjs/aspromise": ^1.1.2
"@protobufjs/base64": ^1.1.2
@@ -29216,7 +29799,7 @@ __metadata:
"@protobufjs/utf8": ^1.1.0
"@types/node": ">=13.7.0"
long: ^5.0.0
- checksum: ba0e6b60541bbf818bb148e90f5eb68bd99004e29a6034ad9895a381cbd352be8dce5376e47ae21b2e05559f2505b4a5f4a3c8fa62402822c6ab4dcdfb89ffb3
+ checksum: 53bf83b9a726b05d43da35bb990dba7536759787dccea9a67b8f31be9df470ba17f1f1b982ca19956cfc7726f3ec7e0e883ca4ad93b5ec753cc025a637fc704f
languageName: node
linkType: hard
@@ -29335,7 +29918,7 @@ __metadata:
languageName: node
linkType: hard
-"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.9.4":
+"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.11.2, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.9.4":
version: 6.14.0
resolution: "qs@npm:6.14.0"
dependencies:
@@ -29393,7 +29976,7 @@ __metadata:
languageName: node
linkType: hard
-"raf-schd@npm:^4.0.2":
+"raf-schd@npm:^4.0.2, raf-schd@npm:^4.0.3":
version: 4.0.3
resolution: "raf-schd@npm:4.0.3"
checksum: 45514041c5ad31fa96aef3bb3c572a843b92da2f2cd1cb4a47c9ad58e48761d3a4126e18daa32b2bfa0bc2551a42d8f324a0e40e536cb656969929602b4e8b58
@@ -29796,10 +30379,10 @@ __metadata:
languageName: node
linkType: hard
-"react-is@npm:^19.0.0":
- version: 19.1.0
- resolution: "react-is@npm:19.1.0"
- checksum: 3eb4eac7f09bf178bdc6fa98d384f5f243b85de7c99679a88b0154ead4d818ad94386ccb00ea31ec52409ffd13299057f5ec6ca2eaec06f9f7eddc1ad4832332
+"react-is@npm:^19.0.0, react-is@npm:^19.1.1":
+ version: 19.1.1
+ resolution: "react-is@npm:19.1.1"
+ checksum: e60ed01c27fe4d22b08f8a31f18831d144a801d08a909ca31fb1d02721b4f4cde0759148d6341f660a4d6ce54a78e22b8b39520b67e2e76254e583885868ab43
languageName: node
linkType: hard
@@ -29850,6 +30433,38 @@ __metadata:
languageName: node
linkType: hard
+"react-redux@npm:^8.1.3":
+ version: 8.1.3
+ resolution: "react-redux@npm:8.1.3"
+ dependencies:
+ "@babel/runtime": ^7.12.1
+ "@types/hoist-non-react-statics": ^3.3.1
+ "@types/use-sync-external-store": ^0.0.3
+ hoist-non-react-statics: ^3.3.2
+ react-is: ^18.0.0
+ use-sync-external-store: ^1.0.0
+ peerDependencies:
+ "@types/react": ^16.8 || ^17.0 || ^18.0
+ "@types/react-dom": ^16.8 || ^17.0 || ^18.0
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ react-native: ">=0.59"
+ redux: ^4 || ^5.0.0-beta.0
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ "@types/react-dom":
+ optional: true
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ redux:
+ optional: true
+ checksum: 192ea6f6053148ec80a4148ec607bc259403b937e515f616a1104ca5ab357e97e98b8245ed505a17afee67a72341d4a559eaca9607968b4a422aa9b44ba7eb89
+ languageName: node
+ linkType: hard
+
"react-redux@npm:^9.1.2":
version: 9.1.2
resolution: "react-redux@npm:9.1.2"
@@ -30115,7 +30730,7 @@ __metadata:
languageName: node
linkType: hard
-"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.8, readable-stream@npm:~2.3.6":
+"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.5, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.8, readable-stream@npm:~2.3.6":
version: 2.3.8
resolution: "readable-stream@npm:2.3.8"
dependencies:
@@ -30237,7 +30852,7 @@ __metadata:
languageName: node
linkType: hard
-"redux@npm:^4.0.0, redux@npm:^4.0.4":
+"redux@npm:^4.0.0, redux@npm:^4.0.4, redux@npm:^4.2.1":
version: 4.2.1
resolution: "redux@npm:4.2.1"
dependencies:
@@ -31224,6 +31839,15 @@ __metadata:
languageName: node
linkType: hard
+"semver@npm:^5.0.1":
+ version: 5.7.2
+ resolution: "semver@npm:5.7.2"
+ bin:
+ semver: bin/semver
+ checksum: fb4ab5e0dd1c22ce0c937ea390b4a822147a9c53dbd2a9a0132f12fe382902beef4fbf12cf51bb955248d8d15874ce8cd89532569756384f994309825f10b686
+ languageName: node
+ linkType: hard
+
"semver@npm:^6.3.0, semver@npm:^6.3.1":
version: 6.3.1
resolution: "semver@npm:6.3.1"
@@ -32958,7 +33582,7 @@ __metadata:
languageName: node
linkType: hard
-"through@npm:^2.3.6":
+"through@npm:^2.3.6, through@npm:~2.3":
version: 2.3.8
resolution: "through@npm:2.3.8"
checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd
@@ -33468,6 +34092,13 @@ __metadata:
languageName: node
linkType: hard
+"type-detect@npm:^4.0.0":
+ version: 4.1.0
+ resolution: "type-detect@npm:4.1.0"
+ checksum: 3b32f873cd02bc7001b00a61502b7ddc4b49278aabe68d652f732e1b5d768c072de0bc734b427abf59d0520a5f19a2e07309ab921ef02018fa1cb4af155cdb37
+ languageName: node
+ linkType: hard
+
"type-fest@npm:^0.13.1":
version: 0.13.1
resolution: "type-fest@npm:0.13.1"
@@ -33496,7 +34127,14 @@ __metadata:
languageName: node
linkType: hard
-"type-is@npm:^1.6.16, type-is@npm:^1.6.4, type-is@npm:~1.6.18":
+"type-fest@npm:^4.3.1":
+ version: 4.41.0
+ resolution: "type-fest@npm:4.41.0"
+ checksum: 7055c0e3eb188425d07403f1d5dc175ca4c4f093556f26871fe22041bc93d137d54bef5851afa320638ca1379106c594f5aa153caa654ac1a7f22c71588a4e80
+ languageName: node
+ linkType: hard
+
+"type-is@npm:^1.6.16, type-is@npm:^1.6.18, type-is@npm:~1.6.18":
version: 1.6.18
resolution: "type-is@npm:1.6.18"
dependencies:
@@ -33838,7 +34476,7 @@ __metadata:
languageName: node
linkType: hard
-"underscore@npm:^1.12.1":
+"underscore@npm:^1.12.1, underscore@npm:^1.13.3":
version: 1.13.7
resolution: "underscore@npm:1.13.7"
checksum: 174b011af29e4fbe2c70eb2baa8bfab0d0336cf2f5654f364484967bc6264a86224d0134b9176e4235c8cceae00d11839f0fd4824268de04b11c78aca1241684
@@ -33866,12 +34504,12 @@ __metadata:
languageName: node
linkType: hard
-"undici@npm:^5.28.4":
- version: 5.28.4
- resolution: "undici@npm:5.28.4"
+"undici@npm:^5.28.2, undici@npm:^5.28.4":
+ version: 5.29.0
+ resolution: "undici@npm:5.29.0"
dependencies:
"@fastify/busboy": ^2.0.0
- checksum: a8193132d84540e4dc1895ecc8dbaa176e8a49d26084d6fbe48a292e28397cd19ec5d13bc13e604484e76f94f6e334b2bdc740d5f06a6e50c44072818d0c19f9
+ checksum: a25b5462c1b6ffb974f5ffc492ffd64146a9983aad0cbda6fde65e2b22f6f1acd43f09beacc66cc47624a113bd0c684ffc60366102b6a21b038fbfafb7d75195
languageName: node
linkType: hard
@@ -34160,6 +34798,25 @@ __metadata:
languageName: node
linkType: hard
+"urllib@npm:^3.23.0":
+ version: 3.27.3
+ resolution: "urllib@npm:3.27.3"
+ dependencies:
+ default-user-agent: ^1.0.0
+ digest-header: ^1.0.0
+ form-data-encoder: ^1.7.2
+ formdata-node: ^4.3.3
+ formstream: ^1.1.1
+ mime-types: ^2.1.35
+ pump: ^3.0.0
+ qs: ^6.11.2
+ type-fest: ^4.3.1
+ undici: ^5.28.2
+ ylru: ^1.3.2
+ checksum: b587fe23f9ec2b24a279a89dc4be0de7b81437edddc62d1e20c064d7c715f755af316ca6b22173e5fc2f4e76fe8c002734b087a725d90abd39e40a0730553299
+ languageName: node
+ linkType: hard
+
"urlpattern-polyfill@npm:^10.0.0":
version: 10.0.0
resolution: "urlpattern-polyfill@npm:10.0.0"
@@ -34199,7 +34856,7 @@ __metadata:
languageName: node
linkType: hard
-"use-memo-one@npm:^1.1.1":
+"use-memo-one@npm:^1.1.1, use-memo-one@npm:^1.1.3":
version: 1.1.3
resolution: "use-memo-one@npm:1.1.3"
peerDependencies:
@@ -34236,12 +34893,12 @@ __metadata:
languageName: node
linkType: hard
-"use-sync-external-store@npm:^1.0.0, use-sync-external-store@npm:^1.2.0":
- version: 1.2.2
- resolution: "use-sync-external-store@npm:1.2.2"
+"use-sync-external-store@npm:^1.0.0, use-sync-external-store@npm:^1.2.0, use-sync-external-store@npm:^1.2.2":
+ version: 1.5.0
+ resolution: "use-sync-external-store@npm:1.5.0"
peerDependencies:
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- checksum: fe07c071c4da3645f112c38c0e57beb479a8838616ff4e92598256ecce527f2888c08febc7f9b2f0ce2f0e18540ba3cde41eb2035e4fafcb4f52955037098a81
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ checksum: 5e639c9273200adb6985b512c96a3a02c458bc8ca1a72e91da9cdc6426144fc6538dca434b0f99b28fb1baabc82e1c383ba7900b25ccdcb43758fb058dc66c34
languageName: node
linkType: hard
@@ -34544,6 +35201,13 @@ __metadata:
languageName: node
linkType: hard
+"web-streams-polyfill@npm:4.0.0-beta.3":
+ version: 4.0.0-beta.3
+ resolution: "web-streams-polyfill@npm:4.0.0-beta.3"
+ checksum: dfec1fbf52b9140e4183a941e380487b6c3d5d3838dd1259be81506c1c9f2abfcf5aeb670aeeecfd9dff4271a6d8fef931b193c7bedfb42542a3b05ff36c0d16
+ languageName: node
+ linkType: hard
+
"web-streams-polyfill@npm:^3.0.3":
version: 3.3.3
resolution: "web-streams-polyfill@npm:3.3.3"
@@ -34871,6 +35535,15 @@ __metadata:
languageName: node
linkType: hard
+"win-release@npm:^1.0.0":
+ version: 1.1.1
+ resolution: "win-release@npm:1.1.1"
+ dependencies:
+ semver: ^5.0.1
+ checksum: 8943898cc4badaf8598342d63093e49ae9a64c140cf190e81472d3a8890f3387b8408181412e1b58658fe7777ce5d1e3f02eee4beeaee49909d1d17a72d52fc1
+ languageName: node
+ linkType: hard
+
"winston-transport@npm:^4.5.0, winston-transport@npm:^4.7.0":
version: 4.8.0
resolution: "winston-transport@npm:4.8.0"
@@ -35095,12 +35768,12 @@ __metadata:
languageName: node
linkType: hard
-"yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.7.0, yaml@npm:^2.7.1":
- version: 2.7.1
- resolution: "yaml@npm:2.7.1"
+"yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.7.0, yaml@npm:^2.7.1, yaml@npm:^2.8.1":
+ version: 2.8.1
+ resolution: "yaml@npm:2.8.1"
bin:
yaml: bin.mjs
- checksum: 385f8115ddfafdf8e599813cca8b2bf4e3f6a01b919fff5ae7da277e164df684d7dfe558b4085172094792b5a04786d3c55fa8b74abb0ee029873f031150bb80
+ checksum: 35b46150d48bc1da2fd5b1521a48a4fa36d68deaabe496f3c3fa9646d5796b6b974f3930a02c4b5aee6c85c860d7d7f79009416724465e835f40b87898c36de4
languageName: node
linkType: hard
@@ -35158,7 +35831,7 @@ __metadata:
languageName: node
linkType: hard
-"ylru@npm:^1.2.0":
+"ylru@npm:^1.2.0, ylru@npm:^1.3.2":
version: 1.4.0
resolution: "ylru@npm:1.4.0"
checksum: e0bf797476487e3d57a6e8790cbb749cff2089e2afc87e46bc84ce7605c329d578ff422c8e8c2ddf167681ddd218af0f58e099733ae1044cba9e9472ebedc01d
@@ -35287,6 +35960,26 @@ __metadata:
languageName: node
linkType: hard
+"zustand@npm:^4.3.0":
+ version: 4.5.7
+ resolution: "zustand@npm:4.5.7"
+ dependencies:
+ use-sync-external-store: ^1.2.2
+ peerDependencies:
+ "@types/react": ">=16.8"
+ immer: ">=9.0.6"
+ react: ">=16.8"
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ checksum: 103ab43456bbc3be6afe79b18a93c7fa46ffaa1aa35c45b213f13f4cd0868fee78b43c6805c6d80a822297df2e455fd021c28be94b80529ec4806b2724f20219
+ languageName: node
+ linkType: hard
+
"zwitch@npm:^2.0.0":
version: 2.0.4
resolution: "zwitch@npm:2.0.4"