@backstage/frontend-app-api 0.8.0 → 0.9.0-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.
Files changed (70) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/core-app-api/src/apis/system/ApiRegistry.esm.js +50 -0
  3. package/dist/core-app-api/src/apis/system/ApiRegistry.esm.js.map +1 -0
  4. package/dist/extensions/Root.esm.js +19 -0
  5. package/dist/extensions/Root.esm.js.map +1 -0
  6. package/dist/frontend-plugin-api/src/wiring/createExtension.esm.js.map +1 -1
  7. package/dist/frontend-plugin-api/src/wiring/createExtensionOverrides.esm.js.map +1 -1
  8. package/dist/frontend-plugin-api/src/wiring/createFrontendPlugin.esm.js.map +1 -1
  9. package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -1
  10. package/dist/index.d.ts +2 -10
  11. package/dist/tree/instantiateAppNodeTree.esm.js +8 -5
  12. package/dist/tree/instantiateAppNodeTree.esm.js.map +1 -1
  13. package/dist/tree/resolveAppNodeSpecs.esm.js.map +1 -1
  14. package/dist/tree/resolveAppTree.esm.js +40 -20
  15. package/dist/tree/resolveAppTree.esm.js.map +1 -1
  16. package/dist/wiring/createApp.esm.js +113 -164
  17. package/dist/wiring/createApp.esm.js.map +1 -1
  18. package/package.json +5 -4
  19. package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js +0 -30
  20. package/dist/apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js.map +0 -1
  21. package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js +0 -15
  22. package/dist/apis/implementations/IconsApi/DefaultIconsApi.esm.js.map +0 -1
  23. package/dist/app/src/components/Root/LogoFull.esm.js +0 -33
  24. package/dist/app/src/components/Root/LogoFull.esm.js.map +0 -1
  25. package/dist/app/src/components/Root/LogoIcon.esm.js +0 -33
  26. package/dist/app/src/components/Root/LogoIcon.esm.js.map +0 -1
  27. package/dist/app-defaults/src/defaults/apis.esm.js +0 -221
  28. package/dist/app-defaults/src/defaults/apis.esm.js.map +0 -1
  29. package/dist/app-defaults/src/defaults/components.esm.js +0 -46
  30. package/dist/app-defaults/src/defaults/components.esm.js.map +0 -1
  31. package/dist/app-defaults/src/defaults/icons.esm.js +0 -51
  32. package/dist/app-defaults/src/defaults/icons.esm.js.map +0 -1
  33. package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js +0 -89
  34. package/dist/core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js.map +0 -1
  35. package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js +0 -76
  36. package/dist/core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js.map +0 -1
  37. package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js +0 -234
  38. package/dist/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js.map +0 -1
  39. package/dist/core-app-api/src/app/AppThemeProvider.esm.js +0 -60
  40. package/dist/core-app-api/src/app/AppThemeProvider.esm.js.map +0 -1
  41. package/dist/core-app-api/src/app/isProtectedApp.esm.js +0 -8
  42. package/dist/core-app-api/src/app/isProtectedApp.esm.js.map +0 -1
  43. package/dist/core-app-api/src/lib/subjects.esm.js +0 -69
  44. package/dist/core-app-api/src/lib/subjects.esm.js.map +0 -1
  45. package/dist/core-plugin-api/src/translation/TranslationRef.esm.js +0 -13
  46. package/dist/core-plugin-api/src/translation/TranslationRef.esm.js.map +0 -1
  47. package/dist/core-plugin-api/src/translation/TranslationResource.esm.js +0 -13
  48. package/dist/core-plugin-api/src/translation/TranslationResource.esm.js.map +0 -1
  49. package/dist/extensions/App.esm.js +0 -31
  50. package/dist/extensions/App.esm.js.map +0 -1
  51. package/dist/extensions/AppLayout.esm.js +0 -26
  52. package/dist/extensions/AppLayout.esm.js.map +0 -1
  53. package/dist/extensions/AppNav.esm.js +0 -66
  54. package/dist/extensions/AppNav.esm.js.map +0 -1
  55. package/dist/extensions/AppRoot.esm.js +0 -126
  56. package/dist/extensions/AppRoot.esm.js.map +0 -1
  57. package/dist/extensions/AppRoutes.esm.js +0 -39
  58. package/dist/extensions/AppRoutes.esm.js.map +0 -1
  59. package/dist/extensions/components.esm.js +0 -52
  60. package/dist/extensions/components.esm.js.map +0 -1
  61. package/dist/extensions/elements.esm.js +0 -32
  62. package/dist/extensions/elements.esm.js.map +0 -1
  63. package/dist/extensions/themes.esm.js +0 -33
  64. package/dist/extensions/themes.esm.js.map +0 -1
  65. package/dist/routing/RouteTracker.esm.js +0 -69
  66. package/dist/routing/RouteTracker.esm.js.map +0 -1
  67. package/dist/tree/createAppTree.esm.js +0 -21
  68. package/dist/tree/createAppTree.esm.js.map +0 -1
  69. package/dist/wiring/InternalAppContext.esm.js +0 -6
  70. package/dist/wiring/InternalAppContext.esm.js.map +0 -1
@@ -1,3 +1,5 @@
1
+ import { toInternalExtension } from '../frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js';
2
+
1
3
  function indent(str) {
2
4
  return str.replace(/^/gm, " ");
3
5
  }
@@ -11,8 +13,7 @@ class SerializableAppNode {
11
13
  constructor(spec) {
12
14
  this.spec = spec;
13
15
  }
14
- setParent(parent) {
15
- const input = this.spec.attachTo.input;
16
+ setParent(parent, input) {
16
17
  this.edges.attachedTo = { node: parent, input };
17
18
  const parentInputEdges = parent.edges.attachments.get(input);
18
19
  if (parentInputEdges) {
@@ -44,36 +45,55 @@ class SerializableAppNode {
44
45
  ].join("\n");
45
46
  }
46
47
  }
48
+ function makeRedirectKey(attachTo) {
49
+ return `${attachTo.id}%${attachTo.input}`;
50
+ }
51
+ const isValidAttachmentPoint = (attachTo, nodes) => {
52
+ if (!nodes.has(attachTo.id)) {
53
+ return false;
54
+ }
55
+ return attachTo.input in toInternalExtension(nodes.get(attachTo.id).spec.extension).inputs;
56
+ };
47
57
  function resolveAppTree(rootNodeId, specs) {
48
58
  const nodes = /* @__PURE__ */ new Map();
49
- let rootNode = void 0;
50
- const orphansByParent = /* @__PURE__ */ new Map();
59
+ const redirectTargetsByKey = /* @__PURE__ */ new Map();
51
60
  for (const spec of specs) {
52
61
  if (nodes.has(spec.id)) {
53
62
  throw new Error(`Unexpected duplicate extension id '${spec.id}'`);
54
63
  }
55
64
  const node = new SerializableAppNode(spec);
56
65
  nodes.set(spec.id, node);
66
+ const internal = toInternalExtension(spec.extension);
67
+ for (const [inputName, input] of Object.entries(internal.inputs)) {
68
+ if (input.replaces) {
69
+ for (const replace of input.replaces) {
70
+ const key = makeRedirectKey(replace);
71
+ if (redirectTargetsByKey.has(key)) {
72
+ throw new Error(
73
+ `Duplicate redirect target for input '${inputName}' in extension '${spec.id}'`
74
+ );
75
+ }
76
+ redirectTargetsByKey.set(key, { id: spec.id, input: inputName });
77
+ }
78
+ }
79
+ }
80
+ }
81
+ const orphans = new Array();
82
+ let rootNode = void 0;
83
+ for (const node of nodes.values()) {
84
+ const spec = node.spec;
57
85
  if (spec.id === rootNodeId) {
58
86
  rootNode = node;
59
87
  } else {
60
- const parent = nodes.get(spec.attachTo.id);
88
+ let attachTo = node.spec.attachTo;
89
+ if (!isValidAttachmentPoint(attachTo, nodes)) {
90
+ attachTo = redirectTargetsByKey.get(makeRedirectKey(attachTo)) ?? attachTo;
91
+ }
92
+ const parent = nodes.get(attachTo.id);
61
93
  if (parent) {
62
- node.setParent(parent);
94
+ node.setParent(parent, attachTo.input);
63
95
  } else {
64
- const orphanNodesForParent = orphansByParent.get(spec.attachTo.id);
65
- if (orphanNodesForParent) {
66
- orphanNodesForParent.push(node);
67
- } else {
68
- orphansByParent.set(spec.attachTo.id, [node]);
69
- }
70
- }
71
- }
72
- const orphanedChildren = orphansByParent.get(spec.id);
73
- if (orphanedChildren) {
74
- orphansByParent.delete(spec.id);
75
- for (const orphan of orphanedChildren) {
76
- orphan.setParent(node);
96
+ orphans.push(node);
77
97
  }
78
98
  }
79
99
  }
@@ -83,7 +103,7 @@ function resolveAppTree(rootNodeId, specs) {
83
103
  return {
84
104
  root: rootNode,
85
105
  nodes,
86
- orphans: Array.from(orphansByParent.values()).flat()
106
+ orphans
87
107
  };
88
108
  }
89
109
 
@@ -1 +1 @@
1
- {"version":3,"file":"resolveAppTree.esm.js","sources":["../../src/tree/resolveAppTree.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 AppTree,\n AppNode,\n AppNodeInstance,\n AppNodeSpec,\n} from '@backstage/frontend-plugin-api';\n\nfunction indent(str: string) {\n return str.replace(/^/gm, ' ');\n}\n\n/** @internal */\nclass SerializableAppNode implements AppNode {\n public readonly spec: AppNodeSpec;\n public readonly edges = {\n attachedTo: undefined as { node: AppNode; input: string } | undefined,\n attachments: new Map<string, SerializableAppNode[]>(),\n };\n public readonly instance?: AppNodeInstance;\n\n constructor(spec: AppNodeSpec) {\n this.spec = spec;\n }\n\n setParent(parent: SerializableAppNode) {\n const input = this.spec.attachTo.input;\n\n this.edges.attachedTo = { node: parent, input };\n\n const parentInputEdges = parent.edges.attachments.get(input);\n if (parentInputEdges) {\n parentInputEdges.push(this);\n } else {\n parent.edges.attachments.set(input, [this]);\n }\n }\n\n toJSON() {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n return {\n id: this.spec.id,\n output:\n dataRefs && dataRefs.length > 0\n ? dataRefs.map(ref => ref.id)\n : undefined,\n attachments:\n this.edges.attachments.size > 0\n ? Object.fromEntries(this.edges.attachments)\n : undefined,\n };\n }\n\n toString(): string {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n const out =\n dataRefs && dataRefs.length > 0\n ? ` out=[${[...dataRefs].map(r => r.id).join(', ')}]`\n : '';\n\n if (this.edges.attachments.size === 0) {\n return `<${this.spec.id}${out} />`;\n }\n\n return [\n `<${this.spec.id}${out}>`,\n ...[...this.edges.attachments.entries()].map(([k, v]) =>\n indent([`${k} [`, ...v.map(e => indent(e.toString())), `]`].join('\\n')),\n ),\n `</${this.spec.id}>`,\n ].join('\\n');\n }\n}\n\n/**\n * Build the app tree by iterating through all node specs and constructing the app\n * tree with all attachments in the same order as they appear in the input specs array.\n * @internal\n */\nexport function resolveAppTree(\n rootNodeId: string,\n specs: AppNodeSpec[],\n): AppTree {\n const nodes = new Map<string, SerializableAppNode>();\n\n // A node with the provided rootNodeId must be found in the tree, and it must not be attached to anything\n let rootNode: AppNode | undefined = undefined;\n\n // While iterating through the inputs specs we keep track of all nodes that were created\n // before their parent, and attach them later when the parent is created.\n // As we find the parents and attach the children, we remove them from this map. This means\n // that after iterating through all input specs, this will be a map for each root node.\n const orphansByParent = new Map<\n string /* parentId */,\n SerializableAppNode[]\n >();\n\n for (const spec of specs) {\n // The main check with a more helpful error message happens in resolveAppNodeSpecs\n if (nodes.has(spec.id)) {\n throw new Error(`Unexpected duplicate extension id '${spec.id}'`);\n }\n\n const node = new SerializableAppNode(spec);\n nodes.set(spec.id, node);\n\n // TODO: For now we simply ignore the attachTo spec of the root node, but it'd be cleaner if we could avoid defining it\n if (spec.id === rootNodeId) {\n rootNode = node;\n } else {\n const parent = nodes.get(spec.attachTo.id);\n if (parent) {\n node.setParent(parent);\n } else {\n const orphanNodesForParent = orphansByParent.get(spec.attachTo.id);\n if (orphanNodesForParent) {\n orphanNodesForParent.push(node);\n } else {\n orphansByParent.set(spec.attachTo.id, [node]);\n }\n }\n }\n\n const orphanedChildren = orphansByParent.get(spec.id);\n if (orphanedChildren) {\n orphansByParent.delete(spec.id);\n for (const orphan of orphanedChildren) {\n orphan.setParent(node);\n }\n }\n }\n\n if (!rootNode) {\n throw new Error(`No root node with id '${rootNodeId}' found in app tree`);\n }\n\n return {\n root: rootNode,\n nodes,\n orphans: Array.from(orphansByParent.values()).flat(),\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolveAppTree.esm.js","sources":["../../src/tree/resolveAppTree.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 AppTree,\n AppNode,\n AppNodeInstance,\n AppNodeSpec,\n} from '@backstage/frontend-plugin-api';\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nfunction indent(str: string) {\n return str.replace(/^/gm, ' ');\n}\n\n/** @internal */\nclass SerializableAppNode implements AppNode {\n public readonly spec: AppNodeSpec;\n public readonly edges = {\n attachedTo: undefined as { node: AppNode; input: string } | undefined,\n attachments: new Map<string, SerializableAppNode[]>(),\n };\n public readonly instance?: AppNodeInstance;\n\n constructor(spec: AppNodeSpec) {\n this.spec = spec;\n }\n\n setParent(parent: SerializableAppNode, input: string) {\n this.edges.attachedTo = { node: parent, input };\n\n const parentInputEdges = parent.edges.attachments.get(input);\n if (parentInputEdges) {\n parentInputEdges.push(this);\n } else {\n parent.edges.attachments.set(input, [this]);\n }\n }\n\n toJSON() {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n return {\n id: this.spec.id,\n output:\n dataRefs && dataRefs.length > 0\n ? dataRefs.map(ref => ref.id)\n : undefined,\n attachments:\n this.edges.attachments.size > 0\n ? Object.fromEntries(this.edges.attachments)\n : undefined,\n };\n }\n\n toString(): string {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n const out =\n dataRefs && dataRefs.length > 0\n ? ` out=[${[...dataRefs].map(r => r.id).join(', ')}]`\n : '';\n\n if (this.edges.attachments.size === 0) {\n return `<${this.spec.id}${out} />`;\n }\n\n return [\n `<${this.spec.id}${out}>`,\n ...[...this.edges.attachments.entries()].map(([k, v]) =>\n indent([`${k} [`, ...v.map(e => indent(e.toString())), `]`].join('\\n')),\n ),\n `</${this.spec.id}>`,\n ].join('\\n');\n }\n}\n\nfunction makeRedirectKey(attachTo: { id: string; input: string }) {\n return `${attachTo.id}%${attachTo.input}`;\n}\n\nconst isValidAttachmentPoint = (\n attachTo: { id: string; input: string },\n nodes: Map<string, SerializableAppNode>,\n) => {\n if (!nodes.has(attachTo.id)) {\n return false;\n }\n\n return (\n attachTo.input in\n toInternalExtension(nodes.get(attachTo.id)!.spec.extension).inputs\n );\n};\n\n/**\n * Build the app tree by iterating through all node specs and constructing the app\n * tree with all attachments in the same order as they appear in the input specs array.\n * @internal\n */\nexport function resolveAppTree(\n rootNodeId: string,\n specs: AppNodeSpec[],\n): AppTree {\n const nodes = new Map<string, SerializableAppNode>();\n\n const redirectTargetsByKey = new Map<string, { id: string; input: string }>();\n\n for (const spec of specs) {\n // The main check with a more helpful error message happens in resolveAppNodeSpecs\n if (nodes.has(spec.id)) {\n throw new Error(`Unexpected duplicate extension id '${spec.id}'`);\n }\n\n const node = new SerializableAppNode(spec);\n nodes.set(spec.id, node);\n\n const internal = toInternalExtension(spec.extension);\n for (const [inputName, input] of Object.entries(internal.inputs)) {\n if (input.replaces) {\n for (const replace of input.replaces) {\n const key = makeRedirectKey(replace);\n if (redirectTargetsByKey.has(key)) {\n throw new Error(\n `Duplicate redirect target for input '${inputName}' in extension '${spec.id}'`,\n );\n }\n redirectTargetsByKey.set(key, { id: spec.id, input: inputName });\n }\n }\n }\n }\n\n const orphans = new Array<SerializableAppNode>();\n\n // A node with the provided rootNodeId must be found in the tree, and it must not be attached to anything\n let rootNode: AppNode | undefined = undefined;\n\n for (const node of nodes.values()) {\n const spec = node.spec;\n\n // TODO: For now we simply ignore the attachTo spec of the root node, but it'd be cleaner if we could avoid defining it\n if (spec.id === rootNodeId) {\n rootNode = node;\n } else {\n let attachTo = node.spec.attachTo;\n\n if (!isValidAttachmentPoint(attachTo, nodes)) {\n attachTo =\n redirectTargetsByKey.get(makeRedirectKey(attachTo)) ?? attachTo;\n }\n\n const parent = nodes.get(attachTo.id);\n if (parent) {\n node.setParent(parent, attachTo.input);\n } else {\n orphans.push(node);\n }\n }\n }\n\n if (!rootNode) {\n throw new Error(`No root node with id '${rootNodeId}' found in app tree`);\n }\n\n return {\n root: rootNode,\n nodes,\n orphans,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -1,66 +1,29 @@
1
1
  import React from 'react';
2
2
  import { ConfigReader } from '@backstage/config';
3
- import { ApiBlueprint, coreExtensionData, ThemeBlueprint, TranslationBlueprint, IconBundleBlueprint, appTreeApiRef, routeResolutionApiRef, componentsApiRef, iconsApiRef } from '@backstage/frontend-plugin-api';
4
- import { App } from '../extensions/App.esm.js';
5
- import { AppRoutes } from '../extensions/AppRoutes.esm.js';
6
- import { AppLayout } from '../extensions/AppLayout.esm.js';
7
- import { AppNav } from '../extensions/AppNav.esm.js';
8
- import { discoveryApiRef, errorApiRef, fetchApiRef, featureFlagsApiRef, identityApiRef, appThemeApiRef, configApiRef } from '@backstage/core-plugin-api';
3
+ import { createApiFactory, appTreeApiRef, routeResolutionApiRef, coreExtensionData, ApiBlueprint } from '@backstage/frontend-plugin-api';
4
+ import { configApiRef, identityApiRef, featureFlagsApiRef } from '@backstage/core-plugin-api';
9
5
  import { getAvailableFeatures } from './discovery.esm.js';
10
- import { ApiFactoryRegistry, AppThemeSelector, ApiResolver, ApiProvider } from '@backstage/core-app-api';
11
- import { isProtectedApp } from '../core-app-api/src/app/isProtectedApp.esm.js';
12
- import { AppThemeProvider } from '../core-app-api/src/app/AppThemeProvider.esm.js';
13
- import { AppIdentityProxy } from '../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js';
14
- import { LocalStorageFeatureFlags } from '../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags.esm.js';
6
+ import { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';
15
7
  import { defaultConfigLoaderSync } from '../core-app-api/src/app/defaultConfigLoader.esm.js';
16
8
  import { overrideBaseUrlConfigs } from '../core-app-api/src/app/overrideBaseUrlConfigs.esm.js';
17
- import { AppLanguageSelector } from '../core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector.esm.js';
18
- import { I18nextTranslationApi } from '../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.esm.js';
19
9
  import { resolveExtensionDefinition } from '../frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js';
20
- import { apis } from '../app-defaults/src/defaults/apis.esm.js';
21
- import '@material-ui/core/Button';
22
- import '@backstage/core-components';
23
- import 'react-router-dom';
24
- import { icons } from '../app-defaults/src/defaults/icons.esm.js';
25
- import '@backstage/theme';
26
- import '@material-ui/icons/Brightness2';
27
- import '@material-ui/icons/WbSunny';
28
- import { LightTheme, DarkTheme } from '../extensions/themes.esm.js';
29
- import { oauthRequestDialogAppRootElement, alertDisplayAppRootElement } from '../extensions/elements.esm.js';
30
10
  import { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode.esm.js';
31
- import { appLanguageApiRef, translationApiRef } from '@backstage/core-plugin-api/alpha';
32
11
  import { RouteResolver } from '../routing/RouteResolver.esm.js';
33
12
  import { resolveRouteBindings } from '../routing/resolveRouteBindings.esm.js';
34
13
  import { collectRouteIds } from '../routing/collectRouteIds.esm.js';
35
- import { createAppTree } from '../tree/createAppTree.esm.js';
36
- import { DefaultProgressComponent, DefaultErrorBoundaryComponent, DefaultNotFoundErrorPageComponent } from '../extensions/components.esm.js';
37
- import { InternalAppContext } from './InternalAppContext.esm.js';
38
- import { AppRoot } from '../extensions/AppRoot.esm.js';
39
14
  import { toInternalBackstagePlugin } from '../frontend-plugin-api/src/wiring/createFrontendPlugin.esm.js';
40
15
  import { toInternalExtensionOverrides } from '../frontend-plugin-api/src/wiring/createExtensionOverrides.esm.js';
41
- import { DefaultComponentsApi } from '../apis/implementations/ComponentsApi/DefaultComponentsApi.esm.js';
42
- import { DefaultIconsApi } from '../apis/implementations/IconsApi/DefaultIconsApi.esm.js';
43
16
  import { stringifyError } from '@backstage/errors';
44
17
  import { getBasePath } from '../routing/getBasePath.esm.js';
18
+ import { Root } from '../extensions/Root.esm.js';
19
+ import { resolveAppTree } from '../tree/resolveAppTree.esm.js';
20
+ import { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs.esm.js';
21
+ import { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig.esm.js';
22
+ import { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree.esm.js';
23
+ import { ApiRegistry } from '../core-app-api/src/apis/system/ApiRegistry.esm.js';
24
+ import { AppIdentityProxy } from '../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js';
25
+ import appPlugin from '@backstage/plugin-app';
45
26
 
46
- const DefaultApis = apis.map(
47
- (factory) => ApiBlueprint.make({ namespace: factory.api.id, params: { factory } })
48
- );
49
- const builtinExtensions = [
50
- App,
51
- AppRoot,
52
- AppRoutes,
53
- AppNav,
54
- AppLayout,
55
- DefaultProgressComponent,
56
- DefaultErrorBoundaryComponent,
57
- DefaultNotFoundErrorPageComponent,
58
- LightTheme,
59
- DarkTheme,
60
- oauthRequestDialogAppRootElement,
61
- alertDisplayAppRootElement,
62
- ...DefaultApis
63
- ].map((def) => resolveExtensionDefinition(def));
64
27
  function deduplicateFeatures(allFeatures) {
65
28
  const features = Array.from(new Set(allFeatures));
66
29
  const seenIds = /* @__PURE__ */ new Set();
@@ -103,7 +66,6 @@ function createApp(options) {
103
66
  }
104
67
  }
105
68
  const app = createSpecializedApp({
106
- icons: options?.icons,
107
69
  config,
108
70
  features: [...discoveredFeatures, ...providedFeatures],
109
71
  bindRoutes: options?.bindRoutes
@@ -117,52 +79,94 @@ function createApp(options) {
117
79
  }
118
80
  };
119
81
  }
82
+ class AppTreeApiProxy {
83
+ constructor(tree) {
84
+ this.tree = tree;
85
+ }
86
+ #safeToUse = false;
87
+ getTree() {
88
+ if (!this.#safeToUse) {
89
+ throw new Error(
90
+ `You can't access the AppTreeApi during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`
91
+ );
92
+ }
93
+ return { tree: this.tree };
94
+ }
95
+ initialize() {
96
+ this.#safeToUse = true;
97
+ }
98
+ }
99
+ class RouteResolutionApiProxy {
100
+ constructor(tree, routeBindings, basePath) {
101
+ this.tree = tree;
102
+ this.routeBindings = routeBindings;
103
+ this.basePath = basePath;
104
+ }
105
+ #delegate;
106
+ #routeObjects;
107
+ resolve(anyRouteRef, options) {
108
+ if (!this.#delegate) {
109
+ throw new Error(
110
+ `You can't access the RouteResolver during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`
111
+ );
112
+ }
113
+ return this.#delegate.resolve(anyRouteRef, options);
114
+ }
115
+ initialize() {
116
+ const routeInfo = extractRouteInfoFromAppNode(this.tree.root);
117
+ this.#delegate = new RouteResolver(
118
+ routeInfo.routePaths,
119
+ routeInfo.routeParents,
120
+ routeInfo.routeObjects,
121
+ this.routeBindings,
122
+ this.basePath
123
+ );
124
+ this.#routeObjects = routeInfo.routeObjects;
125
+ return routeInfo;
126
+ }
127
+ getRouteObjects() {
128
+ return this.#routeObjects;
129
+ }
130
+ }
120
131
  function createSpecializedApp(options) {
121
132
  const {
122
- features: duplicatedFeatures = [],
133
+ features: featuresWithoutApp = [],
123
134
  config = new ConfigReader({}, "empty-config")
124
135
  } = options ?? {};
125
- const features = deduplicateFeatures(duplicatedFeatures);
126
- const tree = createAppTree({
127
- features,
128
- builtinExtensions,
129
- config
130
- });
131
- const routeInfo = extractRouteInfoFromAppNode(tree.root);
132
- const routeBindings = resolveRouteBindings(
133
- options?.bindRoutes,
134
- config,
135
- collectRouteIds(features)
136
+ const features = deduplicateFeatures([appPlugin, ...featuresWithoutApp]);
137
+ const tree = resolveAppTree(
138
+ "root",
139
+ resolveAppNodeSpecs({
140
+ features,
141
+ builtinExtensions: [resolveExtensionDefinition(Root)],
142
+ parameters: readAppExtensionsConfig(config),
143
+ forbidden: /* @__PURE__ */ new Set(["root"])
144
+ })
136
145
  );
137
- const appIdentityProxy = new AppIdentityProxy();
138
- const apiHolder = createApiHolder(
146
+ const factories = createApiFactories({ tree });
147
+ const appTreeApi = new AppTreeApiProxy(tree);
148
+ const routeResolutionApi = new RouteResolutionApiProxy(
139
149
  tree,
140
- config,
141
- appIdentityProxy,
142
- new RouteResolver(
143
- routeInfo.routePaths,
144
- routeInfo.routeParents,
145
- routeInfo.routeObjects,
146
- routeBindings,
147
- getBasePath(config)
150
+ resolveRouteBindings(
151
+ options?.bindRoutes,
152
+ config,
153
+ collectRouteIds(features)
148
154
  ),
149
- options?.icons
155
+ getBasePath(config)
150
156
  );
151
- if (isProtectedApp()) {
152
- const discoveryApi = apiHolder.get(discoveryApiRef);
153
- const errorApi = apiHolder.get(errorApiRef);
154
- const fetchApi = apiHolder.get(fetchApiRef);
155
- if (!discoveryApi || !errorApi || !fetchApi) {
156
- throw new Error(
157
- "App is running in protected mode but missing required APIs"
158
- );
159
- }
160
- appIdentityProxy.enableCookieAuth({
161
- discoveryApi,
162
- errorApi,
163
- fetchApi
164
- });
165
- }
157
+ const appIdentityProxy = new AppIdentityProxy();
158
+ const apiHolder = createApiHolder({
159
+ factories,
160
+ staticFactories: [
161
+ createApiFactory(appTreeApiRef, appTreeApi),
162
+ createApiFactory(configApiRef, config),
163
+ createApiFactory(routeResolutionApiRef, routeResolutionApi),
164
+ createApiFactory(identityApiRef, appIdentityProxy)
165
+ ]
166
+ });
167
+ instantiateAppNodeTree(tree.root, apiHolder);
168
+ routeResolutionApi.initialize();
169
+ appTreeApi.initialize();
166
170
  const featureFlagApi = apiHolder.get(featureFlagsApiRef);
167
171
  if (featureFlagApi) {
168
172
  for (const feature of features) {
@@ -182,94 +186,39 @@ function createSpecializedApp(options) {
182
186
  }
183
187
  }
184
188
  const rootEl = tree.root.instance.getData(coreExtensionData.reactElement);
185
- const AppComponent = () => /* @__PURE__ */ React.createElement(ApiProvider, { apis: apiHolder }, /* @__PURE__ */ React.createElement(AppThemeProvider, null, /* @__PURE__ */ React.createElement(
186
- InternalAppContext.Provider,
187
- {
188
- value: { appIdentityProxy, routeObjects: routeInfo.routeObjects }
189
- },
190
- rootEl
191
- )));
189
+ const AppComponent = () => rootEl;
192
190
  return {
193
191
  createRoot() {
194
192
  return /* @__PURE__ */ React.createElement(AppComponent, null);
195
193
  }
196
194
  };
197
195
  }
198
- function createApiHolder(tree, configApi, appIdentityProxy, routeResolutionApi, icons$1) {
196
+ function createApiFactories(options) {
197
+ const emptyApiHolder = ApiRegistry.from([]);
198
+ const factories = new Array();
199
+ for (const apiNode of options.tree.root.edges.attachments.get("apis") ?? []) {
200
+ instantiateAppNodeTree(apiNode, emptyApiHolder);
201
+ const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory);
202
+ if (!apiFactory) {
203
+ throw new Error(
204
+ `No API factory found in for extension ${apiNode.spec.id}`
205
+ );
206
+ }
207
+ factories.push(apiFactory);
208
+ }
209
+ return factories;
210
+ }
211
+ function createApiHolder(options) {
199
212
  const factoryRegistry = new ApiFactoryRegistry();
200
- const pluginApis = tree.root.edges.attachments.get("apis")?.map((e) => e.instance?.getData(ApiBlueprint.dataRefs.factory)).filter((x) => !!x) ?? [];
201
- const themeExtensions = tree.root.edges.attachments.get("themes")?.map((e) => e.instance?.getData(ThemeBlueprint.dataRefs.theme)).filter((x) => !!x) ?? [];
202
- const translationResources = tree.root.edges.attachments.get("translations")?.map((e) => e.instance?.getData(TranslationBlueprint.dataRefs.translation)).filter(
203
- (x) => !!x
204
- ) ?? [];
205
- const extensionIcons = tree.root.edges.attachments.get("icons")?.map((e) => e.instance?.getData(IconBundleBlueprint.dataRefs.icons)).reduce((acc, bundle) => ({ ...acc, ...bundle }), {});
206
- for (const factory of pluginApis) {
213
+ for (const factory of options.factories) {
207
214
  factoryRegistry.register("default", factory);
208
215
  }
209
- factoryRegistry.register("default", {
210
- api: featureFlagsApiRef,
211
- deps: {},
212
- factory: () => new LocalStorageFeatureFlags()
213
- });
214
- factoryRegistry.register("static", {
215
- api: identityApiRef,
216
- deps: {},
217
- factory: () => appIdentityProxy
218
- });
219
- factoryRegistry.register("static", {
220
- api: appTreeApiRef,
221
- deps: {},
222
- factory: () => ({
223
- getTree: () => ({ tree })
224
- })
225
- });
226
- factoryRegistry.register("static", {
227
- api: routeResolutionApiRef,
228
- deps: {},
229
- factory: () => routeResolutionApi
230
- });
231
- factoryRegistry.register("static", {
232
- api: componentsApiRef,
233
- deps: {},
234
- factory: () => DefaultComponentsApi.fromTree(tree)
235
- });
236
- factoryRegistry.register("static", {
237
- api: iconsApiRef,
238
- deps: {},
239
- factory: () => new DefaultIconsApi({ ...icons, ...extensionIcons, ...icons$1 })
240
- });
241
- factoryRegistry.register("static", {
242
- api: appThemeApiRef,
243
- deps: {},
244
- // TODO: add extension for registering themes
245
- factory: () => AppThemeSelector.createWithStorage(themeExtensions)
246
- });
247
- factoryRegistry.register("static", {
248
- api: appLanguageApiRef,
249
- deps: {},
250
- factory: () => AppLanguageSelector.createWithStorage()
251
- });
252
- factoryRegistry.register("static", {
253
- api: configApiRef,
254
- deps: {},
255
- factory: () => configApi
256
- });
257
- factoryRegistry.register("static", {
258
- api: appLanguageApiRef,
259
- deps: {},
260
- factory: () => AppLanguageSelector.createWithStorage()
261
- });
262
- factoryRegistry.register("static", {
263
- api: translationApiRef,
264
- deps: { languageApi: appLanguageApiRef },
265
- factory: ({ languageApi }) => I18nextTranslationApi.create({
266
- languageApi,
267
- resources: translationResources
268
- })
269
- });
216
+ for (const factory of options.staticFactories) {
217
+ factoryRegistry.register("static", factory);
218
+ }
270
219
  ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());
271
220
  return new ApiResolver(factoryRegistry);
272
221
  }
273
222
 
274
- export { builtinExtensions, createApp, createSpecializedApp };
223
+ export { createApp, createSpecializedApp };
275
224
  //# sourceMappingURL=createApp.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createApp.esm.js","sources":["../../src/wiring/createApp.tsx"],"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 React, { JSX, ReactNode } from 'react';\nimport { ConfigReader } from '@backstage/config';\nimport {\n ApiBlueprint,\n AppTree,\n appTreeApiRef,\n componentsApiRef,\n coreExtensionData,\n FrontendFeature,\n IconBundleBlueprint,\n iconsApiRef,\n RouteResolutionApi,\n routeResolutionApiRef,\n ThemeBlueprint,\n TranslationBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport { App } from '../extensions/App';\nimport { AppRoutes } from '../extensions/AppRoutes';\nimport { AppLayout } from '../extensions/AppLayout';\nimport { AppNav } from '../extensions/AppNav';\nimport {\n AnyApiFactory,\n ApiHolder,\n appThemeApiRef,\n ConfigApi,\n configApiRef,\n IconComponent,\n featureFlagsApiRef,\n identityApiRef,\n AppTheme,\n errorApiRef,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/core-plugin-api';\nimport { getAvailableFeatures } from './discovery';\nimport {\n ApiFactoryRegistry,\n ApiProvider,\n ApiResolver,\n AppThemeSelector,\n} from '@backstage/core-app-api';\n\n// TODO: Get rid of all of these\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isProtectedApp } from '../../../core-app-api/src/app/isProtectedApp';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppThemeProvider } from '../../../core-app-api/src/app/AppThemeProvider';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { LocalStorageFeatureFlags } from '../../../core-app-api/src/apis/implementations/FeatureFlagsApi/LocalStorageFeatureFlags';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultConfigLoader';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { overrideBaseUrlConfigs } from '../../../core-app-api/src/app/overrideBaseUrlConfigs';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppLanguageSelector } from '../../../core-app-api/src/apis/implementations/AppLanguageApi/AppLanguageSelector';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { I18nextTranslationApi } from '../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { apis as defaultApis } from '../../../app-defaults/src/defaults';\nimport { DarkTheme, LightTheme } from '../extensions/themes';\nimport {\n oauthRequestDialogAppRootElement,\n alertDisplayAppRootElement,\n} from '../extensions/elements';\nimport { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode';\nimport {\n appLanguageApiRef,\n translationApiRef,\n} from '@backstage/core-plugin-api/alpha';\nimport { CreateAppRouteBinder } from '../routing';\nimport { RouteResolver } from '../routing/RouteResolver';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\nimport { createAppTree } from '../tree';\nimport {\n DefaultProgressComponent,\n DefaultErrorBoundaryComponent,\n DefaultNotFoundErrorPageComponent,\n} from '../extensions/components';\nimport { InternalAppContext } from './InternalAppContext';\nimport { AppRoot } from '../extensions/AppRoot';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides';\nimport { DefaultComponentsApi } from '../apis/implementations/ComponentsApi';\nimport { DefaultIconsApi } from '../apis/implementations/IconsApi';\nimport { stringifyError } from '@backstage/errors';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { icons as defaultIcons } from '../../../app-defaults/src/defaults';\nimport { getBasePath } from '../routing/getBasePath';\n\nconst DefaultApis = defaultApis.map(factory =>\n ApiBlueprint.make({ namespace: factory.api.id, params: { factory } }),\n);\n\nexport const builtinExtensions = [\n App,\n AppRoot,\n AppRoutes,\n AppNav,\n AppLayout,\n DefaultProgressComponent,\n DefaultErrorBoundaryComponent,\n DefaultNotFoundErrorPageComponent,\n LightTheme,\n DarkTheme,\n oauthRequestDialogAppRootElement,\n alertDisplayAppRootElement,\n ...DefaultApis,\n].map(def => resolveExtensionDefinition(def));\n\nfunction deduplicateFeatures(\n allFeatures: FrontendFeature[],\n): FrontendFeature[] {\n // Start by removing duplicates by reference\n const features = Array.from(new Set(allFeatures));\n\n // Plugins are deduplicated by ID, last one wins\n const seenIds = new Set<string>();\n return features\n .reverse()\n .filter(feature => {\n if (feature.$$type !== '@backstage/BackstagePlugin') {\n return true;\n }\n if (seenIds.has(feature.id)) {\n return false;\n }\n seenIds.add(feature.id);\n return true;\n })\n .reverse();\n}\n\n/**\n * A source of dynamically loaded frontend features.\n *\n * @public\n */\nexport interface CreateAppFeatureLoader {\n /**\n * Returns name of this loader. suitable for showing to users.\n */\n getLoaderName(): string;\n\n /**\n * Loads a number of features dynamically.\n */\n load(options: { config: ConfigApi }): Promise<{\n features: FrontendFeature[];\n }>;\n}\n\n/** @public */\nexport function createApp(options?: {\n /** @deprecated - Please use {@link @backstage/frontend-plugin-api#IconBundleBlueprint} to make new icon bundles which can be installed in the app seperately */\n icons?: { [key in string]: IconComponent };\n features?: (FrontendFeature | CreateAppFeatureLoader)[];\n configLoader?: () => Promise<{ config: ConfigApi }>;\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n /**\n * The component to render while loading the app (waiting for config, features, etc)\n *\n * Is the text \"Loading...\" by default.\n * If set to \"null\" then no loading fallback component is rendered. *\n */\n loadingComponent?: ReactNode;\n}): {\n createRoot(): JSX.Element;\n} {\n let suspenseFallback = options?.loadingComponent;\n if (suspenseFallback === undefined) {\n suspenseFallback = 'Loading...';\n }\n\n async function appLoader() {\n const config =\n (await options?.configLoader?.().then(c => c.config)) ??\n ConfigReader.fromConfigs(\n overrideBaseUrlConfigs(defaultConfigLoaderSync()),\n );\n\n const discoveredFeatures = getAvailableFeatures(config);\n\n const providedFeatures: FrontendFeature[] = [];\n for (const entry of options?.features ?? []) {\n if ('load' in entry) {\n try {\n const result = await entry.load({ config });\n providedFeatures.push(...result.features);\n } catch (e) {\n throw new Error(\n `Failed to read frontend features from loader '${entry.getLoaderName()}', ${stringifyError(\n e,\n )}`,\n );\n }\n } else {\n providedFeatures.push(entry);\n }\n }\n\n const app = createSpecializedApp({\n icons: options?.icons,\n config,\n features: [...discoveredFeatures, ...providedFeatures],\n bindRoutes: options?.bindRoutes,\n }).createRoot();\n\n return { default: () => app };\n }\n\n return {\n createRoot() {\n const LazyApp = React.lazy(appLoader);\n return (\n <React.Suspense fallback={suspenseFallback}>\n <LazyApp />\n </React.Suspense>\n );\n },\n };\n}\n\n/**\n * Synchronous version of {@link createApp}, expecting all features and\n * config to have been loaded already.\n *\n * @public\n */\nexport function createSpecializedApp(options?: {\n /** @deprecated - Please use {@link @backstage/frontend-plugin-api#IconBundleBlueprint} to make new icon bundles which can be installed in the app seperately */\n icons?: { [key in string]: IconComponent };\n features?: FrontendFeature[];\n config?: ConfigApi;\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n}): { createRoot(): JSX.Element } {\n const {\n features: duplicatedFeatures = [],\n config = new ConfigReader({}, 'empty-config'),\n } = options ?? {};\n\n const features = deduplicateFeatures(duplicatedFeatures);\n\n const tree = createAppTree({\n features,\n builtinExtensions,\n config,\n });\n\n const routeInfo = extractRouteInfoFromAppNode(tree.root);\n const routeBindings = resolveRouteBindings(\n options?.bindRoutes,\n config,\n collectRouteIds(features),\n );\n\n const appIdentityProxy = new AppIdentityProxy();\n const apiHolder = createApiHolder(\n tree,\n config,\n appIdentityProxy,\n new RouteResolver(\n routeInfo.routePaths,\n routeInfo.routeParents,\n routeInfo.routeObjects,\n routeBindings,\n getBasePath(config),\n ),\n options?.icons,\n );\n\n if (isProtectedApp()) {\n const discoveryApi = apiHolder.get(discoveryApiRef);\n const errorApi = apiHolder.get(errorApiRef);\n const fetchApi = apiHolder.get(fetchApiRef);\n if (!discoveryApi || !errorApi || !fetchApi) {\n throw new Error(\n 'App is running in protected mode but missing required APIs',\n );\n }\n appIdentityProxy.enableCookieAuth({\n discoveryApi,\n errorApi,\n fetchApi,\n });\n }\n\n const featureFlagApi = apiHolder.get(featureFlagsApiRef);\n if (featureFlagApi) {\n for (const feature of features) {\n if (feature.$$type === '@backstage/BackstagePlugin') {\n toInternalBackstagePlugin(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.id,\n }),\n );\n }\n if (feature.$$type === '@backstage/ExtensionOverrides') {\n toInternalExtensionOverrides(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({ name: flag.name, pluginId: '' }),\n );\n }\n }\n }\n\n const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement);\n\n const AppComponent = () => (\n <ApiProvider apis={apiHolder}>\n <AppThemeProvider>\n <InternalAppContext.Provider\n value={{ appIdentityProxy, routeObjects: routeInfo.routeObjects }}\n >\n {rootEl}\n </InternalAppContext.Provider>\n </AppThemeProvider>\n </ApiProvider>\n );\n\n return {\n createRoot() {\n return <AppComponent />;\n },\n };\n}\n\nfunction createApiHolder(\n tree: AppTree,\n configApi: ConfigApi,\n appIdentityProxy: AppIdentityProxy,\n routeResolutionApi: RouteResolutionApi,\n icons?: { [key in string]: IconComponent },\n): ApiHolder {\n const factoryRegistry = new ApiFactoryRegistry();\n\n const pluginApis =\n tree.root.edges.attachments\n .get('apis')\n ?.map(e => e.instance?.getData(ApiBlueprint.dataRefs.factory))\n .filter((x): x is AnyApiFactory => !!x) ?? [];\n\n const themeExtensions =\n tree.root.edges.attachments\n .get('themes')\n ?.map(e => e.instance?.getData(ThemeBlueprint.dataRefs.theme))\n .filter((x): x is AppTheme => !!x) ?? [];\n\n const translationResources =\n tree.root.edges.attachments\n .get('translations')\n ?.map(e => e.instance?.getData(TranslationBlueprint.dataRefs.translation))\n .filter(\n (x): x is typeof TranslationBlueprint.dataRefs.translation.T => !!x,\n ) ?? [];\n\n const extensionIcons = tree.root.edges.attachments\n .get('icons')\n ?.map(e => e.instance?.getData(IconBundleBlueprint.dataRefs.icons))\n .reduce((acc, bundle) => ({ ...acc, ...bundle }), {});\n\n for (const factory of pluginApis) {\n factoryRegistry.register('default', factory);\n }\n\n // TODO: properly discovery feature flags, maybe rework the whole thing\n factoryRegistry.register('default', {\n api: featureFlagsApiRef,\n deps: {},\n factory: () => new LocalStorageFeatureFlags(),\n });\n\n factoryRegistry.register('static', {\n api: identityApiRef,\n deps: {},\n factory: () => appIdentityProxy,\n });\n\n factoryRegistry.register('static', {\n api: appTreeApiRef,\n deps: {},\n factory: () => ({\n getTree: () => ({ tree }),\n }),\n });\n\n factoryRegistry.register('static', {\n api: routeResolutionApiRef,\n deps: {},\n factory: () => routeResolutionApi,\n });\n\n factoryRegistry.register('static', {\n api: componentsApiRef,\n deps: {},\n factory: () => DefaultComponentsApi.fromTree(tree),\n });\n\n factoryRegistry.register('static', {\n api: iconsApiRef,\n deps: {},\n factory: () =>\n new DefaultIconsApi({ ...defaultIcons, ...extensionIcons, ...icons }),\n });\n\n factoryRegistry.register('static', {\n api: appThemeApiRef,\n deps: {},\n // TODO: add extension for registering themes\n factory: () => AppThemeSelector.createWithStorage(themeExtensions),\n });\n\n factoryRegistry.register('static', {\n api: appLanguageApiRef,\n deps: {},\n factory: () => AppLanguageSelector.createWithStorage(),\n });\n\n factoryRegistry.register('static', {\n api: configApiRef,\n deps: {},\n factory: () => configApi,\n });\n\n factoryRegistry.register('static', {\n api: appLanguageApiRef,\n deps: {},\n factory: () => AppLanguageSelector.createWithStorage(),\n });\n\n factoryRegistry.register('static', {\n api: translationApiRef,\n deps: { languageApi: appLanguageApiRef },\n factory: ({ languageApi }) =>\n I18nextTranslationApi.create({\n languageApi,\n resources: translationResources,\n }),\n });\n\n ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());\n\n return new ApiResolver(factoryRegistry);\n}\n"],"names":["defaultApis","icons","defaultIcons"],"mappings":"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"}
1
+ {"version":3,"file":"createApp.esm.js","sources":["../../src/wiring/createApp.tsx"],"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 React, { JSX, ReactNode } from 'react';\nimport { ConfigReader } from '@backstage/config';\nimport {\n ApiBlueprint,\n AppTree,\n AppTreeApi,\n appTreeApiRef,\n coreExtensionData,\n FrontendFeature,\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApiResolveOptions,\n RouteResolutionApi,\n createApiFactory,\n routeResolutionApiRef,\n} from '@backstage/frontend-plugin-api';\n\nimport {\n AnyApiFactory,\n ApiHolder,\n ConfigApi,\n configApiRef,\n featureFlagsApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport { getAvailableFeatures } from './discovery';\nimport { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';\n\n// TODO: Get rid of all of these\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { defaultConfigLoaderSync } from '../../../core-app-api/src/app/defaultConfigLoader';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { overrideBaseUrlConfigs } from '../../../core-app-api/src/app/overrideBaseUrlConfigs';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nimport { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode';\n\nimport { CreateAppRouteBinder } from '../routing';\nimport { RouteResolver } from '../routing/RouteResolver';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalBackstagePlugin } from '../../../frontend-plugin-api/src/wiring/createFrontendPlugin';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides';\nimport { stringifyError } from '@backstage/errors';\nimport { getBasePath } from '../routing/getBasePath';\nimport { Root } from '../extensions/Root';\nimport { resolveAppTree } from '../tree/resolveAppTree';\nimport { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs';\nimport { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig';\nimport { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';\nimport { BackstageRouteObject } from '../routing/types';\nimport appPlugin from '@backstage/plugin-app';\n\nfunction deduplicateFeatures(\n allFeatures: FrontendFeature[],\n): FrontendFeature[] {\n // Start by removing duplicates by reference\n const features = Array.from(new Set(allFeatures));\n\n // Plugins are deduplicated by ID, last one wins\n const seenIds = new Set<string>();\n return features\n .reverse()\n .filter(feature => {\n if (feature.$$type !== '@backstage/BackstagePlugin') {\n return true;\n }\n if (seenIds.has(feature.id)) {\n return false;\n }\n seenIds.add(feature.id);\n return true;\n })\n .reverse();\n}\n\n/**\n * A source of dynamically loaded frontend features.\n *\n * @public\n */\nexport interface CreateAppFeatureLoader {\n /**\n * Returns name of this loader. suitable for showing to users.\n */\n getLoaderName(): string;\n\n /**\n * Loads a number of features dynamically.\n */\n load(options: { config: ConfigApi }): Promise<{\n features: FrontendFeature[];\n }>;\n}\n\n/** @public */\nexport function createApp(options?: {\n features?: (FrontendFeature | CreateAppFeatureLoader)[];\n configLoader?: () => Promise<{ config: ConfigApi }>;\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n /**\n * The component to render while loading the app (waiting for config, features, etc)\n *\n * Is the text \"Loading...\" by default.\n * If set to \"null\" then no loading fallback component is rendered. *\n */\n loadingComponent?: ReactNode;\n}): {\n createRoot(): JSX.Element;\n} {\n let suspenseFallback = options?.loadingComponent;\n if (suspenseFallback === undefined) {\n suspenseFallback = 'Loading...';\n }\n\n async function appLoader() {\n const config =\n (await options?.configLoader?.().then(c => c.config)) ??\n ConfigReader.fromConfigs(\n overrideBaseUrlConfigs(defaultConfigLoaderSync()),\n );\n\n const discoveredFeatures = getAvailableFeatures(config);\n\n const providedFeatures: FrontendFeature[] = [];\n for (const entry of options?.features ?? []) {\n if ('load' in entry) {\n try {\n const result = await entry.load({ config });\n providedFeatures.push(...result.features);\n } catch (e) {\n throw new Error(\n `Failed to read frontend features from loader '${entry.getLoaderName()}', ${stringifyError(\n e,\n )}`,\n );\n }\n } else {\n providedFeatures.push(entry);\n }\n }\n\n const app = createSpecializedApp({\n config,\n features: [...discoveredFeatures, ...providedFeatures],\n bindRoutes: options?.bindRoutes,\n }).createRoot();\n\n return { default: () => app };\n }\n\n return {\n createRoot() {\n const LazyApp = React.lazy(appLoader);\n return (\n <React.Suspense fallback={suspenseFallback}>\n <LazyApp />\n </React.Suspense>\n );\n },\n };\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass AppTreeApiProxy implements AppTreeApi {\n #safeToUse: boolean = false;\n\n constructor(private readonly tree: AppTree) {}\n\n getTree() {\n if (!this.#safeToUse) {\n throw new Error(\n `You can't access the AppTreeApi during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n return { tree: this.tree };\n }\n\n initialize() {\n this.#safeToUse = true;\n }\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass RouteResolutionApiProxy implements RouteResolutionApi {\n #delegate: RouteResolutionApi | undefined;\n #routeObjects: BackstageRouteObject[] | undefined;\n\n constructor(\n private readonly tree: AppTree,\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly basePath: string,\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: RouteResolutionApiResolveOptions,\n ): RouteFunc<TParams> | undefined {\n if (!this.#delegate) {\n throw new Error(\n `You can't access the RouteResolver during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n\n return this.#delegate.resolve(anyRouteRef, options);\n }\n\n initialize() {\n const routeInfo = extractRouteInfoFromAppNode(this.tree.root);\n\n this.#delegate = new RouteResolver(\n routeInfo.routePaths,\n routeInfo.routeParents,\n routeInfo.routeObjects,\n this.routeBindings,\n this.basePath,\n );\n this.#routeObjects = routeInfo.routeObjects;\n\n return routeInfo;\n }\n\n getRouteObjects() {\n return this.#routeObjects;\n }\n}\n\n/**\n * Synchronous version of {@link createApp}, expecting all features and\n * config to have been loaded already.\n *\n * @public\n */\nexport function createSpecializedApp(options?: {\n features?: FrontendFeature[];\n config?: ConfigApi;\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n}): { createRoot(): JSX.Element } {\n const {\n features: featuresWithoutApp = [],\n config = new ConfigReader({}, 'empty-config'),\n } = options ?? {};\n\n const features = deduplicateFeatures([appPlugin, ...featuresWithoutApp]);\n\n const tree = resolveAppTree(\n 'root',\n resolveAppNodeSpecs({\n features,\n builtinExtensions: [resolveExtensionDefinition(Root)],\n parameters: readAppExtensionsConfig(config),\n forbidden: new Set(['root']),\n }),\n );\n\n const factories = createApiFactories({ tree });\n const appTreeApi = new AppTreeApiProxy(tree);\n const routeResolutionApi = new RouteResolutionApiProxy(\n tree,\n resolveRouteBindings(\n options?.bindRoutes,\n config,\n collectRouteIds(features),\n ),\n getBasePath(config),\n );\n\n const appIdentityProxy = new AppIdentityProxy();\n const apiHolder = createApiHolder({\n factories,\n staticFactories: [\n createApiFactory(appTreeApiRef, appTreeApi),\n createApiFactory(configApiRef, config),\n createApiFactory(routeResolutionApiRef, routeResolutionApi),\n createApiFactory(identityApiRef, appIdentityProxy),\n ],\n });\n\n // Now instantiate the entire tree, which will skip anything that's already been instantiated\n instantiateAppNodeTree(tree.root, apiHolder);\n\n routeResolutionApi.initialize();\n appTreeApi.initialize();\n\n const featureFlagApi = apiHolder.get(featureFlagsApiRef);\n if (featureFlagApi) {\n for (const feature of features) {\n if (feature.$$type === '@backstage/BackstagePlugin') {\n toInternalBackstagePlugin(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.id,\n }),\n );\n }\n if (feature.$$type === '@backstage/ExtensionOverrides') {\n toInternalExtensionOverrides(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({ name: flag.name, pluginId: '' }),\n );\n }\n }\n }\n\n const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement);\n\n const AppComponent = () => rootEl;\n\n return {\n createRoot() {\n return <AppComponent />;\n },\n };\n}\n\nfunction createApiFactories(options: { tree: AppTree }): AnyApiFactory[] {\n const emptyApiHolder = ApiRegistry.from([]);\n const factories = new Array<AnyApiFactory>();\n\n for (const apiNode of options.tree.root.edges.attachments.get('apis') ?? []) {\n instantiateAppNodeTree(apiNode, emptyApiHolder);\n const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory);\n if (!apiFactory) {\n throw new Error(\n `No API factory found in for extension ${apiNode.spec.id}`,\n );\n }\n factories.push(apiFactory);\n }\n\n return factories;\n}\n\nfunction createApiHolder(options: {\n factories: AnyApiFactory[];\n staticFactories: AnyApiFactory[];\n}): ApiHolder {\n const factoryRegistry = new ApiFactoryRegistry();\n\n for (const factory of options.factories) {\n factoryRegistry.register('default', factory);\n }\n\n for (const factory of options.staticFactories) {\n factoryRegistry.register('static', factory);\n }\n\n ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());\n\n return new ApiResolver(factoryRegistry);\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAgFA,SAAS,oBACP,WACmB,EAAA;AAEnB,EAAA,MAAM,WAAW,KAAM,CAAA,IAAA,CAAK,IAAI,GAAA,CAAI,WAAW,CAAC,CAAA,CAAA;AAGhD,EAAM,MAAA,OAAA,uBAAc,GAAY,EAAA,CAAA;AAChC,EAAA,OAAO,QACJ,CAAA,OAAA,EACA,CAAA,MAAA,CAAO,CAAW,OAAA,KAAA;AACjB,IAAI,IAAA,OAAA,CAAQ,WAAW,4BAA8B,EAAA;AACnD,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AACA,IAAA,IAAI,OAAQ,CAAA,GAAA,CAAI,OAAQ,CAAA,EAAE,CAAG,EAAA;AAC3B,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AACA,IAAQ,OAAA,CAAA,GAAA,CAAI,QAAQ,EAAE,CAAA,CAAA;AACtB,IAAO,OAAA,IAAA,CAAA;AAAA,GACR,EACA,OAAQ,EAAA,CAAA;AACb,CAAA;AAsBO,SAAS,UAAU,OAaxB,EAAA;AACA,EAAA,IAAI,mBAAmB,OAAS,EAAA,gBAAA,CAAA;AAChC,EAAA,IAAI,qBAAqB,KAAW,CAAA,EAAA;AAClC,IAAmB,gBAAA,GAAA,YAAA,CAAA;AAAA,GACrB;AAEA,EAAA,eAAe,SAAY,GAAA;AACzB,IAAM,MAAA,MAAA,GACH,MAAM,OAAA,EAAS,YAAe,IAAA,CAAE,KAAK,CAAK,CAAA,KAAA,CAAA,CAAE,MAAM,CAAA,IACnD,YAAa,CAAA,WAAA;AAAA,MACX,sBAAA,CAAuB,yBAAyB,CAAA;AAAA,KAClD,CAAA;AAEF,IAAM,MAAA,kBAAA,GAAqB,qBAAqB,MAAM,CAAA,CAAA;AAEtD,IAAA,MAAM,mBAAsC,EAAC,CAAA;AAC7C,IAAA,KAAA,MAAW,KAAS,IAAA,OAAA,EAAS,QAAY,IAAA,EAAI,EAAA;AAC3C,MAAA,IAAI,UAAU,KAAO,EAAA;AACnB,QAAI,IAAA;AACF,UAAA,MAAM,SAAS,MAAM,KAAA,CAAM,IAAK,CAAA,EAAE,QAAQ,CAAA,CAAA;AAC1C,UAAiB,gBAAA,CAAA,IAAA,CAAK,GAAG,MAAA,CAAO,QAAQ,CAAA,CAAA;AAAA,iBACjC,CAAG,EAAA;AACV,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAiD,8CAAA,EAAA,KAAA,CAAM,aAAc,EAAC,CAAM,GAAA,EAAA,cAAA;AAAA,cAC1E,CAAA;AAAA,aACD,CAAA,CAAA;AAAA,WACH,CAAA;AAAA,SACF;AAAA,OACK,MAAA;AACL,QAAA,gBAAA,CAAiB,KAAK,KAAK,CAAA,CAAA;AAAA,OAC7B;AAAA,KACF;AAEA,IAAA,MAAM,MAAM,oBAAqB,CAAA;AAAA,MAC/B,MAAA;AAAA,MACA,QAAU,EAAA,CAAC,GAAG,kBAAA,EAAoB,GAAG,gBAAgB,CAAA;AAAA,MACrD,YAAY,OAAS,EAAA,UAAA;AAAA,KACtB,EAAE,UAAW,EAAA,CAAA;AAEd,IAAO,OAAA,EAAE,OAAS,EAAA,MAAM,GAAI,EAAA,CAAA;AAAA,GAC9B;AAEA,EAAO,OAAA;AAAA,IACL,UAAa,GAAA;AACX,MAAM,MAAA,OAAA,GAAU,KAAM,CAAA,IAAA,CAAK,SAAS,CAAA,CAAA;AACpC,MACE,uBAAA,KAAA,CAAA,aAAA,CAAC,MAAM,QAAN,EAAA,EAAe,UAAU,gBACxB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,aAAQ,CACX,CAAA,CAAA;AAAA,KAEJ;AAAA,GACF,CAAA;AACF,CAAA;AAGA,MAAM,eAAsC,CAAA;AAAA,EAG1C,YAA6B,IAAe,EAAA;AAAf,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA,CAAA;AAAA,GAAgB;AAAA,EAF7C,UAAsB,GAAA,KAAA,CAAA;AAAA,EAItB,OAAU,GAAA;AACR,IAAI,IAAA,CAAC,KAAK,UAAY,EAAA;AACpB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,+IAAA,CAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAO,OAAA,EAAE,IAAM,EAAA,IAAA,CAAK,IAAK,EAAA,CAAA;AAAA,GAC3B;AAAA,EAEA,UAAa,GAAA;AACX,IAAA,IAAA,CAAK,UAAa,GAAA,IAAA,CAAA;AAAA,GACpB;AACF,CAAA;AAGA,MAAM,uBAAsD,CAAA;AAAA,EAI1D,WAAA,CACmB,IACA,EAAA,aAAA,EAIA,QACjB,EAAA;AANiB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA,CAAA;AACA,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA,CAAA;AAIA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA,CAAA;AAAA,GAChB;AAAA,EAVH,SAAA,CAAA;AAAA,EACA,aAAA,CAAA;AAAA,EAWA,OAAA,CACE,aAIA,OACgC,EAAA;AAChC,IAAI,IAAA,CAAC,KAAK,SAAW,EAAA;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,kJAAA,CAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,OAAO,IAAK,CAAA,SAAA,CAAU,OAAQ,CAAA,WAAA,EAAa,OAAO,CAAA,CAAA;AAAA,GACpD;AAAA,EAEA,UAAa,GAAA;AACX,IAAA,MAAM,SAAY,GAAA,2BAAA,CAA4B,IAAK,CAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAE5D,IAAA,IAAA,CAAK,YAAY,IAAI,aAAA;AAAA,MACnB,SAAU,CAAA,UAAA;AAAA,MACV,SAAU,CAAA,YAAA;AAAA,MACV,SAAU,CAAA,YAAA;AAAA,MACV,IAAK,CAAA,aAAA;AAAA,MACL,IAAK,CAAA,QAAA;AAAA,KACP,CAAA;AACA,IAAA,IAAA,CAAK,gBAAgB,SAAU,CAAA,YAAA,CAAA;AAE/B,IAAO,OAAA,SAAA,CAAA;AAAA,GACT;AAAA,EAEA,eAAkB,GAAA;AAChB,IAAA,OAAO,IAAK,CAAA,aAAA,CAAA;AAAA,GACd;AACF,CAAA;AAQO,SAAS,qBAAqB,OAIH,EAAA;AAChC,EAAM,MAAA;AAAA,IACJ,QAAA,EAAU,qBAAqB,EAAC;AAAA,IAChC,MAAS,GAAA,IAAI,YAAa,CAAA,IAAI,cAAc,CAAA;AAAA,GAC9C,GAAI,WAAW,EAAC,CAAA;AAEhB,EAAA,MAAM,WAAW,mBAAoB,CAAA,CAAC,SAAW,EAAA,GAAG,kBAAkB,CAAC,CAAA,CAAA;AAEvE,EAAA,MAAM,IAAO,GAAA,cAAA;AAAA,IACX,MAAA;AAAA,IACA,mBAAoB,CAAA;AAAA,MAClB,QAAA;AAAA,MACA,iBAAmB,EAAA,CAAC,0BAA2B,CAAA,IAAI,CAAC,CAAA;AAAA,MACpD,UAAA,EAAY,wBAAwB,MAAM,CAAA;AAAA,MAC1C,SAAW,kBAAA,IAAI,GAAI,CAAA,CAAC,MAAM,CAAC,CAAA;AAAA,KAC5B,CAAA;AAAA,GACH,CAAA;AAEA,EAAA,MAAM,SAAY,GAAA,kBAAA,CAAmB,EAAE,IAAA,EAAM,CAAA,CAAA;AAC7C,EAAM,MAAA,UAAA,GAAa,IAAI,eAAA,CAAgB,IAAI,CAAA,CAAA;AAC3C,EAAA,MAAM,qBAAqB,IAAI,uBAAA;AAAA,IAC7B,IAAA;AAAA,IACA,oBAAA;AAAA,MACE,OAAS,EAAA,UAAA;AAAA,MACT,MAAA;AAAA,MACA,gBAAgB,QAAQ,CAAA;AAAA,KAC1B;AAAA,IACA,YAAY,MAAM,CAAA;AAAA,GACpB,CAAA;AAEA,EAAM,MAAA,gBAAA,GAAmB,IAAI,gBAAiB,EAAA,CAAA;AAC9C,EAAA,MAAM,YAAY,eAAgB,CAAA;AAAA,IAChC,SAAA;AAAA,IACA,eAAiB,EAAA;AAAA,MACf,gBAAA,CAAiB,eAAe,UAAU,CAAA;AAAA,MAC1C,gBAAA,CAAiB,cAAc,MAAM,CAAA;AAAA,MACrC,gBAAA,CAAiB,uBAAuB,kBAAkB,CAAA;AAAA,MAC1D,gBAAA,CAAiB,gBAAgB,gBAAgB,CAAA;AAAA,KACnD;AAAA,GACD,CAAA,CAAA;AAGD,EAAuB,sBAAA,CAAA,IAAA,CAAK,MAAM,SAAS,CAAA,CAAA;AAE3C,EAAA,kBAAA,CAAmB,UAAW,EAAA,CAAA;AAC9B,EAAA,UAAA,CAAW,UAAW,EAAA,CAAA;AAEtB,EAAM,MAAA,cAAA,GAAiB,SAAU,CAAA,GAAA,CAAI,kBAAkB,CAAA,CAAA;AACvD,EAAA,IAAI,cAAgB,EAAA;AAClB,IAAA,KAAA,MAAW,WAAW,QAAU,EAAA;AAC9B,MAAI,IAAA,OAAA,CAAQ,WAAW,4BAA8B,EAAA;AACnD,QAA0B,yBAAA,CAAA,OAAO,EAAE,YAAa,CAAA,OAAA;AAAA,UAAQ,CAAA,IAAA,KACtD,eAAe,YAAa,CAAA;AAAA,YAC1B,MAAM,IAAK,CAAA,IAAA;AAAA,YACX,UAAU,OAAQ,CAAA,EAAA;AAAA,WACnB,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AACA,MAAI,IAAA,OAAA,CAAQ,WAAW,+BAAiC,EAAA;AACtD,QAA6B,4BAAA,CAAA,OAAO,EAAE,YAAa,CAAA,OAAA;AAAA,UAAQ,CAAA,IAAA,KACzD,eAAe,YAAa,CAAA,EAAE,MAAM,IAAK,CAAA,IAAA,EAAM,QAAU,EAAA,EAAA,EAAI,CAAA;AAAA,SAC/D,CAAA;AAAA,OACF;AAAA,KACF;AAAA,GACF;AAEA,EAAA,MAAM,SAAS,IAAK,CAAA,IAAA,CAAK,QAAU,CAAA,OAAA,CAAQ,kBAAkB,YAAY,CAAA,CAAA;AAEzE,EAAA,MAAM,eAAe,MAAM,MAAA,CAAA;AAE3B,EAAO,OAAA;AAAA,IACL,UAAa,GAAA;AACX,MAAA,2CAAQ,YAAa,EAAA,IAAA,CAAA,CAAA;AAAA,KACvB;AAAA,GACF,CAAA;AACF,CAAA;AAEA,SAAS,mBAAmB,OAA6C,EAAA;AACvE,EAAA,MAAM,cAAiB,GAAA,WAAA,CAAY,IAAK,CAAA,EAAE,CAAA,CAAA;AAC1C,EAAM,MAAA,SAAA,GAAY,IAAI,KAAqB,EAAA,CAAA;AAE3C,EAAW,KAAA,MAAA,OAAA,IAAW,OAAQ,CAAA,IAAA,CAAK,IAAK,CAAA,KAAA,CAAM,YAAY,GAAI,CAAA,MAAM,CAAK,IAAA,EAAI,EAAA;AAC3E,IAAA,sBAAA,CAAuB,SAAS,cAAc,CAAA,CAAA;AAC9C,IAAA,MAAM,aAAa,OAAQ,CAAA,QAAA,EAAU,OAAQ,CAAA,YAAA,CAAa,SAAS,OAAO,CAAA,CAAA;AAC1E,IAAA,IAAI,CAAC,UAAY,EAAA;AACf,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sCAAA,EAAyC,OAAQ,CAAA,IAAA,CAAK,EAAE,CAAA,CAAA;AAAA,OAC1D,CAAA;AAAA,KACF;AACA,IAAA,SAAA,CAAU,KAAK,UAAU,CAAA,CAAA;AAAA,GAC3B;AAEA,EAAO,OAAA,SAAA,CAAA;AACT,CAAA;AAEA,SAAS,gBAAgB,OAGX,EAAA;AACZ,EAAM,MAAA,eAAA,GAAkB,IAAI,kBAAmB,EAAA,CAAA;AAE/C,EAAW,KAAA,MAAA,OAAA,IAAW,QAAQ,SAAW,EAAA;AACvC,IAAgB,eAAA,CAAA,QAAA,CAAS,WAAW,OAAO,CAAA,CAAA;AAAA,GAC7C;AAEA,EAAW,KAAA,MAAA,OAAA,IAAW,QAAQ,eAAiB,EAAA;AAC7C,IAAgB,eAAA,CAAA,QAAA,CAAS,UAAU,OAAO,CAAA,CAAA;AAAA,GAC5C;AAEA,EAAA,WAAA,CAAY,iBAAkB,CAAA,eAAA,EAAiB,eAAgB,CAAA,UAAA,EAAY,CAAA,CAAA;AAE3E,EAAO,OAAA,IAAI,YAAY,eAAe,CAAA,CAAA;AACxC;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/frontend-app-api",
3
- "version": "0.8.0",
3
+ "version": "0.9.0-next.0",
4
4
  "backstage": {
5
5
  "role": "web-library"
6
6
  },
@@ -37,7 +37,8 @@
37
37
  "@backstage/core-components": "^0.14.10",
38
38
  "@backstage/core-plugin-api": "^1.9.3",
39
39
  "@backstage/errors": "^1.2.4",
40
- "@backstage/frontend-plugin-api": "^0.7.0",
40
+ "@backstage/frontend-plugin-api": "^0.8.0-next.0",
41
+ "@backstage/plugin-app": "^0.1.0-next.0",
41
42
  "@backstage/theme": "^0.5.6",
42
43
  "@backstage/types": "^1.1.1",
43
44
  "@backstage/version-bridge": "^1.0.8",
@@ -47,8 +48,8 @@
47
48
  "lodash": "^4.17.21"
48
49
  },
49
50
  "devDependencies": {
50
- "@backstage/cli": "^0.27.0",
51
- "@backstage/test-utils": "^1.5.10",
51
+ "@backstage/cli": "^0.27.1-next.0",
52
+ "@backstage/test-utils": "^1.6.0-next.0",
52
53
  "@testing-library/jest-dom": "^6.0.0",
53
54
  "@testing-library/react": "^15.0.0"
54
55
  },
@@ -1,30 +0,0 @@
1
- import { createComponentExtension } from '@backstage/frontend-plugin-api';
2
-
3
- class DefaultComponentsApi {
4
- #components;
5
- static fromTree(tree) {
6
- const componentEntries = tree.root.edges.attachments.get("components")?.reduce((map, e) => {
7
- const data = e.instance?.getData(
8
- createComponentExtension.componentDataRef
9
- );
10
- if (data) {
11
- map.set(data.ref.id, data.impl);
12
- }
13
- return map;
14
- }, /* @__PURE__ */ new Map());
15
- return new DefaultComponentsApi(componentEntries ?? /* @__PURE__ */ new Map());
16
- }
17
- constructor(components) {
18
- this.#components = components;
19
- }
20
- getComponent(ref) {
21
- const impl = this.#components.get(ref.id);
22
- if (!impl) {
23
- throw new Error(`No implementation found for component ref ${ref}`);
24
- }
25
- return impl;
26
- }
27
- }
28
-
29
- export { DefaultComponentsApi };
30
- //# sourceMappingURL=DefaultComponentsApi.esm.js.map