frontend-app-api: fix phased predicate type checks

Use internal extension shapes when reading predicate metadata and type the finalized app test helper explicitly. This fixes the typecheck breakage introduced by the phased predicate and override changes.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-03-14 15:43:01 +01:00
parent d65d4812f6
commit b6fc7f861f
2 changed files with 23 additions and 19 deletions
@@ -56,12 +56,8 @@ function combinePredicates(
}
function getExtensionPredicate(options: {
extension: Extension<any, any>;
internalExtension: ReturnType<typeof toInternalExtension>;
}) {
if (options.extension.version === 'v2') {
return options.extension.if;
}
if (options.internalExtension.version === 'v2') {
return options.internalExtension.if;
}
@@ -109,20 +105,27 @@ export function resolveAppNodeSpecs(options: {
const pluginExtensions = plugins.flatMap(plugin => {
const internalPlugin = OpaqueFrontendPlugin.toInternal(plugin);
return internalPlugin.extensions
.map(extension => ({
...extension,
plugin,
if: combinePredicates(
internalPlugin.if,
extension.version === 'v2' ? extension.if : undefined,
),
}))
.map(extension => {
const internalExtension = toInternalExtension(extension);
return {
...internalExtension,
plugin,
if: combinePredicates(
internalPlugin.if,
internalExtension.version === 'v2'
? internalExtension.if
: undefined,
),
};
})
.filter(filterForbidden);
});
const moduleExtensions = modules.flatMap(mod => {
const internalModule = toInternalFrontendModule(mod);
return internalModule.extensions
.flatMap(extension => {
const internalExtension = toInternalExtension(extension);
// Modules for plugins that are not installed are ignored
const plugin = plugins.find(p => p.pluginId === mod.pluginId);
if (!plugin) {
@@ -131,11 +134,13 @@ export function resolveAppNodeSpecs(options: {
return [
{
...extension,
...internalExtension,
plugin,
if: combinePredicates(
internalModule.if,
extension.version === 'v2' ? extension.if : undefined,
internalExtension.version === 'v2'
? internalExtension.if
: undefined,
),
},
];
@@ -159,7 +164,7 @@ export function resolveAppNodeSpecs(options: {
source: plugin,
attachTo: internalExtension.attachTo,
disabled: internalExtension.disabled,
if: getExtensionPredicate({ extension, internalExtension }),
if: getExtensionPredicate({ internalExtension }),
config: undefined as unknown,
},
};
@@ -173,7 +178,7 @@ export function resolveAppNodeSpecs(options: {
plugin: appPlugin,
attachTo: internalExtension.attachTo,
disabled: internalExtension.disabled,
if: getExtensionPredicate({ extension, internalExtension }),
if: getExtensionPredicate({ internalExtension }),
config: undefined as unknown,
},
};
@@ -194,7 +199,6 @@ export function resolveAppNodeSpecs(options: {
configuredExtensions[index].params.attachTo = internalExtension.attachTo;
configuredExtensions[index].params.disabled = internalExtension.disabled;
configuredExtensions[index].params.if = getExtensionPredicate({
extension,
internalExtension,
});
} else {
@@ -206,7 +210,7 @@ export function resolveAppNodeSpecs(options: {
source: extension.plugin,
attachTo: internalExtension.attachTo,
disabled: internalExtension.disabled,
if: getExtensionPredicate({ extension, internalExtension }),
if: getExtensionPredicate({ internalExtension }),
config: undefined,
},
});
@@ -17,10 +17,10 @@
import {
Extension,
FeatureFlagConfig,
FilterPredicate,
IconElement,
OverridableFrontendPlugin,
} from '@backstage/frontend-plugin-api';
import { FilterPredicate } from '@backstage/filter-predicates';
import { JsonObject } from '@backstage/types';
import { OpaqueType } from '@internal/opaque';