@backstage/frontend-app-api 0.16.2-next.0 → 0.16.2-next.2

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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # @backstage/frontend-app-api
2
2
 
3
+ ## 0.16.2-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @backstage/errors@1.3.0-next.0
9
+ - @backstage/core-app-api@1.20.0-next.2
10
+ - @backstage/config@1.3.7-next.0
11
+ - @backstage/core-plugin-api@1.12.5-next.2
12
+ - @backstage/filter-predicates@0.1.2-next.0
13
+ - @backstage/frontend-defaults@0.5.1-next.2
14
+ - @backstage/frontend-plugin-api@0.16.0-next.2
15
+
16
+ ## 0.16.2-next.1
17
+
18
+ ### Patch Changes
19
+
20
+ - 400aa23: Wrapped extension permission authorization in a try/catch to surface errors as `ForwardedError` with a clear message.
21
+ - Updated dependencies
22
+ - @backstage/core-app-api@1.20.0-next.1
23
+ - @backstage/frontend-plugin-api@0.16.0-next.1
24
+ - @backstage/core-plugin-api@1.12.5-next.1
25
+ - @backstage/frontend-defaults@0.5.1-next.1
26
+
3
27
  ## 0.16.2-next.0
4
28
 
5
29
  ### Patch Changes
@@ -1,4 +1,6 @@
1
1
  import { OpaqueRouteRef } from '../frontend-internal/src/routing/OpaqueRouteRef.esm.js';
2
+ import '../frontend-internal/src/routing/OpaqueSubRouteRef.esm.js';
3
+ import '../frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js';
2
4
 
3
5
  function createRouteAliasResolver(routeRefsById) {
4
6
  const resolver = (routeRef, pluginId) => {
@@ -1 +1 @@
1
- {"version":3,"file":"RouteAliasResolver.esm.js","sources":["../../src/routing/RouteAliasResolver.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RouteRef } from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\nimport { OpaqueRouteRef } from '@internal/frontend';\n\n/**\n * @internal\n */\nexport type RouteAliasResolver = {\n (routeRef: RouteRef, pluginId?: string): RouteRef;\n (routeRef?: RouteRef, pluginId?: string): RouteRef | undefined;\n};\n\n/**\n * Creates a route alias resolver that resolves aliases based on the route IDs\n * @internal\n */\nexport function createRouteAliasResolver(\n routeRefsById: RouteRefsById,\n): RouteAliasResolver {\n const resolver = (routeRef: RouteRef | undefined, pluginId?: string) => {\n if (!routeRef) {\n return undefined;\n }\n\n let currentRef = routeRef;\n for (let i = 0; i < 100; i++) {\n const alias = OpaqueRouteRef.toInternal(currentRef).alias;\n if (alias) {\n if (pluginId) {\n const [aliasPluginId] = alias.split('.');\n if (aliasPluginId !== pluginId) {\n throw new Error(\n `Refused to resolve alias '${alias}' for ${currentRef} as it points to a different plugin, the expected plugin is '${pluginId}' but the alias points to '${aliasPluginId}'`,\n );\n }\n }\n const aliasRef = routeRefsById.routes.get(alias);\n if (!aliasRef) {\n throw new Error(\n `Unable to resolve RouteRef alias '${alias}' for ${currentRef}`,\n );\n }\n if (aliasRef.$$type === '@backstage/SubRouteRef') {\n throw new Error(\n `RouteRef alias '${alias}' for ${currentRef} points to a SubRouteRef, which is not supported`,\n );\n }\n currentRef = aliasRef;\n } else {\n return currentRef;\n }\n }\n throw new Error(`Alias loop detected for ${routeRef}`);\n };\n\n return resolver as RouteAliasResolver;\n}\n\n/**\n * Creates a route alias resolver that resolves aliases based on a map of route refs to their aliases\n * @internal\n */\nexport function createExactRouteAliasResolver(\n routeAliases: Map<RouteRef, RouteRef | undefined>,\n): RouteAliasResolver {\n const resolver = (routeRef?: RouteRef) => {\n if (routeRef && routeAliases.has(routeRef)) {\n return routeAliases.get(routeRef);\n }\n return routeRef;\n };\n return resolver as RouteAliasResolver;\n}\n"],"names":[],"mappings":";;AAgCO,SAAS,yBACd,aAAA,EACoB;AACpB,EAAA,MAAM,QAAA,GAAW,CAAC,QAAA,EAAgC,QAAA,KAAsB;AACtE,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,UAAA,GAAa,QAAA;AACjB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,EAAK,CAAA,EAAA,EAAK;AAC5B,MAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,UAAA,CAAW,UAAU,CAAA,CAAE,KAAA;AACpD,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,MAAM,CAAC,aAAa,CAAA,GAAI,KAAA,CAAM,MAAM,GAAG,CAAA;AACvC,UAAA,IAAI,kBAAkB,QAAA,EAAU;AAC9B,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,6BAA6B,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA,6DAAA,EAAgE,QAAQ,8BAA8B,aAAa,CAAA,CAAA;AAAA,aAC1K;AAAA,UACF;AAAA,QACF;AACA,QAAA,MAAM,QAAA,GAAW,aAAA,CAAc,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AAC/C,QAAA,IAAI,CAAC,QAAA,EAAU;AACb,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,kCAAA,EAAqC,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA;AAAA,WAC/D;AAAA,QACF;AACA,QAAA,IAAI,QAAA,CAAS,WAAW,wBAAA,EAA0B;AAChD,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,gBAAA,EAAmB,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA,gDAAA;AAAA,WAC7C;AAAA,QACF;AACA,QAAA,UAAA,GAAa,QAAA;AAAA,MACf,CAAA,MAAO;AACL,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,IACF;AACA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,QAAQ,CAAA,CAAE,CAAA;AAAA,EACvD,CAAA;AAEA,EAAA,OAAO,QAAA;AACT;AAMO,SAAS,8BACd,YAAA,EACoB;AACpB,EAAA,MAAM,QAAA,GAAW,CAAC,QAAA,KAAwB;AACxC,IAAA,IAAI,QAAA,IAAY,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA,EAAG;AAC1C,MAAA,OAAO,YAAA,CAAa,IAAI,QAAQ,CAAA;AAAA,IAClC;AACA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACA,EAAA,OAAO,QAAA;AACT;;;;"}
1
+ {"version":3,"file":"RouteAliasResolver.esm.js","sources":["../../src/routing/RouteAliasResolver.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { RouteRef } from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\nimport { OpaqueRouteRef } from '@internal/frontend';\n\n/**\n * @internal\n */\nexport type RouteAliasResolver = {\n (routeRef: RouteRef, pluginId?: string): RouteRef;\n (routeRef?: RouteRef, pluginId?: string): RouteRef | undefined;\n};\n\n/**\n * Creates a route alias resolver that resolves aliases based on the route IDs\n * @internal\n */\nexport function createRouteAliasResolver(\n routeRefsById: RouteRefsById,\n): RouteAliasResolver {\n const resolver = (routeRef: RouteRef | undefined, pluginId?: string) => {\n if (!routeRef) {\n return undefined;\n }\n\n let currentRef = routeRef;\n for (let i = 0; i < 100; i++) {\n const alias = OpaqueRouteRef.toInternal(currentRef).alias;\n if (alias) {\n if (pluginId) {\n const [aliasPluginId] = alias.split('.');\n if (aliasPluginId !== pluginId) {\n throw new Error(\n `Refused to resolve alias '${alias}' for ${currentRef} as it points to a different plugin, the expected plugin is '${pluginId}' but the alias points to '${aliasPluginId}'`,\n );\n }\n }\n const aliasRef = routeRefsById.routes.get(alias);\n if (!aliasRef) {\n throw new Error(\n `Unable to resolve RouteRef alias '${alias}' for ${currentRef}`,\n );\n }\n if (aliasRef.$$type === '@backstage/SubRouteRef') {\n throw new Error(\n `RouteRef alias '${alias}' for ${currentRef} points to a SubRouteRef, which is not supported`,\n );\n }\n currentRef = aliasRef;\n } else {\n return currentRef;\n }\n }\n throw new Error(`Alias loop detected for ${routeRef}`);\n };\n\n return resolver as RouteAliasResolver;\n}\n\n/**\n * Creates a route alias resolver that resolves aliases based on a map of route refs to their aliases\n * @internal\n */\nexport function createExactRouteAliasResolver(\n routeAliases: Map<RouteRef, RouteRef | undefined>,\n): RouteAliasResolver {\n const resolver = (routeRef?: RouteRef) => {\n if (routeRef && routeAliases.has(routeRef)) {\n return routeAliases.get(routeRef);\n }\n return routeRef;\n };\n return resolver as RouteAliasResolver;\n}\n"],"names":[],"mappings":";;;;AAgCO,SAAS,yBACd,aAAA,EACoB;AACpB,EAAA,MAAM,QAAA,GAAW,CAAC,QAAA,EAAgC,QAAA,KAAsB;AACtE,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,OAAO,MAAA;AAAA,IACT;AAEA,IAAA,IAAI,UAAA,GAAa,QAAA;AACjB,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,EAAK,CAAA,EAAA,EAAK;AAC5B,MAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,UAAA,CAAW,UAAU,CAAA,CAAE,KAAA;AACpD,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,IAAI,QAAA,EAAU;AACZ,UAAA,MAAM,CAAC,aAAa,CAAA,GAAI,KAAA,CAAM,MAAM,GAAG,CAAA;AACvC,UAAA,IAAI,kBAAkB,QAAA,EAAU;AAC9B,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,6BAA6B,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA,6DAAA,EAAgE,QAAQ,8BAA8B,aAAa,CAAA,CAAA;AAAA,aAC1K;AAAA,UACF;AAAA,QACF;AACA,QAAA,MAAM,QAAA,GAAW,aAAA,CAAc,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA;AAC/C,QAAA,IAAI,CAAC,QAAA,EAAU;AACb,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,kCAAA,EAAqC,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA;AAAA,WAC/D;AAAA,QACF;AACA,QAAA,IAAI,QAAA,CAAS,WAAW,wBAAA,EAA0B;AAChD,UAAA,MAAM,IAAI,KAAA;AAAA,YACR,CAAA,gBAAA,EAAmB,KAAK,CAAA,MAAA,EAAS,UAAU,CAAA,gDAAA;AAAA,WAC7C;AAAA,QACF;AACA,QAAA,UAAA,GAAa,QAAA;AAAA,MACf,CAAA,MAAO;AACL,QAAA,OAAO,UAAA;AAAA,MACT;AAAA,IACF;AACA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,QAAQ,CAAA,CAAE,CAAA;AAAA,EACvD,CAAA;AAEA,EAAA,OAAO,QAAA;AACT;AAMO,SAAS,8BACd,YAAA,EACoB;AACpB,EAAA,MAAM,QAAA,GAAW,CAAC,QAAA,KAAwB;AACxC,IAAA,IAAI,QAAA,IAAY,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA,EAAG;AAC1C,MAAA,OAAO,YAAA,CAAa,IAAI,QAAQ,CAAA;AAAA,IAClC;AACA,IAAA,OAAO,QAAA;AAAA,EACT,CAAA;AACA,EAAA,OAAO,QAAA;AACT;;;;"}
@@ -1,8 +1,8 @@
1
1
  import { generatePath, matchRoutes } from 'react-router-dom';
2
2
  import mapValues from 'lodash/mapValues';
3
3
  import { OpaqueExternalRouteRef } from '../frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js';
4
- import { OpaqueSubRouteRef } from '../frontend-internal/src/routing/OpaqueSubRouteRef.esm.js';
5
4
  import { OpaqueRouteRef } from '../frontend-internal/src/routing/OpaqueRouteRef.esm.js';
5
+ import { OpaqueSubRouteRef } from '../frontend-internal/src/routing/OpaqueSubRouteRef.esm.js';
6
6
 
7
7
  function joinPaths(...paths) {
8
8
  const normalized = paths.join("/").replace(/\/\/+/g, "/");
@@ -2,6 +2,9 @@ import { createFrontendPlugin } from '@backstage/frontend-plugin-api';
2
2
  import { isInternalFrontendModule, toInternalFrontendModule } from '../frontend-plugin-api/src/wiring/createFrontendModule.esm.js';
3
3
  import { toInternalExtension } from '../frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js';
4
4
  import { OpaqueFrontendPlugin } from '../frontend-internal/src/wiring/InternalFrontendPlugin.esm.js';
5
+ import '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
6
+ import '../frontend-internal/src/wiring/InternalExtensionInput.esm.js';
7
+ import '../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
5
8
 
6
9
  function normalizePlugin(plugin) {
7
10
  if (!plugin.pluginId && "id" in plugin && typeof plugin.id === "string") {
@@ -1 +1 @@
1
- {"version":3,"file":"resolveAppNodeSpecs.esm.js","sources":["../../src/tree/resolveAppNodeSpecs.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 createFrontendPlugin,\n Extension,\n FrontendFeature,\n FrontendPlugin,\n} from '@backstage/frontend-plugin-api';\nimport { FilterPredicate } from '@backstage/filter-predicates';\nimport { ExtensionParameters } from './readAppExtensionsConfig';\nimport { AppNodeSpec } from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isInternalFrontendModule,\n toInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { ErrorCollector } from '../wiring/createErrorCollector';\n\nfunction normalizePlugin(plugin: FrontendPlugin): FrontendPlugin {\n // Ensure pluginId is always set for plugins in the app\n if (!plugin.pluginId && 'id' in plugin && typeof plugin.id === 'string') {\n (plugin as any).pluginId = plugin.id;\n }\n return plugin;\n}\n\nfunction combinePredicates(\n left: FilterPredicate | undefined,\n right: FilterPredicate | undefined,\n) {\n if (!left) {\n return right;\n }\n if (!right) {\n return left;\n }\n\n return { $all: [left, right] };\n}\n\nfunction getExtensionPredicate(options: {\n internalExtension: ReturnType<typeof toInternalExtension>;\n}) {\n if (options.internalExtension.version === 'v2') {\n return options.internalExtension.if;\n }\n return undefined;\n}\n\n/** @internal */\nexport function resolveAppNodeSpecs(options: {\n features?: FrontendFeature[];\n builtinExtensions?: Extension<any, any>[];\n parameters?: Array<ExtensionParameters>;\n forbidden?: Set<string>;\n collector: ErrorCollector;\n}): AppNodeSpec[] {\n const {\n builtinExtensions = [],\n parameters = [],\n forbidden = new Set(),\n features = [],\n collector,\n } = options;\n\n const plugins = features\n .filter(OpaqueFrontendPlugin.isType)\n .map(normalizePlugin);\n const modules = features.filter(isInternalFrontendModule);\n\n const filterForbidden = (\n extension: Extension<any, any> & { plugin: FrontendPlugin },\n ) => {\n if (forbidden.has(extension.id)) {\n collector.report({\n code: 'EXTENSION_IGNORED',\n message: `It is forbidden to override the '${extension.id}' extension, attempted by the '${extension.plugin.pluginId}' plugin`,\n context: {\n plugin: extension.plugin,\n extensionId: extension.id,\n },\n });\n return false;\n }\n return true;\n };\n\n const pluginExtensions = plugins.flatMap(plugin => {\n const internalPlugin = OpaqueFrontendPlugin.toInternal(plugin);\n return internalPlugin.extensions\n .map(extension => {\n const internalExtension = toInternalExtension(extension);\n return {\n ...internalExtension,\n plugin,\n if: combinePredicates(\n internalPlugin.if,\n internalExtension.version === 'v2'\n ? internalExtension.if\n : undefined,\n ),\n };\n })\n .filter(filterForbidden);\n });\n const moduleExtensions = modules.flatMap(mod => {\n const internalModule = toInternalFrontendModule(mod);\n return internalModule.extensions\n .flatMap(extension => {\n const internalExtension = toInternalExtension(extension);\n\n // Modules for plugins that are not installed are ignored\n const plugin = plugins.find(p => p.pluginId === mod.pluginId);\n if (!plugin) {\n return [];\n }\n\n return [\n {\n ...internalExtension,\n plugin,\n if: combinePredicates(\n internalModule.if,\n internalExtension.version === 'v2'\n ? internalExtension.if\n : undefined,\n ),\n },\n ];\n })\n .filter(filterForbidden);\n });\n\n const appPlugin =\n plugins.find(plugin => plugin.pluginId === 'app') ??\n createFrontendPlugin({\n pluginId: 'app',\n });\n\n const configuredExtensions = [\n ...pluginExtensions.map(({ plugin, ...extension }) => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n plugin,\n source: plugin,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n if: getExtensionPredicate({ internalExtension }),\n config: undefined as unknown,\n },\n };\n }),\n ...builtinExtensions.map(extension => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n source: appPlugin,\n plugin: appPlugin,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n if: getExtensionPredicate({ internalExtension }),\n config: undefined as unknown,\n },\n };\n }),\n ];\n\n // Install all module overrides\n for (const extension of moduleExtensions) {\n const internalExtension = toInternalExtension(extension);\n\n // Check if our override is overriding an extension that already exists\n const index = configuredExtensions.findIndex(\n e => e.extension.id === extension.id,\n );\n if (index !== -1) {\n // Only implementation, attachment point and default disabled status are overridden, the source is kept\n configuredExtensions[index].extension = internalExtension;\n configuredExtensions[index].params.attachTo = internalExtension.attachTo;\n configuredExtensions[index].params.disabled = internalExtension.disabled;\n configuredExtensions[index].params.if = getExtensionPredicate({\n internalExtension,\n });\n } else {\n // Add the extension as a new one when not overriding an existing one\n configuredExtensions.push({\n extension: internalExtension,\n params: {\n plugin: extension.plugin,\n source: extension.plugin,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n if: getExtensionPredicate({ internalExtension }),\n config: undefined,\n },\n });\n }\n }\n\n const seenExtensionIds = new Set<string>();\n const deduplicatedExtensions = configuredExtensions.filter(\n ({ extension, params }) => {\n if (seenExtensionIds.has(extension.id)) {\n collector.report({\n code: 'EXTENSION_IGNORED',\n message: `The '${extension.id}' extension from the '${params.plugin.pluginId}' plugin is a duplicate and will be ignored`,\n context: {\n plugin: params.plugin,\n extensionId: extension.id,\n },\n });\n return false;\n }\n seenExtensionIds.add(extension.id);\n return true;\n },\n );\n\n const order = new Map<string, (typeof deduplicatedExtensions)[number]>();\n for (const overrideParam of parameters) {\n const extensionId = overrideParam.id;\n\n if (forbidden.has(extensionId)) {\n collector.report({\n code: 'INVALID_EXTENSION_CONFIG_KEY',\n message: `Configuration of the '${extensionId}' extension is forbidden`,\n context: {\n extensionId,\n },\n });\n continue;\n }\n\n const existing = deduplicatedExtensions.find(\n e => e.extension.id === extensionId,\n );\n if (existing) {\n if (overrideParam.attachTo) {\n existing.params.attachTo = overrideParam.attachTo;\n }\n if (overrideParam.config) {\n // TODO: merge config?\n existing.params.config = overrideParam.config;\n }\n if (\n Boolean(existing.params.disabled) !== Boolean(overrideParam.disabled)\n ) {\n existing.params.disabled = Boolean(overrideParam.disabled);\n }\n order.set(extensionId, existing);\n } else {\n collector.report({\n code: 'INVALID_EXTENSION_CONFIG_KEY',\n message: `Extension ${extensionId} does not exist`,\n context: {\n extensionId,\n },\n });\n }\n }\n\n const orderedExtensions = [\n ...order.values(),\n ...deduplicatedExtensions.filter(e => !order.has(e.extension.id)),\n ];\n\n return orderedExtensions.map(param => ({\n id: param.extension.id,\n attachTo: param.params.attachTo,\n extension: param.extension,\n disabled: param.params.disabled,\n if: param.params.if,\n plugin: param.params.plugin,\n source: param.params.source,\n config: param.params.config,\n }));\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolveAppNodeSpecs.esm.js","sources":["../../src/tree/resolveAppNodeSpecs.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 createFrontendPlugin,\n Extension,\n FrontendFeature,\n FrontendPlugin,\n} from '@backstage/frontend-plugin-api';\nimport { FilterPredicate } from '@backstage/filter-predicates';\nimport { ExtensionParameters } from './readAppExtensionsConfig';\nimport { AppNodeSpec } from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isInternalFrontendModule,\n toInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { ErrorCollector } from '../wiring/createErrorCollector';\n\nfunction normalizePlugin(plugin: FrontendPlugin): FrontendPlugin {\n // Ensure pluginId is always set for plugins in the app\n if (!plugin.pluginId && 'id' in plugin && typeof plugin.id === 'string') {\n (plugin as any).pluginId = plugin.id;\n }\n return plugin;\n}\n\nfunction combinePredicates(\n left: FilterPredicate | undefined,\n right: FilterPredicate | undefined,\n) {\n if (!left) {\n return right;\n }\n if (!right) {\n return left;\n }\n\n return { $all: [left, right] };\n}\n\nfunction getExtensionPredicate(options: {\n internalExtension: ReturnType<typeof toInternalExtension>;\n}) {\n if (options.internalExtension.version === 'v2') {\n return options.internalExtension.if;\n }\n return undefined;\n}\n\n/** @internal */\nexport function resolveAppNodeSpecs(options: {\n features?: FrontendFeature[];\n builtinExtensions?: Extension<any, any>[];\n parameters?: Array<ExtensionParameters>;\n forbidden?: Set<string>;\n collector: ErrorCollector;\n}): AppNodeSpec[] {\n const {\n builtinExtensions = [],\n parameters = [],\n forbidden = new Set(),\n features = [],\n collector,\n } = options;\n\n const plugins = features\n .filter(OpaqueFrontendPlugin.isType)\n .map(normalizePlugin);\n const modules = features.filter(isInternalFrontendModule);\n\n const filterForbidden = (\n extension: Extension<any, any> & { plugin: FrontendPlugin },\n ) => {\n if (forbidden.has(extension.id)) {\n collector.report({\n code: 'EXTENSION_IGNORED',\n message: `It is forbidden to override the '${extension.id}' extension, attempted by the '${extension.plugin.pluginId}' plugin`,\n context: {\n plugin: extension.plugin,\n extensionId: extension.id,\n },\n });\n return false;\n }\n return true;\n };\n\n const pluginExtensions = plugins.flatMap(plugin => {\n const internalPlugin = OpaqueFrontendPlugin.toInternal(plugin);\n return internalPlugin.extensions\n .map(extension => {\n const internalExtension = toInternalExtension(extension);\n return {\n ...internalExtension,\n plugin,\n if: combinePredicates(\n internalPlugin.if,\n internalExtension.version === 'v2'\n ? internalExtension.if\n : undefined,\n ),\n };\n })\n .filter(filterForbidden);\n });\n const moduleExtensions = modules.flatMap(mod => {\n const internalModule = toInternalFrontendModule(mod);\n return internalModule.extensions\n .flatMap(extension => {\n const internalExtension = toInternalExtension(extension);\n\n // Modules for plugins that are not installed are ignored\n const plugin = plugins.find(p => p.pluginId === mod.pluginId);\n if (!plugin) {\n return [];\n }\n\n return [\n {\n ...internalExtension,\n plugin,\n if: combinePredicates(\n internalModule.if,\n internalExtension.version === 'v2'\n ? internalExtension.if\n : undefined,\n ),\n },\n ];\n })\n .filter(filterForbidden);\n });\n\n const appPlugin =\n plugins.find(plugin => plugin.pluginId === 'app') ??\n createFrontendPlugin({\n pluginId: 'app',\n });\n\n const configuredExtensions = [\n ...pluginExtensions.map(({ plugin, ...extension }) => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n plugin,\n source: plugin,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n if: getExtensionPredicate({ internalExtension }),\n config: undefined as unknown,\n },\n };\n }),\n ...builtinExtensions.map(extension => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n source: appPlugin,\n plugin: appPlugin,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n if: getExtensionPredicate({ internalExtension }),\n config: undefined as unknown,\n },\n };\n }),\n ];\n\n // Install all module overrides\n for (const extension of moduleExtensions) {\n const internalExtension = toInternalExtension(extension);\n\n // Check if our override is overriding an extension that already exists\n const index = configuredExtensions.findIndex(\n e => e.extension.id === extension.id,\n );\n if (index !== -1) {\n // Only implementation, attachment point and default disabled status are overridden, the source is kept\n configuredExtensions[index].extension = internalExtension;\n configuredExtensions[index].params.attachTo = internalExtension.attachTo;\n configuredExtensions[index].params.disabled = internalExtension.disabled;\n configuredExtensions[index].params.if = getExtensionPredicate({\n internalExtension,\n });\n } else {\n // Add the extension as a new one when not overriding an existing one\n configuredExtensions.push({\n extension: internalExtension,\n params: {\n plugin: extension.plugin,\n source: extension.plugin,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n if: getExtensionPredicate({ internalExtension }),\n config: undefined,\n },\n });\n }\n }\n\n const seenExtensionIds = new Set<string>();\n const deduplicatedExtensions = configuredExtensions.filter(\n ({ extension, params }) => {\n if (seenExtensionIds.has(extension.id)) {\n collector.report({\n code: 'EXTENSION_IGNORED',\n message: `The '${extension.id}' extension from the '${params.plugin.pluginId}' plugin is a duplicate and will be ignored`,\n context: {\n plugin: params.plugin,\n extensionId: extension.id,\n },\n });\n return false;\n }\n seenExtensionIds.add(extension.id);\n return true;\n },\n );\n\n const order = new Map<string, (typeof deduplicatedExtensions)[number]>();\n for (const overrideParam of parameters) {\n const extensionId = overrideParam.id;\n\n if (forbidden.has(extensionId)) {\n collector.report({\n code: 'INVALID_EXTENSION_CONFIG_KEY',\n message: `Configuration of the '${extensionId}' extension is forbidden`,\n context: {\n extensionId,\n },\n });\n continue;\n }\n\n const existing = deduplicatedExtensions.find(\n e => e.extension.id === extensionId,\n );\n if (existing) {\n if (overrideParam.attachTo) {\n existing.params.attachTo = overrideParam.attachTo;\n }\n if (overrideParam.config) {\n // TODO: merge config?\n existing.params.config = overrideParam.config;\n }\n if (\n Boolean(existing.params.disabled) !== Boolean(overrideParam.disabled)\n ) {\n existing.params.disabled = Boolean(overrideParam.disabled);\n }\n order.set(extensionId, existing);\n } else {\n collector.report({\n code: 'INVALID_EXTENSION_CONFIG_KEY',\n message: `Extension ${extensionId} does not exist`,\n context: {\n extensionId,\n },\n });\n }\n }\n\n const orderedExtensions = [\n ...order.values(),\n ...deduplicatedExtensions.filter(e => !order.has(e.extension.id)),\n ];\n\n return orderedExtensions.map(param => ({\n id: param.extension.id,\n attachTo: param.params.attachTo,\n extension: param.extension,\n disabled: param.params.disabled,\n if: param.params.if,\n plugin: param.params.plugin,\n source: param.params.source,\n config: param.params.config,\n }));\n}\n"],"names":[],"mappings":"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"}
@@ -2,6 +2,9 @@ import { featureFlagsApiRef, ApiBlueprint } from '@backstage/frontend-plugin-api
2
2
  import { instantiateAppNodeSubtree } from '../tree/instantiateAppNodeTree.esm.js';
3
3
  import { isInternalFrontendModule, toInternalFrontendModule } from '../frontend-plugin-api/src/wiring/createFrontendModule.esm.js';
4
4
  import { OpaqueFrontendPlugin } from '../frontend-internal/src/wiring/InternalFrontendPlugin.esm.js';
5
+ import '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
6
+ import '../frontend-internal/src/wiring/InternalExtensionInput.esm.js';
7
+ import '../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
5
8
 
6
9
  function registerFeatureFlagDeclarationsInHolder(apis, features) {
7
10
  const featureFlagApi = apis.get(featureFlagsApiRef);
@@ -1 +1 @@
1
- {"version":3,"file":"apiFactories.esm.js","sources":["../../src/wiring/apiFactories.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 ApiBlueprint,\n AnyApiFactory,\n ApiHolder,\n AppNode,\n FrontendFeature,\n featureFlagsApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\nimport { instantiateAppNodeSubtree } from '../tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isInternalFrontendModule,\n toInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\nimport { ErrorCollector } from './createErrorCollector';\nimport {\n FrontendApiRegistry,\n FrontendApiResolver,\n} from './FrontendApiRegistry';\nimport { type ExtensionPredicateContext } from './predicates';\n\nexport type ApiFactoryEntry = {\n node: AppNode;\n pluginId: string;\n factory: AnyApiFactory;\n};\n\n/**\n * Registers feature flag declarations on an already prepared API holder.\n *\n * This is primarily used when bootstrap reuses APIs from a provided session\n * state rather than building a fresh registry from bootstrap-visible factories.\n */\nexport function registerFeatureFlagDeclarationsInHolder(\n apis: ApiHolder,\n features: FrontendFeature[],\n) {\n const featureFlagApi = apis.get(featureFlagsApiRef);\n if (featureFlagApi) {\n registerFeatureFlagDeclarations(featureFlagApi, features);\n }\n}\n\n/**\n * Decorates the feature flags API factory so plugin and module declarations are\n * registered whenever that API is instantiated.\n */\nexport function wrapFeatureFlagApiFactory(\n factory: AnyApiFactory,\n features: FrontendFeature[],\n) {\n if (factory.api.id !== featureFlagsApiRef.id) {\n return factory;\n }\n\n return {\n ...factory,\n factory(deps) {\n const featureFlagApi = factory.factory(\n deps,\n ) as typeof featureFlagsApiRef.T;\n registerFeatureFlagDeclarations(featureFlagApi, features);\n return featureFlagApi;\n },\n } as AnyApiFactory;\n}\n\n/**\n * Reconciles deferred API factories into the finalized API registry.\n *\n * It preserves bootstrap-frozen APIs, allows safe deferred additions, and\n * reports cases where bootstrap-visible extensions relied on APIs that only\n * became available during finalization.\n */\nexport function syncFinalApiFactories(options: {\n deferredApiNodes: Iterable<AppNode>;\n appApiRegistry: FrontendApiRegistry;\n apiResolver: FrontendApiResolver;\n collector: ErrorCollector;\n features: FrontendFeature[];\n bootstrapApiFactoryEntries: ReadonlyMap<string, ApiFactoryEntry>;\n bootstrapMissingApiAccesses: Map<string, { node: AppNode; apiRefId: string }>;\n predicateContext: ExtensionPredicateContext;\n}) {\n const finalApiEntries = collectApiFactoryEntries({\n apiNodes: options.deferredApiNodes,\n collector: options.collector,\n predicateContext: options.predicateContext,\n entries: new Map(options.bootstrapApiFactoryEntries),\n });\n // Only newly introduced or still-safe overrides are registered here. Any\n // bootstrap-materialized API remains frozen for the lifetime of the app.\n const changedEntries = Array.from(finalApiEntries.values()).filter(entry => {\n const bootstrapEntry = options.bootstrapApiFactoryEntries.get(\n entry.factory.api.id,\n );\n if (!bootstrapEntry) {\n return true;\n }\n if (bootstrapEntry.factory === entry.factory) {\n return false;\n }\n if (options.apiResolver.isMaterialized(entry.factory.api.id)) {\n options.collector.report({\n code: 'EXTENSION_BOOTSTRAP_API_OVERRIDE_IGNORED',\n message:\n `Extension '${entry.node.spec.id}' tried to override API ` +\n `'${entry.factory.api.id}' after it had already been materialized during bootstrap. ` +\n 'The bootstrap implementation was kept and the deferred override was ignored.',\n context: {\n node: entry.node,\n apiRefId: entry.factory.api.id,\n bootstrapNode: bootstrapEntry.node,\n pluginId: entry.pluginId,\n bootstrapPluginId: bootstrapEntry.pluginId,\n },\n });\n return false;\n }\n return true;\n });\n const changedFactories = changedEntries.map(entry =>\n wrapFeatureFlagApiFactory(entry.factory, options.features),\n );\n options.appApiRegistry.setAll(changedFactories);\n options.apiResolver.invalidate(\n changedFactories.map(factory => factory.api.id),\n );\n for (const bootstrapAccess of options.bootstrapMissingApiAccesses.values()) {\n if (\n options.bootstrapApiFactoryEntries.has(bootstrapAccess.apiRefId) ||\n !finalApiEntries.has(bootstrapAccess.apiRefId)\n ) {\n continue;\n }\n\n options.collector.report({\n code: 'EXTENSION_BOOTSTRAP_API_UNAVAILABLE',\n message:\n `Extension '${bootstrapAccess.node.spec.id}' tried to access API ` +\n `'${bootstrapAccess.apiRefId}' during bootstrap before it was available. ` +\n 'That API became available during finalization, so bootstrap-visible extensions must not depend on deferred APIs.',\n context: {\n node: bootstrapAccess.node,\n apiRefId: bootstrapAccess.apiRefId,\n },\n });\n }\n}\n\nconst EMPTY_API_HOLDER: ApiHolder = {\n get() {\n return undefined;\n },\n};\n\nfunction registerFeatureFlagDeclarations(\n featureFlagApi: typeof featureFlagsApiRef.T,\n features: FrontendFeature[],\n) {\n for (const feature of features) {\n if (OpaqueFrontendPlugin.isType(feature)) {\n OpaqueFrontendPlugin.toInternal(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n description: flag.description,\n pluginId: feature.id,\n }),\n );\n }\n if (isInternalFrontendModule(feature)) {\n toInternalFrontendModule(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n description: flag.description,\n pluginId: feature.pluginId,\n }),\n );\n }\n }\n}\n\n/**\n * Instantiates API extension subtrees in isolation and extracts the factories\n * they provide without mutating the live app tree.\n *\n * The collected entries are later used both for bootstrap registration and for\n * the finalization-time reconciliation of deferred API roots.\n */\nexport function collectApiFactoryEntries(options: {\n apiNodes: Iterable<AppNode>;\n collector: ErrorCollector;\n predicateContext?: ExtensionPredicateContext;\n entries?: Map<string, ApiFactoryEntry>;\n}): Map<string, ApiFactoryEntry> {\n const factoriesById = options.entries ?? new Map<string, ApiFactoryEntry>();\n for (const apiNode of options.apiNodes) {\n // API extensions are instantiated in isolation so we can inspect the\n // produced factories without mutating the live app tree.\n const detachedApiNode = instantiateAppNodeSubtree({\n rootNode: apiNode,\n apis: EMPTY_API_HOLDER,\n collector: options.collector,\n predicateContext: options.predicateContext,\n writeNodeInstances: false,\n reuseExistingInstances: false,\n });\n if (!detachedApiNode) {\n continue;\n }\n const apiFactory = detachedApiNode.instance?.getData(\n ApiBlueprint.dataRefs.factory,\n );\n if (apiFactory) {\n const apiRefId = apiFactory.api.id;\n const ownerId = getApiOwnerId(apiRefId);\n const pluginId = apiNode.spec.plugin.pluginId ?? 'app';\n const existingFactory = factoriesById.get(apiRefId);\n\n // This allows modules to override factories provided by the plugin, but\n // it rejects API overrides from other plugins. In the event of a\n // conflict, the owning plugin is attempted to be inferred from the API\n // reference ID.\n if (existingFactory && existingFactory.pluginId !== pluginId) {\n const shouldReplace =\n ownerId === pluginId && existingFactory.pluginId !== ownerId;\n const acceptedPluginId = shouldReplace\n ? pluginId\n : existingFactory.pluginId;\n const rejectedPluginId = shouldReplace\n ? existingFactory.pluginId\n : pluginId;\n\n options.collector.report({\n code: 'API_FACTORY_CONFLICT',\n message: `API '${apiRefId}' is already provided by plugin '${acceptedPluginId}', cannot also be provided by '${rejectedPluginId}'.`,\n context: {\n node: apiNode,\n apiRefId,\n pluginId: rejectedPluginId,\n existingPluginId: acceptedPluginId,\n },\n });\n if (shouldReplace) {\n factoriesById.set(apiRefId, {\n pluginId,\n node: apiNode,\n factory: apiFactory,\n });\n }\n continue;\n }\n\n factoriesById.set(apiRefId, {\n pluginId,\n node: apiNode,\n factory: apiFactory,\n });\n } else {\n options.collector.report({\n code: 'API_EXTENSION_INVALID',\n message: `API extension '${apiNode.spec.id}' did not output an API factory`,\n context: {\n node: apiNode,\n },\n });\n }\n }\n\n return factoriesById;\n}\n\n// TODO(Rugvip): It would be good if this was more explicit, but I think that\n// might need to wait for some future update for API factories.\nfunction getApiOwnerId(apiRefId: string): string {\n const [prefix, ...rest] = apiRefId.split('.');\n if (!prefix) {\n return apiRefId;\n }\n if (prefix === 'core') {\n return 'app';\n }\n if (prefix === 'plugin' && rest[0]) {\n return rest[0];\n }\n return prefix;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apiFactories.esm.js","sources":["../../src/wiring/apiFactories.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 ApiBlueprint,\n AnyApiFactory,\n ApiHolder,\n AppNode,\n FrontendFeature,\n featureFlagsApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\nimport { instantiateAppNodeSubtree } from '../tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isInternalFrontendModule,\n toInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\nimport { ErrorCollector } from './createErrorCollector';\nimport {\n FrontendApiRegistry,\n FrontendApiResolver,\n} from './FrontendApiRegistry';\nimport { type ExtensionPredicateContext } from './predicates';\n\nexport type ApiFactoryEntry = {\n node: AppNode;\n pluginId: string;\n factory: AnyApiFactory;\n};\n\n/**\n * Registers feature flag declarations on an already prepared API holder.\n *\n * This is primarily used when bootstrap reuses APIs from a provided session\n * state rather than building a fresh registry from bootstrap-visible factories.\n */\nexport function registerFeatureFlagDeclarationsInHolder(\n apis: ApiHolder,\n features: FrontendFeature[],\n) {\n const featureFlagApi = apis.get(featureFlagsApiRef);\n if (featureFlagApi) {\n registerFeatureFlagDeclarations(featureFlagApi, features);\n }\n}\n\n/**\n * Decorates the feature flags API factory so plugin and module declarations are\n * registered whenever that API is instantiated.\n */\nexport function wrapFeatureFlagApiFactory(\n factory: AnyApiFactory,\n features: FrontendFeature[],\n) {\n if (factory.api.id !== featureFlagsApiRef.id) {\n return factory;\n }\n\n return {\n ...factory,\n factory(deps) {\n const featureFlagApi = factory.factory(\n deps,\n ) as typeof featureFlagsApiRef.T;\n registerFeatureFlagDeclarations(featureFlagApi, features);\n return featureFlagApi;\n },\n } as AnyApiFactory;\n}\n\n/**\n * Reconciles deferred API factories into the finalized API registry.\n *\n * It preserves bootstrap-frozen APIs, allows safe deferred additions, and\n * reports cases where bootstrap-visible extensions relied on APIs that only\n * became available during finalization.\n */\nexport function syncFinalApiFactories(options: {\n deferredApiNodes: Iterable<AppNode>;\n appApiRegistry: FrontendApiRegistry;\n apiResolver: FrontendApiResolver;\n collector: ErrorCollector;\n features: FrontendFeature[];\n bootstrapApiFactoryEntries: ReadonlyMap<string, ApiFactoryEntry>;\n bootstrapMissingApiAccesses: Map<string, { node: AppNode; apiRefId: string }>;\n predicateContext: ExtensionPredicateContext;\n}) {\n const finalApiEntries = collectApiFactoryEntries({\n apiNodes: options.deferredApiNodes,\n collector: options.collector,\n predicateContext: options.predicateContext,\n entries: new Map(options.bootstrapApiFactoryEntries),\n });\n // Only newly introduced or still-safe overrides are registered here. Any\n // bootstrap-materialized API remains frozen for the lifetime of the app.\n const changedEntries = Array.from(finalApiEntries.values()).filter(entry => {\n const bootstrapEntry = options.bootstrapApiFactoryEntries.get(\n entry.factory.api.id,\n );\n if (!bootstrapEntry) {\n return true;\n }\n if (bootstrapEntry.factory === entry.factory) {\n return false;\n }\n if (options.apiResolver.isMaterialized(entry.factory.api.id)) {\n options.collector.report({\n code: 'EXTENSION_BOOTSTRAP_API_OVERRIDE_IGNORED',\n message:\n `Extension '${entry.node.spec.id}' tried to override API ` +\n `'${entry.factory.api.id}' after it had already been materialized during bootstrap. ` +\n 'The bootstrap implementation was kept and the deferred override was ignored.',\n context: {\n node: entry.node,\n apiRefId: entry.factory.api.id,\n bootstrapNode: bootstrapEntry.node,\n pluginId: entry.pluginId,\n bootstrapPluginId: bootstrapEntry.pluginId,\n },\n });\n return false;\n }\n return true;\n });\n const changedFactories = changedEntries.map(entry =>\n wrapFeatureFlagApiFactory(entry.factory, options.features),\n );\n options.appApiRegistry.setAll(changedFactories);\n options.apiResolver.invalidate(\n changedFactories.map(factory => factory.api.id),\n );\n for (const bootstrapAccess of options.bootstrapMissingApiAccesses.values()) {\n if (\n options.bootstrapApiFactoryEntries.has(bootstrapAccess.apiRefId) ||\n !finalApiEntries.has(bootstrapAccess.apiRefId)\n ) {\n continue;\n }\n\n options.collector.report({\n code: 'EXTENSION_BOOTSTRAP_API_UNAVAILABLE',\n message:\n `Extension '${bootstrapAccess.node.spec.id}' tried to access API ` +\n `'${bootstrapAccess.apiRefId}' during bootstrap before it was available. ` +\n 'That API became available during finalization, so bootstrap-visible extensions must not depend on deferred APIs.',\n context: {\n node: bootstrapAccess.node,\n apiRefId: bootstrapAccess.apiRefId,\n },\n });\n }\n}\n\nconst EMPTY_API_HOLDER: ApiHolder = {\n get() {\n return undefined;\n },\n};\n\nfunction registerFeatureFlagDeclarations(\n featureFlagApi: typeof featureFlagsApiRef.T,\n features: FrontendFeature[],\n) {\n for (const feature of features) {\n if (OpaqueFrontendPlugin.isType(feature)) {\n OpaqueFrontendPlugin.toInternal(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n description: flag.description,\n pluginId: feature.id,\n }),\n );\n }\n if (isInternalFrontendModule(feature)) {\n toInternalFrontendModule(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n description: flag.description,\n pluginId: feature.pluginId,\n }),\n );\n }\n }\n}\n\n/**\n * Instantiates API extension subtrees in isolation and extracts the factories\n * they provide without mutating the live app tree.\n *\n * The collected entries are later used both for bootstrap registration and for\n * the finalization-time reconciliation of deferred API roots.\n */\nexport function collectApiFactoryEntries(options: {\n apiNodes: Iterable<AppNode>;\n collector: ErrorCollector;\n predicateContext?: ExtensionPredicateContext;\n entries?: Map<string, ApiFactoryEntry>;\n}): Map<string, ApiFactoryEntry> {\n const factoriesById = options.entries ?? new Map<string, ApiFactoryEntry>();\n for (const apiNode of options.apiNodes) {\n // API extensions are instantiated in isolation so we can inspect the\n // produced factories without mutating the live app tree.\n const detachedApiNode = instantiateAppNodeSubtree({\n rootNode: apiNode,\n apis: EMPTY_API_HOLDER,\n collector: options.collector,\n predicateContext: options.predicateContext,\n writeNodeInstances: false,\n reuseExistingInstances: false,\n });\n if (!detachedApiNode) {\n continue;\n }\n const apiFactory = detachedApiNode.instance?.getData(\n ApiBlueprint.dataRefs.factory,\n );\n if (apiFactory) {\n const apiRefId = apiFactory.api.id;\n const ownerId = getApiOwnerId(apiRefId);\n const pluginId = apiNode.spec.plugin.pluginId ?? 'app';\n const existingFactory = factoriesById.get(apiRefId);\n\n // This allows modules to override factories provided by the plugin, but\n // it rejects API overrides from other plugins. In the event of a\n // conflict, the owning plugin is attempted to be inferred from the API\n // reference ID.\n if (existingFactory && existingFactory.pluginId !== pluginId) {\n const shouldReplace =\n ownerId === pluginId && existingFactory.pluginId !== ownerId;\n const acceptedPluginId = shouldReplace\n ? pluginId\n : existingFactory.pluginId;\n const rejectedPluginId = shouldReplace\n ? existingFactory.pluginId\n : pluginId;\n\n options.collector.report({\n code: 'API_FACTORY_CONFLICT',\n message: `API '${apiRefId}' is already provided by plugin '${acceptedPluginId}', cannot also be provided by '${rejectedPluginId}'.`,\n context: {\n node: apiNode,\n apiRefId,\n pluginId: rejectedPluginId,\n existingPluginId: acceptedPluginId,\n },\n });\n if (shouldReplace) {\n factoriesById.set(apiRefId, {\n pluginId,\n node: apiNode,\n factory: apiFactory,\n });\n }\n continue;\n }\n\n factoriesById.set(apiRefId, {\n pluginId,\n node: apiNode,\n factory: apiFactory,\n });\n } else {\n options.collector.report({\n code: 'API_EXTENSION_INVALID',\n message: `API extension '${apiNode.spec.id}' did not output an API factory`,\n context: {\n node: apiNode,\n },\n });\n }\n }\n\n return factoriesById;\n}\n\n// TODO(Rugvip): It would be good if this was more explicit, but I think that\n// might need to wait for some future update for API factories.\nfunction getApiOwnerId(apiRefId: string): string {\n const [prefix, ...rest] = apiRefId.split('.');\n if (!prefix) {\n return apiRefId;\n }\n if (prefix === 'core') {\n return 'app';\n }\n if (prefix === 'plugin' && rest[0]) {\n return rest[0];\n }\n return prefix;\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
1
  import once from 'lodash/once';
2
2
  import { stringifyEntityRef, parseEntityRef } from '../catalog-model/src/entity/ref.esm.js';
3
3
  import { OpaqueFrontendPlugin } from '../frontend-internal/src/wiring/InternalFrontendPlugin.esm.js';
4
+ import '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
5
+ import '../frontend-internal/src/wiring/InternalExtensionInput.esm.js';
6
+ import '../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
4
7
 
5
8
  function createPluginInfoAttacher(config, infoResolver = async (ctx) => ctx.defaultResolver({
6
9
  packageJson: await ctx.packageJson(),
@@ -1 +1 @@
1
- {"version":3,"file":"createPluginInfoAttacher.esm.js","sources":["../../src/wiring/createPluginInfoAttacher.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { ConfigApi } from '@backstage/frontend-plugin-api';\nimport {\n FrontendFeature,\n FrontendPluginInfo,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport once from 'lodash/once';\n// Avoid full dependency on catalog-model\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n parseEntityRef,\n stringifyEntityRef,\n} from '../../../catalog-model/src/entity/ref';\n\n/**\n * A function that resolves plugin info from a plugin manifest and package.json.\n *\n * @public\n */\nexport type FrontendPluginInfoResolver = (ctx: {\n packageJson(): Promise<JsonObject | undefined>;\n manifest(): Promise<JsonObject | undefined>;\n defaultResolver(sources: {\n packageJson: JsonObject | undefined;\n manifest: JsonObject | undefined;\n }): Promise<{ info: FrontendPluginInfo }>;\n}) => Promise<{ info: FrontendPluginInfo }>;\n\nexport function createPluginInfoAttacher(\n config: ConfigApi,\n infoResolver: FrontendPluginInfoResolver = async ctx =>\n ctx.defaultResolver({\n packageJson: await ctx.packageJson(),\n manifest: await ctx.manifest(),\n }),\n): (feature: FrontendFeature) => FrontendFeature {\n const applyInfoOverrides = createPluginInfoOverrider(config);\n\n return (feature: FrontendFeature) => {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n return feature;\n }\n\n const plugin = OpaqueFrontendPlugin.toInternal(feature);\n\n return {\n ...plugin,\n info: once(async () => {\n const manifestLoader = plugin.infoOptions?.manifest;\n const packageJsonLoader = plugin.infoOptions?.packageJson;\n\n const { info: resolvedInfo } = await infoResolver({\n manifest: async () => manifestLoader?.(),\n packageJson: async () => packageJsonLoader?.(),\n defaultResolver: async sources => ({\n info: {\n ...resolvePackageInfo(sources.packageJson),\n ...resolveManifestInfo(sources.manifest),\n },\n }),\n });\n\n const infoWithOverrides = applyInfoOverrides(\n plugin.pluginId,\n resolvedInfo,\n );\n return normalizePluginInfo(infoWithOverrides);\n }),\n };\n };\n}\n\nfunction normalizePluginInfo(info: FrontendPluginInfo) {\n return {\n ...info,\n ownerEntityRefs: info.ownerEntityRefs?.map(ref =>\n stringifyEntityRef(\n parseEntityRef(ref, {\n defaultKind: 'Group',\n }),\n ),\n ),\n };\n}\n\nfunction createPluginInfoOverrider(config: ConfigApi) {\n const overrideConfigs =\n config.getOptionalConfigArray('app.pluginOverrides') ?? [];\n\n const overrideMatchers = overrideConfigs.map(overrideConfig => {\n const pluginIdMatcher = makeStringMatcher(\n overrideConfig.getOptionalString('match.pluginId'),\n );\n const packageNameMatcher = makeStringMatcher(\n overrideConfig.getOptionalString('match.packageName'),\n );\n const description = overrideConfig.getOptionalString('info.description');\n const ownerEntityRefs = overrideConfig.getOptionalStringArray(\n 'info.ownerEntityRefs',\n );\n const links = overrideConfig\n .getOptionalConfigArray('info.links')\n ?.map(linkConfig => ({\n title: linkConfig.getString('title'),\n url: linkConfig.getString('url'),\n }));\n\n return {\n test(pluginId: string, packageName?: string) {\n return packageNameMatcher(packageName) && pluginIdMatcher(pluginId);\n },\n info: {\n description,\n ownerEntityRefs,\n links,\n },\n };\n });\n\n return (pluginId: string, info: FrontendPluginInfo) => {\n const { packageName } = info;\n for (const matcher of overrideMatchers) {\n if (matcher.test(pluginId, packageName)) {\n if (matcher.info.description) {\n info.description = matcher.info.description;\n }\n if (matcher.info.ownerEntityRefs) {\n info.ownerEntityRefs = matcher.info.ownerEntityRefs;\n }\n if (matcher.info.links) {\n info.links = matcher.info.links;\n }\n }\n }\n return info;\n };\n}\n\nfunction resolveManifestInfo(manifest?: JsonValue) {\n if (!isJsonObject(manifest) || !isJsonObject(manifest.metadata)) {\n return undefined;\n }\n\n const info: FrontendPluginInfo = {};\n\n if (isJsonObject(manifest.spec) && typeof manifest.spec.owner === 'string') {\n info.ownerEntityRefs = [\n stringifyEntityRef(\n parseEntityRef(manifest.spec.owner, {\n defaultKind: 'Group',\n defaultNamespace: manifest.metadata.namespace?.toString(),\n }),\n ),\n ];\n }\n\n if (Array.isArray(manifest.metadata.links)) {\n info.links = manifest.metadata.links.filter(isJsonObject).map(link => ({\n title: String(link.title),\n url: String(link.url),\n }));\n }\n\n return info;\n}\n\nfunction resolvePackageInfo(packageJson?: JsonObject) {\n if (!packageJson) {\n return undefined;\n }\n\n const info: FrontendPluginInfo = {\n packageName: packageJson?.name?.toString(),\n version: packageJson?.version?.toString(),\n description: packageJson?.description?.toString(),\n };\n\n const links: { title: string; url: string }[] = [];\n\n if (typeof packageJson.homepage === 'string') {\n links.push({\n title: 'Homepage',\n url: packageJson.homepage,\n });\n }\n\n if (\n isJsonObject(packageJson.repository) &&\n typeof packageJson.repository?.url === 'string'\n ) {\n try {\n const url = new URL(packageJson.repository?.url);\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n // TODO(Rugvip): Support more variants\n if (\n url.hostname === 'github.com' &&\n typeof packageJson.repository.directory === 'string'\n ) {\n const path = `${url.pathname}/tree/-/${packageJson.repository.directory}`;\n url.pathname = path.replaceAll('//', '/');\n }\n\n links.push({\n title: 'Repository',\n url: url.toString(),\n });\n }\n } catch {\n /* ignored */\n }\n }\n\n if (links.length > 0) {\n info.links = links;\n }\n return info;\n}\n\nfunction makeStringMatcher(pattern: string | undefined) {\n if (!pattern) {\n return () => true;\n }\n if (pattern.startsWith('/') && pattern.endsWith('/') && pattern.length > 2) {\n const regex = new RegExp(pattern.slice(1, -1));\n return (str?: string) => (str ? regex.test(str) : false);\n }\n\n return (str?: string) => str === pattern;\n}\n\nfunction isJsonObject(value?: JsonValue): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createPluginInfoAttacher.esm.js","sources":["../../src/wiring/createPluginInfoAttacher.ts"],"sourcesContent":["/*\n * Copyright 2025 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 { ConfigApi } from '@backstage/frontend-plugin-api';\nimport {\n FrontendFeature,\n FrontendPluginInfo,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\nimport { JsonObject, JsonValue } from '@backstage/types';\nimport once from 'lodash/once';\n// Avoid full dependency on catalog-model\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n parseEntityRef,\n stringifyEntityRef,\n} from '../../../catalog-model/src/entity/ref';\n\n/**\n * A function that resolves plugin info from a plugin manifest and package.json.\n *\n * @public\n */\nexport type FrontendPluginInfoResolver = (ctx: {\n packageJson(): Promise<JsonObject | undefined>;\n manifest(): Promise<JsonObject | undefined>;\n defaultResolver(sources: {\n packageJson: JsonObject | undefined;\n manifest: JsonObject | undefined;\n }): Promise<{ info: FrontendPluginInfo }>;\n}) => Promise<{ info: FrontendPluginInfo }>;\n\nexport function createPluginInfoAttacher(\n config: ConfigApi,\n infoResolver: FrontendPluginInfoResolver = async ctx =>\n ctx.defaultResolver({\n packageJson: await ctx.packageJson(),\n manifest: await ctx.manifest(),\n }),\n): (feature: FrontendFeature) => FrontendFeature {\n const applyInfoOverrides = createPluginInfoOverrider(config);\n\n return (feature: FrontendFeature) => {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n return feature;\n }\n\n const plugin = OpaqueFrontendPlugin.toInternal(feature);\n\n return {\n ...plugin,\n info: once(async () => {\n const manifestLoader = plugin.infoOptions?.manifest;\n const packageJsonLoader = plugin.infoOptions?.packageJson;\n\n const { info: resolvedInfo } = await infoResolver({\n manifest: async () => manifestLoader?.(),\n packageJson: async () => packageJsonLoader?.(),\n defaultResolver: async sources => ({\n info: {\n ...resolvePackageInfo(sources.packageJson),\n ...resolveManifestInfo(sources.manifest),\n },\n }),\n });\n\n const infoWithOverrides = applyInfoOverrides(\n plugin.pluginId,\n resolvedInfo,\n );\n return normalizePluginInfo(infoWithOverrides);\n }),\n };\n };\n}\n\nfunction normalizePluginInfo(info: FrontendPluginInfo) {\n return {\n ...info,\n ownerEntityRefs: info.ownerEntityRefs?.map(ref =>\n stringifyEntityRef(\n parseEntityRef(ref, {\n defaultKind: 'Group',\n }),\n ),\n ),\n };\n}\n\nfunction createPluginInfoOverrider(config: ConfigApi) {\n const overrideConfigs =\n config.getOptionalConfigArray('app.pluginOverrides') ?? [];\n\n const overrideMatchers = overrideConfigs.map(overrideConfig => {\n const pluginIdMatcher = makeStringMatcher(\n overrideConfig.getOptionalString('match.pluginId'),\n );\n const packageNameMatcher = makeStringMatcher(\n overrideConfig.getOptionalString('match.packageName'),\n );\n const description = overrideConfig.getOptionalString('info.description');\n const ownerEntityRefs = overrideConfig.getOptionalStringArray(\n 'info.ownerEntityRefs',\n );\n const links = overrideConfig\n .getOptionalConfigArray('info.links')\n ?.map(linkConfig => ({\n title: linkConfig.getString('title'),\n url: linkConfig.getString('url'),\n }));\n\n return {\n test(pluginId: string, packageName?: string) {\n return packageNameMatcher(packageName) && pluginIdMatcher(pluginId);\n },\n info: {\n description,\n ownerEntityRefs,\n links,\n },\n };\n });\n\n return (pluginId: string, info: FrontendPluginInfo) => {\n const { packageName } = info;\n for (const matcher of overrideMatchers) {\n if (matcher.test(pluginId, packageName)) {\n if (matcher.info.description) {\n info.description = matcher.info.description;\n }\n if (matcher.info.ownerEntityRefs) {\n info.ownerEntityRefs = matcher.info.ownerEntityRefs;\n }\n if (matcher.info.links) {\n info.links = matcher.info.links;\n }\n }\n }\n return info;\n };\n}\n\nfunction resolveManifestInfo(manifest?: JsonValue) {\n if (!isJsonObject(manifest) || !isJsonObject(manifest.metadata)) {\n return undefined;\n }\n\n const info: FrontendPluginInfo = {};\n\n if (isJsonObject(manifest.spec) && typeof manifest.spec.owner === 'string') {\n info.ownerEntityRefs = [\n stringifyEntityRef(\n parseEntityRef(manifest.spec.owner, {\n defaultKind: 'Group',\n defaultNamespace: manifest.metadata.namespace?.toString(),\n }),\n ),\n ];\n }\n\n if (Array.isArray(manifest.metadata.links)) {\n info.links = manifest.metadata.links.filter(isJsonObject).map(link => ({\n title: String(link.title),\n url: String(link.url),\n }));\n }\n\n return info;\n}\n\nfunction resolvePackageInfo(packageJson?: JsonObject) {\n if (!packageJson) {\n return undefined;\n }\n\n const info: FrontendPluginInfo = {\n packageName: packageJson?.name?.toString(),\n version: packageJson?.version?.toString(),\n description: packageJson?.description?.toString(),\n };\n\n const links: { title: string; url: string }[] = [];\n\n if (typeof packageJson.homepage === 'string') {\n links.push({\n title: 'Homepage',\n url: packageJson.homepage,\n });\n }\n\n if (\n isJsonObject(packageJson.repository) &&\n typeof packageJson.repository?.url === 'string'\n ) {\n try {\n const url = new URL(packageJson.repository?.url);\n if (url.protocol === 'http:' || url.protocol === 'https:') {\n // TODO(Rugvip): Support more variants\n if (\n url.hostname === 'github.com' &&\n typeof packageJson.repository.directory === 'string'\n ) {\n const path = `${url.pathname}/tree/-/${packageJson.repository.directory}`;\n url.pathname = path.replaceAll('//', '/');\n }\n\n links.push({\n title: 'Repository',\n url: url.toString(),\n });\n }\n } catch {\n /* ignored */\n }\n }\n\n if (links.length > 0) {\n info.links = links;\n }\n return info;\n}\n\nfunction makeStringMatcher(pattern: string | undefined) {\n if (!pattern) {\n return () => true;\n }\n if (pattern.startsWith('/') && pattern.endsWith('/') && pattern.length > 2) {\n const regex = new RegExp(pattern.slice(1, -1));\n return (str?: string) => (str ? regex.test(str) : false);\n }\n\n return (str?: string) => str === pattern;\n}\n\nfunction isJsonObject(value?: JsonValue): value is JsonObject {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { createApiRef, featureFlagsApiRef } from '@backstage/frontend-plugin-api';
2
+ import { ForwardedError } from '@backstage/errors';
2
3
 
3
4
  const EMPTY_PREDICATE_CONTEXT = {
4
5
  featureFlags: [],
@@ -37,17 +38,24 @@ function createPredicateContextLoader(options) {
37
38
  let allowedPermissions = [];
38
39
  const permissionApi = options.apis.get(localPermissionApiRef);
39
40
  if (permissionApi) {
40
- const permissionNames = options.predicateReferences.permissions;
41
- const responses = await Promise.all(
42
- permissionNames.map(
43
- (name) => permissionApi.authorize({
44
- permission: { name, type: "basic", attributes: {} }
45
- })
46
- )
47
- );
48
- allowedPermissions = permissionNames.filter(
49
- (_, i) => responses[i].result === "ALLOW"
50
- );
41
+ try {
42
+ const permissionNames = options.predicateReferences.permissions;
43
+ const responses = await Promise.all(
44
+ permissionNames.map(
45
+ (name) => permissionApi.authorize({
46
+ permission: { name, type: "basic", attributes: {} }
47
+ })
48
+ )
49
+ );
50
+ allowedPermissions = permissionNames.filter(
51
+ (_, i) => responses[i].result === "ALLOW"
52
+ );
53
+ } catch (error) {
54
+ throw new ForwardedError(
55
+ "Failed to authorize extension permissions",
56
+ error
57
+ );
58
+ }
51
59
  }
52
60
  return {
53
61
  featureFlags: getActiveFeatureFlags(),
@@ -1 +1 @@
1
- {"version":3,"file":"predicates.esm.js","sources":["../../src/wiring/predicates.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 ApiHolder,\n createApiRef,\n featureFlagsApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { FilterPredicate } from '@backstage/filter-predicates';\nimport type {\n EvaluatePermissionRequest,\n EvaluatePermissionResponse,\n} from '@backstage/plugin-permission-common';\n\nexport type ExtensionPredicateContext = {\n featureFlags: string[];\n permissions: string[];\n};\n\nexport const EMPTY_PREDICATE_CONTEXT: ExtensionPredicateContext = {\n featureFlags: [],\n permissions: [],\n};\n\n// Minimal local permission API interface to avoid a dependency on @backstage/plugin-permission-react\ntype MinimalPermissionApi = {\n authorize(\n request: EvaluatePermissionRequest,\n ): Promise<EvaluatePermissionResponse>;\n};\n\nexport const localPermissionApiRef = createApiRef<MinimalPermissionApi>({\n id: 'plugin.permission.api',\n});\n\nexport function createPredicateContextLoader(options: {\n apis: ApiHolder;\n predicateReferences: ExtensionPredicateContext;\n}) {\n function getActiveFeatureFlags() {\n const featureFlagsApi = options.apis.get(featureFlagsApiRef);\n if (!featureFlagsApi) {\n return [];\n }\n\n return options.predicateReferences.featureFlags.filter(name =>\n featureFlagsApi.isActive(name),\n );\n }\n\n function getImmediate(): ExtensionPredicateContext | undefined {\n if (options.predicateReferences.permissions.length > 0) {\n const permissionApi = options.apis.get(localPermissionApiRef);\n if (permissionApi) {\n return undefined;\n }\n }\n\n return {\n featureFlags: getActiveFeatureFlags(),\n permissions: [],\n };\n }\n\n async function load() {\n const immediatePredicateContext = getImmediate();\n if (immediatePredicateContext) {\n return immediatePredicateContext;\n }\n\n let allowedPermissions: string[] = [];\n const permissionApi = options.apis.get(localPermissionApiRef);\n if (permissionApi) {\n const permissionNames = options.predicateReferences.permissions;\n const responses = await Promise.all(\n permissionNames.map(name =>\n permissionApi.authorize({\n permission: { name, type: 'basic', attributes: {} },\n }),\n ),\n );\n allowedPermissions = permissionNames.filter(\n (_, i) => responses[i].result === 'ALLOW',\n );\n }\n\n return {\n featureFlags: getActiveFeatureFlags(),\n permissions: allowedPermissions,\n };\n }\n\n return {\n getImmediate,\n load,\n };\n}\n\nexport function collectPredicateReferences(\n nodes: Iterable<{ spec: { if?: FilterPredicate } }>,\n): ExtensionPredicateContext {\n const featureFlags = new Set<string>();\n const permissions = new Set<string>();\n\n for (const node of nodes) {\n if (node.spec.if === undefined) {\n continue;\n }\n\n for (const name of extractFeatureFlagNames(node.spec.if)) {\n featureFlags.add(name);\n }\n for (const name of extractPermissionNames(node.spec.if)) {\n permissions.add(name);\n }\n }\n\n return {\n featureFlags: Array.from(featureFlags),\n permissions: Array.from(permissions),\n };\n}\n\n/**\n * Recursively walks a FilterPredicate and returns all string values referenced\n * by `featureFlags: { $contains: '...' }` expressions. This lets us call\n * `isActive()` only for the flags that are actually used in predicates rather\n * than fetching the full registered-flag list.\n */\nfunction extractFeatureFlagNames(predicate: FilterPredicate): string[] {\n return extractPredicateKeyNames(predicate, 'featureFlags');\n}\n\n/**\n * Recursively walks a FilterPredicate and returns all string values referenced\n * by `permissions: { $contains: '...' }` expressions. This lets us issue a\n * single batched authorize call for only the permissions actually referenced.\n */\nfunction extractPermissionNames(predicate: FilterPredicate): string[] {\n return extractPredicateKeyNames(predicate, 'permissions');\n}\n\nfunction extractPredicateKeyNames(\n predicate: FilterPredicate,\n key: string,\n): string[] {\n if (typeof predicate !== 'object' || predicate === null) {\n return [];\n }\n const obj = predicate as Record<string, unknown>;\n if (Array.isArray(obj.$all)) {\n return (obj.$all as FilterPredicate[]).flatMap(p =>\n extractPredicateKeyNames(p, key),\n );\n }\n if (Array.isArray(obj.$any)) {\n return (obj.$any as FilterPredicate[]).flatMap(p =>\n extractPredicateKeyNames(p, key),\n );\n }\n if (obj.$not !== undefined) {\n return extractPredicateKeyNames(obj.$not as FilterPredicate, key);\n }\n const value = obj[key];\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const contains = (value as Record<string, unknown>).$contains;\n if (typeof contains === 'string') {\n return [contains];\n }\n }\n return [];\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"predicates.esm.js","sources":["../../src/wiring/predicates.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 ApiHolder,\n createApiRef,\n featureFlagsApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { FilterPredicate } from '@backstage/filter-predicates';\nimport type {\n EvaluatePermissionRequest,\n EvaluatePermissionResponse,\n} from '@backstage/plugin-permission-common';\nimport { ForwardedError } from '@backstage/errors';\n\nexport type ExtensionPredicateContext = {\n featureFlags: string[];\n permissions: string[];\n};\n\nexport const EMPTY_PREDICATE_CONTEXT: ExtensionPredicateContext = {\n featureFlags: [],\n permissions: [],\n};\n\n// Minimal local permission API interface to avoid a dependency on @backstage/plugin-permission-react\ntype MinimalPermissionApi = {\n authorize(\n request: EvaluatePermissionRequest,\n ): Promise<EvaluatePermissionResponse>;\n};\n\nexport const localPermissionApiRef = createApiRef<MinimalPermissionApi>({\n id: 'plugin.permission.api',\n});\n\nexport function createPredicateContextLoader(options: {\n apis: ApiHolder;\n predicateReferences: ExtensionPredicateContext;\n}) {\n function getActiveFeatureFlags() {\n const featureFlagsApi = options.apis.get(featureFlagsApiRef);\n if (!featureFlagsApi) {\n return [];\n }\n\n return options.predicateReferences.featureFlags.filter(name =>\n featureFlagsApi.isActive(name),\n );\n }\n\n function getImmediate(): ExtensionPredicateContext | undefined {\n if (options.predicateReferences.permissions.length > 0) {\n const permissionApi = options.apis.get(localPermissionApiRef);\n if (permissionApi) {\n return undefined;\n }\n }\n\n return {\n featureFlags: getActiveFeatureFlags(),\n permissions: [],\n };\n }\n\n async function load() {\n const immediatePredicateContext = getImmediate();\n if (immediatePredicateContext) {\n return immediatePredicateContext;\n }\n\n let allowedPermissions: string[] = [];\n const permissionApi = options.apis.get(localPermissionApiRef);\n if (permissionApi) {\n try {\n const permissionNames = options.predicateReferences.permissions;\n const responses = await Promise.all(\n permissionNames.map(name =>\n permissionApi.authorize({\n permission: { name, type: 'basic', attributes: {} },\n }),\n ),\n );\n allowedPermissions = permissionNames.filter(\n (_, i) => responses[i].result === 'ALLOW',\n );\n } catch (error) {\n throw new ForwardedError(\n 'Failed to authorize extension permissions',\n error,\n );\n }\n }\n\n return {\n featureFlags: getActiveFeatureFlags(),\n permissions: allowedPermissions,\n };\n }\n\n return {\n getImmediate,\n load,\n };\n}\n\nexport function collectPredicateReferences(\n nodes: Iterable<{ spec: { if?: FilterPredicate } }>,\n): ExtensionPredicateContext {\n const featureFlags = new Set<string>();\n const permissions = new Set<string>();\n\n for (const node of nodes) {\n if (node.spec.if === undefined) {\n continue;\n }\n\n for (const name of extractFeatureFlagNames(node.spec.if)) {\n featureFlags.add(name);\n }\n for (const name of extractPermissionNames(node.spec.if)) {\n permissions.add(name);\n }\n }\n\n return {\n featureFlags: Array.from(featureFlags),\n permissions: Array.from(permissions),\n };\n}\n\n/**\n * Recursively walks a FilterPredicate and returns all string values referenced\n * by `featureFlags: { $contains: '...' }` expressions. This lets us call\n * `isActive()` only for the flags that are actually used in predicates rather\n * than fetching the full registered-flag list.\n */\nfunction extractFeatureFlagNames(predicate: FilterPredicate): string[] {\n return extractPredicateKeyNames(predicate, 'featureFlags');\n}\n\n/**\n * Recursively walks a FilterPredicate and returns all string values referenced\n * by `permissions: { $contains: '...' }` expressions. This lets us issue a\n * single batched authorize call for only the permissions actually referenced.\n */\nfunction extractPermissionNames(predicate: FilterPredicate): string[] {\n return extractPredicateKeyNames(predicate, 'permissions');\n}\n\nfunction extractPredicateKeyNames(\n predicate: FilterPredicate,\n key: string,\n): string[] {\n if (typeof predicate !== 'object' || predicate === null) {\n return [];\n }\n const obj = predicate as Record<string, unknown>;\n if (Array.isArray(obj.$all)) {\n return (obj.$all as FilterPredicate[]).flatMap(p =>\n extractPredicateKeyNames(p, key),\n );\n }\n if (Array.isArray(obj.$any)) {\n return (obj.$any as FilterPredicate[]).flatMap(p =>\n extractPredicateKeyNames(p, key),\n );\n }\n if (obj.$not !== undefined) {\n return extractPredicateKeyNames(obj.$not as FilterPredicate, key);\n }\n const value = obj[key];\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n const contains = (value as Record<string, unknown>).$contains;\n if (typeof contains === 'string') {\n return [contains];\n }\n }\n return [];\n}\n"],"names":[],"mappings":"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"}
@@ -12,13 +12,16 @@ import { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig.esm.js'
12
12
  import { createPluginInfoAttacher } from './createPluginInfoAttacher.esm.js';
13
13
  import { createErrorCollector } from './createErrorCollector.esm.js';
14
14
  import { createPhaseApis, setIdentityApiTarget, instantiateAndInitializePhaseTree } from './phaseApis.esm.js';
15
- import { collectPredicateReferences, createPredicateContextLoader, EMPTY_PREDICATE_CONTEXT } from './predicates.esm.js';
15
+ import { collectPredicateReferences, EMPTY_PREDICATE_CONTEXT, createPredicateContextLoader } from './predicates.esm.js';
16
16
  import { FrontendApiRegistry } from './FrontendApiRegistry.esm.js';
17
17
  import { registerFeatureFlagDeclarationsInHolder, collectApiFactoryEntries, wrapFeatureFlagApiFactory, syncFinalApiFactories } from './apiFactories.esm.js';
18
18
  import { classifyBootstrapTree, createBootstrapApp, prepareFinalizedTree, clearFinalizationBoundaryInstances, attachThrowingFinalizationChild } from './treeLifecycle.esm.js';
19
19
  import { OpaqueType } from '../opaque-internal/src/OpaqueType.esm.js';
20
20
  import { OpaqueFrontendPlugin } from '../frontend-internal/src/wiring/InternalFrontendPlugin.esm.js';
21
21
  import { createExtensionDataContainer } from '../frontend-internal/src/wiring/createExtensionDataContainer.esm.js';
22
+ import '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
23
+ import '../frontend-internal/src/wiring/InternalExtensionInput.esm.js';
24
+ import '../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
22
25
 
23
26
  function deduplicateFeatures(allFeatures) {
24
27
  const features = Array.from(new Set(allFeatures));
@@ -1 +1 @@
1
- {"version":3,"file":"prepareSpecializedApp.esm.js","sources":["../../src/wiring/prepareSpecializedApp.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 { ConfigReader } from '@backstage/config';\nimport { isError } from '@backstage/errors';\nimport {\n AnyApiFactory,\n ApiHolder,\n AppTree,\n ConfigApi,\n coreExtensionData,\n AppNode,\n ExtensionFactoryMiddleware,\n FrontendFeature,\n IdentityApi,\n identityApiRef,\n createExtensionDataRef,\n} from '@backstage/frontend-plugin-api';\nimport {\n createExtensionDataContainer,\n OpaqueFrontendPlugin,\n} from '@internal/frontend';\nimport { OpaqueType } from '@internal/opaque';\nimport { ComponentType, ReactNode } from 'react';\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n resolveExtensionDefinition,\n toInternalExtension,\n} from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nimport { CreateAppRouteBinder } from '../routing';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\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 {\n createPluginInfoAttacher,\n FrontendPluginInfoResolver,\n} from './createPluginInfoAttacher';\nimport {\n AppError,\n createErrorCollector,\n ErrorCollector,\n} from './createErrorCollector';\nimport {\n createPhaseApis,\n instantiateAndInitializePhaseTree,\n setIdentityApiTarget,\n} from './phaseApis';\nimport {\n collectPredicateReferences,\n createPredicateContextLoader,\n EMPTY_PREDICATE_CONTEXT,\n type ExtensionPredicateContext,\n} from './predicates';\nimport { FrontendApiRegistry } from './FrontendApiRegistry';\nimport {\n ApiFactoryEntry,\n collectApiFactoryEntries,\n registerFeatureFlagDeclarationsInHolder,\n syncFinalApiFactories,\n wrapFeatureFlagApiFactory,\n} from './apiFactories';\nimport {\n attachThrowingFinalizationChild,\n BootstrapClassification,\n classifyBootstrapTree,\n clearFinalizationBoundaryInstances,\n createBootstrapApp,\n prepareFinalizedTree,\n} from './treeLifecycle';\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 (!OpaqueFrontendPlugin.isType(feature)) {\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\ntype SignInPageProps = {\n onSignInSuccess(identityApi: IdentityApi): void;\n children?: ReactNode;\n};\n\n/**\n * Result of bootstrapping a prepared specialized app.\n *\n * @public\n */\nexport type BootstrapSpecializedApp = {\n apis: ApiHolder;\n element: JSX.Element;\n tree: AppTree;\n};\n\n/**\n * Result of finalizing a prepared specialized app.\n *\n * @public\n */\nexport type FinalizedSpecializedApp = {\n apis: ApiHolder;\n element: JSX.Element;\n sessionState: SpecializedAppSessionState;\n tree: AppTree;\n errors?: AppError[];\n};\n\ntype SignInRuntime = {\n readyIdentityApi?: IdentityApi;\n requiresSignIn: boolean;\n};\n\ntype FinalizationState = {\n started: boolean;\n promise: Promise<FinalizedSpecializedApp>;\n resolve(app: FinalizedSpecializedApp): void;\n reject(error: unknown): void;\n};\n\ntype FinalizationMode = 'onFinalized' | 'finalize';\n\ntype InternalSpecializedAppSessionState = {\n apis: ApiHolder;\n identityApi?: IdentityApi;\n predicateContext: ExtensionPredicateContext;\n};\n\n/**\n * Opaque reusable session state for specialized apps.\n *\n * @public\n */\nexport type SpecializedAppSessionState = {\n $$type: '@backstage/SpecializedAppSessionState';\n};\n\nconst OpaqueSpecializedAppSessionState = OpaqueType.create<{\n public: SpecializedAppSessionState;\n versions: InternalSpecializedAppSessionState & {\n version: 'v1';\n };\n}>({\n type: '@backstage/SpecializedAppSessionState',\n versions: ['v1'],\n});\n\nconst signInPageComponentDataRef = createExtensionDataRef<\n ComponentType<SignInPageProps>\n>().with({ id: 'core.sign-in-page.component' });\n\n/**\n * Options for {@link prepareSpecializedApp}.\n *\n * @public\n */\nexport type PrepareSpecializedAppOptions = {\n /**\n * The list of features to load.\n */\n features?: FrontendFeature[];\n\n /**\n * The config API implementation to use. For most normal apps, this should be\n * specified.\n *\n * If none is given, a new _empty_ config will be used during startup. In\n * later stages of the app lifecycle, the config API in the API holder will be\n * used.\n */\n config?: ConfigApi;\n\n /**\n * Allows for the binding of plugins' external route refs within the app.\n */\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n\n /**\n * Advanced, more rarely used options.\n */\n advanced?: {\n /**\n * A reusable specialized app session state to use.\n *\n * This can be obtained from either the app passed to\n * {@link PreparedSpecializedApp.onFinalized} or from\n * {@link PreparedSpecializedApp.finalize}, and reused in a future app\n * instance to skip sign-in and session preparation.\n */\n sessionState?: SpecializedAppSessionState;\n\n /**\n * Applies one or more middleware on every extension, as they are added to\n * the application.\n *\n * This is an advanced use case for modifying extension data on the fly as\n * it gets emitted by extensions being instantiated.\n */\n extensionFactoryMiddleware?:\n | ExtensionFactoryMiddleware\n | ExtensionFactoryMiddleware[];\n\n /**\n * Allows for customizing how plugin info is retrieved.\n */\n pluginInfoResolver?: FrontendPluginInfoResolver;\n };\n};\n\n/**\n * Result of {@link prepareSpecializedApp}.\n *\n * @public\n */\nexport type PreparedSpecializedApp = {\n getBootstrapApp(): BootstrapSpecializedApp;\n onFinalized(callback: (app: FinalizedSpecializedApp) => void): () => void;\n finalize(): FinalizedSpecializedApp;\n};\n\n// Internal options type, not exported in the public API\nexport interface CreateSpecializedAppInternalOptions\n extends PrepareSpecializedAppOptions {\n __internal?: {\n apiFactoryOverrides?: AnyApiFactory[];\n };\n}\n\nexport function createSessionStateFromApis(\n apis: ApiHolder,\n): SpecializedAppSessionState {\n return OpaqueSpecializedAppSessionState.createInstance('v1', {\n apis,\n identityApi: apis.get(identityApiRef),\n predicateContext: EMPTY_PREDICATE_CONTEXT,\n });\n}\n\n/**\n * Prepares an app without instantiating the full extension tree.\n *\n * @remarks\n *\n * This is useful for split sign-in flows where the sign-in page should be\n * rendered first, and the full app finalized once an identity has been\n * captured.\n *\n * @public\n */\nexport function prepareSpecializedApp(\n options?: PrepareSpecializedAppOptions,\n): PreparedSpecializedApp {\n const internalOptions = options as CreateSpecializedAppInternalOptions;\n const config = options?.config ?? new ConfigReader({}, 'empty-config');\n const features = deduplicateFeatures(options?.features ?? []).map(\n createPluginInfoAttacher(config, options?.advanced?.pluginInfoResolver),\n );\n\n const collector = createErrorCollector();\n\n const tree = resolveAppTree(\n 'root',\n resolveAppNodeSpecs({\n features,\n builtinExtensions: [\n resolveExtensionDefinition(Root, { namespace: 'root' }),\n ],\n parameters: readAppExtensionsConfig(config),\n forbidden: new Set(['root']),\n collector,\n }),\n collector,\n );\n\n const appBasePath = getBasePath(config);\n const routeRefsById = collectRouteIds(features, collector);\n const routeBindings = resolveRouteBindings(\n options?.bindRoutes,\n config,\n routeRefsById,\n collector,\n );\n\n const mergedExtensionFactoryMiddleware = mergeExtensionFactoryMiddleware(\n options?.advanced?.extensionFactoryMiddleware,\n );\n const providedSessionState = options?.advanced?.sessionState;\n const providedSessionData = providedSessionState\n ? OpaqueSpecializedAppSessionState.toInternal(providedSessionState)\n : undefined;\n const providedApis = providedSessionData?.apis;\n // Bootstrap only renders the parts of the tree that are known to be safe\n // before predicate context and sign-in have been resolved.\n const bootstrapClassification = classifyBootstrapTree({\n tree,\n collector,\n });\n const predicateReferences = collectPredicateReferences(tree.nodes.values());\n const appApiRegistry = new FrontendApiRegistry();\n const internalStaticFactories =\n internalOptions?.__internal?.apiFactoryOverrides ?? [];\n const phaseStaticFactories = [...internalStaticFactories];\n const bootstrapApiFactoryEntries = new Map<string, ApiFactoryEntry>();\n const bootstrapMissingApiAccesses = new Map<\n string,\n { node: AppNode; apiRefId: string }\n >();\n\n if (providedApis) {\n // Reused session state already carries a fully prepared API holder, so the\n // bootstrap path only needs to register feature flag declarations on top.\n registerFeatureFlagDeclarationsInHolder(providedApis, features);\n } else {\n // Bootstrap materializes only the immediately visible API factories. Any\n // predicate-gated API roots are revisited during finalization.\n collectApiFactoryEntries({\n apiNodes: (tree.root.edges.attachments.get('apis') ?? []).filter(\n apiNode => !bootstrapClassification.deferredApiRoots.has(apiNode),\n ),\n collector,\n entries: bootstrapApiFactoryEntries,\n });\n const apiFactories = Array.from(\n bootstrapApiFactoryEntries.values(),\n entry => wrapFeatureFlagApiFactory(entry.factory, features),\n );\n appApiRegistry.registerAll(apiFactories);\n }\n const phase = createPhaseApis({\n tree,\n config,\n appApiRegistry,\n fallbackApis: providedApis,\n includeConfigApi: !providedApis,\n appBasePath,\n routeBindings,\n staticFactories: phaseStaticFactories,\n });\n const predicateContextLoader = createPredicateContextLoader({\n apis: phase.apis,\n predicateReferences,\n });\n let signInRuntime: SignInRuntime | undefined;\n let finalized: FinalizedSpecializedApp | undefined;\n let bootstrapApp: BootstrapSpecializedApp | undefined;\n\n function updateIdentityApiTarget(identityApi?: IdentityApi) {\n if (!identityApi) {\n return;\n }\n\n setIdentityApiTarget({\n identityApiProxy: phase.identityApiProxy,\n identityApi,\n signOutTargetUrl: appBasePath || '/',\n });\n }\n\n function createSessionState(predicateContext: ExtensionPredicateContext) {\n const identityApi =\n signInRuntime?.readyIdentityApi ?? providedSessionData?.identityApi;\n // As soon as a real identity is available we swap the phase proxy over so\n // the finalized tree observes the same API instance.\n updateIdentityApiTarget(identityApi);\n const sessionState = OpaqueSpecializedAppSessionState.createInstance('v1', {\n apis: phase.apis,\n identityApi,\n predicateContext,\n });\n return sessionState;\n }\n\n function getSynchronousSessionState() {\n if (providedSessionState) {\n return providedSessionState;\n }\n // The direct finalize() path is intentionally synchronous. If sign-in is\n // still pending we refuse to guess and force the caller to wait.\n if (signInRuntime?.requiresSignIn) {\n return undefined;\n }\n\n const predicateContext = predicateContextLoader.getImmediate();\n if (!predicateContext) {\n return undefined;\n }\n\n return createSessionState(predicateContext);\n }\n\n function loadAsyncSessionState() {\n if (providedSessionState) {\n return Promise.resolve(providedSessionState);\n }\n if (signInRuntime?.requiresSignIn && !signInRuntime.readyIdentityApi) {\n return Promise.reject(\n new Error(\n 'prepareSpecializedApp requires waiting for the bootstrap app to be ready before calling finalize()',\n ),\n );\n }\n\n // For apps without sign-in we can sometimes finalize immediately from the\n // already available predicate context, skipping the async loader.\n if (!signInRuntime?.requiresSignIn) {\n const immediateSessionState = getSynchronousSessionState();\n if (immediateSessionState) {\n return Promise.resolve(immediateSessionState);\n }\n }\n\n return predicateContextLoader.load().then(createSessionState);\n }\n\n function finalizeWithSessionState(\n finalizedSessionState: SpecializedAppSessionState,\n ) {\n return finalizeFromSessionState({\n finalized,\n finalizedSessionState,\n tree,\n collector,\n phase,\n extensionFactoryMiddleware: mergedExtensionFactoryMiddleware,\n routeRefsById,\n appBasePath,\n providedApis,\n features,\n appApiRegistry,\n bootstrapClassification,\n bootstrapApiFactoryEntries,\n bootstrapMissingApiAccesses,\n });\n }\n\n function finalizeWithBootstrapError(\n error: Error,\n finalizedSessionState?: SpecializedAppSessionState,\n ) {\n return finalizeFromBootstrapError({\n finalized,\n error,\n finalizedSessionState,\n tree,\n collector,\n phase,\n extensionFactoryMiddleware: mergedExtensionFactoryMiddleware,\n routeRefsById,\n signInRuntime,\n providedSessionData,\n });\n }\n\n const finalization = createFinalizationController({\n getFinalized() {\n return finalized;\n },\n setFinalized(finalizedApp) {\n finalized = finalizedApp;\n },\n finalizeFromSessionState: finalizeWithSessionState,\n finalizeFromBootstrapError: finalizeWithBootstrapError,\n });\n\n function getBootstrapApp() {\n if (bootstrapApp) {\n return bootstrapApp;\n }\n\n const runtime: SignInRuntime = {\n requiresSignIn: false,\n };\n if (!providedSessionState) {\n phase.identityApiProxy.setTargetHandlers({\n onTargetSet(identityApi) {\n runtime.readyIdentityApi = identityApi;\n // Sign-in completion only auto-starts finalization for onFinalized().\n // The direct finalize() path stays explicit and synchronous.\n if (finalization.getMode() === 'onFinalized') {\n finalization.start(loadAsyncSessionState);\n }\n },\n });\n }\n\n const result = createBootstrapApp({\n tree,\n apis: phase.apis,\n collector,\n routeRefsById,\n routeResolutionApi: phase.routeResolutionApi,\n appTreeApi: phase.appTreeApi,\n extensionFactoryMiddleware: mergedExtensionFactoryMiddleware,\n disableSignIn: Boolean(providedSessionState),\n skipBootstrapChild({ child }) {\n return bootstrapClassification.deferredRoots.has(child);\n },\n onMissingApi({ node, apiRefId }) {\n bootstrapMissingApiAccesses.set(`${node.spec.id}:${apiRefId}`, {\n node,\n apiRefId,\n });\n },\n hasSignInPage(signInPageNode) {\n return Boolean(\n signInPageNode?.instance?.getData(signInPageComponentDataRef),\n );\n },\n });\n if (!result.requiresSignIn) {\n phase.identityApiProxy.clearTargetHandlers();\n }\n\n runtime.requiresSignIn = result.requiresSignIn;\n signInRuntime = runtime;\n bootstrapApp = { ...result.bootstrapApp, apis: phase.apis };\n\n return bootstrapApp;\n }\n\n return {\n getBootstrapApp,\n onFinalized(callback) {\n finalization.selectMode('onFinalized');\n // Subscribing to finalization also ensures the bootstrap tree exists,\n // because sign-in may need to capture identity before finalization starts.\n getBootstrapApp();\n\n let subscribed = true;\n\n if (finalized) {\n const finalizedApp = finalized;\n Promise.resolve().then(() => {\n if (subscribed) {\n callback(finalizedApp);\n }\n });\n return () => {\n subscribed = false;\n };\n }\n\n // If sign-in is still in progress we wait for the shared promise created\n // by the sign-in callback. Otherwise we can start finalization right away.\n const finalizedAppPromise =\n signInRuntime?.requiresSignIn && !signInRuntime.readyIdentityApi\n ? finalization.getPromise()\n : finalization.start(loadAsyncSessionState);\n finalizedAppPromise\n .then(finalizedApp => {\n if (subscribed) {\n callback(finalizedApp);\n }\n })\n .catch(() => {});\n\n return () => {\n subscribed = false;\n };\n },\n finalize() {\n finalization.selectMode('finalize');\n if (finalized) {\n return finalized;\n }\n if (!providedSessionState) {\n // finalize() still depends on bootstrap classification and sign-in\n // discovery unless a reusable session was supplied up front, so we make\n // sure the bootstrap tree has been prepared first.\n getBootstrapApp();\n }\n\n // Direct finalization never waits for async session preparation. Callers\n // must either provide sessionState during prepareSpecializedApp() or\n // invoke finalize() only when the predicate context is already available\n // synchronously.\n const finalizedSessionState = signInRuntime?.requiresSignIn\n ? undefined\n : getSynchronousSessionState();\n if (!finalizedSessionState) {\n if (signInRuntime?.requiresSignIn) {\n throw new Error(\n 'prepareSpecializedApp requires waiting for the bootstrap app to be ready before calling finalize()',\n );\n }\n throw new Error(\n 'prepareSpecializedApp requires waiting for asynchronous finalization before calling finalize()',\n );\n }\n\n finalized = finalizeWithSessionState(finalizedSessionState);\n return finalized;\n },\n };\n}\n\n/**\n * Materializes the fully finalized app tree from a prepared session state.\n *\n * This is responsible for switching the identity proxy to the resolved target,\n * synchronizing any deferred API factories, and re-instantiating the parts of\n * the tree that are only valid once predicate context is available.\n */\nfunction finalizeFromSessionState(options: {\n finalized?: FinalizedSpecializedApp;\n finalizedSessionState: SpecializedAppSessionState;\n tree: AppTree;\n collector: ErrorCollector;\n phase: ReturnType<typeof createPhaseApis>;\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n routeRefsById: ReturnType<typeof collectRouteIds>;\n appBasePath: string;\n providedApis?: ApiHolder;\n features: FrontendFeature[];\n appApiRegistry: FrontendApiRegistry;\n bootstrapClassification: BootstrapClassification;\n bootstrapApiFactoryEntries: Map<string, ApiFactoryEntry>;\n bootstrapMissingApiAccesses: Map<string, { node: AppNode; apiRefId: string }>;\n}): FinalizedSpecializedApp {\n if (options.finalized) {\n return options.finalized;\n }\n\n const sessionStateData = OpaqueSpecializedAppSessionState.toInternal(\n options.finalizedSessionState,\n );\n if (sessionStateData.identityApi) {\n // Finalization retargets the identity proxy before any additional nodes are\n // instantiated so the full tree observes the captured identity immediately.\n setIdentityApiTarget({\n identityApiProxy: options.phase.identityApiProxy,\n identityApi: sessionStateData.identityApi,\n signOutTargetUrl: options.appBasePath || '/',\n });\n }\n if (!options.providedApis) {\n // Deferred API roots are synchronized at finalization time, but bootstrap-\n // materialized APIs stay frozen if they were already observed earlier.\n syncFinalApiFactories({\n deferredApiNodes: options.bootstrapClassification.deferredApiRoots,\n appApiRegistry: options.appApiRegistry,\n apiResolver: options.phase.apis,\n collector: options.collector,\n features: options.features,\n bootstrapApiFactoryEntries: options.bootstrapApiFactoryEntries,\n bootstrapMissingApiAccesses: options.bootstrapMissingApiAccesses,\n predicateContext: sessionStateData.predicateContext,\n });\n }\n\n prepareFinalizedTree({\n tree: options.tree,\n });\n // Finalization re-instantiates the boundary subtree so predicate-gated app\n // content can be re-evaluated without disturbing preserved bootstrap nodes.\n clearFinalizationBoundaryInstances(options.tree);\n instantiateAndInitializePhaseTree({\n tree: options.tree,\n apis: options.phase.apis,\n collector: options.collector,\n extensionFactoryMiddleware: options.extensionFactoryMiddleware,\n routeResolutionApi: options.phase.routeResolutionApi,\n appTreeApi: options.phase.appTreeApi,\n routeRefsById: options.routeRefsById,\n predicateContext: sessionStateData.predicateContext,\n });\n\n const element = options.tree.root.instance?.getData(\n coreExtensionData.reactElement,\n );\n if (!element) {\n throw new Error('Expected finalized app tree to expose a root element');\n }\n\n return {\n apis: options.phase.apis,\n element,\n sessionState: options.finalizedSessionState,\n tree: options.tree,\n errors: options.collector.collectErrors(),\n };\n}\n\n/**\n * Builds a finalized app that rethrows a bootstrap-time failure through the\n * normal app root boundary.\n *\n * This keeps the error handling path aligned with normal finalization while\n * preserving any session state that was already resolved before the failure.\n */\nfunction finalizeFromBootstrapError(options: {\n finalized?: FinalizedSpecializedApp;\n error: Error;\n finalizedSessionState?: SpecializedAppSessionState;\n tree: AppTree;\n collector: ErrorCollector;\n phase: ReturnType<typeof createPhaseApis>;\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n routeRefsById: ReturnType<typeof collectRouteIds>;\n signInRuntime?: SignInRuntime;\n providedSessionData?: InternalSpecializedAppSessionState;\n}): FinalizedSpecializedApp {\n if (options.finalized) {\n return options.finalized;\n }\n\n // If finalization fails after session state was already prepared, keep using\n // it so the error app reflects the same identity and API view.\n const finalizedSessionState =\n options.finalizedSessionState ??\n OpaqueSpecializedAppSessionState.createInstance('v1', {\n apis: options.phase.apis,\n identityApi:\n options.signInRuntime?.readyIdentityApi ??\n options.providedSessionData?.identityApi,\n predicateContext: EMPTY_PREDICATE_CONTEXT,\n });\n\n prepareFinalizedTree({\n tree: options.tree,\n });\n clearFinalizationBoundaryInstances(options.tree);\n // The final app reports bootstrap failures through app/root.children so the\n // normal app root boundary renders the error state for us.\n attachThrowingFinalizationChild(options.tree, options.error);\n instantiateAndInitializePhaseTree({\n tree: options.tree,\n apis: options.phase.apis,\n collector: options.collector,\n extensionFactoryMiddleware: options.extensionFactoryMiddleware,\n routeResolutionApi: options.phase.routeResolutionApi,\n appTreeApi: options.phase.appTreeApi,\n routeRefsById: options.routeRefsById,\n });\n\n const element = options.tree.root.instance?.getData(\n coreExtensionData.reactElement,\n );\n if (!element) {\n throw new Error('Expected finalized app tree to expose a root element');\n }\n\n return {\n apis: options.phase.apis,\n element,\n sessionState: finalizedSessionState,\n tree: options.tree,\n };\n}\n\n/**\n * Owns the callback-driven finalization lifecycle for a prepared app.\n *\n * The controller enforces the selected finalization mode, memoizes the shared\n * async finalization promise for `onFinalized()` subscribers, and funnels both\n * successful and failing async finalization through the same resolution path.\n */\nfunction createFinalizationController(options: {\n getFinalized(): FinalizedSpecializedApp | undefined;\n setFinalized(finalizedApp: FinalizedSpecializedApp): void;\n finalizeFromSessionState(\n finalizedSessionState: SpecializedAppSessionState,\n ): FinalizedSpecializedApp;\n finalizeFromBootstrapError(\n error: Error,\n finalizedSessionState?: SpecializedAppSessionState,\n ): FinalizedSpecializedApp;\n}) {\n let finalizationState: FinalizationState | undefined;\n let finalizationMode: FinalizationMode | undefined;\n\n function getState(): FinalizationState {\n if (finalizationState) {\n return finalizationState;\n }\n\n // onFinalized() subscribers all fan into the same promise so that the full\n // finalization flow only ever runs once.\n let resolve: ((app: FinalizedSpecializedApp) => void) | undefined;\n let reject: ((error: unknown) => void) | undefined;\n const promise = new Promise<FinalizedSpecializedApp>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n if (!resolve || !reject) {\n throw new Error('Failed to create finalization state');\n }\n\n finalizationState = {\n started: false,\n promise,\n resolve,\n reject,\n };\n return finalizationState;\n }\n\n return {\n getMode() {\n return finalizationMode;\n },\n getPromise() {\n return getState().promise;\n },\n selectMode(mode: FinalizationMode) {\n if (finalizationMode && finalizationMode !== mode) {\n throw new Error(\n `prepareSpecializedApp only supports using either onFinalized() or finalize(), not both`,\n );\n }\n\n // A prepared app now has one owner: either the callback-driven path or\n // the direct finalize() path, never both.\n finalizationMode = mode;\n },\n start(loader: () => Promise<SpecializedAppSessionState>) {\n const finalized = options.getFinalized();\n if (finalized) {\n return Promise.resolve(finalized);\n }\n\n const state = getState();\n if (state.started) {\n return state.promise;\n }\n state.started = true;\n\n // If loading finishes but final tree materialization fails, we still\n // want to preserve the resolved session state when building the error app.\n let finalizedSessionState: SpecializedAppSessionState | undefined;\n loader()\n .then(sessionState => {\n finalizedSessionState = sessionState;\n const finalizedApp = options.finalizeFromSessionState(sessionState);\n options.setFinalized(finalizedApp);\n state.resolve(finalizedApp);\n })\n .catch(error => {\n try {\n const bootstrapFailure = isError(error)\n ? error\n : new Error(String(error));\n const finalizedApp = options.finalizeFromBootstrapError(\n bootstrapFailure,\n finalizedSessionState,\n );\n options.setFinalized(finalizedApp);\n state.resolve(finalizedApp);\n } catch (finalizationError) {\n finalizationState = undefined;\n state.reject(finalizationError);\n }\n });\n\n return state.promise;\n },\n };\n}\n\n/**\n * Combines one or more extension factory middlewares into a single middleware\n * invocation chain that preserves Backstage's extension data container shape.\n */\nfunction mergeExtensionFactoryMiddleware(\n middlewares?: ExtensionFactoryMiddleware | ExtensionFactoryMiddleware[],\n): ExtensionFactoryMiddleware | undefined {\n if (!middlewares) {\n return undefined;\n }\n if (!Array.isArray(middlewares)) {\n return middlewares;\n }\n if (middlewares.length <= 1) {\n return middlewares[0];\n }\n return middlewares.reduce((prev, next) => {\n if (!prev || !next) {\n return prev ?? next;\n }\n return (orig, ctx) => {\n const internalExt = toInternalExtension(ctx.node.spec.extension);\n if (internalExt.version !== 'v2') {\n return orig();\n }\n return next(ctxOverrides => {\n return createExtensionDataContainer(\n prev(orig, {\n node: ctx.node,\n apis: ctx.apis,\n config: ctxOverrides?.config ?? ctx.config,\n }),\n 'extension factory middleware',\n );\n }, ctx);\n };\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prepareSpecializedApp.esm.js","sources":["../../src/wiring/prepareSpecializedApp.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 { ConfigReader } from '@backstage/config';\nimport { isError } from '@backstage/errors';\nimport {\n AnyApiFactory,\n ApiHolder,\n AppTree,\n ConfigApi,\n coreExtensionData,\n AppNode,\n ExtensionFactoryMiddleware,\n FrontendFeature,\n IdentityApi,\n identityApiRef,\n createExtensionDataRef,\n} from '@backstage/frontend-plugin-api';\nimport {\n createExtensionDataContainer,\n OpaqueFrontendPlugin,\n} from '@internal/frontend';\nimport { OpaqueType } from '@internal/opaque';\nimport { ComponentType, ReactNode } from 'react';\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n resolveExtensionDefinition,\n toInternalExtension,\n} from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nimport { CreateAppRouteBinder } from '../routing';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\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 {\n createPluginInfoAttacher,\n FrontendPluginInfoResolver,\n} from './createPluginInfoAttacher';\nimport {\n AppError,\n createErrorCollector,\n ErrorCollector,\n} from './createErrorCollector';\nimport {\n createPhaseApis,\n instantiateAndInitializePhaseTree,\n setIdentityApiTarget,\n} from './phaseApis';\nimport {\n collectPredicateReferences,\n createPredicateContextLoader,\n EMPTY_PREDICATE_CONTEXT,\n type ExtensionPredicateContext,\n} from './predicates';\nimport { FrontendApiRegistry } from './FrontendApiRegistry';\nimport {\n ApiFactoryEntry,\n collectApiFactoryEntries,\n registerFeatureFlagDeclarationsInHolder,\n syncFinalApiFactories,\n wrapFeatureFlagApiFactory,\n} from './apiFactories';\nimport {\n attachThrowingFinalizationChild,\n BootstrapClassification,\n classifyBootstrapTree,\n clearFinalizationBoundaryInstances,\n createBootstrapApp,\n prepareFinalizedTree,\n} from './treeLifecycle';\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 (!OpaqueFrontendPlugin.isType(feature)) {\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\ntype SignInPageProps = {\n onSignInSuccess(identityApi: IdentityApi): void;\n children?: ReactNode;\n};\n\n/**\n * Result of bootstrapping a prepared specialized app.\n *\n * @public\n */\nexport type BootstrapSpecializedApp = {\n apis: ApiHolder;\n element: JSX.Element;\n tree: AppTree;\n};\n\n/**\n * Result of finalizing a prepared specialized app.\n *\n * @public\n */\nexport type FinalizedSpecializedApp = {\n apis: ApiHolder;\n element: JSX.Element;\n sessionState: SpecializedAppSessionState;\n tree: AppTree;\n errors?: AppError[];\n};\n\ntype SignInRuntime = {\n readyIdentityApi?: IdentityApi;\n requiresSignIn: boolean;\n};\n\ntype FinalizationState = {\n started: boolean;\n promise: Promise<FinalizedSpecializedApp>;\n resolve(app: FinalizedSpecializedApp): void;\n reject(error: unknown): void;\n};\n\ntype FinalizationMode = 'onFinalized' | 'finalize';\n\ntype InternalSpecializedAppSessionState = {\n apis: ApiHolder;\n identityApi?: IdentityApi;\n predicateContext: ExtensionPredicateContext;\n};\n\n/**\n * Opaque reusable session state for specialized apps.\n *\n * @public\n */\nexport type SpecializedAppSessionState = {\n $$type: '@backstage/SpecializedAppSessionState';\n};\n\nconst OpaqueSpecializedAppSessionState = OpaqueType.create<{\n public: SpecializedAppSessionState;\n versions: InternalSpecializedAppSessionState & {\n version: 'v1';\n };\n}>({\n type: '@backstage/SpecializedAppSessionState',\n versions: ['v1'],\n});\n\nconst signInPageComponentDataRef = createExtensionDataRef<\n ComponentType<SignInPageProps>\n>().with({ id: 'core.sign-in-page.component' });\n\n/**\n * Options for {@link prepareSpecializedApp}.\n *\n * @public\n */\nexport type PrepareSpecializedAppOptions = {\n /**\n * The list of features to load.\n */\n features?: FrontendFeature[];\n\n /**\n * The config API implementation to use. For most normal apps, this should be\n * specified.\n *\n * If none is given, a new _empty_ config will be used during startup. In\n * later stages of the app lifecycle, the config API in the API holder will be\n * used.\n */\n config?: ConfigApi;\n\n /**\n * Allows for the binding of plugins' external route refs within the app.\n */\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n\n /**\n * Advanced, more rarely used options.\n */\n advanced?: {\n /**\n * A reusable specialized app session state to use.\n *\n * This can be obtained from either the app passed to\n * {@link PreparedSpecializedApp.onFinalized} or from\n * {@link PreparedSpecializedApp.finalize}, and reused in a future app\n * instance to skip sign-in and session preparation.\n */\n sessionState?: SpecializedAppSessionState;\n\n /**\n * Applies one or more middleware on every extension, as they are added to\n * the application.\n *\n * This is an advanced use case for modifying extension data on the fly as\n * it gets emitted by extensions being instantiated.\n */\n extensionFactoryMiddleware?:\n | ExtensionFactoryMiddleware\n | ExtensionFactoryMiddleware[];\n\n /**\n * Allows for customizing how plugin info is retrieved.\n */\n pluginInfoResolver?: FrontendPluginInfoResolver;\n };\n};\n\n/**\n * Result of {@link prepareSpecializedApp}.\n *\n * @public\n */\nexport type PreparedSpecializedApp = {\n getBootstrapApp(): BootstrapSpecializedApp;\n onFinalized(callback: (app: FinalizedSpecializedApp) => void): () => void;\n finalize(): FinalizedSpecializedApp;\n};\n\n// Internal options type, not exported in the public API\nexport interface CreateSpecializedAppInternalOptions\n extends PrepareSpecializedAppOptions {\n __internal?: {\n apiFactoryOverrides?: AnyApiFactory[];\n };\n}\n\nexport function createSessionStateFromApis(\n apis: ApiHolder,\n): SpecializedAppSessionState {\n return OpaqueSpecializedAppSessionState.createInstance('v1', {\n apis,\n identityApi: apis.get(identityApiRef),\n predicateContext: EMPTY_PREDICATE_CONTEXT,\n });\n}\n\n/**\n * Prepares an app without instantiating the full extension tree.\n *\n * @remarks\n *\n * This is useful for split sign-in flows where the sign-in page should be\n * rendered first, and the full app finalized once an identity has been\n * captured.\n *\n * @public\n */\nexport function prepareSpecializedApp(\n options?: PrepareSpecializedAppOptions,\n): PreparedSpecializedApp {\n const internalOptions = options as CreateSpecializedAppInternalOptions;\n const config = options?.config ?? new ConfigReader({}, 'empty-config');\n const features = deduplicateFeatures(options?.features ?? []).map(\n createPluginInfoAttacher(config, options?.advanced?.pluginInfoResolver),\n );\n\n const collector = createErrorCollector();\n\n const tree = resolveAppTree(\n 'root',\n resolveAppNodeSpecs({\n features,\n builtinExtensions: [\n resolveExtensionDefinition(Root, { namespace: 'root' }),\n ],\n parameters: readAppExtensionsConfig(config),\n forbidden: new Set(['root']),\n collector,\n }),\n collector,\n );\n\n const appBasePath = getBasePath(config);\n const routeRefsById = collectRouteIds(features, collector);\n const routeBindings = resolveRouteBindings(\n options?.bindRoutes,\n config,\n routeRefsById,\n collector,\n );\n\n const mergedExtensionFactoryMiddleware = mergeExtensionFactoryMiddleware(\n options?.advanced?.extensionFactoryMiddleware,\n );\n const providedSessionState = options?.advanced?.sessionState;\n const providedSessionData = providedSessionState\n ? OpaqueSpecializedAppSessionState.toInternal(providedSessionState)\n : undefined;\n const providedApis = providedSessionData?.apis;\n // Bootstrap only renders the parts of the tree that are known to be safe\n // before predicate context and sign-in have been resolved.\n const bootstrapClassification = classifyBootstrapTree({\n tree,\n collector,\n });\n const predicateReferences = collectPredicateReferences(tree.nodes.values());\n const appApiRegistry = new FrontendApiRegistry();\n const internalStaticFactories =\n internalOptions?.__internal?.apiFactoryOverrides ?? [];\n const phaseStaticFactories = [...internalStaticFactories];\n const bootstrapApiFactoryEntries = new Map<string, ApiFactoryEntry>();\n const bootstrapMissingApiAccesses = new Map<\n string,\n { node: AppNode; apiRefId: string }\n >();\n\n if (providedApis) {\n // Reused session state already carries a fully prepared API holder, so the\n // bootstrap path only needs to register feature flag declarations on top.\n registerFeatureFlagDeclarationsInHolder(providedApis, features);\n } else {\n // Bootstrap materializes only the immediately visible API factories. Any\n // predicate-gated API roots are revisited during finalization.\n collectApiFactoryEntries({\n apiNodes: (tree.root.edges.attachments.get('apis') ?? []).filter(\n apiNode => !bootstrapClassification.deferredApiRoots.has(apiNode),\n ),\n collector,\n entries: bootstrapApiFactoryEntries,\n });\n const apiFactories = Array.from(\n bootstrapApiFactoryEntries.values(),\n entry => wrapFeatureFlagApiFactory(entry.factory, features),\n );\n appApiRegistry.registerAll(apiFactories);\n }\n const phase = createPhaseApis({\n tree,\n config,\n appApiRegistry,\n fallbackApis: providedApis,\n includeConfigApi: !providedApis,\n appBasePath,\n routeBindings,\n staticFactories: phaseStaticFactories,\n });\n const predicateContextLoader = createPredicateContextLoader({\n apis: phase.apis,\n predicateReferences,\n });\n let signInRuntime: SignInRuntime | undefined;\n let finalized: FinalizedSpecializedApp | undefined;\n let bootstrapApp: BootstrapSpecializedApp | undefined;\n\n function updateIdentityApiTarget(identityApi?: IdentityApi) {\n if (!identityApi) {\n return;\n }\n\n setIdentityApiTarget({\n identityApiProxy: phase.identityApiProxy,\n identityApi,\n signOutTargetUrl: appBasePath || '/',\n });\n }\n\n function createSessionState(predicateContext: ExtensionPredicateContext) {\n const identityApi =\n signInRuntime?.readyIdentityApi ?? providedSessionData?.identityApi;\n // As soon as a real identity is available we swap the phase proxy over so\n // the finalized tree observes the same API instance.\n updateIdentityApiTarget(identityApi);\n const sessionState = OpaqueSpecializedAppSessionState.createInstance('v1', {\n apis: phase.apis,\n identityApi,\n predicateContext,\n });\n return sessionState;\n }\n\n function getSynchronousSessionState() {\n if (providedSessionState) {\n return providedSessionState;\n }\n // The direct finalize() path is intentionally synchronous. If sign-in is\n // still pending we refuse to guess and force the caller to wait.\n if (signInRuntime?.requiresSignIn) {\n return undefined;\n }\n\n const predicateContext = predicateContextLoader.getImmediate();\n if (!predicateContext) {\n return undefined;\n }\n\n return createSessionState(predicateContext);\n }\n\n function loadAsyncSessionState() {\n if (providedSessionState) {\n return Promise.resolve(providedSessionState);\n }\n if (signInRuntime?.requiresSignIn && !signInRuntime.readyIdentityApi) {\n return Promise.reject(\n new Error(\n 'prepareSpecializedApp requires waiting for the bootstrap app to be ready before calling finalize()',\n ),\n );\n }\n\n // For apps without sign-in we can sometimes finalize immediately from the\n // already available predicate context, skipping the async loader.\n if (!signInRuntime?.requiresSignIn) {\n const immediateSessionState = getSynchronousSessionState();\n if (immediateSessionState) {\n return Promise.resolve(immediateSessionState);\n }\n }\n\n return predicateContextLoader.load().then(createSessionState);\n }\n\n function finalizeWithSessionState(\n finalizedSessionState: SpecializedAppSessionState,\n ) {\n return finalizeFromSessionState({\n finalized,\n finalizedSessionState,\n tree,\n collector,\n phase,\n extensionFactoryMiddleware: mergedExtensionFactoryMiddleware,\n routeRefsById,\n appBasePath,\n providedApis,\n features,\n appApiRegistry,\n bootstrapClassification,\n bootstrapApiFactoryEntries,\n bootstrapMissingApiAccesses,\n });\n }\n\n function finalizeWithBootstrapError(\n error: Error,\n finalizedSessionState?: SpecializedAppSessionState,\n ) {\n return finalizeFromBootstrapError({\n finalized,\n error,\n finalizedSessionState,\n tree,\n collector,\n phase,\n extensionFactoryMiddleware: mergedExtensionFactoryMiddleware,\n routeRefsById,\n signInRuntime,\n providedSessionData,\n });\n }\n\n const finalization = createFinalizationController({\n getFinalized() {\n return finalized;\n },\n setFinalized(finalizedApp) {\n finalized = finalizedApp;\n },\n finalizeFromSessionState: finalizeWithSessionState,\n finalizeFromBootstrapError: finalizeWithBootstrapError,\n });\n\n function getBootstrapApp() {\n if (bootstrapApp) {\n return bootstrapApp;\n }\n\n const runtime: SignInRuntime = {\n requiresSignIn: false,\n };\n if (!providedSessionState) {\n phase.identityApiProxy.setTargetHandlers({\n onTargetSet(identityApi) {\n runtime.readyIdentityApi = identityApi;\n // Sign-in completion only auto-starts finalization for onFinalized().\n // The direct finalize() path stays explicit and synchronous.\n if (finalization.getMode() === 'onFinalized') {\n finalization.start(loadAsyncSessionState);\n }\n },\n });\n }\n\n const result = createBootstrapApp({\n tree,\n apis: phase.apis,\n collector,\n routeRefsById,\n routeResolutionApi: phase.routeResolutionApi,\n appTreeApi: phase.appTreeApi,\n extensionFactoryMiddleware: mergedExtensionFactoryMiddleware,\n disableSignIn: Boolean(providedSessionState),\n skipBootstrapChild({ child }) {\n return bootstrapClassification.deferredRoots.has(child);\n },\n onMissingApi({ node, apiRefId }) {\n bootstrapMissingApiAccesses.set(`${node.spec.id}:${apiRefId}`, {\n node,\n apiRefId,\n });\n },\n hasSignInPage(signInPageNode) {\n return Boolean(\n signInPageNode?.instance?.getData(signInPageComponentDataRef),\n );\n },\n });\n if (!result.requiresSignIn) {\n phase.identityApiProxy.clearTargetHandlers();\n }\n\n runtime.requiresSignIn = result.requiresSignIn;\n signInRuntime = runtime;\n bootstrapApp = { ...result.bootstrapApp, apis: phase.apis };\n\n return bootstrapApp;\n }\n\n return {\n getBootstrapApp,\n onFinalized(callback) {\n finalization.selectMode('onFinalized');\n // Subscribing to finalization also ensures the bootstrap tree exists,\n // because sign-in may need to capture identity before finalization starts.\n getBootstrapApp();\n\n let subscribed = true;\n\n if (finalized) {\n const finalizedApp = finalized;\n Promise.resolve().then(() => {\n if (subscribed) {\n callback(finalizedApp);\n }\n });\n return () => {\n subscribed = false;\n };\n }\n\n // If sign-in is still in progress we wait for the shared promise created\n // by the sign-in callback. Otherwise we can start finalization right away.\n const finalizedAppPromise =\n signInRuntime?.requiresSignIn && !signInRuntime.readyIdentityApi\n ? finalization.getPromise()\n : finalization.start(loadAsyncSessionState);\n finalizedAppPromise\n .then(finalizedApp => {\n if (subscribed) {\n callback(finalizedApp);\n }\n })\n .catch(() => {});\n\n return () => {\n subscribed = false;\n };\n },\n finalize() {\n finalization.selectMode('finalize');\n if (finalized) {\n return finalized;\n }\n if (!providedSessionState) {\n // finalize() still depends on bootstrap classification and sign-in\n // discovery unless a reusable session was supplied up front, so we make\n // sure the bootstrap tree has been prepared first.\n getBootstrapApp();\n }\n\n // Direct finalization never waits for async session preparation. Callers\n // must either provide sessionState during prepareSpecializedApp() or\n // invoke finalize() only when the predicate context is already available\n // synchronously.\n const finalizedSessionState = signInRuntime?.requiresSignIn\n ? undefined\n : getSynchronousSessionState();\n if (!finalizedSessionState) {\n if (signInRuntime?.requiresSignIn) {\n throw new Error(\n 'prepareSpecializedApp requires waiting for the bootstrap app to be ready before calling finalize()',\n );\n }\n throw new Error(\n 'prepareSpecializedApp requires waiting for asynchronous finalization before calling finalize()',\n );\n }\n\n finalized = finalizeWithSessionState(finalizedSessionState);\n return finalized;\n },\n };\n}\n\n/**\n * Materializes the fully finalized app tree from a prepared session state.\n *\n * This is responsible for switching the identity proxy to the resolved target,\n * synchronizing any deferred API factories, and re-instantiating the parts of\n * the tree that are only valid once predicate context is available.\n */\nfunction finalizeFromSessionState(options: {\n finalized?: FinalizedSpecializedApp;\n finalizedSessionState: SpecializedAppSessionState;\n tree: AppTree;\n collector: ErrorCollector;\n phase: ReturnType<typeof createPhaseApis>;\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n routeRefsById: ReturnType<typeof collectRouteIds>;\n appBasePath: string;\n providedApis?: ApiHolder;\n features: FrontendFeature[];\n appApiRegistry: FrontendApiRegistry;\n bootstrapClassification: BootstrapClassification;\n bootstrapApiFactoryEntries: Map<string, ApiFactoryEntry>;\n bootstrapMissingApiAccesses: Map<string, { node: AppNode; apiRefId: string }>;\n}): FinalizedSpecializedApp {\n if (options.finalized) {\n return options.finalized;\n }\n\n const sessionStateData = OpaqueSpecializedAppSessionState.toInternal(\n options.finalizedSessionState,\n );\n if (sessionStateData.identityApi) {\n // Finalization retargets the identity proxy before any additional nodes are\n // instantiated so the full tree observes the captured identity immediately.\n setIdentityApiTarget({\n identityApiProxy: options.phase.identityApiProxy,\n identityApi: sessionStateData.identityApi,\n signOutTargetUrl: options.appBasePath || '/',\n });\n }\n if (!options.providedApis) {\n // Deferred API roots are synchronized at finalization time, but bootstrap-\n // materialized APIs stay frozen if they were already observed earlier.\n syncFinalApiFactories({\n deferredApiNodes: options.bootstrapClassification.deferredApiRoots,\n appApiRegistry: options.appApiRegistry,\n apiResolver: options.phase.apis,\n collector: options.collector,\n features: options.features,\n bootstrapApiFactoryEntries: options.bootstrapApiFactoryEntries,\n bootstrapMissingApiAccesses: options.bootstrapMissingApiAccesses,\n predicateContext: sessionStateData.predicateContext,\n });\n }\n\n prepareFinalizedTree({\n tree: options.tree,\n });\n // Finalization re-instantiates the boundary subtree so predicate-gated app\n // content can be re-evaluated without disturbing preserved bootstrap nodes.\n clearFinalizationBoundaryInstances(options.tree);\n instantiateAndInitializePhaseTree({\n tree: options.tree,\n apis: options.phase.apis,\n collector: options.collector,\n extensionFactoryMiddleware: options.extensionFactoryMiddleware,\n routeResolutionApi: options.phase.routeResolutionApi,\n appTreeApi: options.phase.appTreeApi,\n routeRefsById: options.routeRefsById,\n predicateContext: sessionStateData.predicateContext,\n });\n\n const element = options.tree.root.instance?.getData(\n coreExtensionData.reactElement,\n );\n if (!element) {\n throw new Error('Expected finalized app tree to expose a root element');\n }\n\n return {\n apis: options.phase.apis,\n element,\n sessionState: options.finalizedSessionState,\n tree: options.tree,\n errors: options.collector.collectErrors(),\n };\n}\n\n/**\n * Builds a finalized app that rethrows a bootstrap-time failure through the\n * normal app root boundary.\n *\n * This keeps the error handling path aligned with normal finalization while\n * preserving any session state that was already resolved before the failure.\n */\nfunction finalizeFromBootstrapError(options: {\n finalized?: FinalizedSpecializedApp;\n error: Error;\n finalizedSessionState?: SpecializedAppSessionState;\n tree: AppTree;\n collector: ErrorCollector;\n phase: ReturnType<typeof createPhaseApis>;\n extensionFactoryMiddleware?: ExtensionFactoryMiddleware;\n routeRefsById: ReturnType<typeof collectRouteIds>;\n signInRuntime?: SignInRuntime;\n providedSessionData?: InternalSpecializedAppSessionState;\n}): FinalizedSpecializedApp {\n if (options.finalized) {\n return options.finalized;\n }\n\n // If finalization fails after session state was already prepared, keep using\n // it so the error app reflects the same identity and API view.\n const finalizedSessionState =\n options.finalizedSessionState ??\n OpaqueSpecializedAppSessionState.createInstance('v1', {\n apis: options.phase.apis,\n identityApi:\n options.signInRuntime?.readyIdentityApi ??\n options.providedSessionData?.identityApi,\n predicateContext: EMPTY_PREDICATE_CONTEXT,\n });\n\n prepareFinalizedTree({\n tree: options.tree,\n });\n clearFinalizationBoundaryInstances(options.tree);\n // The final app reports bootstrap failures through app/root.children so the\n // normal app root boundary renders the error state for us.\n attachThrowingFinalizationChild(options.tree, options.error);\n instantiateAndInitializePhaseTree({\n tree: options.tree,\n apis: options.phase.apis,\n collector: options.collector,\n extensionFactoryMiddleware: options.extensionFactoryMiddleware,\n routeResolutionApi: options.phase.routeResolutionApi,\n appTreeApi: options.phase.appTreeApi,\n routeRefsById: options.routeRefsById,\n });\n\n const element = options.tree.root.instance?.getData(\n coreExtensionData.reactElement,\n );\n if (!element) {\n throw new Error('Expected finalized app tree to expose a root element');\n }\n\n return {\n apis: options.phase.apis,\n element,\n sessionState: finalizedSessionState,\n tree: options.tree,\n };\n}\n\n/**\n * Owns the callback-driven finalization lifecycle for a prepared app.\n *\n * The controller enforces the selected finalization mode, memoizes the shared\n * async finalization promise for `onFinalized()` subscribers, and funnels both\n * successful and failing async finalization through the same resolution path.\n */\nfunction createFinalizationController(options: {\n getFinalized(): FinalizedSpecializedApp | undefined;\n setFinalized(finalizedApp: FinalizedSpecializedApp): void;\n finalizeFromSessionState(\n finalizedSessionState: SpecializedAppSessionState,\n ): FinalizedSpecializedApp;\n finalizeFromBootstrapError(\n error: Error,\n finalizedSessionState?: SpecializedAppSessionState,\n ): FinalizedSpecializedApp;\n}) {\n let finalizationState: FinalizationState | undefined;\n let finalizationMode: FinalizationMode | undefined;\n\n function getState(): FinalizationState {\n if (finalizationState) {\n return finalizationState;\n }\n\n // onFinalized() subscribers all fan into the same promise so that the full\n // finalization flow only ever runs once.\n let resolve: ((app: FinalizedSpecializedApp) => void) | undefined;\n let reject: ((error: unknown) => void) | undefined;\n const promise = new Promise<FinalizedSpecializedApp>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n if (!resolve || !reject) {\n throw new Error('Failed to create finalization state');\n }\n\n finalizationState = {\n started: false,\n promise,\n resolve,\n reject,\n };\n return finalizationState;\n }\n\n return {\n getMode() {\n return finalizationMode;\n },\n getPromise() {\n return getState().promise;\n },\n selectMode(mode: FinalizationMode) {\n if (finalizationMode && finalizationMode !== mode) {\n throw new Error(\n `prepareSpecializedApp only supports using either onFinalized() or finalize(), not both`,\n );\n }\n\n // A prepared app now has one owner: either the callback-driven path or\n // the direct finalize() path, never both.\n finalizationMode = mode;\n },\n start(loader: () => Promise<SpecializedAppSessionState>) {\n const finalized = options.getFinalized();\n if (finalized) {\n return Promise.resolve(finalized);\n }\n\n const state = getState();\n if (state.started) {\n return state.promise;\n }\n state.started = true;\n\n // If loading finishes but final tree materialization fails, we still\n // want to preserve the resolved session state when building the error app.\n let finalizedSessionState: SpecializedAppSessionState | undefined;\n loader()\n .then(sessionState => {\n finalizedSessionState = sessionState;\n const finalizedApp = options.finalizeFromSessionState(sessionState);\n options.setFinalized(finalizedApp);\n state.resolve(finalizedApp);\n })\n .catch(error => {\n try {\n const bootstrapFailure = isError(error)\n ? error\n : new Error(String(error));\n const finalizedApp = options.finalizeFromBootstrapError(\n bootstrapFailure,\n finalizedSessionState,\n );\n options.setFinalized(finalizedApp);\n state.resolve(finalizedApp);\n } catch (finalizationError) {\n finalizationState = undefined;\n state.reject(finalizationError);\n }\n });\n\n return state.promise;\n },\n };\n}\n\n/**\n * Combines one or more extension factory middlewares into a single middleware\n * invocation chain that preserves Backstage's extension data container shape.\n */\nfunction mergeExtensionFactoryMiddleware(\n middlewares?: ExtensionFactoryMiddleware | ExtensionFactoryMiddleware[],\n): ExtensionFactoryMiddleware | undefined {\n if (!middlewares) {\n return undefined;\n }\n if (!Array.isArray(middlewares)) {\n return middlewares;\n }\n if (middlewares.length <= 1) {\n return middlewares[0];\n }\n return middlewares.reduce((prev, next) => {\n if (!prev || !next) {\n return prev ?? next;\n }\n return (orig, ctx) => {\n const internalExt = toInternalExtension(ctx.node.spec.extension);\n if (internalExt.version !== 'v2') {\n return orig();\n }\n return next(ctxOverrides => {\n return createExtensionDataContainer(\n prev(orig, {\n node: ctx.node,\n apis: ctx.apis,\n config: ctxOverrides?.config ?? ctx.config,\n }),\n 'extension factory middleware',\n );\n }, ctx);\n };\n });\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/frontend-app-api",
3
- "version": "0.16.2-next.0",
3
+ "version": "0.16.2-next.2",
4
4
  "backstage": {
5
5
  "role": "web-library"
6
6
  },
@@ -32,24 +32,24 @@
32
32
  "test": "backstage-cli package test"
33
33
  },
34
34
  "dependencies": {
35
- "@backstage/config": "1.3.6",
36
- "@backstage/core-app-api": "1.19.7-next.0",
37
- "@backstage/core-plugin-api": "1.12.5-next.0",
38
- "@backstage/errors": "1.2.7",
39
- "@backstage/filter-predicates": "0.1.1",
40
- "@backstage/frontend-defaults": "0.5.1-next.0",
41
- "@backstage/frontend-plugin-api": "0.15.2-next.0",
35
+ "@backstage/config": "1.3.7-next.0",
36
+ "@backstage/core-app-api": "1.20.0-next.2",
37
+ "@backstage/core-plugin-api": "1.12.5-next.2",
38
+ "@backstage/errors": "1.3.0-next.0",
39
+ "@backstage/filter-predicates": "0.1.2-next.0",
40
+ "@backstage/frontend-defaults": "0.5.1-next.2",
41
+ "@backstage/frontend-plugin-api": "0.16.0-next.2",
42
42
  "@backstage/types": "1.2.2",
43
43
  "@backstage/version-bridge": "1.0.12",
44
44
  "lodash": "^4.17.21",
45
45
  "zod": "^3.25.76 || ^4.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@backstage/cli": "0.36.1-next.0",
49
- "@backstage/frontend-test-utils": "0.5.2-next.0",
50
- "@backstage/plugin-app": "0.4.3-next.0",
51
- "@backstage/plugin-permission-common": "0.9.7",
52
- "@backstage/test-utils": "1.7.17-next.0",
48
+ "@backstage/cli": "0.36.1-next.2",
49
+ "@backstage/frontend-test-utils": "0.5.2-next.2",
50
+ "@backstage/plugin-app": "0.4.3-next.2",
51
+ "@backstage/plugin-permission-common": "0.9.8-next.0",
52
+ "@backstage/test-utils": "1.7.17-next.2",
53
53
  "@testing-library/jest-dom": "^6.0.0",
54
54
  "@testing-library/react": "^16.0.0",
55
55
  "@types/react": "^18.0.0",