diff --git a/dynamic-demo-plugin/yarn.lock b/dynamic-demo-plugin/yarn.lock
index 8377df54e56..9064807b2be 100644
--- a/dynamic-demo-plugin/yarn.lock
+++ b/dynamic-demo-plugin/yarn.lock
@@ -149,7 +149,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@openshift-console/dynamic-plugin-sdk-webpack@portal:../frontend/packages/console-dynamic-plugin-sdk/dist/webpack::locator=%40console%2Fdynamic-demo-plugin%40workspace%3A."
dependencies:
- "@openshift/dynamic-plugin-sdk": "npm:^8.0.0"
+ "@openshift/dynamic-plugin-sdk": "npm:^8.2.0"
"@openshift/dynamic-plugin-sdk-webpack": "npm:^5.1.0"
ajv: "npm:^6.12.3"
chalk: "npm:2.4.x"
@@ -158,7 +158,7 @@ __metadata:
glob: "npm:7.x"
lodash: "npm:^4.17.21"
read-pkg: "npm:5.x"
- semver: "npm:6.x"
+ semver: "npm:^7.7.4"
peerDependencies:
typescript: ">=5.9.3"
webpack: ">=5.100.0"
@@ -169,7 +169,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@openshift-console/dynamic-plugin-sdk@portal:../frontend/packages/console-dynamic-plugin-sdk/dist/core::locator=%40console%2Fdynamic-demo-plugin%40workspace%3A."
dependencies:
- "@openshift/dynamic-plugin-sdk": "npm:^8.0.0"
+ "@openshift/dynamic-plugin-sdk": "npm:^8.2.0"
immutable: "npm:3.x"
lodash: "npm:^4.17.21"
reselect: "npm:4.x"
@@ -213,9 +213,9 @@ __metadata:
languageName: node
linkType: hard
-"@openshift/dynamic-plugin-sdk@npm:^8.0.0":
- version: 8.0.0
- resolution: "@openshift/dynamic-plugin-sdk@npm:8.0.0"
+"@openshift/dynamic-plugin-sdk@npm:^8.2.0":
+ version: 8.2.0
+ resolution: "@openshift/dynamic-plugin-sdk@npm:8.2.0"
dependencies:
lodash: "npm:^4.17.23"
semver: "npm:^7.7.3"
@@ -223,7 +223,7 @@ __metadata:
yup: "npm:^1.7.1"
peerDependencies:
react: ^18 || ^19
- checksum: 10c0/a5c7bc5198f75fcc9fad242ffb51e264e860218e7035b87204d429cc47fa566208cc3474af739d3e05a2adc1bfe41ae9e09d3afd93a5b8c0d29f1814ee9f3127
+ checksum: 10c0/a9e3e1a145014a13310acd4630c7a2b7a7545b6c089e1d41e99ceaec061c1a270051d44bd26ddd09ec61bb02533ecfab55663cedb41a20d63acf824067eb63ab
languageName: node
linkType: hard
@@ -3437,16 +3437,7 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:6.x":
- version: 6.3.1
- resolution: "semver@npm:6.3.1"
- bin:
- semver: bin/semver.js
- checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d
- languageName: node
- linkType: hard
-
-"semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.7.3":
+"semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.7.3, semver@npm:^7.7.4":
version: 7.7.4
resolution: "semver@npm:7.7.4"
bin:
diff --git a/frontend/.yarnrc.yml b/frontend/.yarnrc.yml
index 7db9b30059a..4feef5fc40a 100644
--- a/frontend/.yarnrc.yml
+++ b/frontend/.yarnrc.yml
@@ -10,4 +10,8 @@ nodeLinker: node-modules
npmMinimalAgeGate: 3d
+npmPreapprovedPackages:
+ - "@openshift/*"
+ - "@patternfly/*"
+
yarnPath: .yarn/releases/yarn-4.12.0.cjs
diff --git a/frontend/package.json b/frontend/package.json
index e88734239d5..3819bee34a8 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -149,7 +149,7 @@
]
},
"dependencies": {
- "@openshift/dynamic-plugin-sdk": "^8.0.0",
+ "@openshift/dynamic-plugin-sdk": "^8.2.0",
"@openshift/dynamic-plugin-sdk-webpack": "^5.1.0",
"@patternfly/patternfly": "~6.4.0",
"@patternfly/quickstarts": "~6.4.0",
@@ -222,7 +222,7 @@
"redux-thunk": "2.4.0",
"reselect": "4.x",
"sanitize-html": "^2.3.2",
- "semver": "6.x",
+ "semver": "^7.7.4",
"showdown": "1.8.6",
"subscriptions-transport-ws": "^0.9.16",
"typesafe-actions": "^4.2.1",
@@ -255,7 +255,7 @@
"@types/react-dom": "^18.3.7",
"@types/react-jsonschema-form": "^1.3.8",
"@types/react-virtualized": "9.x",
- "@types/semver": "^6.0.0",
+ "@types/semver": "^7.7.1",
"@types/showdown": "1.9.4",
"@yarnpkg/parsers": "^3.0.2",
"axe-core": "^4.10.2",
diff --git a/frontend/packages/console-dynamic-plugin-sdk/CHANGELOG-core.md b/frontend/packages/console-dynamic-plugin-sdk/CHANGELOG-core.md
index 0de6b3403d1..2a560c93823 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/CHANGELOG-core.md
+++ b/frontend/packages/console-dynamic-plugin-sdk/CHANGELOG-core.md
@@ -12,6 +12,7 @@ table in [Console dynamic plugins README](./README.md).
## 4.22.0-prerelease.2 - TBD
+- **Breaking**: `useResolvedExtensions` hook now supports a single predicate parameter ([#16115], [CONSOLE-5065])
- **Breaking**: Changed `AlertAction` extension to use `LaunchOverlay` instead of `LaunchModal`. The `action` callback parameter is now typed as `LaunchOverlay`, which does not support the optional `id` parameter that `LaunchModal` had. Plugins implementing `console.alert-action` extensions must update their action callbacks accordingly. ([CONSOLE-4447])
- **Breaking**: Removed support for `console.page/resource/tab` and `console.dashboards/overview/detail/item`
extensions. Use `console.tab/horizontalNav` and `console.dashboards/custom/overview/detail/item` extensions
@@ -21,8 +22,9 @@ table in [Console dynamic plugins README](./README.md).
- **Deprecated**: `useUserSettings` hook has been renamed to `useUserPreference` for consistency ([OCPBUGS-44612], [#16057])
- **Type breaking**: Changed `useDeleteModal` hook's `redirectTo` parameter type from `LocationDescriptor` (from `history`) to `To` (from `react-router`) ([CONSOLE-4990], [#15959])
- **Type breaking**: Changed `FileUploadHandler` return type from `void` to `To | void`. Handlers can now return a path (from `react-router`) for programmatic navigation instead of calling history methods directly ([CONSOLE-4990], [#15959])
+- **Type breaking**: Removed `ExtensionTypeGuard` type, use `ExtensionPredicate` type instead ([#16115], [CONSOLE-5065])
- The following types are now re-exported from `@openshift/dynamic-plugin-sdk` instead of being defined
- by Console: `CodeRef`, `EncodedCodeRef`, `LoadedExtension`, and `ResolvedExtension` ([CONSOLE-3769], [#15904])
+ locally: `CodeRef`, `EncodedCodeRef`, `LoadedExtension`, `ResolvedExtension` and `ExtensionPredicate` ([CONSOLE-3769], [#15904], [#16115])
## 4.22.0-prerelease.1 - 2025-01-21
@@ -54,7 +56,7 @@ table in [Console dynamic plugins README](./README.md).
- **Type breaking**: Fix `popupComponent` prop type in extension `console.dashboards/overview/health/resource` ([CONSOLE-4796], [#15526])
- **Type breaking**: `AlwaysOnExtension` and `ModelDefinition` types are removed from `api/common-types`. ([CONSOLE-3769], [#15509])
- The following types are now re-exported from `@openshift/dynamic-plugin-sdk` instead of being defined
- locally: `ExtensionFlags`, `ExtensionTypeGuard`, `ResolvedCodeRefProperties`, `RemoteEntryModule`, and `Update`. ([CONSOLE-4840], [#15509], [#15671])
+ locally: `ExtensionFlags`, `ExtensionTypeGuard`, `ResolvedCodeRefProperties`, `RemoteEntryModule`, and `Update` ([CONSOLE-4840], [#15509], [#15671])
- Add optional `fetch` property to extension `console.dashboards/overview/health/url` ([CONSOLE-4796], [#15526])
- Add optional `infrastructure` parameter to `PrometheusHealthHandler` type ([CONSOLE-4796], [#15526])
- Allow `K8sResourceKind` in `TopologyDataObject`, `TopologyResourcesObject`, and `OverviewItem` types ([CONSOLE-4840], [#15699])
@@ -196,6 +198,7 @@ table in [Console dynamic plugins README](./README.md).
[CONSOLE-4990]: https://issues.redhat.com/browse/CONSOLE-4990
[CONSOLE-5039]: https://issues.redhat.com/browse/CONSOLE-5039
[CONSOLE-5050]: https://issues.redhat.com/browse/CONSOLE-5050
+[CONSOLE-5065]: https://issues.redhat.com/browse/CONSOLE-5065
[CONSOLE-5093]: https://issues.redhat.com/browse/CONSOLE-5093
[OCPBUGS-19048]: https://issues.redhat.com/browse/OCPBUGS-19048
[OCPBUGS-30077]: https://issues.redhat.com/browse/OCPBUGS-30077
@@ -274,3 +277,4 @@ table in [Console dynamic plugins README](./README.md).
[#16019]: https://github.com/openshift/console/pull/16019
[#16043]: https://github.com/openshift/console/pull/16043
[#16057]: https://github.com/openshift/console/pull/16057
+[#16115]: https://github.com/openshift/console/pull/16115
diff --git a/frontend/packages/console-dynamic-plugin-sdk/docs/api.md b/frontend/packages/console-dynamic-plugin-sdk/docs/api.md
index 525c798e0e7..36d76e998e0 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/docs/api.md
+++ b/frontend/packages/console-dynamic-plugin-sdk/docs/api.md
@@ -3096,14 +3096,16 @@ Quick start context values object.
### Summary
-React hook for consuming Console extensions with resolved `CodeRef` properties.
This hook accepts the same argument(s) as `useExtensions` hook and returns an adapted list of extension instances, resolving all code references within each extension's properties.
Initially, the hook returns an empty array. Once the resolution is complete, the React component is re-rendered with the hook returning an adapted list of extensions.
When the list of matching extensions changes, the resolution is restarted. The hook will continue to return the previous result until the resolution completes.
The hook's result elements are guaranteed to be referentially stable across re-renders.
+React hook for consuming Console extensions containing code references.
Initially, this hook returns a result tuple `[resolvedExtensions: [], resolved: false, errors: []]`.
Once the resolution is complete, this hook re-renders the component with a result tuple containing
all matching extensions that had their code references resolved successfully along with any errors
that occurred during the process.
When the list of matching extensions changes, the resolution is restarted. In such case, the hook
will _not_ re-render the component with empty initial result since it's preferable to use existing
state until the current resolution completes.
The hook's result is guaranteed to be referentially stable across re-renders, assuming referential
stability of the `predicate` parameter.
### Example
```ts
-const [navItemExtensions, navItemsResolved] = useResolvedExtensions(isNavItem);
-// process adapted extensions and render your component
+const Example = () => {
+ const [navItems, navItemsResolved] = useResolvedExtensions(isNavItem);
+ // process extensions and render your component
+};
```
@@ -3112,13 +3114,13 @@ const [navItemExtensions, navItemsResolved] = useResolvedExtensions(isN
| Parameter Name | Description |
| -------------- | ----------- |
-| `typeGuards` | A list of callbacks that each accept a dynamic plugin extension as an argument and return a boolean flag indicating whether or not the extension meets desired type constraints |
+| `predicate` | Predicate (type guard) to filter extensions of a specific type. |
### Returns
-Tuple containing a list of adapted extension instances with resolved code references, a boolean flag indicating whether the resolution is complete, and a list of errors detected during the resolution.
+Tuple `[resolvedExtensions, resolved, errors]` containing a list of matching extensions,
a boolean flag indicating whether the resolution is complete, and a list of errors detected during
the resolution.
### Source
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/api/dynamic-core-api.ts b/frontend/packages/console-dynamic-plugin-sdk/src/api/dynamic-core-api.ts
index 7753294ec07..52479b7977f 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/api/dynamic-core-api.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/api/dynamic-core-api.ts
@@ -46,18 +46,33 @@ export * from './common-types';
export * from './utils';
/**
- * React hook for consuming Console extensions with resolved `CodeRef` properties.
- * This hook accepts the same argument(s) as `useExtensions` hook and returns an adapted list of extension instances, resolving all code references within each extension's properties.
- * Initially, the hook returns an empty array. Once the resolution is complete, the React component is re-rendered with the hook returning an adapted list of extensions.
- * When the list of matching extensions changes, the resolution is restarted. The hook will continue to return the previous result until the resolution completes.
- * The hook's result elements are guaranteed to be referentially stable across re-renders.
+ * React hook for consuming Console extensions containing code references.
+ *
+ * Initially, this hook returns a result tuple `[resolvedExtensions: [], resolved: false, errors: []]`.
+ *
+ * Once the resolution is complete, this hook re-renders the component with a result tuple containing
+ * all matching extensions that had their code references resolved successfully along with any errors
+ * that occurred during the process.
+ *
+ * When the list of matching extensions changes, the resolution is restarted. In such case, the hook
+ * will _not_ re-render the component with empty initial result since it's preferable to use existing
+ * state until the current resolution completes.
+ *
+ * The hook's result is guaranteed to be referentially stable across re-renders, assuming referential
+ * stability of the `predicate` parameter.
+ *
* @example
* ```ts
- * const [navItemExtensions, navItemsResolved] = useResolvedExtensions(isNavItem);
- * // process adapted extensions and render your component
+ * const Example = () => {
+ * const [navItems, navItemsResolved] = useResolvedExtensions(isNavItem);
+ * // process extensions and render your component
+ * };
* ```
- * @param typeGuards A list of callbacks that each accept a dynamic plugin extension as an argument and return a boolean flag indicating whether or not the extension meets desired type constraints
- * @returns Tuple containing a list of adapted extension instances with resolved code references, a boolean flag indicating whether the resolution is complete, and a list of errors detected during the resolution.
+ *
+ * @param predicate Predicate (type guard) to filter extensions of a specific type.
+ * @returns Tuple `[resolvedExtensions, resolved, errors]` containing a list of matching extensions,
+ * a boolean flag indicating whether the resolution is complete, and a list of errors detected during
+ * the resolution.
*/
export const useResolvedExtensions: UseResolvedExtensions = require('@console/dynamic-plugin-sdk/src/api/useResolvedExtensions')
.useResolvedExtensions;
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/api/useResolvedExtensions.ts b/frontend/packages/console-dynamic-plugin-sdk/src/api/useResolvedExtensions.ts
index 2d005213dba..d66e9ed8159 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/api/useResolvedExtensions.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/api/useResolvedExtensions.ts
@@ -1,42 +1,15 @@
-import { useState, useEffect } from 'react';
+import type { UseResolvedExtensionsOptions as UseResolvedExtensionsOptionsSDK } from '@openshift/dynamic-plugin-sdk';
+import { useResolvedExtensions as useResolvedExtensionsSDK } from '@openshift/dynamic-plugin-sdk';
import { useExtensions } from '@console/plugin-sdk/src/api/useExtensions';
-import { resolveExtension } from '../coderefs/coderef-resolver';
import type { UseResolvedExtensions } from '../extensions/console-types';
-import type { Extension, ExtensionTypeGuard, ResolvedExtension } from '../types';
-import { settleAllPromises } from '../utils/promise';
+import type { Extension, ExtensionPredicate, ResolvedExtension } from '../types';
+
+const hookOptions: UseResolvedExtensionsOptionsSDK = {
+ useExtensionsImpl: useExtensions,
+};
export const useResolvedExtensions: UseResolvedExtensions = (
- ...typeGuards: ExtensionTypeGuard[]
+ predicate: ExtensionPredicate,
): [ResolvedExtension[], boolean, any[]] => {
- const extensions = useExtensions(...typeGuards);
-
- const [resolvedExtensions, setResolvedExtensions] = useState[]>([]);
- const [resolved, setResolved] = useState(false);
- const [errors, setErrors] = useState([]);
-
- useEffect(() => {
- let disposed = false;
-
- // eslint-disable-next-line promise/catch-or-return
- settleAllPromises(
- extensions.map((e) => resolveExtension>(e)),
- ).then(([fulfilledValues, rejectedReasons]) => {
- if (!disposed) {
- setResolvedExtensions(fulfilledValues);
- setErrors(rejectedReasons);
- setResolved(true);
-
- if (rejectedReasons.length > 0) {
- // eslint-disable-next-line no-console
- console.error('Detected errors while resolving Console extensions', rejectedReasons);
- }
- }
- });
-
- return () => {
- disposed = true;
- };
- }, [extensions]);
-
- return [resolvedExtensions, resolved, errors];
+ return useResolvedExtensionsSDK(predicate, hookOptions);
};
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/coderefs/__tests__/coderef-resolver.spec.ts b/frontend/packages/console-dynamic-plugin-sdk/src/coderefs/__tests__/coderef-resolver.spec.ts
deleted file mode 100644
index a69ca986075..00000000000
--- a/frontend/packages/console-dynamic-plugin-sdk/src/coderefs/__tests__/coderef-resolver.spec.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { isEncodedCodeRef, parseEncodedCodeRefValue } from '../coderef-resolver';
-
-const originalConsole = { ...console };
-const consoleMock = jest.fn();
-
-beforeEach(() => {
- jest.resetAllMocks();
- // eslint-disable-next-line no-console
- ['log', 'info', 'warn', 'error'].forEach((key) => (console[key] = consoleMock));
-});
-
-afterEach(() => {
- // eslint-disable-next-line no-console
- ['log', 'info', 'warn', 'error'].forEach((key) => (console[key] = originalConsole[key]));
-});
-
-describe('isEncodedCodeRef', () => {
- it('returns true if obj is structured as { $codeRef: string }', () => {
- expect(isEncodedCodeRef({})).toBe(false);
- expect(isEncodedCodeRef({ $codeRef: true })).toBe(false);
- expect(isEncodedCodeRef({ $codeRef: 'foo' })).toBe(true);
- expect(isEncodedCodeRef({ $codeRef: 'foo', bar: true })).toBe(false);
- });
-});
-
-describe('parseEncodedCodeRefValue', () => {
- it('returns [moduleName, exportName] tuple if value has the right format', () => {
- expect(parseEncodedCodeRefValue('foo.bar')).toEqual(['foo', 'bar']);
- expect(parseEncodedCodeRefValue('foo')).toEqual(['foo', 'default']);
- });
-
- it('returns an empty array if value does not have the expected format', () => {
- expect(parseEncodedCodeRefValue('')).toEqual([]);
- expect(parseEncodedCodeRefValue('.')).toEqual([]);
- expect(parseEncodedCodeRefValue('.bar')).toEqual([]);
- expect(parseEncodedCodeRefValue('.bar.')).toEqual([]);
- });
-});
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/coderefs/coderef-resolver.ts b/frontend/packages/console-dynamic-plugin-sdk/src/coderefs/coderef-resolver.ts
deleted file mode 100644
index fc71c324aaf..00000000000
--- a/frontend/packages/console-dynamic-plugin-sdk/src/coderefs/coderef-resolver.ts
+++ /dev/null
@@ -1,97 +0,0 @@
-/* eslint-disable no-console */
-
-import type {
- AnyObject,
- ExtractExtensionProperties,
- ResolvedExtension,
-} from '@openshift/dynamic-plugin-sdk';
-import { applyCodeRefSymbol } from '@openshift/dynamic-plugin-sdk';
-import { isPlainObject, isEqual, isFunction, isNil, cloneDeep } from 'lodash';
-import type { Extension, EncodedCodeRef, CodeRef } from '../types';
-import { deepForOwn } from '../utils/object';
-import { settleAllPromises } from '../utils/promise';
-
-/**
- * Extract the SDK's internal CodeRef symbol by applying it to a dummy function.
- *
- * This ensures we can detect code refs created by the SDK, which uses its own
- * private Symbol instance.
- */
-const codeRefSymbol = Object.getOwnPropertySymbols(applyCodeRefSymbol(() => Promise.resolve()))[0];
-
-if (!codeRefSymbol) {
- throw new Error('Failed to extract CodeRef symbol from the SDK');
-}
-
-export const isEncodedCodeRef = (obj): obj is EncodedCodeRef =>
- isPlainObject(obj) &&
- isEqual(Object.getOwnPropertyNames(obj), ['$codeRef']) &&
- typeof (obj as EncodedCodeRef).$codeRef === 'string';
-
-export const isExecutableCodeRef = (obj): obj is CodeRef =>
- isFunction(obj) &&
- isEqual(Object.getOwnPropertySymbols(obj), [codeRefSymbol]) &&
- obj[codeRefSymbol] === true;
-
-const codeRefErrorSymbol = Symbol('error');
-export const isCodeRefError = (ref: CodeRef) => !!ref[codeRefErrorSymbol];
-export const getCodeRefError = (ref: CodeRef) => ref[codeRefErrorSymbol];
-export const setCodeRefError = (ref: CodeRef, e: any) => {
- ref[codeRefErrorSymbol] = e;
- return ref;
-};
-
-/**
- * Parse the `EncodedCodeRef` value into `[moduleName, exportName]` tuple.
- *
- * Returns an empty array if the value doesn't match the expected format.
- */
-export const parseEncodedCodeRefValue = (value: string): [string, string] | [] => {
- const match = value.match(/^([^.]+)(?:\.(.+)){0,1}$/);
- return match ? [match[1], match[2] || 'default'] : [];
-};
-
-/**
- * Returns an extension with its `CodeRef` properties replaced with referenced objects.
- *
- * The resulting Promise resolves with a new extension instance; its `properties` object
- * is cloned in order to preserve the original extension.
- */
-export const resolveExtension = async <
- E extends Extension,
- P extends AnyObject = ExtractExtensionProperties,
- R = ResolvedExtension
->(
- extension: E,
-): Promise => {
- const clonedProperties = cloneDeep(extension.properties);
- const valueResolutions: Promise[] = [];
-
- deepForOwn(clonedProperties, isExecutableCodeRef, (ref, key, obj) => {
- if (isCodeRefError(ref)) {
- throw getCodeRefError(ref);
- }
- valueResolutions.push(
- ref()
- .then((resolvedValue) => {
- obj[key] = resolvedValue;
-
- if (isNil(resolvedValue)) {
- console.warn(`Code reference property '${key}' resolved to null or undefined`);
- }
- })
- .catch((e) => {
- setCodeRefError(ref, e ?? true);
- return e;
- }),
- );
- });
-
- await settleAllPromises(valueResolutions);
-
- // Return a new extension object with the resolved properties
- return ({
- ...extension,
- properties: clonedProperties,
- } as unknown) as R;
-};
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts
index 6e2ee814326..c78f79fe3ac 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/extensions/console-types.ts
@@ -29,7 +29,7 @@ import type {
ResolvedExtension,
Selector,
} from '../api/common-types';
-import type { Extension, ExtensionTypeGuard } from '../types';
+import type { Extension, ExtensionPredicate } from '../types';
import type { CustomDataSource } from './dashboard-data-source';
export type OwnerReference = {
@@ -264,7 +264,7 @@ export type UseK8sWatchResources = (
) => WatchK8sResults;
export type UseResolvedExtensions = (
- ...typeGuards: ExtensionTypeGuard[]
+ predicate: ExtensionPredicate,
) => [ResolvedExtension[], boolean, any[]];
export type GetSegmentAnalytics = () => {
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/runtime/plugin-init.ts b/frontend/packages/console-dynamic-plugin-sdk/src/runtime/plugin-init.ts
index 5c3e1af2107..1c152f59b71 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/runtime/plugin-init.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/runtime/plugin-init.ts
@@ -1,4 +1,4 @@
-import type { PluginStore } from '@openshift/dynamic-plugin-sdk';
+import type { PluginStore, LocalPluginManifest } from '@openshift/dynamic-plugin-sdk';
import { consoleLogger } from '@openshift/dynamic-plugin-sdk';
import * as _ from 'lodash';
import {
@@ -88,42 +88,50 @@ const registerLegacyPluginEntryCallback = () => {
window[REMOTE_ENTRY_CALLBACK](patchedPluginName, entryModule);
};
- // consoleLogger is suppressed in unit tests and in production
consoleLogger.info(
- `Legacy plugin entry callback "${previousConsoleCallbackName}" has been registered.`,
+ `Legacy plugin entry callback ${previousConsoleCallbackName} has been registered`,
);
};
/**
- * Loads and enables all dynamic plugins listed in `dynamicPluginNames`.
+ * Loads and enables all Console plugins.
*
- * Precondition: Application {@link PluginStore} must be initialized.
+ * Precondition: {@link PluginStore} must be initialized.
*/
-export const initConsolePlugins = _.once((pluginStore: PluginStore) => {
- // Initialize dynamic plugin infrastructure
- registerLegacyPluginEntryCallback();
+export const initConsolePlugins = _.once(
+ (pluginStore: PluginStore, localPlugins: LocalPluginManifest[]) => {
+ // Polyfill the legacy plugin entry callback function
+ registerLegacyPluginEntryCallback();
- // Initialize webpack share scope object and start loading plugins
- initSharedScope()
- .then(() => {
- monkeyPatchSharedScope();
- })
- .then(() => {
- dynamicPluginNames.forEach((pluginName) => {
- loadAndEnablePlugin(pluginName, pluginStore, (errorMessage, errorCause) => {
- // eslint-disable-next-line no-console
- console.error(..._.compact([errorMessage, errorCause]));
- addTestError(`${errorMessage}: ${String(errorCause)}`);
+ // Initialize webpack share scope object and start loading plugins
+ initSharedScope()
+ .then(() => {
+ // Patch webpack share scope object for backwards compatibility
+ monkeyPatchSharedScope();
+
+ if (process.env.NODE_ENV !== 'production') {
+ // Expose webpack share scope object for debugging
+ window.webpackSharedScope = getSharedScope();
+ }
+ })
+ .then(() => {
+ // Load all static (local) plugins
+ // TODO: consider awaiting load completion for local plugins before loading remote plugins
+ localPlugins.forEach((plugin) => pluginStore.loadPlugin(plugin));
+ })
+ .then(() => {
+ // Load all dynamic (remote) plugins
+ dynamicPluginNames.forEach((pluginName) => {
+ loadAndEnablePlugin(pluginName, pluginStore, (errorMessage, errorCause) => {
+ // eslint-disable-next-line no-console
+ console.error(..._.compact([errorMessage, errorCause]));
+ addTestError(`${errorMessage}: ${String(errorCause)}`);
+ });
});
+ })
+ .catch((err) => {
+ // eslint-disable-next-line no-console
+ console.error('Error while loading Console plugins', err);
});
-
- if (process.env.NODE_ENV !== 'production') {
- // Expose webpack share scope object for debugging
- window.webpackSharedScope = getSharedScope();
- }
- })
- .catch((err) => {
- // eslint-disable-next-line no-console
- console.error('Failed to initialize webpack share scope for dynamic plugins', err);
- });
-});
+ },
+);
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/types.ts b/frontend/packages/console-dynamic-plugin-sdk/src/types.ts
index 050451352a3..ba5fbfd542d 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/types.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/types.ts
@@ -3,7 +3,7 @@ export type {
EncodedCodeRef,
Extension,
ExtensionFlags,
- ExtensionPredicate as ExtensionTypeGuard,
+ ExtensionPredicate,
LoadedAndResolvedExtension as ResolvedExtension,
LoadedExtension,
MapCodeRefsToValues as ResolvedCodeRefProperties,
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/validation/ExtensionValidator.ts b/frontend/packages/console-dynamic-plugin-sdk/src/validation/ExtensionValidator.ts
index 067bf384f91..cec2857789b 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/validation/ExtensionValidator.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/validation/ExtensionValidator.ts
@@ -1,16 +1,18 @@
import * as fs from 'fs';
import * as path from 'path';
+import { isEncodedCodeRef, parseEncodedCodeRef } from '@openshift/dynamic-plugin-sdk';
import * as _ from 'lodash';
import type { Compilation, NormalModule, Module } from 'webpack';
import type { ConsolePluginBuildMetadata } from '../build-types';
-import { isEncodedCodeRef, parseEncodedCodeRefValue } from '../coderefs/coderef-resolver';
import type { Extension, EncodedCodeRef } from '../types';
import { deepForOwn } from '../utils/object';
import { BaseValidator } from './BaseValidator';
type ExtensionCodeRefData = {
index: number;
- propToCodeRefValue: { [propName: string]: string };
+ propToEncodedCodeRef: {
+ [propName: string]: EncodedCodeRef;
+ };
};
/**
@@ -61,13 +63,13 @@ export const guessModuleFilePath = (
export const collectCodeRefData = (extensions: Extension[]) =>
extensions.reduce((acc, e, index) => {
- const data: ExtensionCodeRefData = { index, propToCodeRefValue: {} };
+ const data: ExtensionCodeRefData = { index, propToEncodedCodeRef: {} };
deepForOwn(e.properties, isEncodedCodeRef, (ref, key) => {
- data.propToCodeRefValue[key] = ref.$codeRef;
+ data.propToEncodedCodeRef[key] = ref;
});
- if (!_.isEmpty(data.propToCodeRefValue)) {
+ if (!_.isEmpty(data.propToEncodedCodeRef)) {
acc.push(data);
}
@@ -116,9 +118,9 @@ export class ExtensionValidator extends BaseValidator {
// Each exposed module must have at least one code reference
Object.keys(exposedModules).forEach((moduleName) => {
const moduleReferenced = codeRefs.some((data) =>
- Object.values(data.propToCodeRefValue).some((value) => {
- const [parsedModuleName] = parseEncodedCodeRefValue(value);
- return parsedModuleName && moduleName === parsedModuleName;
+ Object.values(data.propToEncodedCodeRef).some((ref) => {
+ const refData = parseEncodedCodeRef(ref);
+ return refData && refData.moduleName === moduleName;
}),
);
@@ -129,15 +131,16 @@ export class ExtensionValidator extends BaseValidator {
// Each code reference must point to a valid webpack module export
codeRefs.forEach((data) => {
- Object.entries(data.propToCodeRefValue).forEach(([propName, codeRefValue]) => {
- const [moduleName, exportName] = parseEncodedCodeRefValue(codeRefValue);
+ Object.entries(data.propToEncodedCodeRef).forEach(([propName, ref]) => {
const errorTrace = `in extension [${data.index}] property '${propName}'`;
+ const refData = parseEncodedCodeRef(ref);
- if (!moduleName || !exportName) {
- this.result.addError(`Invalid code reference '${codeRefValue}' ${errorTrace}`);
+ if (!refData) {
+ this.result.addError(`Invalid code reference '${ref.$codeRef}' ${errorTrace}`);
return;
}
+ const { moduleName, exportName } = refData;
const foundModule = webpackModules[moduleName];
if (!foundModule) {
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/validation/ValidationResult.ts b/frontend/packages/console-dynamic-plugin-sdk/src/validation/ValidationResult.ts
index 915772fd3ea..f49e0974d4d 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/validation/ValidationResult.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/validation/ValidationResult.ts
@@ -33,15 +33,6 @@ class ValidationAssertions {
this.result.assertThat(!!semver.validRange(obj), `${objPath} semver range is not valid`);
}
}
-
- validRegistrationMethod(registrationMethod: any) {
- if (typeof registrationMethod === 'string') {
- this.result.assertThat(
- ['callback'].includes(registrationMethod),
- `registrationMethod must be one of: 'callback'`,
- );
- }
- }
}
export class ValidationResult {
diff --git a/frontend/packages/console-dynamic-plugin-sdk/src/validation/__tests__/ExtensionValidator.spec.ts b/frontend/packages/console-dynamic-plugin-sdk/src/validation/__tests__/ExtensionValidator.spec.ts
index 212fb43dca1..487d71f3a95 100644
--- a/frontend/packages/console-dynamic-plugin-sdk/src/validation/__tests__/ExtensionValidator.spec.ts
+++ b/frontend/packages/console-dynamic-plugin-sdk/src/validation/__tests__/ExtensionValidator.spec.ts
@@ -38,9 +38,9 @@ describe('collectCodeRefData', () => {
expect(collectCodeRefData(extensions)).toEqual([
{
index: 1,
- propToCodeRefValue: {
- qux: 'mod.a',
- test: 'mod.b',
+ propToEncodedCodeRef: {
+ qux: { $codeRef: 'mod.a' },
+ test: { $codeRef: 'mod.b' },
},
},
]);
diff --git a/frontend/packages/console-plugin-sdk/src/api/useExtensions.ts b/frontend/packages/console-plugin-sdk/src/api/useExtensions.ts
index 7e8d50523b0..61d04b44bc9 100644
--- a/frontend/packages/console-plugin-sdk/src/api/useExtensions.ts
+++ b/frontend/packages/console-plugin-sdk/src/api/useExtensions.ts
@@ -10,12 +10,13 @@ import { useTranslatedExtensions } from '../utils/useTranslatedExtensions';
*
* This hook re-renders the component whenever the list of matching extensions changes.
*
- * The hook's result is guaranteed to be referentially stable across re-renders.
+ * The hook's result is guaranteed to be referentially stable across re-renders,
+ * assuming referential stability of the `predicate` parameter.
*
* @example
* ```ts
* const Example = () => {
- * const navItemExtensions = useExtensions(isNavItem);
+ * const navItems = useExtensions(isNavItem);
* // process extensions and render your component
* };
* ```
@@ -25,7 +26,6 @@ import { useTranslatedExtensions } from '../utils/useTranslatedExtensions';
* @see {@link useTranslatedExtensions}
* @see {@link useSortedExtensions}
*/
-// TODO: consider exposing hook via Console plugin SDK and move ^^ doc to exported symbol
export const useExtensions: typeof useExtensionsSDK = (predicate) => {
const extensions = useExtensionsSDK(predicate);
const sortedExtensions = useSortedExtensions(extensions);
diff --git a/frontend/packages/console-plugin-sdk/src/codegen/local-plugins.ts b/frontend/packages/console-plugin-sdk/src/codegen/local-plugins.ts
index fb4bde2cbfc..21dc87164ca 100644
--- a/frontend/packages/console-plugin-sdk/src/codegen/local-plugins.ts
+++ b/frontend/packages/console-plugin-sdk/src/codegen/local-plugins.ts
@@ -1,10 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
+import { isEncodedCodeRef, parseEncodedCodeRef } from '@openshift/dynamic-plugin-sdk';
import * as _ from 'lodash';
-import {
- isEncodedCodeRef,
- parseEncodedCodeRefValue,
-} from '@console/dynamic-plugin-sdk/src/coderefs/coderef-resolver';
import { extensionsFile } from '@console/dynamic-plugin-sdk/src/constants';
import type { ConsoleExtensionsJSON } from '@console/dynamic-plugin-sdk/src/schema/console-extensions';
import type { EncodedCodeRef } from '@console/dynamic-plugin-sdk/src/types';
@@ -90,17 +87,18 @@ export const getExecutableCodeRefSource = (
pkg: PluginPackage,
validationResult: ValidationResult,
) => {
- const [moduleName, exportName] = parseEncodedCodeRefValue(ref.$codeRef);
- const exposedModules = pkg.consolePlugin.exposedModules || {};
-
const errorTrace = `in property '${propName}'`;
const emptyCodeRefSource = '() => Promise.resolve(null)';
+ const refData = parseEncodedCodeRef(ref);
- if (!moduleName || !exportName) {
+ if (!refData) {
validationResult.addError(`Invalid code reference '${ref.$codeRef}' ${errorTrace}`);
return emptyCodeRefSource;
}
+ const { moduleName, exportName } = refData;
+ const exposedModules = pkg.consolePlugin.exposedModules || {};
+
if (!exposedModules[moduleName]) {
validationResult.addError(`Module '${moduleName}' is not exposed ${errorTrace}`);
return emptyCodeRefSource;
diff --git a/frontend/packages/console-shared/src/components/catalog/hooks/__tests__/useCatalogExtensions.spec.ts b/frontend/packages/console-shared/src/components/catalog/hooks/__tests__/useCatalogExtensions.spec.ts
index 11f060cc163..fa2c2d16aa6 100644
--- a/frontend/packages/console-shared/src/components/catalog/hooks/__tests__/useCatalogExtensions.spec.ts
+++ b/frontend/packages/console-shared/src/components/catalog/hooks/__tests__/useCatalogExtensions.spec.ts
@@ -17,7 +17,7 @@ let mockExtensions: (
)[] = [];
jest.mock('@console/dynamic-plugin-sdk/src/api/useResolvedExtensions', () => ({
- useResolvedExtensions: (typeGuard) => [mockExtensions.filter(typeGuard), true],
+ useResolvedExtensions: (predicate) => [mockExtensions.filter(predicate), true],
}));
describe('useCatalogExtensions', () => {
diff --git a/frontend/packages/console-shared/src/hooks/useCreateResourceExtension.ts b/frontend/packages/console-shared/src/hooks/useCreateResourceExtension.ts
index 00e736e1772..60413cdc8e4 100644
--- a/frontend/packages/console-shared/src/hooks/useCreateResourceExtension.ts
+++ b/frontend/packages/console-shared/src/hooks/useCreateResourceExtension.ts
@@ -8,11 +8,11 @@ import { useExtensions } from '@console/plugin-sdk/src/api/useExtensions';
export const useCreateResourceExtension = (
modelReference: GroupVersionKind,
): LoadedExtension => {
- const createResourceTypeGuard = useCallback(
+ const createResourcePredicate = useCallback(
(e: Extension): e is CreateResource =>
isCreateResource(e) && referenceForExtensionModel(e.properties.model) === modelReference,
[modelReference],
);
- const [extensionPage] = useExtensions(createResourceTypeGuard);
+ const [extensionPage] = useExtensions(createResourcePredicate);
return extensionPage;
};
diff --git a/frontend/packages/console-shared/src/hooks/useDetailsItemExtensionsForResource.ts b/frontend/packages/console-shared/src/hooks/useDetailsItemExtensionsForResource.ts
index 9870fac47d2..35ff44a867e 100644
--- a/frontend/packages/console-shared/src/hooks/useDetailsItemExtensionsForResource.ts
+++ b/frontend/packages/console-shared/src/hooks/useDetailsItemExtensionsForResource.ts
@@ -6,7 +6,7 @@ import type {
DetailsItemColumn,
} from '@console/dynamic-plugin-sdk/src/extensions/details-item';
import { isDetailsItem } from '@console/dynamic-plugin-sdk/src/extensions/details-item';
-import type { ResolvedExtension, ExtensionTypeGuard } from '@console/dynamic-plugin-sdk/src/types';
+import type { ResolvedExtension, ExtensionPredicate } from '@console/dynamic-plugin-sdk/src/types';
import { referenceFor, referenceForExtensionModel } from '@console/internal/module/k8s/k8s';
/**
@@ -20,7 +20,7 @@ export const useDetailsItemExtensionsForResource: UseDetailsItemExtensionsForRes
obj,
column,
) => {
- const typeGuard = useCallback>(
+ const predicate = useCallback>(
(e): e is DetailsItem => {
if (!isDetailsItem(e)) return false;
const columnMatches = e.properties.column === column;
@@ -30,7 +30,7 @@ export const useDetailsItemExtensionsForResource: UseDetailsItemExtensionsForRes
[obj, column],
);
- const [extensions] = useResolvedExtensions(typeGuard);
+ const [extensions] = useResolvedExtensions(predicate);
return useMemo(
() =>
diff --git a/frontend/public/plugins.ts b/frontend/public/plugins.ts
index 129de945ebc..14e3b9db936 100644
--- a/frontend/public/plugins.ts
+++ b/frontend/public/plugins.ts
@@ -1,4 +1,4 @@
-import { consoleLogger, PluginStore } from '@openshift/dynamic-plugin-sdk';
+import { PluginStore } from '@openshift/dynamic-plugin-sdk';
import { getSharedScope } from '@console/dynamic-plugin-sdk/src/runtime/plugin-shared-modules';
import type { LocalPluginManifest } from '@openshift/dynamic-plugin-sdk';
import type { Middleware } from 'redux';
@@ -8,7 +8,6 @@ import { valid as semver } from 'semver';
import { consoleFetch } from '@console/dynamic-plugin-sdk/src/utils/fetch/console-fetch';
import { ValidationResult } from '@console/dynamic-plugin-sdk/src/validation/ValidationResult';
import { REMOTE_ENTRY_CALLBACK } from '@console/dynamic-plugin-sdk/src/constants';
-import { noop } from 'lodash';
import { initConsolePlugins } from '@console/dynamic-plugin-sdk/src/runtime/plugin-init';
/**
@@ -23,12 +22,6 @@ const CURRENT_OPENSHIFT_VERSION = semver(window.SERVER_FLAGS.releaseVersion) ??
*/
const localPlugins: LocalPluginManifest[] = require('../get-local-plugins').default;
-// Suppress plugin SDK consoleLogger.info during testing to reduce noise
-// TODO: remove when upgrading to SDK 8.1 (codename "Blue")
-if (process.env.NODE_ENV === 'test') {
- consoleLogger.info = noop;
-}
-
const localPluginNames = localPlugins.map((p) => p.name);
/** Checks if a plugin name is allowed to be loaded in Console. */
@@ -36,6 +29,13 @@ const isAllowedPluginName = (name: string) => {
return localPluginNames.includes(name) || dynamicPluginNames.includes(name);
};
+if (process.env.NODE_ENV !== 'test') {
+ // eslint-disable-next-line no-console
+ console.info(`Static plugins: [${localPluginNames.join(', ') ?? '(empty)'}]`);
+ // eslint-disable-next-line no-console
+ console.info(`Dynamic plugins: [${dynamicPluginNames.join(', ') ?? '(empty)'}]`);
+}
+
/**
* Provides access to Console plugins and their extensions.
*
@@ -97,26 +97,23 @@ export const pluginStore = new PluginStore({
result.assertions.validDNSSubdomainName(manifest.name, 'metadata.name');
// Only allow 'callback' registration method
- result.assertions.validRegistrationMethod(manifest.registrationMethod);
+ result.assertThat(
+ manifest.registrationMethod === 'callback',
+ "registrationMethod must be 'callback', other methods are not supported",
+ );
result.report();
// No issues, return manifest as-is
return manifest;
},
-
- // Double check that only 'callback' registration method is used
- getPluginEntryModule: ({ name }) => {
- throw new Error(
- `Plugin "${name}" tried to load with registrationMethod "custom", but only "callback" is supported.`,
- );
- },
},
});
-localPlugins.forEach((plugin) => pluginStore.loadPlugin(plugin));
-
-initConsolePlugins(pluginStore);
+if (process.env.NODE_ENV !== 'production') {
+ // Expose Console plugin store for debugging
+ window.pluginStore = pluginStore;
+}
/** Redux middleware that updates PluginStore FeatureFlags when redux actions are dispatched. */
export const featureFlagMiddleware: Middleware<{}, RootState> = (s) => {
@@ -135,14 +132,4 @@ export const featureFlagMiddleware: Middleware<{}, RootState> = (s) => {
};
};
-if (process.env.NODE_ENV !== 'production') {
- // Expose Console plugin store for debugging
- window.pluginStore = pluginStore;
-}
-
-if (process.env.NODE_ENV !== 'test') {
- // eslint-disable-next-line no-console
- console.info(`Static plugins: [${localPluginNames.join(', ')}]`);
- // eslint-disable-next-line no-console
- console.info(`Dynamic plugins: [${dynamicPluginNames.join(', ')}]`);
-}
+initConsolePlugins(pluginStore, localPlugins);
diff --git a/frontend/setup-tests.js b/frontend/setup-tests.js
index 92f22f56c13..11ee228044f 100644
--- a/frontend/setup-tests.js
+++ b/frontend/setup-tests.js
@@ -1,6 +1,10 @@
+import { consoleLogger } from '@openshift/dynamic-plugin-sdk';
import '@testing-library/jest-dom';
import { configure } from '@testing-library/react';
// Configure RTL to use 'data-test' instead of 'data-testid' for consistency
// with Cypress tests, which also use data-test attributes
configure({ testIdAttribute: 'data-test' });
+
+// Suppress plugin SDK consoleLogger.info to reduce noise
+consoleLogger.info = () => {};
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index c30597086bd..7bce7b89598 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -3380,9 +3380,9 @@ __metadata:
languageName: node
linkType: hard
-"@openshift/dynamic-plugin-sdk@npm:^8.0.0":
- version: 8.0.0
- resolution: "@openshift/dynamic-plugin-sdk@npm:8.0.0"
+"@openshift/dynamic-plugin-sdk@npm:^8.2.0":
+ version: 8.2.0
+ resolution: "@openshift/dynamic-plugin-sdk@npm:8.2.0"
dependencies:
lodash: "npm:^4.17.23"
semver: "npm:^7.7.3"
@@ -3390,7 +3390,7 @@ __metadata:
yup: "npm:^1.7.1"
peerDependencies:
react: ^18 || ^19
- checksum: 10c0/a5c7bc5198f75fcc9fad242ffb51e264e860218e7035b87204d429cc47fa566208cc3474af739d3e05a2adc1bfe41ae9e09d3afd93a5b8c0d29f1814ee9f3127
+ checksum: 10c0/a9e3e1a145014a13310acd4630c7a2b7a7545b6c089e1d41e99ceaec061c1a270051d44bd26ddd09ec61bb02533ecfab55663cedb41a20d63acf824067eb63ab
languageName: node
linkType: hard
@@ -4925,17 +4925,10 @@ __metadata:
languageName: node
linkType: hard
-"@types/semver@npm:^6.0.0":
- version: 6.2.2
- resolution: "@types/semver@npm:6.2.2"
- checksum: 10c0/08c70b4ba72bde8e733a235a4fbfa94646cbbdddff30bf91b0f3f9ed8ce759fecc81b3b9d13b762d893923e2d683f6da8bdaa7bfbc6293cb7f119e8dc5ef6562
- languageName: node
- linkType: hard
-
-"@types/semver@npm:^7.3.12":
- version: 7.3.13
- resolution: "@types/semver@npm:7.3.13"
- checksum: 10c0/73295bb1fee46f8c76c7a759feeae5a3022f5bedfdc17d16982092e4b33af17560234fb94861560c20992a702a1e1b9a173bb623a96f95f80892105f5e7d25e3
+"@types/semver@npm:^7.3.12, @types/semver@npm:^7.7.1":
+ version: 7.7.1
+ resolution: "@types/semver@npm:7.7.1"
+ checksum: 10c0/c938aef3bf79a73f0f3f6037c16e2e759ff40c54122ddf0b2583703393d8d3127130823facb880e694caa324eb6845628186aac1997ee8b31dc2d18fafe26268
languageName: node
linkType: hard
@@ -17745,7 +17738,7 @@ __metadata:
"@graphql-codegen/typescript": "npm:^1.15.1"
"@graphql-codegen/typescript-graphql-files-modules": "npm:^1.15.1"
"@graphql-codegen/typescript-operations": "npm:^1.15.1"
- "@openshift/dynamic-plugin-sdk": "npm:^8.0.0"
+ "@openshift/dynamic-plugin-sdk": "npm:^8.2.0"
"@openshift/dynamic-plugin-sdk-webpack": "npm:^5.1.0"
"@patternfly/patternfly": "npm:~6.4.0"
"@patternfly/quickstarts": "npm:~6.4.0"
@@ -17785,7 +17778,7 @@ __metadata:
"@types/react-dom": "npm:^18.3.7"
"@types/react-jsonschema-form": "npm:^1.3.8"
"@types/react-virtualized": "npm:9.x"
- "@types/semver": "npm:^6.0.0"
+ "@types/semver": "npm:^7.7.1"
"@types/showdown": "npm:1.9.4"
"@xterm/addon-fit": "npm:0.10.0"
"@xterm/xterm": "npm:^5.5.0"
@@ -17887,7 +17880,7 @@ __metadata:
sanitize-html: "npm:^2.3.2"
sass: "npm:^1.42.1"
sass-loader: "npm:^10.1.1"
- semver: "npm:6.x"
+ semver: "npm:^7.7.4"
showdown: "npm:1.8.6"
style-loader: "npm:^4.0.0"
subscriptions-transport-ws: "npm:^0.9.16"
@@ -20411,7 +20404,7 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:6.x, semver@npm:^6.0.0, semver@npm:^6.1.0, semver@npm:^6.1.2, semver@npm:^6.3.0, semver@npm:^6.3.1":
+"semver@npm:^6.0.0, semver@npm:^6.1.0, semver@npm:^6.1.2, semver@npm:^6.3.0, semver@npm:^6.3.1":
version: 6.3.1
resolution: "semver@npm:6.3.1"
bin:
@@ -20420,7 +20413,7 @@ __metadata:
languageName: node
linkType: hard
-"semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.4, semver@npm:^7.6.3, semver@npm:^7.7.1, semver@npm:^7.7.2, semver@npm:^7.7.3":
+"semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.4, semver@npm:^7.6.3, semver@npm:^7.7.1, semver@npm:^7.7.2, semver@npm:^7.7.3, semver@npm:^7.7.4":
version: 7.7.4
resolution: "semver@npm:7.7.4"
bin: