Merge remote-tracking branch 'origin/master' into bui-toast

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor

# Conflicts:
#	packages/frontend-defaults/src/createPublicSignInApp.test.tsx
#	yarn.lock
This commit is contained in:
Patrik Oldsberg
2026-03-17 12:05:59 +01:00
2867 changed files with 84530 additions and 39061 deletions
+45
View File
@@ -1,5 +1,50 @@
# @backstage/frontend-defaults
## 0.5.0-next.1
### Minor Changes
- 92af1ae: **BREAKING**: Removed the `allowUnknownExtensionConfig` option from `createApp`. This flag had no effect and was a no-op, so no behavioral changes are expected.
- 33de79d: **BREAKING**: Removed the deprecated `createPublicSignInApp` function. Use `createApp` from `@backstage/frontend-defaults` with `appModulePublicSignIn` from `@backstage/plugin-app/alpha` instead.
### Patch Changes
- Updated dependencies
- @backstage/frontend-plugin-api@0.15.0-next.1
- @backstage/frontend-app-api@0.16.0-next.1
- @backstage/core-components@0.18.8-next.1
- @backstage/plugin-app@0.4.1-next.2
## 0.4.1-next.0
### Patch Changes
- Updated dependencies
- @backstage/frontend-plugin-api@0.14.2-next.0
- @backstage/plugin-app@0.4.1-next.0
- @backstage/config@1.3.6
- @backstage/core-components@0.18.8-next.0
- @backstage/errors@1.2.7
- @backstage/frontend-app-api@0.15.1-next.0
## 0.4.0
### Minor Changes
- 55b2ef6: **BREAKING**: The `API_FACTORY_CONFLICT` warning is now treated as an error and will prevent the app from starting.
### Patch Changes
- 122d39c: Completely removed support for the deprecated `app.experimental.packages` configuration. Replace existing usage directly with `app.packages`.
- c38b74d: Dependency update for tests.
- 53b6549: Plugins in the new frontend system now have a `pluginId` field rather than `id` to better align with naming conventions used throughout the frontend and backend systems. The old field is still present but marked as deprecated. All internal code has been updated to prefer `pluginId` while maintaining backward compatibility by falling back to `id` when needed.
- a7e0d50: Updated `react-router-dom` peer dependency to `^6.30.2` and explicitly disabled v7 future flags to suppress deprecation warnings.
- Updated dependencies
- @backstage/frontend-app-api@0.15.0
- @backstage/core-components@0.18.7
- @backstage/frontend-plugin-api@0.14.0
- @backstage/plugin-app@0.4.0
## 0.4.0-next.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/frontend-defaults",
"version": "0.4.0-next.2",
"version": "0.5.0-next.1",
"backstage": {
"role": "web-library"
},
+1 -7
View File
@@ -8,7 +8,7 @@ import { AppErrorTypes } from '@backstage/frontend-app-api';
import { Config } from '@backstage/config';
import { ConfigApi } from '@backstage/frontend-plugin-api';
import { CreateAppRouteBinder } from '@backstage/frontend-app-api';
import { ExtensionFactoryMiddleware } from '@backstage/frontend-plugin-api';
import { ExtensionFactoryMiddleware } from '@backstage/frontend-app-api';
import { FrontendFeature } from '@backstage/frontend-plugin-api';
import { FrontendFeatureLoader } from '@backstage/frontend-plugin-api';
import { FrontendPluginInfoResolver } from '@backstage/frontend-app-api';
@@ -23,7 +23,6 @@ export function createApp(options?: CreateAppOptions): {
// @public
export interface CreateAppOptions {
advanced?: {
allowUnknownExtensionConfig?: boolean;
configLoader?: () => Promise<{
config: ConfigApi;
}>;
@@ -37,11 +36,6 @@ export interface CreateAppOptions {
features?: (FrontendFeature | FrontendFeatureLoader)[];
}
// @public @deprecated (undocumented)
export function createPublicSignInApp(options?: CreateAppOptions): {
createRoot(): JSX_2;
};
// @public (undocumented)
export function discoverAvailableFeatures(config: Config): {
features: (FrontendFeature | FrontendFeatureLoader)[];
@@ -283,7 +283,9 @@ describe('createApp', () => {
).resolves.toBeInTheDocument();
});
it('should allow unknown extension config if the flag is set', async () => {
it('should warn about unknown extension config', async () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
const app = createApp({
features: [
appPlugin,
@@ -300,7 +302,6 @@ describe('createApp', () => {
}),
],
advanced: {
allowUnknownExtensionConfig: true,
configLoader: async () => ({
config: mockApis.config({
data: {
@@ -316,6 +317,12 @@ describe('createApp', () => {
await renderWithEffects(app.createRoot());
await expect(screen.findByText('Derp')).resolves.toBeInTheDocument();
expect(warnSpy).toHaveBeenCalledWith('App startup encountered warnings:');
expect(warnSpy).toHaveBeenCalledWith(
'INVALID_EXTENSION_CONFIG_KEY: Extension unknown:lols/wut does not exist',
);
warnSpy.mockRestore();
});
it('should make the app structure available through the AppTreeApi', async () => {
let appTreeApi: AppTreeApi | undefined = undefined;
@@ -390,11 +397,13 @@ describe('createApp', () => {
<component:app/core-progress out=[core.swappableComponent] />
<component:app/core-not-found-error-page out=[core.swappableComponent] />
<component:app/core-error-display out=[core.swappableComponent] />
<component:app/core-page-layout out=[core.swappableComponent] />
]
</api:app/swappable-components>
<api:app/icons out=[core.api.factory] />
<api:app/feature-flags out=[core.api.factory] />
<api:app/plugin-wrapper out=[core.api.factory] />
<api:app/plugin-header-actions out=[core.api.factory] />
<api:app/translations out=[core.api.factory] />
<api:app/components out=[core.api.factory] />
]
+1 -12
View File
@@ -18,7 +18,6 @@ import { JSX, lazy, ReactNode, Suspense } from 'react';
import {
ConfigApi,
coreExtensionData,
ExtensionFactoryMiddleware,
FrontendFeature,
FrontendFeatureLoader,
} from '@backstage/frontend-plugin-api';
@@ -31,6 +30,7 @@ import { ConfigReader } from '@backstage/config';
import {
CreateAppRouteBinder,
createSpecializedApp,
ExtensionFactoryMiddleware,
FrontendPluginInfoResolver,
} from '@backstage/frontend-app-api';
import appPlugin from '@backstage/plugin-app';
@@ -58,17 +58,6 @@ export interface CreateAppOptions {
* Advanced, more rarely used options.
*/
advanced?: {
/**
* If set to true, the system will silently accept and move on if
* encountering config for extensions that do not exist. The default is to
* reject such config to help catch simple mistakes.
*
* This flag can be useful in some scenarios where you have a dynamic set of
* extensions enabled at different times, but also increases the risk of
* accidentally missing e.g. simple typos in your config.
*/
allowUnknownExtensionConfig?: boolean;
/**
* Sets a custom config loader, replacing the builtin one.
*
@@ -1,132 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createFrontendModule } from '@backstage/frontend-plugin-api';
import { SignInPageBlueprint } from '@backstage/plugin-app-react';
import { render, screen, waitFor } from '@testing-library/react';
import { useEffect } from 'react';
import { createPublicSignInApp } from './createPublicSignInApp';
import { mockApis } from '@backstage/test-utils';
describe('createPublicSignInApp', () => {
beforeEach(() => {
jest.resetAllMocks();
});
it('should render a sign-in page', async () => {
const app = createPublicSignInApp({
advanced: {
configLoader: async () => ({ config: mockApis.config() }),
},
features: [
createFrontendModule({
pluginId: 'app',
extensions: [
SignInPageBlueprint.make({
params: {
loader: async () => () => <div>Sign in page</div>,
},
}),
],
}),
],
});
render(app.createRoot());
await expect(
screen.findByText('Sign in page'),
).resolves.toBeInTheDocument();
});
it('should render the form redirect on sign-in', async () => {
const submitSpy = jest
.spyOn(HTMLFormElement.prototype, 'submit')
.mockReturnValue();
const app = createPublicSignInApp({
advanced: {
configLoader: async () => ({ config: mockApis.config() }),
},
features: [
createFrontendModule({
pluginId: 'app',
extensions: [
SignInPageBlueprint.make({
params: {
loader:
async () =>
({ onSignInSuccess }) => {
useEffect(() => {
onSignInSuccess(
mockApis.identity({ token: 'mock-token' }),
);
}, [onSignInSuccess]);
return <div />;
},
},
}),
],
}),
],
});
const { baseElement } = render(app.createRoot());
await waitFor(() => {
expect(submitSpy).toHaveBeenCalled();
});
expect(baseElement).toMatchInlineSnapshot(`
<body
data-theme-mode="light"
data-theme-name="backstage"
data-unified-theme-stack="[{"mode":"light","name":"backstage"}]"
>
<div>
<div
aria-label="0 notifications."
class="container"
data-react-aria-top-layer="true"
data-theme-mode="dark"
role="region"
tabindex="-1"
/>
<form
action="http://localhost/"
method="POST"
style="visibility: hidden;"
>
<input
name="type"
type="hidden"
value="sign-in"
/>
<input
name="token"
type="hidden"
value="mock-token"
/>
<input
type="submit"
value="Continue"
/>
</form>
</div>
</body>
`);
});
});
@@ -1,29 +0,0 @@
/*
* Copyright 2025 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { appModulePublicSignIn } from '@backstage/plugin-app/alpha';
import { CreateAppOptions, createApp } from './createApp';
/**
* @public
* @deprecated Use {@link @backstage/plugin-app/alpha#appModulePublicSignIn} instead.
*/
export function createPublicSignInApp(options?: CreateAppOptions) {
return createApp({
...options,
features: [...(options?.features ?? []), appModulePublicSignIn],
});
}
-1
View File
@@ -21,7 +21,6 @@
*/
export { createApp, type CreateAppOptions } from './createApp';
export { createPublicSignInApp } from './createPublicSignInApp';
export { discoverAvailableFeatures } from './discovery';
export { resolveAsyncFeatures } from './resolution';
export { maybeCreateErrorPage } from './maybeCreateErrorPage';