@backstage/core-plugin-api 1.10.4 → 1.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/dist/alpha.d.ts +1 -3
- package/dist/extensions/extensions.esm.js +34 -34
- package/dist/extensions/extensions.esm.js.map +1 -1
- package/dist/index.d.ts +2 -3
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @backstage/core-plugin-api
|
|
2
2
|
|
|
3
|
+
## 1.10.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 327d21e: Failure to lazy load an extension will now always result in an error being thrown to be forwarded to error boundaries, rather than being rendered using the `BootErrorPage` app component.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/config@1.3.2
|
|
10
|
+
- @backstage/errors@1.2.7
|
|
11
|
+
- @backstage/types@1.2.1
|
|
12
|
+
- @backstage/version-bridge@1.0.11
|
|
13
|
+
|
|
3
14
|
## 1.10.4
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/dist/alpha.d.ts
CHANGED
|
@@ -383,9 +383,7 @@ type AppLanguageApi = {
|
|
|
383
383
|
declare const appLanguageApiRef: ApiRef<AppLanguageApi>;
|
|
384
384
|
|
|
385
385
|
/** @alpha */
|
|
386
|
-
declare const useTranslationRef: <TMessages extends {
|
|
387
|
-
[x: string]: string;
|
|
388
|
-
}>(translationRef: TranslationRef<string, TMessages>) => {
|
|
386
|
+
declare const useTranslationRef: <TMessages extends { [key in string]: string; }>(translationRef: TranslationRef<string, TMessages>) => {
|
|
389
387
|
t: TranslationFunction<TMessages>;
|
|
390
388
|
};
|
|
391
389
|
|
|
@@ -8,46 +8,37 @@ import 'react-router-dom';
|
|
|
8
8
|
import { attachComponentData } from './componentData.esm.js';
|
|
9
9
|
import { PluginErrorBoundary } from './PluginErrorBoundary.esm.js';
|
|
10
10
|
import { routableExtensionRenderedEvent } from '../analytics/Tracker.esm.js';
|
|
11
|
+
import { ForwardedError } from '@backstage/errors';
|
|
11
12
|
|
|
12
13
|
function createRoutableExtension(options) {
|
|
13
14
|
const { component, mountPoint, name } = options;
|
|
14
15
|
return createReactExtension({
|
|
15
16
|
component: {
|
|
16
|
-
lazy: () => component().then(
|
|
17
|
-
(
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
);
|
|
29
|
-
}
|
|
17
|
+
lazy: () => component().then((InnerComponent) => {
|
|
18
|
+
const RoutableExtensionWrapper = (props) => {
|
|
19
|
+
const analytics = useAnalytics();
|
|
20
|
+
try {
|
|
21
|
+
useRouteRef(mountPoint);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
if (typeof error === "object" && error !== null) {
|
|
24
|
+
const { message } = error;
|
|
25
|
+
if (typeof message === "string" && message.startsWith("No path for ")) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. Routable extension components may not be rendered by other components and must be directly available as an element within the App provider component.`
|
|
28
|
+
);
|
|
30
29
|
}
|
|
31
|
-
throw error;
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const app = useApp();
|
|
45
|
-
const { BootErrorPage } = app.getComponents();
|
|
46
|
-
return /* @__PURE__ */ React.createElement(BootErrorPage, { step: "load-chunk", error });
|
|
47
|
-
};
|
|
48
|
-
return RoutableExtensionWrapper;
|
|
49
|
-
}
|
|
50
|
-
)
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
analytics.captureEvent(routableExtensionRenderedEvent, "");
|
|
35
|
+
}, [analytics]);
|
|
36
|
+
return /* @__PURE__ */ React.createElement(InnerComponent, { ...props });
|
|
37
|
+
};
|
|
38
|
+
const componentName = name || InnerComponent.displayName || InnerComponent.name || "LazyComponent";
|
|
39
|
+
RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`;
|
|
40
|
+
return RoutableExtensionWrapper;
|
|
41
|
+
})
|
|
51
42
|
},
|
|
52
43
|
data: {
|
|
53
44
|
"core.mountPoint": mountPoint
|
|
@@ -70,7 +61,16 @@ function createReactExtension(options) {
|
|
|
70
61
|
if ("lazy" in options.component) {
|
|
71
62
|
const lazyLoader = options.component.lazy;
|
|
72
63
|
Component = lazy(
|
|
73
|
-
() => lazyLoader().then(
|
|
64
|
+
() => lazyLoader().then(
|
|
65
|
+
(component) => ({ default: component }),
|
|
66
|
+
(error) => {
|
|
67
|
+
const ofExtension = name ? ` of the ${name} extension` : "";
|
|
68
|
+
throw new ForwardedError(
|
|
69
|
+
`Failed lazy loading${ofExtension}, try to reload the page`,
|
|
70
|
+
error
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
74
|
);
|
|
75
75
|
} else {
|
|
76
76
|
Component = options.component.sync;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensions.esm.js","sources":["../../src/extensions/extensions.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { lazy, Suspense, useEffect } from 'react';\nimport { AnalyticsContext, useAnalytics } from '../analytics';\nimport { useApp } from '../app';\nimport { RouteRef, useRouteRef } from '../routing';\nimport { attachComponentData } from './componentData';\nimport { Extension, BackstagePlugin } from '../plugin';\nimport { PluginErrorBoundary } from './PluginErrorBoundary';\nimport { routableExtensionRenderedEvent } from '../analytics/Tracker';\n\n/**\n * Lazy or synchronous retrieving of extension components.\n *\n * @public\n */\nexport type ComponentLoader<T> =\n | {\n lazy: () => Promise<T>;\n }\n | {\n sync: T;\n };\n\n/**\n * Extension for components that can have its own URL route (top-level pages, tabs etc.).\n *\n * @remarks\n *\n * We do not use ComponentType as the return type, since it doesn't let us convey the children prop.\n * ComponentType inserts children as an optional prop whether the inner component accepts it or not,\n * making it impossible to make the usage of children type safe.\n *\n * See {@link https://backstage.io/docs/plugins/composability#extensions}.\n *\n * @public\n */\nexport function createRoutableExtension<\n T extends (props: any) => JSX.Element | null,\n>(options: {\n /**\n * A loader for the component that is rendered by this extension.\n */\n component: () => Promise<T>;\n\n /**\n * The mount point to bind this routable extension to.\n *\n * If this extension is placed somewhere in the app element tree of a Backstage\n * app, callers will be able to route to this extensions by calling,\n * `useRouteRef` with this mount point.\n */\n mountPoint: RouteRef;\n\n /**\n * The name of this extension that will represent it at runtime. It is for example\n * used to identify this extension in analytics data.\n *\n * If possible the name should always be the same as the name of the exported\n * variable for this extension.\n */\n name?: string;\n}): Extension<T> {\n const { component, mountPoint, name } = options;\n return createReactExtension({\n component: {\n lazy: () =>\n component().then(\n InnerComponent => {\n const RoutableExtensionWrapper: any = (props: any) => {\n const analytics = useAnalytics();\n\n // Validate that the routing is wired up correctly in the App.tsx\n try {\n useRouteRef(mountPoint);\n } catch (error) {\n if (typeof error === 'object' && error !== null) {\n const { message } = error as { message?: unknown };\n if (\n typeof message === 'string' &&\n message.startsWith('No path for ')\n ) {\n throw new Error(\n `Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` +\n 'Routable extension components may not be rendered by other components and must be ' +\n 'directly available as an element within the App provider component.',\n );\n }\n }\n throw error;\n }\n\n // This event, never exposed to end-users of the analytics API,\n // helps inform which extension metadata gets associated with a\n // navigation event when the route navigated to is a gathered\n // mountpoint.\n useEffect(() => {\n analytics.captureEvent(routableExtensionRenderedEvent, '');\n }, [analytics]);\n\n return <InnerComponent {...props} />;\n };\n\n const componentName =\n name ||\n (InnerComponent as { displayName?: string }).displayName ||\n InnerComponent.name ||\n 'LazyComponent';\n\n RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`;\n\n return RoutableExtensionWrapper as T;\n },\n error => {\n const RoutableExtensionWrapper: any = (_: any) => {\n const app = useApp();\n const { BootErrorPage } = app.getComponents();\n\n return <BootErrorPage step=\"load-chunk\" error={error} />;\n };\n return RoutableExtensionWrapper;\n },\n ),\n },\n data: {\n 'core.mountPoint': mountPoint,\n },\n name,\n });\n}\n\n/**\n * Plain React component extension.\n *\n * @remarks\n *\n * We do not use ComponentType as the return type, since it doesn't let us convey the children prop.\n * ComponentType inserts children as an optional prop whether the inner component accepts it or not,\n * making it impossible to make the usage of children type safe.\n *\n * See {@link https://backstage.io/docs/plugins/composability#extensions}.\n *\n * @public\n */\nexport function createComponentExtension<\n T extends (props: any) => JSX.Element | null,\n>(options: {\n /**\n * A loader or synchronously supplied component that is rendered by this extension.\n */\n component: ComponentLoader<T>;\n\n /**\n * The name of this extension that will represent it at runtime. It is for example\n * used to identify this extension in analytics data.\n *\n * If possible the name should always be the same as the name of the exported\n * variable for this extension.\n */\n name?: string;\n}): Extension<T> {\n const { component, name } = options;\n return createReactExtension({ component, name });\n}\n\n/**\n * Used by {@link createComponentExtension} and {@link createRoutableExtension}.\n *\n * @remarks\n *\n * We do not use ComponentType as the return type, since it doesn't let us convey the children prop.\n * ComponentType inserts children as an optional prop whether the inner component accepts it or not,\n * making it impossible to make the usage of children type safe.\n *\n * See {@link https://backstage.io/docs/plugins/composability#extensions}.\n *\n * @public\n */\nexport function createReactExtension<\n T extends (props: any) => JSX.Element | null,\n>(options: {\n /**\n * A loader or synchronously supplied component that is rendered by this extension.\n */\n component: ComponentLoader<T>;\n\n /**\n * Additional component data that is attached to the top-level extension component.\n */\n data?: Record<string, unknown>;\n\n /**\n * The name of this extension that will represent it at runtime. It is for example\n * used to identify this extension in analytics data.\n *\n * If possible the name should always be the same as the name of the exported\n * variable for this extension.\n */\n name?: string;\n}): Extension<T> {\n const { data = {}, name } = options;\n if (!name) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Declaring extensions without name is DEPRECATED. ' +\n 'Make sure that all usages of createReactExtension, createComponentExtension and createRoutableExtension provide a name.',\n );\n }\n\n let Component: T;\n if ('lazy' in options.component) {\n const lazyLoader = options.component.lazy;\n Component = lazy(() =>\n lazyLoader().then(component => ({ default: component })),\n ) as unknown as T;\n } else {\n Component = options.component.sync;\n }\n const componentName =\n name ||\n (Component as { displayName?: string }).displayName ||\n Component.name ||\n 'Component';\n\n return {\n expose(plugin: BackstagePlugin) {\n const Result: any = (props: any) => {\n const app = useApp();\n const { Progress } = app.getComponents();\n // todo(iamEAP): Account for situations where this is attached via\n // separate calls to attachComponentData().\n const mountPoint = data?.['core.mountPoint'] as\n | { id?: string }\n | undefined;\n\n return (\n <Suspense fallback={<Progress />}>\n <PluginErrorBoundary app={app} plugin={plugin}>\n <AnalyticsContext\n attributes={{\n pluginId: plugin.getId(),\n ...(name && { extension: name }),\n ...(mountPoint && { routeRef: mountPoint.id }),\n }}\n >\n <Component {...props} />\n </AnalyticsContext>\n </PluginErrorBoundary>\n </Suspense>\n );\n };\n\n attachComponentData(Result, 'core.plugin', plugin);\n attachComponentData(Result, 'core.extensionName', name);\n for (const [key, value] of Object.entries(data)) {\n attachComponentData(Result, key, value);\n }\n\n Result.displayName = `Extension(${componentName})`;\n return Result;\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"extensions.esm.js","sources":["../../src/extensions/extensions.tsx"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { lazy, Suspense, useEffect } from 'react';\nimport { AnalyticsContext, useAnalytics } from '../analytics';\nimport { useApp } from '../app';\nimport { RouteRef, useRouteRef } from '../routing';\nimport { attachComponentData } from './componentData';\nimport { Extension, BackstagePlugin } from '../plugin';\nimport { PluginErrorBoundary } from './PluginErrorBoundary';\nimport { routableExtensionRenderedEvent } from '../analytics/Tracker';\nimport { ForwardedError } from '@backstage/errors';\n\n/**\n * Lazy or synchronous retrieving of extension components.\n *\n * @public\n */\nexport type ComponentLoader<T> =\n | {\n lazy: () => Promise<T>;\n }\n | {\n sync: T;\n };\n\n/**\n * Extension for components that can have its own URL route (top-level pages, tabs etc.).\n *\n * @remarks\n *\n * We do not use ComponentType as the return type, since it doesn't let us convey the children prop.\n * ComponentType inserts children as an optional prop whether the inner component accepts it or not,\n * making it impossible to make the usage of children type safe.\n *\n * See {@link https://backstage.io/docs/plugins/composability#extensions}.\n *\n * @public\n */\nexport function createRoutableExtension<\n T extends (props: any) => JSX.Element | null,\n>(options: {\n /**\n * A loader for the component that is rendered by this extension.\n */\n component: () => Promise<T>;\n\n /**\n * The mount point to bind this routable extension to.\n *\n * If this extension is placed somewhere in the app element tree of a Backstage\n * app, callers will be able to route to this extensions by calling,\n * `useRouteRef` with this mount point.\n */\n mountPoint: RouteRef;\n\n /**\n * The name of this extension that will represent it at runtime. It is for example\n * used to identify this extension in analytics data.\n *\n * If possible the name should always be the same as the name of the exported\n * variable for this extension.\n */\n name?: string;\n}): Extension<T> {\n const { component, mountPoint, name } = options;\n return createReactExtension({\n component: {\n lazy: () =>\n component().then(InnerComponent => {\n const RoutableExtensionWrapper: any = (props: any) => {\n const analytics = useAnalytics();\n\n // Validate that the routing is wired up correctly in the App.tsx\n try {\n useRouteRef(mountPoint);\n } catch (error) {\n if (typeof error === 'object' && error !== null) {\n const { message } = error as { message?: unknown };\n if (\n typeof message === 'string' &&\n message.startsWith('No path for ')\n ) {\n throw new Error(\n `Routable extension component with mount point ${mountPoint} was not discovered in the app element tree. ` +\n 'Routable extension components may not be rendered by other components and must be ' +\n 'directly available as an element within the App provider component.',\n );\n }\n }\n throw error;\n }\n\n // This event, never exposed to end-users of the analytics API,\n // helps inform which extension metadata gets associated with a\n // navigation event when the route navigated to is a gathered\n // mountpoint.\n useEffect(() => {\n analytics.captureEvent(routableExtensionRenderedEvent, '');\n }, [analytics]);\n\n return <InnerComponent {...props} />;\n };\n\n const componentName =\n name ||\n (InnerComponent as { displayName?: string }).displayName ||\n InnerComponent.name ||\n 'LazyComponent';\n\n RoutableExtensionWrapper.displayName = `RoutableExtension(${componentName})`;\n\n return RoutableExtensionWrapper as T;\n }),\n },\n data: {\n 'core.mountPoint': mountPoint,\n },\n name,\n });\n}\n\n/**\n * Plain React component extension.\n *\n * @remarks\n *\n * We do not use ComponentType as the return type, since it doesn't let us convey the children prop.\n * ComponentType inserts children as an optional prop whether the inner component accepts it or not,\n * making it impossible to make the usage of children type safe.\n *\n * See {@link https://backstage.io/docs/plugins/composability#extensions}.\n *\n * @public\n */\nexport function createComponentExtension<\n T extends (props: any) => JSX.Element | null,\n>(options: {\n /**\n * A loader or synchronously supplied component that is rendered by this extension.\n */\n component: ComponentLoader<T>;\n\n /**\n * The name of this extension that will represent it at runtime. It is for example\n * used to identify this extension in analytics data.\n *\n * If possible the name should always be the same as the name of the exported\n * variable for this extension.\n */\n name?: string;\n}): Extension<T> {\n const { component, name } = options;\n return createReactExtension({ component, name });\n}\n\n/**\n * Used by {@link createComponentExtension} and {@link createRoutableExtension}.\n *\n * @remarks\n *\n * We do not use ComponentType as the return type, since it doesn't let us convey the children prop.\n * ComponentType inserts children as an optional prop whether the inner component accepts it or not,\n * making it impossible to make the usage of children type safe.\n *\n * See {@link https://backstage.io/docs/plugins/composability#extensions}.\n *\n * @public\n */\nexport function createReactExtension<\n T extends (props: any) => JSX.Element | null,\n>(options: {\n /**\n * A loader or synchronously supplied component that is rendered by this extension.\n */\n component: ComponentLoader<T>;\n\n /**\n * Additional component data that is attached to the top-level extension component.\n */\n data?: Record<string, unknown>;\n\n /**\n * The name of this extension that will represent it at runtime. It is for example\n * used to identify this extension in analytics data.\n *\n * If possible the name should always be the same as the name of the exported\n * variable for this extension.\n */\n name?: string;\n}): Extension<T> {\n const { data = {}, name } = options;\n if (!name) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Declaring extensions without name is DEPRECATED. ' +\n 'Make sure that all usages of createReactExtension, createComponentExtension and createRoutableExtension provide a name.',\n );\n }\n\n let Component: T;\n if ('lazy' in options.component) {\n const lazyLoader = options.component.lazy;\n Component = lazy(() =>\n lazyLoader().then(\n component => ({ default: component }),\n error => {\n const ofExtension = name ? ` of the ${name} extension` : '';\n throw new ForwardedError(\n `Failed lazy loading${ofExtension}, try to reload the page`,\n error,\n );\n },\n ),\n ) as unknown as T;\n } else {\n Component = options.component.sync;\n }\n const componentName =\n name ||\n (Component as { displayName?: string }).displayName ||\n Component.name ||\n 'Component';\n\n return {\n expose(plugin: BackstagePlugin) {\n const Result: any = (props: any) => {\n const app = useApp();\n const { Progress } = app.getComponents();\n // todo(iamEAP): Account for situations where this is attached via\n // separate calls to attachComponentData().\n const mountPoint = data?.['core.mountPoint'] as\n | { id?: string }\n | undefined;\n\n return (\n <Suspense fallback={<Progress />}>\n <PluginErrorBoundary app={app} plugin={plugin}>\n <AnalyticsContext\n attributes={{\n pluginId: plugin.getId(),\n ...(name && { extension: name }),\n ...(mountPoint && { routeRef: mountPoint.id }),\n }}\n >\n <Component {...props} />\n </AnalyticsContext>\n </PluginErrorBoundary>\n </Suspense>\n );\n };\n\n attachComponentData(Result, 'core.plugin', plugin);\n attachComponentData(Result, 'core.extensionName', name);\n for (const [key, value] of Object.entries(data)) {\n attachComponentData(Result, key, value);\n }\n\n Result.displayName = `Extension(${componentName})`;\n return Result;\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
import React$1, { ReactNode, ComponentType, PropsWithChildren, ReactElement } from 'react';
|
|
1
|
+
import React$1, { ReactNode, PropsWithChildren, ComponentType, ReactElement } from 'react';
|
|
3
2
|
import { Observable, JsonValue } from '@backstage/types';
|
|
4
3
|
import { Config } from '@backstage/config';
|
|
5
4
|
import { IdentityApi as IdentityApi$1, BackstagePlugin as BackstagePlugin$1, IconComponent as IconComponent$1 } from '@backstage/core-plugin-api';
|
|
@@ -120,7 +119,7 @@ declare function useApi<T>(apiRef: ApiRef<T>): T;
|
|
|
120
119
|
* @public
|
|
121
120
|
*/
|
|
122
121
|
declare function withApis<T extends {}>(apis: TypesToApiRefs<T>): <TProps extends T>(WrappedComponent: React$1.ComponentType<TProps>) => {
|
|
123
|
-
(props:
|
|
122
|
+
(props: PropsWithChildren<Omit<TProps, keyof T>>): React$1.JSX.Element;
|
|
124
123
|
displayName: string;
|
|
125
124
|
};
|
|
126
125
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/core-plugin-api",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.5",
|
|
4
4
|
"description": "Core API used by Backstage plugins",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "web-library"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"types": "./dist/index.d.ts",
|
|
37
37
|
"typesVersions": {
|
|
38
38
|
"*": {
|
|
39
|
-
"
|
|
39
|
+
"*": [
|
|
40
40
|
"dist/index.d.ts"
|
|
41
41
|
],
|
|
42
42
|
"alpha": [
|
|
@@ -64,9 +64,9 @@
|
|
|
64
64
|
"history": "^5.0.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@backstage/cli": "^0.
|
|
68
|
-
"@backstage/core-app-api": "^1.
|
|
69
|
-
"@backstage/test-utils": "^1.7.
|
|
67
|
+
"@backstage/cli": "^0.31.0",
|
|
68
|
+
"@backstage/core-app-api": "^1.16.0",
|
|
69
|
+
"@backstage/test-utils": "^1.7.6",
|
|
70
70
|
"@testing-library/dom": "^10.0.0",
|
|
71
71
|
"@testing-library/jest-dom": "^6.0.0",
|
|
72
72
|
"@testing-library/react": "^16.0.0",
|