@backstage/frontend-plugin-api 0.13.0 → 0.13.2-next.0
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 +13 -0
- package/dist/analytics/useAnalytics.esm.js +14 -1
- package/dist/analytics/useAnalytics.esm.js.map +1 -1
- package/dist/apis/definitions/AlertApi.esm.js +11 -0
- package/dist/apis/definitions/AlertApi.esm.js.map +1 -0
- package/dist/apis/definitions/AnalyticsApi.esm.js +4 -1
- package/dist/apis/definitions/AnalyticsApi.esm.js.map +1 -1
- package/dist/apis/definitions/AppLanguageApi.esm.js +11 -0
- package/dist/apis/definitions/AppLanguageApi.esm.js.map +1 -0
- package/dist/apis/definitions/AppThemeApi.esm.js +11 -0
- package/dist/apis/definitions/AppThemeApi.esm.js.map +1 -0
- package/dist/apis/definitions/AppTreeApi.esm.js +4 -1
- package/dist/apis/definitions/AppTreeApi.esm.js.map +1 -1
- package/dist/apis/definitions/ConfigApi.esm.js +11 -0
- package/dist/apis/definitions/ConfigApi.esm.js.map +1 -0
- package/dist/apis/definitions/DialogApi.esm.js +4 -1
- package/dist/apis/definitions/DialogApi.esm.js.map +1 -1
- package/dist/apis/definitions/DiscoveryApi.esm.js +11 -0
- package/dist/apis/definitions/DiscoveryApi.esm.js.map +1 -0
- package/dist/apis/definitions/ErrorApi.esm.js +11 -0
- package/dist/apis/definitions/ErrorApi.esm.js.map +1 -0
- package/dist/apis/definitions/FeatureFlagsApi.esm.js +21 -0
- package/dist/apis/definitions/FeatureFlagsApi.esm.js.map +1 -0
- package/dist/apis/definitions/FetchApi.esm.js +11 -0
- package/dist/apis/definitions/FetchApi.esm.js.map +1 -0
- package/dist/apis/definitions/IconsApi.esm.js +4 -1
- package/dist/apis/definitions/IconsApi.esm.js.map +1 -1
- package/dist/apis/definitions/IdentityApi.esm.js +11 -0
- package/dist/apis/definitions/IdentityApi.esm.js.map +1 -0
- package/dist/apis/definitions/OAuthRequestApi.esm.js +11 -0
- package/dist/apis/definitions/OAuthRequestApi.esm.js.map +1 -0
- package/dist/apis/definitions/RouteResolutionApi.esm.js +4 -1
- package/dist/apis/definitions/RouteResolutionApi.esm.js.map +1 -1
- package/dist/apis/definitions/StorageApi.esm.js +11 -0
- package/dist/apis/definitions/StorageApi.esm.js.map +1 -0
- package/dist/apis/definitions/SwappableComponentsApi.esm.js +4 -1
- package/dist/apis/definitions/SwappableComponentsApi.esm.js.map +1 -1
- package/dist/apis/definitions/TranslationApi.esm.js +11 -0
- package/dist/apis/definitions/TranslationApi.esm.js.map +1 -0
- package/dist/apis/definitions/auth.esm.js +51 -0
- package/dist/apis/definitions/auth.esm.js.map +1 -0
- package/dist/apis/system/ApiRef.esm.js +27 -0
- package/dist/apis/system/ApiRef.esm.js.map +1 -0
- package/dist/apis/system/helpers.esm.js +13 -0
- package/dist/apis/system/helpers.esm.js.map +1 -0
- package/dist/apis/system/useApi.esm.js +50 -0
- package/dist/apis/system/useApi.esm.js.map +1 -0
- package/dist/blueprints/NavItemBlueprint.esm.js.map +1 -1
- package/dist/blueprints/PageBlueprint.esm.js +15 -1
- package/dist/blueprints/PageBlueprint.esm.js.map +1 -1
- package/dist/blueprints/SignInPageBlueprint.esm.js +15 -1
- package/dist/blueprints/SignInPageBlueprint.esm.js.map +1 -1
- package/dist/blueprints/ThemeBlueprint.esm.js.map +1 -1
- package/dist/components/ExtensionBoundary.esm.js +2 -1
- package/dist/components/ExtensionBoundary.esm.js.map +1 -1
- package/dist/components/createSwappableComponent.esm.js +14 -1
- package/dist/components/createSwappableComponent.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalExtensionInput.esm.js.map +1 -1
- package/dist/index.d.ts +1595 -148
- package/dist/index.esm.js +20 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/routing/useRouteRef.esm.js +14 -1
- package/dist/routing/useRouteRef.esm.js.map +1 -1
- package/dist/translation/TranslationMessages.esm.js +11 -0
- package/dist/translation/TranslationMessages.esm.js.map +1 -0
- package/dist/translation/TranslationRef.esm.js +62 -0
- package/dist/translation/TranslationRef.esm.js.map +1 -0
- package/dist/translation/TranslationResource.esm.js +21 -0
- package/dist/translation/TranslationResource.esm.js.map +1 -0
- package/dist/translation/useTranslationRef.esm.js +89 -0
- package/dist/translation/useTranslationRef.esm.js.map +1 -0
- package/dist/wiring/createExtension.esm.js +2 -2
- package/dist/wiring/createExtension.esm.js.map +1 -1
- package/package.json +28 -21
package/dist/index.esm.js
CHANGED
|
@@ -1,12 +1,27 @@
|
|
|
1
1
|
export { AnalyticsContext } from './analytics/AnalyticsContext.esm.js';
|
|
2
2
|
export { useAnalytics } from './analytics/useAnalytics.esm.js';
|
|
3
3
|
export { appTreeApiRef } from './apis/definitions/AppTreeApi.esm.js';
|
|
4
|
-
export { SessionState,
|
|
4
|
+
export { SessionState, atlassianAuthApiRef, bitbucketAuthApiRef, bitbucketServerAuthApiRef, githubAuthApiRef, gitlabAuthApiRef, googleAuthApiRef, microsoftAuthApiRef, oktaAuthApiRef, oneloginAuthApiRef, openshiftAuthApiRef, vmwareCloudAuthApiRef } from './apis/definitions/auth.esm.js';
|
|
5
|
+
export { alertApiRef } from './apis/definitions/AlertApi.esm.js';
|
|
6
|
+
export { appLanguageApiRef } from './apis/definitions/AppLanguageApi.esm.js';
|
|
7
|
+
export { appThemeApiRef } from './apis/definitions/AppThemeApi.esm.js';
|
|
5
8
|
export { swappableComponentsApiRef } from './apis/definitions/SwappableComponentsApi.esm.js';
|
|
9
|
+
export { configApiRef } from './apis/definitions/ConfigApi.esm.js';
|
|
10
|
+
export { discoveryApiRef } from './apis/definitions/DiscoveryApi.esm.js';
|
|
11
|
+
export { errorApiRef } from './apis/definitions/ErrorApi.esm.js';
|
|
12
|
+
export { FeatureFlagState, featureFlagsApiRef } from './apis/definitions/FeatureFlagsApi.esm.js';
|
|
13
|
+
export { fetchApiRef } from './apis/definitions/FetchApi.esm.js';
|
|
6
14
|
export { iconsApiRef } from './apis/definitions/IconsApi.esm.js';
|
|
15
|
+
export { identityApiRef } from './apis/definitions/IdentityApi.esm.js';
|
|
7
16
|
export { dialogApiRef } from './apis/definitions/DialogApi.esm.js';
|
|
17
|
+
export { oauthRequestApiRef } from './apis/definitions/OAuthRequestApi.esm.js';
|
|
8
18
|
export { routeResolutionApiRef } from './apis/definitions/RouteResolutionApi.esm.js';
|
|
19
|
+
export { storageApiRef } from './apis/definitions/StorageApi.esm.js';
|
|
9
20
|
export { analyticsApiRef } from './apis/definitions/AnalyticsApi.esm.js';
|
|
21
|
+
export { translationApiRef } from './apis/definitions/TranslationApi.esm.js';
|
|
22
|
+
export { useApi, useApiHolder, withApis } from './apis/system/useApi.esm.js';
|
|
23
|
+
export { createApiRef } from './apis/system/ApiRef.esm.js';
|
|
24
|
+
export { createApiFactory } from './apis/system/helpers.esm.js';
|
|
10
25
|
export { AnalyticsImplementationBlueprint } from './blueprints/AnalyticsImplementationBlueprint.esm.js';
|
|
11
26
|
export { ApiBlueprint } from './blueprints/ApiBlueprint.esm.js';
|
|
12
27
|
export { AppRootElementBlueprint } from './blueprints/AppRootElementBlueprint.esm.js';
|
|
@@ -29,7 +44,10 @@ export { createSubRouteRef } from './routing/SubRouteRef.esm.js';
|
|
|
29
44
|
export { createExternalRouteRef } from './routing/ExternalRouteRef.esm.js';
|
|
30
45
|
export { useRouteRef } from './routing/useRouteRef.esm.js';
|
|
31
46
|
export { useRouteRefParams } from './routing/useRouteRefParams.esm.js';
|
|
32
|
-
export { createTranslationMessages
|
|
47
|
+
export { createTranslationMessages } from './translation/TranslationMessages.esm.js';
|
|
48
|
+
export { createTranslationResource } from './translation/TranslationResource.esm.js';
|
|
49
|
+
export { createTranslationRef } from './translation/TranslationRef.esm.js';
|
|
50
|
+
export { useTranslationRef } from './translation/useTranslationRef.esm.js';
|
|
33
51
|
export { coreExtensionData } from './wiring/coreExtensionData.esm.js';
|
|
34
52
|
export { createExtension } from './wiring/createExtension.esm.js';
|
|
35
53
|
export { createExtensionInput } from './wiring/createExtensionInput.esm.js';
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import { useLocation } from 'react-router-dom';
|
|
3
3
|
import '../apis/definitions/AppTreeApi.esm.js';
|
|
4
|
-
import
|
|
4
|
+
import '../apis/definitions/auth.esm.js';
|
|
5
|
+
import '../apis/definitions/AlertApi.esm.js';
|
|
6
|
+
import '../apis/definitions/AppLanguageApi.esm.js';
|
|
7
|
+
import '../apis/definitions/AppThemeApi.esm.js';
|
|
5
8
|
import '../apis/definitions/SwappableComponentsApi.esm.js';
|
|
9
|
+
import '../apis/definitions/ConfigApi.esm.js';
|
|
10
|
+
import '../apis/definitions/DiscoveryApi.esm.js';
|
|
11
|
+
import '../apis/definitions/ErrorApi.esm.js';
|
|
12
|
+
import '../apis/definitions/FeatureFlagsApi.esm.js';
|
|
13
|
+
import '../apis/definitions/FetchApi.esm.js';
|
|
6
14
|
import '../apis/definitions/IconsApi.esm.js';
|
|
15
|
+
import '../apis/definitions/IdentityApi.esm.js';
|
|
7
16
|
import '../apis/definitions/DialogApi.esm.js';
|
|
17
|
+
import '../apis/definitions/OAuthRequestApi.esm.js';
|
|
8
18
|
import { routeResolutionApiRef } from '../apis/definitions/RouteResolutionApi.esm.js';
|
|
19
|
+
import '../apis/definitions/StorageApi.esm.js';
|
|
9
20
|
import '../apis/definitions/AnalyticsApi.esm.js';
|
|
21
|
+
import '../apis/definitions/TranslationApi.esm.js';
|
|
22
|
+
import { useApi } from '../apis/system/useApi.esm.js';
|
|
10
23
|
|
|
11
24
|
function useRouteRef(routeRef) {
|
|
12
25
|
const { pathname } = useLocation();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRouteRef.esm.js","sources":["../../src/routing/useRouteRef.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 { useMemo } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { AnyRouteRefParams } from './types';\nimport { RouteRef } from './RouteRef';\nimport { SubRouteRef } from './SubRouteRef';\nimport { ExternalRouteRef } from './ExternalRouteRef';\nimport { RouteFunc, routeResolutionApiRef, useApi } from '../apis';\n\n/**\n * React hook for constructing URLs to routes.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}\n *\n * @param routeRef - The ref to route that should be converted to URL.\n * @returns A function that will in turn return the concrete URL of the `routeRef`, or `undefined` if the route is not available.\n * @public\n */\nexport function useRouteRef<TParams extends AnyRouteRefParams>(\n routeRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n): RouteFunc<TParams> | undefined {\n const { pathname } = useLocation();\n const routeResolutionApi = useApi(routeResolutionApiRef);\n\n const routeFunc = useMemo(\n () => routeResolutionApi.resolve(routeRef, { sourcePath: pathname }),\n [routeResolutionApi, routeRef, pathname],\n );\n\n return routeFunc;\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useRouteRef.esm.js","sources":["../../src/routing/useRouteRef.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 { useMemo } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { AnyRouteRefParams } from './types';\nimport { RouteRef } from './RouteRef';\nimport { SubRouteRef } from './SubRouteRef';\nimport { ExternalRouteRef } from './ExternalRouteRef';\nimport { RouteFunc, routeResolutionApiRef, useApi } from '../apis';\n\n/**\n * React hook for constructing URLs to routes.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}\n *\n * @param routeRef - The ref to route that should be converted to URL.\n * @returns A function that will in turn return the concrete URL of the `routeRef`, or `undefined` if the route is not available.\n * @public\n */\nexport function useRouteRef<TParams extends AnyRouteRefParams>(\n routeRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n): RouteFunc<TParams> | undefined {\n const { pathname } = useLocation();\n const routeResolutionApi = useApi(routeResolutionApiRef);\n\n const routeFunc = useMemo(\n () => routeResolutionApi.resolve(routeRef, { sourcePath: pathname }),\n [routeResolutionApi, routeRef, pathname],\n );\n\n return routeFunc;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAmCO,SAAS,YACd,QAAA,EAIgC;AAChC,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,WAAA,EAAY;AACjC,EAAA,MAAM,kBAAA,GAAqB,OAAO,qBAAqB,CAAA;AAEvD,EAAA,MAAM,SAAA,GAAY,OAAA;AAAA,IAChB,MAAM,kBAAA,CAAmB,OAAA,CAAQ,UAAU,EAAE,UAAA,EAAY,UAAU,CAAA;AAAA,IACnE,CAAC,kBAAA,EAAoB,QAAA,EAAU,QAAQ;AAAA,GACzC;AAEA,EAAA,OAAO,SAAA;AACT;;;;"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
function createTranslationMessages(options) {
|
|
2
|
+
return {
|
|
3
|
+
$$type: "@backstage/TranslationMessages",
|
|
4
|
+
id: options.ref.id,
|
|
5
|
+
full: Boolean(options.full),
|
|
6
|
+
messages: options.messages
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { createTranslationMessages };
|
|
11
|
+
//# sourceMappingURL=TranslationMessages.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranslationMessages.esm.js","sources":["../../src/translation/TranslationMessages.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { TranslationRef } from './TranslationRef';\n\n/**\n * Represents a collection of messages to be provided for a given translation ref.\n *\n * @public\n * @remarks\n *\n * This collection of messages can either be used directly as an override for the\n * default messages, or it can be used to provide translations for a language by\n * by being referenced by a {@link TranslationResource}.\n */\nexport interface TranslationMessages<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n TFull extends boolean = boolean,\n> {\n $$type: '@backstage/TranslationMessages';\n /** The ID of the translation ref that these messages are for */\n id: TId;\n /** Whether or not these messages override all known messages */\n full: TFull;\n /** The messages provided for the given translation ref */\n messages: TMessages;\n}\n\n/**\n * Options for {@link createTranslationMessages}.\n *\n * @public\n */\nexport interface TranslationMessagesOptions<\n TId extends string,\n TMessages extends { [key in string]: string },\n TFull extends boolean,\n> {\n ref: TranslationRef<TId, TMessages>;\n\n full?: TFull;\n\n messages: false extends TFull\n ? { [key in keyof TMessages]?: string | null }\n : { [key in keyof TMessages]: string | null };\n}\n\n/**\n * Creates a collection of messages for a given translation ref.\n *\n * @public\n */\nexport function createTranslationMessages<\n TId extends string,\n TMessages extends { [key in string]: string },\n TFull extends boolean,\n>(\n options: TranslationMessagesOptions<TId, TMessages, TFull>,\n): TranslationMessages<TId, TMessages, TFull> {\n return {\n $$type: '@backstage/TranslationMessages',\n id: options.ref.id,\n full: Boolean(options.full) as TFull,\n messages: options.messages as TMessages,\n };\n}\n"],"names":[],"mappings":"AAkEO,SAAS,0BAKd,OAAA,EAC4C;AAC5C,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,gCAAA;AAAA,IACR,EAAA,EAAI,QAAQ,GAAA,CAAI,EAAA;AAAA,IAChB,IAAA,EAAM,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA;AAAA,IAC1B,UAAU,OAAA,CAAQ;AAAA,GACpB;AACF;;;;"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createTranslationResource } from './TranslationResource.esm.js';
|
|
2
|
+
|
|
3
|
+
function flattenMessages(nested) {
|
|
4
|
+
const entries = new Array();
|
|
5
|
+
function visit(obj, prefix) {
|
|
6
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
7
|
+
if (typeof value === "string") {
|
|
8
|
+
entries.push([prefix + key, value]);
|
|
9
|
+
} else {
|
|
10
|
+
visit(value, `${prefix}${key}.`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
visit(nested, "");
|
|
15
|
+
return Object.fromEntries(entries);
|
|
16
|
+
}
|
|
17
|
+
class TranslationRefImpl {
|
|
18
|
+
#id;
|
|
19
|
+
#messages;
|
|
20
|
+
#resources;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.#id = options.id;
|
|
23
|
+
this.#messages = flattenMessages(
|
|
24
|
+
options.messages
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
$$type = "@backstage/TranslationRef";
|
|
28
|
+
version = "v1";
|
|
29
|
+
get id() {
|
|
30
|
+
return this.#id;
|
|
31
|
+
}
|
|
32
|
+
get T() {
|
|
33
|
+
throw new Error("Not implemented");
|
|
34
|
+
}
|
|
35
|
+
getDefaultMessages() {
|
|
36
|
+
return this.#messages;
|
|
37
|
+
}
|
|
38
|
+
setDefaultResource(resources) {
|
|
39
|
+
this.#resources = resources;
|
|
40
|
+
}
|
|
41
|
+
getDefaultResource() {
|
|
42
|
+
return this.#resources;
|
|
43
|
+
}
|
|
44
|
+
toString() {
|
|
45
|
+
return `TranslationRef{id=${this.id}}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function createTranslationRef(config) {
|
|
49
|
+
const ref = new TranslationRefImpl(config);
|
|
50
|
+
if (config.translations) {
|
|
51
|
+
ref.setDefaultResource(
|
|
52
|
+
createTranslationResource({
|
|
53
|
+
ref,
|
|
54
|
+
translations: config.translations
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return ref;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { createTranslationRef };
|
|
62
|
+
//# sourceMappingURL=TranslationRef.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranslationRef.esm.js","sources":["../../src/translation/TranslationRef.ts"],"sourcesContent":["/*\n * Copyright 2023 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 {\n createTranslationResource,\n TranslationResource,\n} from './TranslationResource';\n\n/** @public */\nexport interface TranslationRef<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n> {\n $$type: '@backstage/TranslationRef';\n\n id: TId;\n\n T: TMessages;\n}\n\n/** @internal */\ntype AnyMessages = { [key in string]: string };\n\n/** @ignore */\ntype AnyNestedMessages = { [key in string]: AnyNestedMessages | string };\n\n/**\n * Flattens a nested message declaration into a flat object with dot-separated keys.\n *\n * @ignore\n */\ntype FlattenedMessages<TMessages extends AnyNestedMessages> =\n // Flatten out object keys into a union structure of objects, e.g. { a: 'a', b: 'b' } -> { a: 'a' } | { b: 'b' }\n // Any nested object will be flattened into the individual unions, e.g. { a: 'a', b: { x: 'x', y: 'y' } } -> { a: 'a' } | { 'b.x': 'x', 'b.y': 'y' }\n // We create this structure by first nesting the desired union types into the original object, and\n // then extract them by indexing with `keyof TMessages` to form the union.\n // Throughout this the objects are wrapped up in a function parameter, which allows us to have the\n // final step of flipping this unions around to an intersection by inferring the function parameter.\n {\n [TKey in keyof TMessages]: (\n _: TMessages[TKey] extends infer TValue // \"local variable\" for the value\n ? TValue extends AnyNestedMessages\n ? FlattenedMessages<TValue> extends infer TNested // Recurse into nested messages, \"local variable\" for the result\n ? {\n [TNestedKey in keyof TNested as `${TKey & string}.${TNestedKey &\n string}`]: TNested[TNestedKey];\n }\n : never\n : { [_ in TKey]: TValue } // Primitive object values are passed through with the same key\n : never,\n ) => void;\n // The `[keyof TMessages]` extracts the object values union from our flattened structure, still wrapped up in function parameters.\n // The `extends (_: infer TIntersection) => void` flips the union to an intersection, at which point we have the correct type.\n }[keyof TMessages] extends (_: infer TIntersection) => void\n ? // This object mapping just expands similar to the Expand<> utility type, providing nicer type hints\n {\n readonly [TExpandKey in keyof TIntersection]: TIntersection[TExpandKey];\n }\n : never;\n\n/** @internal */\nexport interface InternalTranslationRef<\n TId extends string = string,\n TMessages extends { [key in string]: string } = { [key in string]: string },\n> extends TranslationRef<TId, TMessages> {\n version: 'v1';\n\n getDefaultMessages(): AnyMessages;\n\n getDefaultResource(): TranslationResource | undefined;\n}\n\n/** @public */\nexport interface TranslationRefOptions<\n TId extends string,\n TNestedMessages extends AnyNestedMessages,\n TTranslations extends {\n [language in string]: () => Promise<{\n default: {\n [key in keyof FlattenedMessages<TNestedMessages>]: string | null;\n };\n }>;\n },\n> {\n id: TId;\n messages: TNestedMessages;\n translations?: TTranslations;\n}\n\nfunction flattenMessages(nested: AnyNestedMessages): AnyMessages {\n const entries = new Array<[string, string]>();\n\n function visit(obj: AnyNestedMessages, prefix: string): void {\n for (const [key, value] of Object.entries(obj)) {\n if (typeof value === 'string') {\n entries.push([prefix + key, value]);\n } else {\n visit(value, `${prefix}${key}.`);\n }\n }\n }\n\n visit(nested, '');\n\n return Object.fromEntries(entries);\n}\n\n/** @internal */\nclass TranslationRefImpl<\n TId extends string,\n TNestedMessages extends AnyNestedMessages,\n> implements InternalTranslationRef<TId, FlattenedMessages<TNestedMessages>>\n{\n #id: TId;\n #messages: FlattenedMessages<TNestedMessages>;\n #resources: TranslationResource | undefined;\n\n constructor(options: TranslationRefOptions<TId, TNestedMessages, any>) {\n this.#id = options.id;\n this.#messages = flattenMessages(\n options.messages,\n ) as FlattenedMessages<TNestedMessages>;\n }\n\n $$type = '@backstage/TranslationRef' as const;\n\n version = 'v1' as const;\n\n get id(): TId {\n return this.#id;\n }\n\n get T(): never {\n throw new Error('Not implemented');\n }\n\n getDefaultMessages(): AnyMessages {\n return this.#messages;\n }\n\n setDefaultResource(resources: TranslationResource): void {\n this.#resources = resources;\n }\n\n getDefaultResource(): TranslationResource | undefined {\n return this.#resources;\n }\n\n toString() {\n return `TranslationRef{id=${this.id}}`;\n }\n}\n\n/** @public */\nexport function createTranslationRef<\n TId extends string,\n const TNestedMessages extends AnyNestedMessages,\n TTranslations extends {\n [language in string]: () => Promise<{\n default: {\n [key in keyof FlattenedMessages<TNestedMessages>]: string | null;\n };\n }>;\n },\n>(\n config: TranslationRefOptions<TId, TNestedMessages, TTranslations>,\n): TranslationRef<TId, FlattenedMessages<TNestedMessages>> {\n const ref = new TranslationRefImpl(config);\n if (config.translations) {\n ref.setDefaultResource(\n createTranslationResource({\n ref,\n translations: config.translations as any,\n }),\n );\n }\n return ref;\n}\n\n/** @internal */\nexport function toInternalTranslationRef<\n TId extends string,\n TMessages extends AnyMessages,\n>(ref: TranslationRef<TId, TMessages>): InternalTranslationRef<TId, TMessages> {\n const r = ref as InternalTranslationRef<TId, TMessages>;\n if (r.$$type !== '@backstage/TranslationRef') {\n throw new Error(`Invalid translation ref, bad type '${r.$$type}'`);\n }\n if (r.version !== 'v1') {\n throw new Error(`Invalid translation ref, bad version '${r.version}'`);\n }\n return r;\n}\n"],"names":[],"mappings":";;AAsGA,SAAS,gBAAgB,MAAA,EAAwC;AAC/D,EAAA,MAAM,OAAA,GAAU,IAAI,KAAA,EAAwB;AAE5C,EAAA,SAAS,KAAA,CAAM,KAAwB,MAAA,EAAsB;AAC3D,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,MAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,QAAA,OAAA,CAAQ,IAAA,CAAK,CAAC,MAAA,GAAS,GAAA,EAAK,KAAK,CAAC,CAAA;AAAA,MACpC,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,KAAA,EAAO,CAAA,EAAG,MAAM,CAAA,EAAG,GAAG,CAAA,CAAA,CAAG,CAAA;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,EAAA,KAAA,CAAM,QAAQ,EAAE,CAAA;AAEhB,EAAA,OAAO,MAAA,CAAO,YAAY,OAAO,CAAA;AACnC;AAGA,MAAM,kBAAA,CAIN;AAAA,EACE,GAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EAEA,YAAY,OAAA,EAA2D;AACrE,IAAA,IAAA,CAAK,MAAM,OAAA,CAAQ,EAAA;AACnB,IAAA,IAAA,CAAK,SAAA,GAAY,eAAA;AAAA,MACf,OAAA,CAAQ;AAAA,KACV;AAAA,EACF;AAAA,EAEA,MAAA,GAAS,2BAAA;AAAA,EAET,OAAA,GAAU,IAAA;AAAA,EAEV,IAAI,EAAA,GAAU;AACZ,IAAA,OAAO,IAAA,CAAK,GAAA;AAAA,EACd;AAAA,EAEA,IAAI,CAAA,GAAW;AACb,IAAA,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAAA,EACnC;AAAA,EAEA,kBAAA,GAAkC;AAChC,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA,EAEA,mBAAmB,SAAA,EAAsC;AACvD,IAAA,IAAA,CAAK,UAAA,GAAa,SAAA;AAAA,EACpB;AAAA,EAEA,kBAAA,GAAsD;AACpD,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA,EAEA,QAAA,GAAW;AACT,IAAA,OAAO,CAAA,kBAAA,EAAqB,KAAK,EAAE,CAAA,CAAA,CAAA;AAAA,EACrC;AACF;AAGO,SAAS,qBAWd,MAAA,EACyD;AACzD,EAAA,MAAM,GAAA,GAAM,IAAI,kBAAA,CAAmB,MAAM,CAAA;AACzC,EAAA,IAAI,OAAO,YAAA,EAAc;AACvB,IAAA,GAAA,CAAI,kBAAA;AAAA,MACF,yBAAA,CAA0B;AAAA,QACxB,GAAA;AAAA,QACA,cAAc,MAAA,CAAO;AAAA,OACtB;AAAA,KACH;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;;;;"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function createTranslationResource(options) {
|
|
2
|
+
return {
|
|
3
|
+
$$type: "@backstage/TranslationResource",
|
|
4
|
+
version: "v1",
|
|
5
|
+
id: options.ref.id,
|
|
6
|
+
resources: Object.entries(options.translations).map(
|
|
7
|
+
([language, loader]) => ({
|
|
8
|
+
language,
|
|
9
|
+
loader: () => loader().then((m) => {
|
|
10
|
+
const value = m.default;
|
|
11
|
+
return {
|
|
12
|
+
messages: value?.$$type === "@backstage/TranslationMessages" ? value.messages : value
|
|
13
|
+
};
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
)
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { createTranslationResource };
|
|
21
|
+
//# sourceMappingURL=TranslationResource.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TranslationResource.esm.js","sources":["../../src/translation/TranslationResource.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { TranslationMessages } from './TranslationMessages';\nimport { TranslationRef } from './TranslationRef';\n\n/** @public */\nexport interface TranslationResource<TId extends string = string> {\n $$type: '@backstage/TranslationResource';\n id: TId;\n}\n\n/** @internal */\nexport type InternalTranslationResourceLoader = () => Promise<{\n messages: { [key in string]: string | null };\n}>;\n\n/** @internal */\nexport interface InternalTranslationResource<TId extends string = string>\n extends TranslationResource<TId> {\n version: 'v1';\n resources: Array<{\n language: string;\n loader: InternalTranslationResourceLoader;\n }>;\n}\n\n/** @internal */\nexport function toInternalTranslationResource<TId extends string>(\n resource: TranslationResource<TId>,\n): InternalTranslationResource<TId> {\n const r = resource as InternalTranslationResource<TId>;\n if (r.$$type !== '@backstage/TranslationResource') {\n throw new Error(`Invalid translation resource, bad type '${r.$$type}'`);\n }\n if (r.version !== 'v1') {\n throw new Error(`Invalid translation resource, bad version '${r.version}'`);\n }\n\n return r;\n}\n\n/** @public */\nexport interface TranslationResourceOptions<\n TId extends string,\n TMessages extends { [key in string]: string },\n TTranslations extends {\n [language in string]: () => Promise<{\n default:\n | TranslationMessages<TId>\n | { [key in keyof TMessages]: string | null };\n }>;\n },\n> {\n ref: TranslationRef<TId, TMessages>;\n\n translations: TTranslations;\n}\n\n/** @public */\nexport function createTranslationResource<\n TId extends string,\n TMessages extends { [key in string]: string },\n TTranslations extends {\n [language in string]: () => Promise<{\n default:\n | TranslationMessages<TId>\n | { [key in keyof TMessages]: string | null };\n }>;\n },\n>(\n options: TranslationResourceOptions<TId, TMessages, TTranslations>,\n): TranslationResource<TId> {\n return {\n $$type: '@backstage/TranslationResource',\n version: 'v1',\n id: options.ref.id,\n resources: Object.entries(options.translations).map(\n ([language, loader]) => ({\n language,\n loader: () =>\n loader().then(m => {\n const value = m.default;\n return {\n messages:\n value?.$$type === '@backstage/TranslationMessages'\n ? value.messages\n : value,\n };\n }),\n }),\n ),\n } as InternalTranslationResource<TId>;\n}\n"],"names":[],"mappings":"AAyEO,SAAS,0BAWd,OAAA,EAC0B;AAC1B,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,gCAAA;AAAA,IACR,OAAA,EAAS,IAAA;AAAA,IACT,EAAA,EAAI,QAAQ,GAAA,CAAI,EAAA;AAAA,IAChB,SAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,OAAA,CAAQ,YAAY,CAAA,CAAE,GAAA;AAAA,MAC9C,CAAC,CAAC,QAAA,EAAU,MAAM,CAAA,MAAO;AAAA,QACvB,QAAA;AAAA,QACA,MAAA,EAAQ,MACN,MAAA,EAAO,CAAE,KAAK,CAAA,CAAA,KAAK;AACjB,UAAA,MAAM,QAAQ,CAAA,CAAE,OAAA;AAChB,UAAA,OAAO;AAAA,YACL,QAAA,EACE,KAAA,EAAO,MAAA,KAAW,gCAAA,GACd,MAAM,QAAA,GACN;AAAA,WACR;AAAA,QACF,CAAC;AAAA,OACL;AAAA;AACF,GACF;AACF;;;;"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
|
|
2
|
+
import '../apis/definitions/AppTreeApi.esm.js';
|
|
3
|
+
import '../apis/definitions/auth.esm.js';
|
|
4
|
+
import '../apis/definitions/AlertApi.esm.js';
|
|
5
|
+
import '../apis/definitions/AppLanguageApi.esm.js';
|
|
6
|
+
import '../apis/definitions/AppThemeApi.esm.js';
|
|
7
|
+
import '../apis/definitions/SwappableComponentsApi.esm.js';
|
|
8
|
+
import '../apis/definitions/ConfigApi.esm.js';
|
|
9
|
+
import '../apis/definitions/DiscoveryApi.esm.js';
|
|
10
|
+
import { errorApiRef } from '../apis/definitions/ErrorApi.esm.js';
|
|
11
|
+
import '../apis/definitions/FeatureFlagsApi.esm.js';
|
|
12
|
+
import '../apis/definitions/FetchApi.esm.js';
|
|
13
|
+
import '../apis/definitions/IconsApi.esm.js';
|
|
14
|
+
import '../apis/definitions/IdentityApi.esm.js';
|
|
15
|
+
import '../apis/definitions/DialogApi.esm.js';
|
|
16
|
+
import '../apis/definitions/OAuthRequestApi.esm.js';
|
|
17
|
+
import '../apis/definitions/RouteResolutionApi.esm.js';
|
|
18
|
+
import '../apis/definitions/StorageApi.esm.js';
|
|
19
|
+
import '../apis/definitions/AnalyticsApi.esm.js';
|
|
20
|
+
import { translationApiRef } from '../apis/definitions/TranslationApi.esm.js';
|
|
21
|
+
import { useApi } from '../apis/system/useApi.esm.js';
|
|
22
|
+
|
|
23
|
+
const loggedRefs = /* @__PURE__ */ new WeakSet();
|
|
24
|
+
const useTranslationRef = (translationRef) => {
|
|
25
|
+
const errorApi = useApi(errorApiRef);
|
|
26
|
+
const translationApi = useApi(translationApiRef);
|
|
27
|
+
const [snapshot, setSnapshot] = useState(
|
|
28
|
+
() => translationApi.getTranslation(translationRef)
|
|
29
|
+
);
|
|
30
|
+
const observable = useMemo(
|
|
31
|
+
() => translationApi.translation$(translationRef),
|
|
32
|
+
[translationApi, translationRef]
|
|
33
|
+
);
|
|
34
|
+
const onError = useCallback(
|
|
35
|
+
(error) => {
|
|
36
|
+
if (!loggedRefs.has(translationRef)) {
|
|
37
|
+
const errMsg = `Failed to load translation resource '${translationRef.id}'; caused by ${error}`;
|
|
38
|
+
console.error(errMsg);
|
|
39
|
+
errorApi.post(new Error(errMsg));
|
|
40
|
+
loggedRefs.add(translationRef);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
[errorApi, translationRef]
|
|
44
|
+
);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const subscription = observable.subscribe({
|
|
47
|
+
next(next) {
|
|
48
|
+
if (next.ready) {
|
|
49
|
+
setSnapshot(next);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
error(error) {
|
|
53
|
+
onError(error);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return () => {
|
|
57
|
+
subscription.unsubscribe();
|
|
58
|
+
};
|
|
59
|
+
}, [observable, onError]);
|
|
60
|
+
const initialRenderRef = useRef(true);
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
if (initialRenderRef.current) {
|
|
63
|
+
initialRenderRef.current = false;
|
|
64
|
+
} else {
|
|
65
|
+
setSnapshot(translationApi.getTranslation(translationRef));
|
|
66
|
+
}
|
|
67
|
+
}, [translationApi, translationRef]);
|
|
68
|
+
if (!snapshot.ready) {
|
|
69
|
+
throw new Promise((resolve) => {
|
|
70
|
+
const subscription = observable.subscribe({
|
|
71
|
+
next(next) {
|
|
72
|
+
if (next.ready) {
|
|
73
|
+
subscription.unsubscribe();
|
|
74
|
+
resolve();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
error(error) {
|
|
78
|
+
subscription.unsubscribe();
|
|
79
|
+
onError(error);
|
|
80
|
+
resolve();
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return { t: snapshot.t };
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export { useTranslationRef };
|
|
89
|
+
//# sourceMappingURL=useTranslationRef.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useTranslationRef.esm.js","sources":["../../src/translation/useTranslationRef.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { errorApiRef, useApi } from '../apis';\nimport {\n translationApiRef,\n TranslationFunction,\n TranslationSnapshot,\n} from '../apis/definitions/TranslationApi';\nimport { TranslationRef } from './TranslationRef';\n\n// Make sure we don't fill the logs with loading errors for the same ref\nconst loggedRefs = new WeakSet<TranslationRef<string, {}>>();\n\n/** @public */\nexport const useTranslationRef = <\n TMessages extends { [key in string]: string },\n>(\n translationRef: TranslationRef<string, TMessages>,\n): { t: TranslationFunction<TMessages> } => {\n const errorApi = useApi(errorApiRef);\n const translationApi = useApi(translationApiRef);\n\n const [snapshot, setSnapshot] = useState<TranslationSnapshot<TMessages>>(() =>\n translationApi.getTranslation(translationRef),\n );\n const observable = useMemo(\n () => translationApi.translation$(translationRef),\n [translationApi, translationRef],\n );\n\n const onError = useCallback(\n (error: Error) => {\n if (!loggedRefs.has(translationRef)) {\n const errMsg = `Failed to load translation resource '${translationRef.id}'; caused by ${error}`;\n // eslint-disable-next-line no-console\n console.error(errMsg);\n errorApi.post(new Error(errMsg));\n loggedRefs.add(translationRef);\n }\n },\n [errorApi, translationRef],\n );\n\n useEffect(() => {\n const subscription = observable.subscribe({\n next(next) {\n if (next.ready) {\n setSnapshot(next);\n }\n },\n error(error) {\n onError(error);\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [observable, onError]);\n\n // Keep track of if the provided translation ref changes, and in that case update the snapshot\n const initialRenderRef = useRef(true);\n useEffect(() => {\n if (initialRenderRef.current) {\n initialRenderRef.current = false;\n } else {\n setSnapshot(translationApi.getTranslation(translationRef));\n }\n }, [translationApi, translationRef]);\n\n if (!snapshot.ready) {\n throw new Promise<void>(resolve => {\n const subscription = observable.subscribe({\n next(next) {\n if (next.ready) {\n subscription.unsubscribe();\n resolve();\n }\n },\n error(error) {\n subscription.unsubscribe();\n onError(error);\n resolve();\n },\n });\n });\n }\n\n return { t: snapshot.t };\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,UAAA,uBAAiB,OAAA,EAAoC;AAGpD,MAAM,iBAAA,GAAoB,CAG/B,cAAA,KAC0C;AAC1C,EAAA,MAAM,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAA,MAAM,cAAA,GAAiB,OAAO,iBAAiB,CAAA;AAE/C,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,QAAA;AAAA,IAAyC,MACvE,cAAA,CAAe,cAAA,CAAe,cAAc;AAAA,GAC9C;AACA,EAAA,MAAM,UAAA,GAAa,OAAA;AAAA,IACjB,MAAM,cAAA,CAAe,YAAA,CAAa,cAAc,CAAA;AAAA,IAChD,CAAC,gBAAgB,cAAc;AAAA,GACjC;AAEA,EAAA,MAAM,OAAA,GAAU,WAAA;AAAA,IACd,CAAC,KAAA,KAAiB;AAChB,MAAA,IAAI,CAAC,UAAA,CAAW,GAAA,CAAI,cAAc,CAAA,EAAG;AACnC,QAAA,MAAM,MAAA,GAAS,CAAA,qCAAA,EAAwC,cAAA,CAAe,EAAE,gBAAgB,KAAK,CAAA,CAAA;AAE7F,QAAA,OAAA,CAAQ,MAAM,MAAM,CAAA;AACpB,QAAA,QAAA,CAAS,IAAA,CAAK,IAAI,KAAA,CAAM,MAAM,CAAC,CAAA;AAC/B,QAAA,UAAA,CAAW,IAAI,cAAc,CAAA;AAAA,MAC/B;AAAA,IACF,CAAA;AAAA,IACA,CAAC,UAAU,cAAc;AAAA,GAC3B;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,YAAA,GAAe,WAAW,SAAA,CAAU;AAAA,MACxC,KAAK,IAAA,EAAM;AACT,QAAA,IAAI,KAAK,KAAA,EAAO;AACd,UAAA,WAAA,CAAY,IAAI,CAAA;AAAA,QAClB;AAAA,MACF,CAAA;AAAA,MACA,MAAM,KAAA,EAAO;AACX,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,MACf;AAAA,KACD,CAAA;AAED,IAAA,OAAO,MAAM;AACX,MAAA,YAAA,CAAa,WAAA,EAAY;AAAA,IAC3B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,UAAA,EAAY,OAAO,CAAC,CAAA;AAGxB,EAAA,MAAM,gBAAA,GAAmB,OAAO,IAAI,CAAA;AACpC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,iBAAiB,OAAA,EAAS;AAC5B,MAAA,gBAAA,CAAiB,OAAA,GAAU,KAAA;AAAA,IAC7B,CAAA,MAAO;AACL,MAAA,WAAA,CAAY,cAAA,CAAe,cAAA,CAAe,cAAc,CAAC,CAAA;AAAA,IAC3D;AAAA,EACF,CAAA,EAAG,CAAC,cAAA,EAAgB,cAAc,CAAC,CAAA;AAEnC,EAAA,IAAI,CAAC,SAAS,KAAA,EAAO;AACnB,IAAA,MAAM,IAAI,QAAc,CAAA,OAAA,KAAW;AACjC,MAAA,MAAM,YAAA,GAAe,WAAW,SAAA,CAAU;AAAA,QACxC,KAAK,IAAA,EAAM;AACT,UAAA,IAAI,KAAK,KAAA,EAAO;AACd,YAAA,YAAA,CAAa,WAAA,EAAY;AACzB,YAAA,OAAA,EAAQ;AAAA,UACV;AAAA,QACF,CAAA;AAAA,QACA,MAAM,KAAA,EAAO;AACX,UAAA,YAAA,CAAa,WAAA,EAAY;AACzB,UAAA,OAAA,CAAQ,KAAK,CAAA;AACb,UAAA,OAAA,EAAQ;AAAA,QACV;AAAA,OACD,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,EAAE,CAAA,EAAG,QAAA,CAAS,CAAA,EAAE;AACzB;;;;"}
|
|
@@ -14,11 +14,11 @@ function bindInputs(inputs, kind, name) {
|
|
|
14
14
|
return Object.fromEntries(
|
|
15
15
|
Object.entries(inputs).map(([inputName, input]) => [
|
|
16
16
|
inputName,
|
|
17
|
-
OpaqueExtensionInput.toInternal(input).withContext({
|
|
17
|
+
OpaqueExtensionInput.toInternal(input).withContext?.({
|
|
18
18
|
kind,
|
|
19
19
|
name,
|
|
20
20
|
input: inputName
|
|
21
|
-
})
|
|
21
|
+
}) ?? input
|
|
22
22
|
])
|
|
23
23
|
);
|
|
24
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExtension.esm.js","sources":["../../src/wiring/createExtension.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { ApiHolder, AppNode } from '../apis';\nimport { Expand } from '@backstage/types';\nimport {\n ResolvedInputValueOverrides,\n resolveInputOverrides,\n} from './resolveInputOverrides';\nimport {\n createExtensionDataContainer,\n OpaqueExtensionInput,\n} from '@internal/frontend';\nimport { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef';\nimport { ExtensionInput } from './createExtensionInput';\nimport { z } from 'zod';\nimport { createSchemaFromZod } from '../schema/createSchemaFromZod';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\nimport { ExtensionDataContainer } from './types';\nimport {\n ExtensionBlueprint,\n ExtensionBlueprintDefineParams,\n} from './createExtensionBlueprint';\nimport { FrontendPlugin } from './createFrontendPlugin';\nimport { FrontendModule } from './createFrontendModule';\n\n/**\n * This symbol is used to pass parameter overrides from the extension override to the blueprint factory\n * @internal\n */\nexport const ctxParamsSymbol = Symbol('params');\n\n/**\n * Convert a single extension input into a matching resolved input.\n * @public\n */\nexport type ResolvedExtensionInput<TExtensionInput extends ExtensionInput> =\n TExtensionInput['extensionData'] extends Array<ExtensionDataRef>\n ? {\n node: AppNode;\n } & ExtensionDataContainer<TExtensionInput['extensionData'][number]>\n : never;\n\n/**\n * Converts an extension input map into a matching collection of resolved inputs.\n * @public\n */\nexport type ResolvedExtensionInputs<\n TInputs extends {\n [name in string]: ExtensionInput;\n },\n> = {\n [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton']\n ? Array<Expand<ResolvedExtensionInput<TInputs[InputName]>>>\n : false extends TInputs[InputName]['config']['optional']\n ? Expand<ResolvedExtensionInput<TInputs[InputName]>>\n : Expand<ResolvedExtensionInput<TInputs[InputName]> | undefined>;\n};\n\ntype ToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\ntype PopUnion<U> = ToIntersection<\n U extends any ? () => U : never\n> extends () => infer R\n ? [rest: Exclude<U, R>, next: R]\n : undefined;\n\n/** @ignore */\ntype JoinStringUnion<\n U,\n TDiv extends string = ', ',\n TResult extends string = '',\n> = PopUnion<U> extends [infer IRest extends string, infer INext extends string]\n ? TResult extends ''\n ? JoinStringUnion<IRest, TDiv, INext>\n : JoinStringUnion<IRest, TDiv, `${TResult}${TDiv}${INext}`>\n : TResult;\n\n/** @ignore */\nexport type RequiredExtensionIds<UExtensionData extends ExtensionDataRef> =\n UExtensionData extends any\n ? UExtensionData['config']['optional'] extends true\n ? never\n : UExtensionData['id']\n : never;\n\n/** @ignore */\nexport type VerifyExtensionFactoryOutput<\n UDeclaredOutput extends ExtensionDataRef,\n UFactoryOutput extends ExtensionDataValue<any, any>,\n> = [RequiredExtensionIds<UDeclaredOutput>] extends [UFactoryOutput['id']]\n ? [UFactoryOutput['id']] extends [UDeclaredOutput['id']]\n ? {}\n : `Error: The extension factory has undeclared output(s): ${JoinStringUnion<\n Exclude<UFactoryOutput['id'], UDeclaredOutput['id']>\n >}`\n : `Error: The extension factory is missing the following output(s): ${JoinStringUnion<\n Exclude<RequiredExtensionIds<UDeclaredOutput>, UFactoryOutput['id']>\n >}`;\n\n/** @ignore */\nexport type VerifyExtensionAttachTo<\n UOutput extends ExtensionDataRef,\n UParentInput extends ExtensionDataRef,\n> = ExtensionDataRef extends UParentInput\n ? {}\n : [RequiredExtensionIds<UParentInput>] extends [RequiredExtensionIds<UOutput>]\n ? {}\n : `Error: This parent extension input requires the following extension data, but it is not declared as guaranteed output of this extension: ${JoinStringUnion<\n Exclude<RequiredExtensionIds<UParentInput>, RequiredExtensionIds<UOutput>>\n >}`;\n\n/**\n * Specifies where an extension should attach in the extension tree.\n *\n * @remarks\n *\n * A standard attachment point declaration will specify the ID of the parent extension, as well as the name of the input to attach to.\n *\n * There are three more advanced forms that are available for more complex use-cases:\n *\n * 1. Relative attachment points: using the `relative` property instead of `id`, the attachment point is resolved relative to the current plugin.\n * 2. Extension input references: using a reference in code to another extension's input in the same plugin. These references are always relative.\n * 3. Array of attachment points: an array of attachment points can be used to clone and attach to multiple extensions at once.\n *\n * @example\n * ```ts\n * // Attach to a specific extension by full ID\n * { id: 'app/routes', input: 'routes' }\n *\n * // Attach to an extension in the same plugin by kind\n * { relative: { kind: 'page' }, input: 'actions' }\n *\n * // Attach to a specific input of another extension\n * const page = ParentBlueprint.make({ ... });\n * const child = ChildBlueprint.make({ attachTo: page.inputs.children });\n *\n * // Attach to multiple parents at once\n * [\n * { id: 'page/home', input: 'widgets' },\n * { relative: { kind: 'page' }, input: 'widgets' },\n * ]\n * ```\n *\n * @public\n */\nexport type ExtensionDefinitionAttachTo<\n UParentInputs extends ExtensionDataRef = ExtensionDataRef,\n> =\n | { id: string; input: string; relative?: never }\n | { relative: { kind?: string; name?: string }; input: string; id?: never }\n | ExtensionInput<UParentInputs>\n | Array<\n | { id: string; input: string; relative?: never }\n | {\n relative: { kind?: string; name?: string };\n input: string;\n id?: never;\n }\n | ExtensionInput<UParentInputs>\n >;\n\n/** @public */\nexport type CreateExtensionOptions<\n TKind extends string | undefined,\n TName extends string | undefined,\n UOutput extends ExtensionDataRef,\n TInputs extends { [inputName in string]: ExtensionInput },\n TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },\n UFactoryOutput extends ExtensionDataValue<any, any>,\n UParentInputs extends ExtensionDataRef,\n> = {\n kind?: TKind;\n name?: TName;\n attachTo: ExtensionDefinitionAttachTo<UParentInputs> &\n VerifyExtensionAttachTo<UOutput, UParentInputs>;\n disabled?: boolean;\n inputs?: TInputs;\n output: Array<UOutput>;\n config?: {\n schema: TConfigSchema;\n };\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: {\n [key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;\n };\n inputs: Expand<ResolvedExtensionInputs<TInputs>>;\n }): Iterable<UFactoryOutput>;\n} & VerifyExtensionFactoryOutput<UOutput, UFactoryOutput>;\n\n/** @public */\nexport type ExtensionDefinitionParameters = {\n kind?: string;\n name?: string;\n configInput?: { [K in string]: any };\n config?: { [K in string]: any };\n output?: ExtensionDataRef;\n inputs?: { [KName in string]: ExtensionInput };\n params?: object | ExtensionBlueprintDefineParams;\n};\n\n/**\n * Same as the one in `createExtensionBlueprint`, but with `ParamsFactory` inlined.\n * It can't be exported because it breaks API reports.\n * @ignore\n */\ntype AnyParamsInput<TParams extends object | ExtensionBlueprintDefineParams> =\n TParams extends ExtensionBlueprintDefineParams<infer IParams>\n ? IParams | ((define: TParams) => ReturnType<TParams>)\n :\n | TParams\n | ((\n define: ExtensionBlueprintDefineParams<TParams, TParams>,\n ) => ReturnType<ExtensionBlueprintDefineParams<TParams, TParams>>);\n\n/** @public */\nexport interface ExtensionDefinition<\n TParams extends ExtensionDefinitionParameters = ExtensionDefinitionParameters,\n> {\n $$type: '@backstage/ExtensionDefinition';\n readonly T: TParams;\n}\n\n/** @public */\nexport interface OverridableExtensionDefinition<\n T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters,\n> extends ExtensionDefinition<T> {\n /**\n * References to the inputs of this extension, which can be used to attach child extensions.\n */\n readonly inputs: {\n [K in keyof T['inputs']]: ExtensionInput<\n T['inputs'][K] extends ExtensionInput<infer IData> ? IData : never\n >;\n };\n\n override<\n TExtensionConfigSchema extends {\n [key in string]: (zImpl: typeof z) => z.ZodType;\n },\n UFactoryOutput extends ExtensionDataValue<any, any>,\n UNewOutput extends ExtensionDataRef,\n TExtraInputs extends { [inputName in string]: ExtensionInput },\n TParamsInput extends AnyParamsInput<NonNullable<T['params']>>,\n UParentInputs extends ExtensionDataRef,\n >(\n args: Expand<\n {\n attachTo?: ExtensionDefinitionAttachTo<UParentInputs> &\n VerifyExtensionAttachTo<\n ExtensionDataRef extends UNewOutput\n ? NonNullable<T['output']>\n : UNewOutput,\n UParentInputs\n >;\n disabled?: boolean;\n inputs?: TExtraInputs & {\n [KName in keyof T['inputs']]?: `Error: Input '${KName &\n string}' is already defined in parent definition`;\n };\n output?: Array<UNewOutput>;\n config?: {\n schema: TExtensionConfigSchema & {\n [KName in keyof T['config']]?: `Error: Config key '${KName &\n string}' is already defined in parent schema`;\n };\n };\n factory?(\n originalFactory: <\n TFactoryParamsReturn extends AnyParamsInput<\n NonNullable<T['params']>\n >,\n >(\n context?: Expand<\n {\n config?: T['config'];\n inputs?: ResolvedInputValueOverrides<NonNullable<T['inputs']>>;\n } & ([T['params']] extends [never]\n ? {}\n : {\n params?: TFactoryParamsReturn extends ExtensionBlueprintDefineParams\n ? TFactoryParamsReturn\n : T['params'] extends ExtensionBlueprintDefineParams\n ? 'Error: This blueprint uses advanced parameter types and requires you to pass parameters as using the following callback syntax: `originalFactory(defineParams => defineParams(<params>))`'\n : Partial<T['params']>;\n })\n >,\n ) => ExtensionDataContainer<NonNullable<T['output']>>,\n context: {\n node: AppNode;\n apis: ApiHolder;\n config: T['config'] & {\n [key in keyof TExtensionConfigSchema]: z.infer<\n ReturnType<TExtensionConfigSchema[key]>\n >;\n };\n inputs: Expand<ResolvedExtensionInputs<T['inputs'] & TExtraInputs>>;\n },\n ): Iterable<UFactoryOutput>;\n } & ([T['params']] extends [never]\n ? {}\n : {\n params?: TParamsInput extends ExtensionBlueprintDefineParams\n ? TParamsInput\n : T['params'] extends ExtensionBlueprintDefineParams\n ? 'Error: This blueprint uses advanced parameter types and requires you to pass parameters as using the following callback syntax: `originalFactory(defineParams => defineParams(<params>))`'\n : Partial<T['params']>;\n })\n > &\n VerifyExtensionFactoryOutput<\n ExtensionDataRef extends UNewOutput\n ? NonNullable<T['output']>\n : UNewOutput,\n UFactoryOutput\n >,\n ): OverridableExtensionDefinition<{\n kind: T['kind'];\n name: T['name'];\n output: ExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput;\n inputs: T['inputs'] & TExtraInputs;\n config: T['config'] & {\n [key in keyof TExtensionConfigSchema]: z.infer<\n ReturnType<TExtensionConfigSchema[key]>\n >;\n };\n configInput: T['configInput'] &\n z.input<\n z.ZodObject<{\n [key in keyof TExtensionConfigSchema]: ReturnType<\n TExtensionConfigSchema[key]\n >;\n }>\n >;\n }>;\n}\n\n/**\n * @internal\n */\nfunction bindInputs(\n inputs: { [inputName in string]: ExtensionInput } | undefined,\n kind?: string,\n name?: string,\n) {\n if (!inputs) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(inputs).map(([inputName, input]) => [\n inputName,\n OpaqueExtensionInput.toInternal(input).withContext({\n kind,\n name,\n input: inputName,\n }),\n ]),\n );\n}\n\n/**\n * Creates a new extension definition for installation in a Backstage app.\n *\n * @remarks\n *\n * This is a low-level function for creation of extensions with arbitrary inputs\n * and outputs and is typically only intended to be used for advanced overrides\n * or framework-level extensions. For most extension creation needs, it is\n * recommended to use existing {@link ExtensionBlueprint}s instead. You can find\n * blueprints both in the `@backstage/frontend-plugin-api` package as well as\n * other plugin libraries. There is also a list of\n * {@link https://backstage.io/docs/frontend-system/building-plugins/common-extension-blueprints | commonly used blueprints}\n * in the frontend system documentation.\n *\n * Extension definitions that are created with this function can be installed in\n * a Backstage app via a {@link FrontendPlugin} or {@link FrontendModule}.\n *\n * For more details on how extensions work, see the\n * {@link https://backstage.io/docs/frontend-system/architecture/extensions | documentation for extensions}.\n *\n * @example\n *\n * ```ts\n * const myExtension = createExtension({\n * name: 'example',\n * attachTo: { id: 'app', input: 'root' },\n * output: [coreExtensionData.reactElement],\n * factory() {\n * return [coreExtensionData.reactElement(<h1>Hello, world!</h1>)];\n * },\n * });\n * ```\n *\n * @public\n */\nexport function createExtension<\n UOutput extends ExtensionDataRef,\n TInputs extends { [inputName in string]: ExtensionInput },\n TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },\n UFactoryOutput extends ExtensionDataValue<any, any>,\n const TKind extends string | undefined = undefined,\n const TName extends string | undefined = undefined,\n UParentInputs extends ExtensionDataRef = ExtensionDataRef,\n>(\n options: CreateExtensionOptions<\n TKind,\n TName,\n UOutput,\n TInputs,\n TConfigSchema,\n UFactoryOutput,\n UParentInputs\n >,\n): OverridableExtensionDefinition<{\n config: string extends keyof TConfigSchema\n ? {}\n : {\n [key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;\n };\n configInput: string extends keyof TConfigSchema\n ? {}\n : z.input<\n z.ZodObject<{\n [key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;\n }>\n >;\n // This inference and remapping back to ExtensionDataRef eliminates any occurrences ConfigurationExtensionDataRef\n output: UOutput extends ExtensionDataRef<\n infer IData,\n infer IId,\n infer IConfig\n >\n ? ExtensionDataRef<IData, IId, IConfig>\n : never;\n inputs: TInputs;\n params: never;\n kind: string | undefined extends TKind ? undefined : TKind;\n name: string | undefined extends TName ? undefined : TName;\n}> {\n const schemaDeclaration = options.config?.schema;\n const configSchema =\n schemaDeclaration &&\n createSchemaFromZod(innerZ =>\n innerZ.object(\n Object.fromEntries(\n Object.entries(schemaDeclaration).map(([k, v]) => [k, v(innerZ)]),\n ),\n ),\n );\n\n return OpaqueExtensionDefinition.createInstance('v2', {\n T: undefined as unknown as {\n config: string extends keyof TConfigSchema\n ? {}\n : {\n [key in keyof TConfigSchema]: z.infer<\n ReturnType<TConfigSchema[key]>\n >;\n };\n configInput: string extends keyof TConfigSchema\n ? {}\n : z.input<\n z.ZodObject<{\n [key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;\n }>\n >;\n output: UOutput;\n inputs: TInputs;\n kind: string | undefined extends TKind ? undefined : TKind;\n name: string | undefined extends TName ? undefined : TName;\n },\n kind: options.kind,\n name: options.name,\n attachTo: options.attachTo,\n disabled: options.disabled ?? false,\n inputs: bindInputs(options.inputs, options.kind, options.name),\n output: options.output,\n configSchema,\n factory: options.factory,\n toString() {\n const parts: string[] = [];\n if (options.kind) {\n parts.push(`kind=${options.kind}`);\n }\n if (options.name) {\n parts.push(`name=${options.name}`);\n }\n const attachTo = [options.attachTo]\n .flat()\n .map(aAny => {\n const a = aAny as ExtensionDefinitionAttachTo;\n if (OpaqueExtensionInput.isType(a)) {\n const { context } = OpaqueExtensionInput.toInternal(a);\n if (!context) {\n return '<detached-input>';\n }\n let id = '<plugin>';\n if (context?.kind) {\n id = `${context?.kind}:${id}`;\n }\n if (context?.name) {\n id = `${id}/${context?.name}`;\n }\n return `${id}@${context.input}`;\n }\n if ('relative' in a && a.relative) {\n let id = '<plugin>';\n if (a.relative.kind) {\n id = `${a.relative.kind}:${id}`;\n }\n if (a.relative.name) {\n id = `${id}/${a.relative.name}`;\n }\n return `${id}@${a.input}`;\n }\n if ('id' in a) {\n return `${a.id}@${a.input}`;\n }\n throw new Error('Invalid attachment point specification');\n })\n .join('+');\n parts.push(`attachTo=${attachTo}`);\n return `ExtensionDefinition{${parts.join(',')}}`;\n },\n override(overrideOptions) {\n if (!Array.isArray(options.output)) {\n throw new Error(\n 'Cannot override an extension that is not declared using the new format with outputs as an array',\n );\n }\n\n // TODO(Rugvip): Making this a type check would be optimal, but it seems\n // like it's tricky to add that and still have the type\n // inference work correctly for the factory output.\n if (overrideOptions.output && !overrideOptions.factory) {\n throw new Error(\n 'Refused to override output without also overriding factory',\n );\n }\n // TODO(Rugvip): Similar to above, would be nice to error during type checking, but don't want to complicate the types too much\n if (overrideOptions.params && overrideOptions.factory) {\n throw new Error(\n 'Refused to override params and factory at the same time',\n );\n }\n\n return createExtension({\n kind: options.kind,\n name: options.name,\n attachTo: (overrideOptions.attachTo ??\n options.attachTo) as ExtensionDefinitionAttachTo,\n disabled: overrideOptions.disabled ?? options.disabled,\n inputs: bindInputs(\n {\n ...(options.inputs ?? {}),\n ...(overrideOptions.inputs ?? {}),\n },\n options.kind,\n options.name,\n ),\n output: (overrideOptions.output ??\n options.output) as ExtensionDataRef[],\n config:\n options.config || overrideOptions.config\n ? {\n schema: {\n ...options.config?.schema,\n ...overrideOptions.config?.schema,\n },\n }\n : undefined,\n factory: ({ node, apis, config, inputs }) => {\n if (!overrideOptions.factory) {\n return options.factory({\n node,\n apis,\n config: config as any,\n inputs: inputs as any,\n [ctxParamsSymbol as any]: overrideOptions.params,\n });\n }\n const parentResult = overrideOptions.factory(\n (innerContext): ExtensionDataContainer<UOutput> => {\n return createExtensionDataContainer<UOutput>(\n options.factory({\n node,\n apis,\n config: (innerContext?.config ?? config) as any,\n inputs: resolveInputOverrides(\n options.inputs,\n inputs,\n innerContext?.inputs,\n ) as any,\n [ctxParamsSymbol as any]: innerContext?.params,\n }) as Iterable<any>,\n 'original extension factory',\n options.output,\n );\n },\n {\n node,\n apis,\n config: config as any,\n inputs: inputs as any,\n },\n );\n\n if (\n typeof parentResult !== 'object' ||\n !parentResult?.[Symbol.iterator]\n ) {\n throw new Error(\n 'extension factory override did not provide an iterable object',\n );\n }\n\n const deduplicatedResult = new Map<\n string,\n ExtensionDataValue<any, any>\n >();\n for (const item of parentResult) {\n deduplicatedResult.set(item.id, item);\n }\n\n return deduplicatedResult.values();\n },\n }) as OverridableExtensionDefinition<any>;\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createExtension.esm.js","sources":["../../src/wiring/createExtension.ts"],"sourcesContent":["/*\n * Copyright 2023 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 { ApiHolder, AppNode } from '../apis';\nimport { Expand } from '@backstage/types';\nimport {\n ResolvedInputValueOverrides,\n resolveInputOverrides,\n} from './resolveInputOverrides';\nimport {\n createExtensionDataContainer,\n OpaqueExtensionInput,\n} from '@internal/frontend';\nimport { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef';\nimport { ExtensionInput } from './createExtensionInput';\nimport { z } from 'zod';\nimport { createSchemaFromZod } from '../schema/createSchemaFromZod';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\nimport { ExtensionDataContainer } from './types';\nimport {\n ExtensionBlueprint,\n ExtensionBlueprintDefineParams,\n} from './createExtensionBlueprint';\nimport { FrontendPlugin } from './createFrontendPlugin';\nimport { FrontendModule } from './createFrontendModule';\n\n/**\n * This symbol is used to pass parameter overrides from the extension override to the blueprint factory\n * @internal\n */\nexport const ctxParamsSymbol = Symbol('params');\n\n/**\n * Convert a single extension input into a matching resolved input.\n * @public\n */\nexport type ResolvedExtensionInput<TExtensionInput extends ExtensionInput> =\n TExtensionInput['extensionData'] extends Array<ExtensionDataRef>\n ? {\n node: AppNode;\n } & ExtensionDataContainer<TExtensionInput['extensionData'][number]>\n : never;\n\n/**\n * Converts an extension input map into a matching collection of resolved inputs.\n * @public\n */\nexport type ResolvedExtensionInputs<\n TInputs extends {\n [name in string]: ExtensionInput;\n },\n> = {\n [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton']\n ? Array<Expand<ResolvedExtensionInput<TInputs[InputName]>>>\n : false extends TInputs[InputName]['config']['optional']\n ? Expand<ResolvedExtensionInput<TInputs[InputName]>>\n : Expand<ResolvedExtensionInput<TInputs[InputName]> | undefined>;\n};\n\ntype ToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\ntype PopUnion<U> = ToIntersection<\n U extends any ? () => U : never\n> extends () => infer R\n ? [rest: Exclude<U, R>, next: R]\n : undefined;\n\n/** @ignore */\ntype JoinStringUnion<\n U,\n TDiv extends string = ', ',\n TResult extends string = '',\n> = PopUnion<U> extends [infer IRest extends string, infer INext extends string]\n ? TResult extends ''\n ? JoinStringUnion<IRest, TDiv, INext>\n : JoinStringUnion<IRest, TDiv, `${TResult}${TDiv}${INext}`>\n : TResult;\n\n/** @ignore */\nexport type RequiredExtensionIds<UExtensionData extends ExtensionDataRef> =\n UExtensionData extends any\n ? UExtensionData['config']['optional'] extends true\n ? never\n : UExtensionData['id']\n : never;\n\n/** @ignore */\nexport type VerifyExtensionFactoryOutput<\n UDeclaredOutput extends ExtensionDataRef,\n UFactoryOutput extends ExtensionDataValue<any, any>,\n> = [RequiredExtensionIds<UDeclaredOutput>] extends [UFactoryOutput['id']]\n ? [UFactoryOutput['id']] extends [UDeclaredOutput['id']]\n ? {}\n : `Error: The extension factory has undeclared output(s): ${JoinStringUnion<\n Exclude<UFactoryOutput['id'], UDeclaredOutput['id']>\n >}`\n : `Error: The extension factory is missing the following output(s): ${JoinStringUnion<\n Exclude<RequiredExtensionIds<UDeclaredOutput>, UFactoryOutput['id']>\n >}`;\n\n/** @ignore */\nexport type VerifyExtensionAttachTo<\n UOutput extends ExtensionDataRef,\n UParentInput extends ExtensionDataRef,\n> = ExtensionDataRef extends UParentInput\n ? {}\n : [RequiredExtensionIds<UParentInput>] extends [RequiredExtensionIds<UOutput>]\n ? {}\n : `Error: This parent extension input requires the following extension data, but it is not declared as guaranteed output of this extension: ${JoinStringUnion<\n Exclude<RequiredExtensionIds<UParentInput>, RequiredExtensionIds<UOutput>>\n >}`;\n\n/**\n * Specifies where an extension should attach in the extension tree.\n *\n * @remarks\n *\n * A standard attachment point declaration will specify the ID of the parent extension, as well as the name of the input to attach to.\n *\n * There are three more advanced forms that are available for more complex use-cases:\n *\n * 1. Relative attachment points: using the `relative` property instead of `id`, the attachment point is resolved relative to the current plugin.\n * 2. Extension input references: using a reference in code to another extension's input in the same plugin. These references are always relative.\n * 3. Array of attachment points: an array of attachment points can be used to clone and attach to multiple extensions at once.\n *\n * @example\n * ```ts\n * // Attach to a specific extension by full ID\n * { id: 'app/routes', input: 'routes' }\n *\n * // Attach to an extension in the same plugin by kind\n * { relative: { kind: 'page' }, input: 'actions' }\n *\n * // Attach to a specific input of another extension\n * const page = ParentBlueprint.make({ ... });\n * const child = ChildBlueprint.make({ attachTo: page.inputs.children });\n *\n * // Attach to multiple parents at once\n * [\n * { id: 'page/home', input: 'widgets' },\n * { relative: { kind: 'page' }, input: 'widgets' },\n * ]\n * ```\n *\n * @public\n */\nexport type ExtensionDefinitionAttachTo<\n UParentInputs extends ExtensionDataRef = ExtensionDataRef,\n> =\n | { id: string; input: string; relative?: never }\n | { relative: { kind?: string; name?: string }; input: string; id?: never }\n | ExtensionInput<UParentInputs>\n | Array<\n | { id: string; input: string; relative?: never }\n | {\n relative: { kind?: string; name?: string };\n input: string;\n id?: never;\n }\n | ExtensionInput<UParentInputs>\n >;\n\n/** @public */\nexport type CreateExtensionOptions<\n TKind extends string | undefined,\n TName extends string | undefined,\n UOutput extends ExtensionDataRef,\n TInputs extends { [inputName in string]: ExtensionInput },\n TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },\n UFactoryOutput extends ExtensionDataValue<any, any>,\n UParentInputs extends ExtensionDataRef,\n> = {\n kind?: TKind;\n name?: TName;\n attachTo: ExtensionDefinitionAttachTo<UParentInputs> &\n VerifyExtensionAttachTo<UOutput, UParentInputs>;\n disabled?: boolean;\n inputs?: TInputs;\n output: Array<UOutput>;\n config?: {\n schema: TConfigSchema;\n };\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: {\n [key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;\n };\n inputs: Expand<ResolvedExtensionInputs<TInputs>>;\n }): Iterable<UFactoryOutput>;\n} & VerifyExtensionFactoryOutput<UOutput, UFactoryOutput>;\n\n/** @public */\nexport type ExtensionDefinitionParameters = {\n kind?: string;\n name?: string;\n configInput?: { [K in string]: any };\n config?: { [K in string]: any };\n output?: ExtensionDataRef;\n inputs?: { [KName in string]: ExtensionInput };\n params?: object | ExtensionBlueprintDefineParams;\n};\n\n/**\n * Same as the one in `createExtensionBlueprint`, but with `ParamsFactory` inlined.\n * It can't be exported because it breaks API reports.\n * @ignore\n */\ntype AnyParamsInput<TParams extends object | ExtensionBlueprintDefineParams> =\n TParams extends ExtensionBlueprintDefineParams<infer IParams>\n ? IParams | ((define: TParams) => ReturnType<TParams>)\n :\n | TParams\n | ((\n define: ExtensionBlueprintDefineParams<TParams, TParams>,\n ) => ReturnType<ExtensionBlueprintDefineParams<TParams, TParams>>);\n\n/** @public */\nexport interface ExtensionDefinition<\n TParams extends ExtensionDefinitionParameters = ExtensionDefinitionParameters,\n> {\n $$type: '@backstage/ExtensionDefinition';\n readonly T: TParams;\n}\n\n/** @public */\nexport interface OverridableExtensionDefinition<\n T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters,\n> extends ExtensionDefinition<T> {\n /**\n * References to the inputs of this extension, which can be used to attach child extensions.\n */\n readonly inputs: {\n [K in keyof T['inputs']]: ExtensionInput<\n T['inputs'][K] extends ExtensionInput<infer IData> ? IData : never\n >;\n };\n\n override<\n TExtensionConfigSchema extends {\n [key in string]: (zImpl: typeof z) => z.ZodType;\n },\n UFactoryOutput extends ExtensionDataValue<any, any>,\n UNewOutput extends ExtensionDataRef,\n TExtraInputs extends { [inputName in string]: ExtensionInput },\n TParamsInput extends AnyParamsInput<NonNullable<T['params']>>,\n UParentInputs extends ExtensionDataRef,\n >(\n args: Expand<\n {\n attachTo?: ExtensionDefinitionAttachTo<UParentInputs> &\n VerifyExtensionAttachTo<\n ExtensionDataRef extends UNewOutput\n ? NonNullable<T['output']>\n : UNewOutput,\n UParentInputs\n >;\n disabled?: boolean;\n inputs?: TExtraInputs & {\n [KName in keyof T['inputs']]?: `Error: Input '${KName &\n string}' is already defined in parent definition`;\n };\n output?: Array<UNewOutput>;\n config?: {\n schema: TExtensionConfigSchema & {\n [KName in keyof T['config']]?: `Error: Config key '${KName &\n string}' is already defined in parent schema`;\n };\n };\n factory?(\n originalFactory: <\n TFactoryParamsReturn extends AnyParamsInput<\n NonNullable<T['params']>\n >,\n >(\n context?: Expand<\n {\n config?: T['config'];\n inputs?: ResolvedInputValueOverrides<NonNullable<T['inputs']>>;\n } & ([T['params']] extends [never]\n ? {}\n : {\n params?: TFactoryParamsReturn extends ExtensionBlueprintDefineParams\n ? TFactoryParamsReturn\n : T['params'] extends ExtensionBlueprintDefineParams\n ? 'Error: This blueprint uses advanced parameter types and requires you to pass parameters as using the following callback syntax: `originalFactory(defineParams => defineParams(<params>))`'\n : Partial<T['params']>;\n })\n >,\n ) => ExtensionDataContainer<NonNullable<T['output']>>,\n context: {\n node: AppNode;\n apis: ApiHolder;\n config: T['config'] & {\n [key in keyof TExtensionConfigSchema]: z.infer<\n ReturnType<TExtensionConfigSchema[key]>\n >;\n };\n inputs: Expand<ResolvedExtensionInputs<T['inputs'] & TExtraInputs>>;\n },\n ): Iterable<UFactoryOutput>;\n } & ([T['params']] extends [never]\n ? {}\n : {\n params?: TParamsInput extends ExtensionBlueprintDefineParams\n ? TParamsInput\n : T['params'] extends ExtensionBlueprintDefineParams\n ? 'Error: This blueprint uses advanced parameter types and requires you to pass parameters as using the following callback syntax: `originalFactory(defineParams => defineParams(<params>))`'\n : Partial<T['params']>;\n })\n > &\n VerifyExtensionFactoryOutput<\n ExtensionDataRef extends UNewOutput\n ? NonNullable<T['output']>\n : UNewOutput,\n UFactoryOutput\n >,\n ): OverridableExtensionDefinition<{\n kind: T['kind'];\n name: T['name'];\n output: ExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput;\n inputs: T['inputs'] & TExtraInputs;\n config: T['config'] & {\n [key in keyof TExtensionConfigSchema]: z.infer<\n ReturnType<TExtensionConfigSchema[key]>\n >;\n };\n configInput: T['configInput'] &\n z.input<\n z.ZodObject<{\n [key in keyof TExtensionConfigSchema]: ReturnType<\n TExtensionConfigSchema[key]\n >;\n }>\n >;\n }>;\n}\n\n/**\n * @internal\n */\nfunction bindInputs(\n inputs: { [inputName in string]: ExtensionInput } | undefined,\n kind?: string,\n name?: string,\n) {\n if (!inputs) {\n return {};\n }\n\n return Object.fromEntries(\n Object.entries(inputs).map(([inputName, input]) => [\n inputName,\n OpaqueExtensionInput.toInternal(input).withContext?.({\n kind,\n name,\n input: inputName,\n }) ?? input,\n ]),\n );\n}\n\n/**\n * Creates a new extension definition for installation in a Backstage app.\n *\n * @remarks\n *\n * This is a low-level function for creation of extensions with arbitrary inputs\n * and outputs and is typically only intended to be used for advanced overrides\n * or framework-level extensions. For most extension creation needs, it is\n * recommended to use existing {@link ExtensionBlueprint}s instead. You can find\n * blueprints both in the `@backstage/frontend-plugin-api` package as well as\n * other plugin libraries. There is also a list of\n * {@link https://backstage.io/docs/frontend-system/building-plugins/common-extension-blueprints | commonly used blueprints}\n * in the frontend system documentation.\n *\n * Extension definitions that are created with this function can be installed in\n * a Backstage app via a {@link FrontendPlugin} or {@link FrontendModule}.\n *\n * For more details on how extensions work, see the\n * {@link https://backstage.io/docs/frontend-system/architecture/extensions | documentation for extensions}.\n *\n * @example\n *\n * ```ts\n * const myExtension = createExtension({\n * name: 'example',\n * attachTo: { id: 'app', input: 'root' },\n * output: [coreExtensionData.reactElement],\n * factory() {\n * return [coreExtensionData.reactElement(<h1>Hello, world!</h1>)];\n * },\n * });\n * ```\n *\n * @public\n */\nexport function createExtension<\n UOutput extends ExtensionDataRef,\n TInputs extends { [inputName in string]: ExtensionInput },\n TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType },\n UFactoryOutput extends ExtensionDataValue<any, any>,\n const TKind extends string | undefined = undefined,\n const TName extends string | undefined = undefined,\n UParentInputs extends ExtensionDataRef = ExtensionDataRef,\n>(\n options: CreateExtensionOptions<\n TKind,\n TName,\n UOutput,\n TInputs,\n TConfigSchema,\n UFactoryOutput,\n UParentInputs\n >,\n): OverridableExtensionDefinition<{\n config: string extends keyof TConfigSchema\n ? {}\n : {\n [key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;\n };\n configInput: string extends keyof TConfigSchema\n ? {}\n : z.input<\n z.ZodObject<{\n [key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;\n }>\n >;\n // This inference and remapping back to ExtensionDataRef eliminates any occurrences ConfigurationExtensionDataRef\n output: UOutput extends ExtensionDataRef<\n infer IData,\n infer IId,\n infer IConfig\n >\n ? ExtensionDataRef<IData, IId, IConfig>\n : never;\n inputs: TInputs;\n params: never;\n kind: string | undefined extends TKind ? undefined : TKind;\n name: string | undefined extends TName ? undefined : TName;\n}> {\n const schemaDeclaration = options.config?.schema;\n const configSchema =\n schemaDeclaration &&\n createSchemaFromZod(innerZ =>\n innerZ.object(\n Object.fromEntries(\n Object.entries(schemaDeclaration).map(([k, v]) => [k, v(innerZ)]),\n ),\n ),\n );\n\n return OpaqueExtensionDefinition.createInstance('v2', {\n T: undefined as unknown as {\n config: string extends keyof TConfigSchema\n ? {}\n : {\n [key in keyof TConfigSchema]: z.infer<\n ReturnType<TConfigSchema[key]>\n >;\n };\n configInput: string extends keyof TConfigSchema\n ? {}\n : z.input<\n z.ZodObject<{\n [key in keyof TConfigSchema]: ReturnType<TConfigSchema[key]>;\n }>\n >;\n output: UOutput;\n inputs: TInputs;\n kind: string | undefined extends TKind ? undefined : TKind;\n name: string | undefined extends TName ? undefined : TName;\n },\n kind: options.kind,\n name: options.name,\n attachTo: options.attachTo,\n disabled: options.disabled ?? false,\n inputs: bindInputs(options.inputs, options.kind, options.name),\n output: options.output,\n configSchema,\n factory: options.factory,\n toString() {\n const parts: string[] = [];\n if (options.kind) {\n parts.push(`kind=${options.kind}`);\n }\n if (options.name) {\n parts.push(`name=${options.name}`);\n }\n const attachTo = [options.attachTo]\n .flat()\n .map(aAny => {\n const a = aAny as ExtensionDefinitionAttachTo;\n if (OpaqueExtensionInput.isType(a)) {\n const { context } = OpaqueExtensionInput.toInternal(a);\n if (!context) {\n return '<detached-input>';\n }\n let id = '<plugin>';\n if (context?.kind) {\n id = `${context?.kind}:${id}`;\n }\n if (context?.name) {\n id = `${id}/${context?.name}`;\n }\n return `${id}@${context.input}`;\n }\n if ('relative' in a && a.relative) {\n let id = '<plugin>';\n if (a.relative.kind) {\n id = `${a.relative.kind}:${id}`;\n }\n if (a.relative.name) {\n id = `${id}/${a.relative.name}`;\n }\n return `${id}@${a.input}`;\n }\n if ('id' in a) {\n return `${a.id}@${a.input}`;\n }\n throw new Error('Invalid attachment point specification');\n })\n .join('+');\n parts.push(`attachTo=${attachTo}`);\n return `ExtensionDefinition{${parts.join(',')}}`;\n },\n override(overrideOptions) {\n if (!Array.isArray(options.output)) {\n throw new Error(\n 'Cannot override an extension that is not declared using the new format with outputs as an array',\n );\n }\n\n // TODO(Rugvip): Making this a type check would be optimal, but it seems\n // like it's tricky to add that and still have the type\n // inference work correctly for the factory output.\n if (overrideOptions.output && !overrideOptions.factory) {\n throw new Error(\n 'Refused to override output without also overriding factory',\n );\n }\n // TODO(Rugvip): Similar to above, would be nice to error during type checking, but don't want to complicate the types too much\n if (overrideOptions.params && overrideOptions.factory) {\n throw new Error(\n 'Refused to override params and factory at the same time',\n );\n }\n\n return createExtension({\n kind: options.kind,\n name: options.name,\n attachTo: (overrideOptions.attachTo ??\n options.attachTo) as ExtensionDefinitionAttachTo,\n disabled: overrideOptions.disabled ?? options.disabled,\n inputs: bindInputs(\n {\n ...(options.inputs ?? {}),\n ...(overrideOptions.inputs ?? {}),\n },\n options.kind,\n options.name,\n ),\n output: (overrideOptions.output ??\n options.output) as ExtensionDataRef[],\n config:\n options.config || overrideOptions.config\n ? {\n schema: {\n ...options.config?.schema,\n ...overrideOptions.config?.schema,\n },\n }\n : undefined,\n factory: ({ node, apis, config, inputs }) => {\n if (!overrideOptions.factory) {\n return options.factory({\n node,\n apis,\n config: config as any,\n inputs: inputs as any,\n [ctxParamsSymbol as any]: overrideOptions.params,\n });\n }\n const parentResult = overrideOptions.factory(\n (innerContext): ExtensionDataContainer<UOutput> => {\n return createExtensionDataContainer<UOutput>(\n options.factory({\n node,\n apis,\n config: (innerContext?.config ?? config) as any,\n inputs: resolveInputOverrides(\n options.inputs,\n inputs,\n innerContext?.inputs,\n ) as any,\n [ctxParamsSymbol as any]: innerContext?.params,\n }) as Iterable<any>,\n 'original extension factory',\n options.output,\n );\n },\n {\n node,\n apis,\n config: config as any,\n inputs: inputs as any,\n },\n );\n\n if (\n typeof parentResult !== 'object' ||\n !parentResult?.[Symbol.iterator]\n ) {\n throw new Error(\n 'extension factory override did not provide an iterable object',\n );\n }\n\n const deduplicatedResult = new Map<\n string,\n ExtensionDataValue<any, any>\n >();\n for (const item of parentResult) {\n deduplicatedResult.set(item.id, item);\n }\n\n return deduplicatedResult.values();\n },\n }) as OverridableExtensionDefinition<any>;\n },\n });\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/frontend-plugin-api",
|
|
3
|
-
"version": "0.13.0",
|
|
3
|
+
"version": "0.13.2-next.0",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "web-library"
|
|
6
6
|
},
|
|
7
7
|
"publishConfig": {
|
|
8
|
-
"access": "public"
|
|
9
|
-
"main": "dist/index.esm.js",
|
|
10
|
-
"types": "dist/index.d.ts"
|
|
8
|
+
"access": "public"
|
|
11
9
|
},
|
|
12
10
|
"repository": {
|
|
13
11
|
"type": "git",
|
|
@@ -16,8 +14,23 @@
|
|
|
16
14
|
},
|
|
17
15
|
"license": "Apache-2.0",
|
|
18
16
|
"sideEffects": false,
|
|
19
|
-
"
|
|
20
|
-
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"import": "./dist/index.esm.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.esm.js"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.esm.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"typesVersions": {
|
|
28
|
+
"*": {
|
|
29
|
+
"package.json": [
|
|
30
|
+
"package.json"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
},
|
|
21
34
|
"files": [
|
|
22
35
|
"dist"
|
|
23
36
|
],
|
|
@@ -31,20 +44,21 @@
|
|
|
31
44
|
"test": "backstage-cli package test"
|
|
32
45
|
},
|
|
33
46
|
"dependencies": {
|
|
34
|
-
"@backstage/
|
|
35
|
-
"@backstage/core-
|
|
36
|
-
"@backstage/
|
|
37
|
-
"@backstage/
|
|
47
|
+
"@backstage/config": "1.3.6",
|
|
48
|
+
"@backstage/core-components": "0.18.4-next.0",
|
|
49
|
+
"@backstage/errors": "1.2.7",
|
|
50
|
+
"@backstage/types": "1.2.2",
|
|
51
|
+
"@backstage/version-bridge": "1.0.11",
|
|
38
52
|
"@material-ui/core": "^4.12.4",
|
|
39
53
|
"lodash": "^4.17.21",
|
|
40
54
|
"zod": "^3.22.4",
|
|
41
55
|
"zod-to-json-schema": "^3.21.4"
|
|
42
56
|
},
|
|
43
57
|
"devDependencies": {
|
|
44
|
-
"@backstage/cli": "
|
|
45
|
-
"@backstage/frontend-app-api": "
|
|
46
|
-
"@backstage/frontend-test-utils": "
|
|
47
|
-
"@backstage/test-utils": "
|
|
58
|
+
"@backstage/cli": "0.34.6-next.0",
|
|
59
|
+
"@backstage/frontend-app-api": "0.13.3-next.0",
|
|
60
|
+
"@backstage/frontend-test-utils": "0.4.2-next.0",
|
|
61
|
+
"@backstage/test-utils": "1.7.14-next.0",
|
|
48
62
|
"@testing-library/jest-dom": "^6.0.0",
|
|
49
63
|
"@testing-library/react": "^16.0.0",
|
|
50
64
|
"@types/react": "^18.0.0",
|
|
@@ -64,12 +78,5 @@
|
|
|
64
78
|
"optional": true
|
|
65
79
|
}
|
|
66
80
|
},
|
|
67
|
-
"typesVersions": {
|
|
68
|
-
"*": {
|
|
69
|
-
"package.json": [
|
|
70
|
-
"package.json"
|
|
71
|
-
]
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
81
|
"module": "./dist/index.esm.js"
|
|
75
82
|
}
|