diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json
index 33b6067858..17f4b1bef3 100644
--- a/packages/frontend-app-api/package.json
+++ b/packages/frontend-app-api/package.json
@@ -34,6 +34,7 @@
],
"dependencies": {
"@backstage/config": "workspace:^",
+ "@backstage/core-app-api": "workspace:^",
"@backstage/core-plugin-api": "workspace:^",
"@backstage/frontend-plugin-api": "workspace:^",
"@backstage/plugin-graphiql": "workspace:^",
diff --git a/packages/frontend-app-api/src/createApp.tsx b/packages/frontend-app-api/src/createApp.tsx
index c50b507642..77f0d1948a 100644
--- a/packages/frontend-app-api/src/createApp.tsx
+++ b/packages/frontend-app-api/src/createApp.tsx
@@ -21,6 +21,7 @@ import {
coreExtensionData,
} from '@backstage/frontend-plugin-api';
import { CoreRouter } from './extensions/CoreRouter';
+import { Core } from './extensions/Core';
import {
createExtensionInstance,
ExtensionInstance,
@@ -31,8 +32,13 @@ import {
readAppExtensionParameters,
} from './wiring/parameters';
import { RoutingProvider } from './routing/RoutingContext';
-import { RouteRef } from '@backstage/core-plugin-api';
+import { ApiHolder, RouteRef } from '@backstage/core-plugin-api';
import { getAvailablePlugins } from './wiring/discovery';
+import {
+ ApiFactoryRegistry,
+ ApiProvider,
+ ApiResolver,
+} from '@backstage/core-app-api';
/** @public */
export function createApp(options: { plugins: BackstagePlugin[] }): {
@@ -40,7 +46,7 @@ export function createApp(options: { plugins: BackstagePlugin[] }): {
} {
const appConfig = ConfigReader.fromConfigs(process.env.APP_CONFIG as any);
- const builtinExtensions = [CoreRouter];
+ const builtinExtensions = [CoreRouter, Core];
const discoveredPlugins = getAvailablePlugins();
// pull in default extension instance from discovered packages
@@ -115,25 +121,59 @@ export function createApp(options: { plugins: BackstagePlugin[] }): {
const routePaths = extractRouteInfoFromInstanceTree(rootInstances);
+ const coreInstance = rootInstances.find(({ id }) => id === 'core');
+ if (!coreInstance) {
+ throw Error('Unable to find core extension instance');
+ }
+
+ const apiHolder = createApiHolder(coreInstance);
+
return {
createRoot() {
- const rootComponents = rootInstances.map(
- e =>
- e.data.get(
- coreExtensionData.reactComponent.id,
- ) as typeof coreExtensionData.reactComponent.T,
- );
+ const rootComponents = rootInstances
+ .map(
+ e =>
+ e.data.get(
+ coreExtensionData.reactComponent.id,
+ ) as typeof coreExtensionData.reactComponent.T,
+ )
+ .filter(Boolean);
return (
-
- {rootComponents.map((Component, i) => (
-
- ))}
-
+
+
+ {rootComponents.map((Component, i) => (
+
+ ))}
+
+
);
},
};
}
+function createApiHolder(coreExtension: ExtensionInstance): ApiHolder {
+ const factoryRegistry = new ApiFactoryRegistry();
+
+ const apiFactories =
+ coreExtension.attachments
+ .get('apis')
+ ?.map(
+ e =>
+ e.data.get(
+ coreExtensionData.apiFactory.id,
+ ) as typeof coreExtensionData.apiFactory.T,
+ )
+ .filter(Boolean) ?? [];
+
+ for (const factory of apiFactories) {
+ factoryRegistry.register('default', factory);
+ }
+
+ ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());
+
+ return new ApiResolver(factoryRegistry);
+}
+
/** @internal */
export function extractRouteInfoFromInstanceTree(
roots: ExtensionInstance[],
diff --git a/packages/frontend-app-api/src/extensions/Core.tsx b/packages/frontend-app-api/src/extensions/Core.tsx
new file mode 100644
index 0000000000..cb93b2888f
--- /dev/null
+++ b/packages/frontend-app-api/src/extensions/Core.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ coreExtensionData,
+ createExtension,
+} from '@backstage/frontend-plugin-api';
+
+export const Core = createExtension({
+ id: 'core',
+ at: 'root',
+ inputs: {
+ apis: {
+ extensionData: {
+ api: coreExtensionData.apiFactory,
+ },
+ },
+ },
+ output: {},
+ factory() {},
+});
diff --git a/yarn.lock b/yarn.lock
index 7075f315dc..4a9b30d532 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4317,6 +4317,7 @@ __metadata:
dependencies:
"@backstage/cli": "workspace:^"
"@backstage/config": "workspace:^"
+ "@backstage/core-app-api": "workspace:^"
"@backstage/core-plugin-api": "workspace:^"
"@backstage/frontend-plugin-api": "workspace:^"
"@backstage/plugin-graphiql": "workspace:^"