@backstage/frontend-app-api 0.13.1 → 0.13.2-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @backstage/frontend-app-api
2
2
 
3
+ ## 0.13.2-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility.
8
+ - Updated dependencies
9
+ - @backstage/core-app-api@1.19.2-next.0
10
+ - @backstage/core-plugin-api@1.11.2-next.0
11
+ - @backstage/config@1.3.6-next.0
12
+ - @backstage/frontend-plugin-api@0.12.2-next.0
13
+ - @backstage/errors@1.2.7
14
+ - @backstage/frontend-defaults@0.3.3-next.0
15
+ - @backstage/types@1.2.2
16
+ - @backstage/version-bridge@1.0.11
17
+
3
18
  ## 0.13.1
4
19
 
5
20
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"ExternalRouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/ExternalRouteRef.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RouteRefImpl } from './RouteRef';\nimport { describeParentCallSite } from './describeParentCallSite';\nimport { AnyRouteRefParams } from './types';\n\n/**\n * Route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface ExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/ExternalRouteRef';\n readonly T: TParams;\n}\n\n/** @internal */\nexport interface InternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends ExternalRouteRef<TParams> {\n readonly version: 'v1';\n getParams(): string[];\n getDescription(): string;\n getDefaultTarget(): string | undefined;\n\n setId(id: string): void;\n}\n\n/** @internal */\nexport function toInternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: ExternalRouteRef<TParams>): InternalExternalRouteRef<TParams> {\n const r = resource as InternalExternalRouteRef<TParams>;\n if (r.$$type !== '@backstage/ExternalRouteRef') {\n throw new Error(`Invalid ExternalRouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isExternalRouteRef(opaque: {\n $$type: string;\n}): opaque is ExternalRouteRef {\n return opaque.$$type === '@backstage/ExternalRouteRef';\n}\n\n/** @internal */\nclass ExternalRouteRefImpl\n extends RouteRefImpl\n implements InternalExternalRouteRef\n{\n readonly $$type = '@backstage/ExternalRouteRef' as any;\n\n constructor(\n readonly params: string[] = [],\n readonly defaultTarget: string | undefined,\n creationSite: string,\n ) {\n super(params, creationSite);\n }\n\n getDefaultTarget() {\n return this.defaultTarget;\n }\n}\n\n/**\n * Creates a route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @param options - Description of the route reference to be created.\n * @public\n */\nexport function createExternalRouteRef<\n TParams extends { [param in TParamKeys]: string } | undefined = undefined,\n TParamKeys extends string = string,\n>(options?: {\n /**\n * The parameters that will be provided to the external route reference.\n */\n readonly params?: string extends TParamKeys\n ? (keyof TParams)[]\n : TParamKeys[];\n\n /**\n * The route (typically in another plugin) that this should map to by default.\n *\n * The string is expected to be on the standard `<plugin id>.<route id>` form,\n * for example `techdocs.docRoot`.\n */\n defaultTarget?: string;\n}): ExternalRouteRef<\n keyof TParams extends never\n ? undefined\n : string extends TParamKeys\n ? TParams\n : { [param in TParamKeys]: string }\n> {\n return new ExternalRouteRefImpl(\n options?.params as string[] | undefined,\n options?.defaultTarget,\n describeParentCallSite(),\n );\n}\n"],"names":[],"mappings":"AAiDO,SAAS,2BAEd,QAAA,EAAwE;AACxE,EAAA,MAAM,CAAA,GAAI,QAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,6BAAA,EAA+B;AAC9C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oCAAA,EAAuC,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EACpE;AAEA,EAAA,OAAO,CAAA;AACT;AAGO,SAAS,mBAAmB,MAAA,EAEJ;AAC7B,EAAA,OAAO,OAAO,MAAA,KAAW,6BAAA;AAC3B;;;;"}
1
+ {"version":3,"file":"ExternalRouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/ExternalRouteRef.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RouteRefImpl } from './RouteRef';\nimport { describeParentCallSite } from './describeParentCallSite';\nimport { AnyRouteRefParams } from './types';\n\n/**\n * Route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface ExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/ExternalRouteRef';\n readonly T: TParams;\n}\n\n/** @internal */\nexport interface InternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends ExternalRouteRef<TParams> {\n readonly version: 'v1';\n getParams(): string[];\n getDescription(): string;\n getDefaultTarget(): string | undefined;\n\n setId(id: string): void;\n}\n\n/** @internal */\nexport function toInternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: ExternalRouteRef<TParams>): InternalExternalRouteRef<TParams> {\n const r = resource as InternalExternalRouteRef<TParams>;\n if (r.$$type !== '@backstage/ExternalRouteRef') {\n throw new Error(`Invalid ExternalRouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isExternalRouteRef(opaque: {\n $$type: string;\n}): opaque is ExternalRouteRef {\n return opaque.$$type === '@backstage/ExternalRouteRef';\n}\n\n/** @internal */\nclass ExternalRouteRefImpl\n extends RouteRefImpl\n implements InternalExternalRouteRef\n{\n readonly $$type = '@backstage/ExternalRouteRef' as any;\n readonly params: string[];\n readonly defaultTarget: string | undefined;\n\n constructor(\n params: string[] = [],\n defaultTarget: string | undefined,\n creationSite: string,\n ) {\n super(params, creationSite);\n this.params = params;\n this.defaultTarget = defaultTarget;\n }\n\n getDefaultTarget() {\n return this.defaultTarget;\n }\n}\n\n/**\n * Creates a route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @param options - Description of the route reference to be created.\n * @public\n */\nexport function createExternalRouteRef<\n TParams extends { [param in TParamKeys]: string } | undefined = undefined,\n TParamKeys extends string = string,\n>(options?: {\n /**\n * The parameters that will be provided to the external route reference.\n */\n readonly params?: string extends TParamKeys\n ? (keyof TParams)[]\n : TParamKeys[];\n\n /**\n * The route (typically in another plugin) that this should map to by default.\n *\n * The string is expected to be on the standard `<plugin id>.<route id>` form,\n * for example `techdocs.docRoot`.\n */\n defaultTarget?: string;\n}): ExternalRouteRef<\n keyof TParams extends never\n ? undefined\n : string extends TParamKeys\n ? TParams\n : { [param in TParamKeys]: string }\n> {\n return new ExternalRouteRefImpl(\n options?.params as string[] | undefined,\n options?.defaultTarget,\n describeParentCallSite(),\n );\n}\n"],"names":[],"mappings":"AAiDO,SAAS,2BAEd,QAAA,EAAwE;AACxE,EAAA,MAAM,CAAA,GAAI,QAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,6BAAA,EAA+B;AAC9C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oCAAA,EAAuC,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EACpE;AAEA,EAAA,OAAO,CAAA;AACT;AAGO,SAAS,mBAAmB,MAAA,EAEJ;AAC7B,EAAA,OAAO,OAAO,MAAA,KAAW,6BAAA;AAC3B;;;;"}
@@ -76,6 +76,14 @@ function resolveBasePath(targetRef, sourceLocation, routePaths, routeParents, ro
76
76
  return `${joinPaths(parentPath, ...diffPaths)}/`;
77
77
  }
78
78
  class RouteResolver {
79
+ routePaths;
80
+ routeParents;
81
+ routeObjects;
82
+ routeBindings;
83
+ appBasePath;
84
+ // base path without a trailing slash
85
+ routeAliasResolver;
86
+ routeRefsById;
79
87
  constructor(routePaths, routeParents, routeObjects, routeBindings, appBasePath, routeAliasResolver, routeRefsById) {
80
88
  this.routePaths = routePaths;
81
89
  this.routeParents = routeParents;
@@ -1 +1 @@
1
- {"version":3,"file":"RouteResolver.esm.js","sources":["../../src/routing/RouteResolver.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApi,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AnyRouteRef, BackstageRouteObject } from './types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isSubRouteRef,\n toInternalSubRouteRef,\n} from '../../../frontend-plugin-api/src/routing/SubRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isExternalRouteRef,\n toInternalExternalRouteRef,\n} from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\nimport { RouteAliasResolver } from './RouteAliasResolver';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n targetRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n routeRefsById: Map<string, RouteRef | SubRouteRef>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let ref: AnyRouteRef = targetRouteRef;\n let path = '';\n\n if (isExternalRouteRef(ref)) {\n let resolvedRoute = routeBindings.get(ref);\n if (!resolvedRoute) {\n const internal = toInternalExternalRouteRef(ref);\n const defaultTarget = internal.getDefaultTarget();\n if (defaultTarget) {\n resolvedRoute = routeRefsById.get(defaultTarget);\n }\n }\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n ref = resolvedRoute;\n }\n\n if (isSubRouteRef(ref)) {\n const internal = toInternalSubRouteRef(ref);\n path = ref.path;\n ref = internal.getParent();\n }\n\n if (!isRouteRef(ref)) {\n throw new Error(\n `Unexpectedly resolved ${targetRouteRef} to a non-route ref ${ref}`,\n );\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(ref);\n if (resolvedPath === undefined) {\n return [undefined, ''];\n }\n\n return [ref, path ? joinPaths(resolvedPath, path) : resolvedPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPaths = refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (path === undefined) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n });\n\n return `${joinPaths(parentPath, ...diffPaths)}/`;\n}\n\nexport class RouteResolver implements RouteResolutionApi {\n constructor(\n private readonly routePaths: Map<RouteRef, string>,\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>,\n private readonly routeObjects: BackstageRouteObject[],\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string, // base path without a trailing slash\n private readonly routeAliasResolver: RouteAliasResolver,\n private readonly routeRefsById: Map<string, RouteRef | SubRouteRef>,\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: { sourcePath?: string },\n ): RouteFunc<TParams> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef?.$$type === '@backstage/RouteRef'\n ? this.routeAliasResolver(anyRouteRef)\n : anyRouteRef,\n this.routePaths,\n this.routeBindings,\n this.routeRefsById,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n const relativeSourceLocation = this.trimPath(options?.sourcePath ?? '');\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath = resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<TParams> = (...[params]) => {\n // We selectively encode some some known-dangerous characters in the\n // params. The reason that we don't perform a blanket `encodeURIComponent`\n // here is that this encoding was added defensively long after the initial\n // release of this code. There's likely to be many users of this code that\n // already encode their parameters knowing that this code didn't do this\n // for them in the past. Therefore, we are extra careful NOT to include\n // the percent character in this set, even though that might seem like a\n // bad idea.\n const encodedParams =\n params &&\n mapValues(params, value => {\n if (typeof value === 'string') {\n return value.replaceAll(/[&?#;\\/]/g, c => encodeURIComponent(c));\n }\n return value;\n });\n return joinPaths(basePath, generatePath(targetPath, encodedParams));\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RouteResolver.esm.js","sources":["../../src/routing/RouteResolver.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApi,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AnyRouteRef, BackstageRouteObject } from './types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isSubRouteRef,\n toInternalSubRouteRef,\n} from '../../../frontend-plugin-api/src/routing/SubRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isExternalRouteRef,\n toInternalExternalRouteRef,\n} from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\nimport { RouteAliasResolver } from './RouteAliasResolver';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n targetRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n routeRefsById: Map<string, RouteRef | SubRouteRef>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let ref: AnyRouteRef = targetRouteRef;\n let path = '';\n\n if (isExternalRouteRef(ref)) {\n let resolvedRoute = routeBindings.get(ref);\n if (!resolvedRoute) {\n const internal = toInternalExternalRouteRef(ref);\n const defaultTarget = internal.getDefaultTarget();\n if (defaultTarget) {\n resolvedRoute = routeRefsById.get(defaultTarget);\n }\n }\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n ref = resolvedRoute;\n }\n\n if (isSubRouteRef(ref)) {\n const internal = toInternalSubRouteRef(ref);\n path = ref.path;\n ref = internal.getParent();\n }\n\n if (!isRouteRef(ref)) {\n throw new Error(\n `Unexpectedly resolved ${targetRouteRef} to a non-route ref ${ref}`,\n );\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(ref);\n if (resolvedPath === undefined) {\n return [undefined, ''];\n }\n\n return [ref, path ? joinPaths(resolvedPath, path) : resolvedPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPaths = refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (path === undefined) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n });\n\n return `${joinPaths(parentPath, ...diffPaths)}/`;\n}\n\nexport class RouteResolver implements RouteResolutionApi {\n private readonly routePaths: Map<RouteRef, string>;\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>;\n private readonly routeObjects: BackstageRouteObject[];\n private readonly routeBindings: Map<ExternalRouteRef, RouteRef | SubRouteRef>;\n private readonly appBasePath: string; // base path without a trailing slash\n private readonly routeAliasResolver: RouteAliasResolver;\n private readonly routeRefsById: Map<string, RouteRef | SubRouteRef>;\n\n constructor(\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n routeBindings: Map<ExternalRouteRef, RouteRef | SubRouteRef>,\n appBasePath: string, // base path without a trailing slash\n routeAliasResolver: RouteAliasResolver,\n routeRefsById: Map<string, RouteRef | SubRouteRef>,\n ) {\n this.routePaths = routePaths;\n this.routeParents = routeParents;\n this.routeObjects = routeObjects;\n this.routeBindings = routeBindings;\n this.appBasePath = appBasePath;\n this.routeAliasResolver = routeAliasResolver;\n this.routeRefsById = routeRefsById;\n }\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: { sourcePath?: string },\n ): RouteFunc<TParams> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef?.$$type === '@backstage/RouteRef'\n ? this.routeAliasResolver(anyRouteRef)\n : anyRouteRef,\n this.routePaths,\n this.routeBindings,\n this.routeRefsById,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n const relativeSourceLocation = this.trimPath(options?.sourcePath ?? '');\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath = resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<TParams> = (...[params]) => {\n // We selectively encode some some known-dangerous characters in the\n // params. The reason that we don't perform a blanket `encodeURIComponent`\n // here is that this encoding was added defensively long after the initial\n // release of this code. There's likely to be many users of this code that\n // already encode their parameters knowing that this code didn't do this\n // for them in the past. Therefore, we are extra careful NOT to include\n // the percent character in this set, even though that might seem like a\n // bad idea.\n const encodedParams =\n params &&\n mapValues(params, value => {\n if (typeof value === 'string') {\n return value.replaceAll(/[&?#;\\/]/g, c => encodeURIComponent(c));\n }\n return value;\n });\n return joinPaths(basePath, generatePath(targetPath, encodedParams));\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -38,11 +38,13 @@ function deduplicateFeatures(allFeatures) {
38
38
  }).reverse();
39
39
  }
40
40
  class AppTreeApiProxy {
41
+ #routeInfo;
42
+ tree;
43
+ appBasePath;
41
44
  constructor(tree, appBasePath) {
42
45
  this.tree = tree;
43
46
  this.appBasePath = appBasePath;
44
47
  }
45
- #routeInfo;
46
48
  checkIfInitialized() {
47
49
  if (!this.#routeInfo) {
48
50
  throw new Error(
@@ -69,12 +71,14 @@ class AppTreeApiProxy {
69
71
  }
70
72
  }
71
73
  class RouteResolutionApiProxy {
74
+ #delegate;
75
+ #routeObjects;
76
+ routeBindings;
77
+ appBasePath;
72
78
  constructor(routeBindings, appBasePath) {
73
79
  this.routeBindings = routeBindings;
74
80
  this.appBasePath = appBasePath;
75
81
  }
76
- #delegate;
77
- #routeObjects;
78
82
  resolve(anyRouteRef, options) {
79
83
  if (!this.#delegate) {
80
84
  throw new Error(
@@ -1 +1 @@
1
- {"version":3,"file":"createSpecializedApp.esm.js","sources":["../../src/wiring/createSpecializedApp.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 {\n ApiBlueprint,\n AppTree,\n AppTreeApi,\n appTreeApiRef,\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApi,\n createApiFactory,\n routeResolutionApiRef,\n AppNode,\n ExtensionFactoryMiddleware,\n FrontendFeature,\n} from '@backstage/frontend-plugin-api';\nimport {\n AnyApiFactory,\n ApiHolder,\n ConfigApi,\n configApiRef,\n featureFlagsApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';\nimport {\n createExtensionDataContainer,\n OpaqueFrontendPlugin,\n} from '@internal/frontend';\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 {\n extractRouteInfoFromAppNode,\n RouteInfo,\n} from '../routing/extractRouteInfoFromAppNode';\n\nimport { CreateAppRouteBinder } from '../routing';\nimport { RouteResolver } from '../routing/RouteResolver';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalFrontendModule,\n isInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\nimport { getBasePath } from '../routing/getBasePath';\nimport { Root } from '../extensions/Root';\nimport { resolveAppTree } from '../tree/resolveAppTree';\nimport { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs';\nimport { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig';\nimport { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';\nimport { BackstageRouteObject } from '../routing/types';\nimport { matchRoutes } from 'react-router-dom';\nimport {\n createPluginInfoAttacher,\n FrontendPluginInfoResolver,\n} from './createPluginInfoAttacher';\nimport { createRouteAliasResolver } from '../routing/RouteAliasResolver';\nimport {\n AppError,\n createErrorCollector,\n ErrorCollector,\n} from './createErrorCollector';\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\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass AppTreeApiProxy implements AppTreeApi {\n #routeInfo?: RouteInfo;\n\n constructor(\n private readonly tree: AppTree,\n private readonly appBasePath: string,\n ) {}\n\n private checkIfInitialized() {\n if (!this.#routeInfo) {\n throw new Error(\n `You can't access the AppTreeApi during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n }\n\n getTree() {\n this.checkIfInitialized();\n\n return { tree: this.tree };\n }\n\n getNodesByRoutePath(routePath: string): { nodes: AppNode[] } {\n this.checkIfInitialized();\n\n let path = routePath;\n if (path.startsWith(this.appBasePath)) {\n path = path.slice(this.appBasePath.length);\n }\n\n const matchedRoutes = matchRoutes(this.#routeInfo!.routeObjects, path);\n\n const matchedAppNodes =\n matchedRoutes\n ?.filter(routeObj => !!routeObj.route.appNode)\n .map(routeObj => routeObj.route.appNode!) || [];\n\n return { nodes: matchedAppNodes };\n }\n\n initialize(routeInfo: RouteInfo) {\n this.#routeInfo = routeInfo;\n }\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass RouteResolutionApiProxy implements RouteResolutionApi {\n #delegate: RouteResolutionApi | undefined;\n #routeObjects: BackstageRouteObject[] | undefined;\n\n constructor(\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string,\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: { sourcePath?: string },\n ): RouteFunc<TParams> | undefined {\n if (!this.#delegate) {\n throw new Error(\n `You can't access the RouteResolver during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n\n return this.#delegate.resolve(anyRouteRef, options);\n }\n\n initialize(\n routeInfo: RouteInfo,\n routeRefsById: Map<string, RouteRef | SubRouteRef>,\n ) {\n this.#delegate = new RouteResolver(\n routeInfo.routePaths,\n routeInfo.routeParents,\n routeInfo.routeObjects,\n this.routeBindings,\n this.appBasePath,\n routeInfo.routeAliasResolver,\n routeRefsById,\n );\n this.#routeObjects = routeInfo.routeObjects;\n\n return routeInfo;\n }\n\n getRouteObjects() {\n return this.#routeObjects;\n }\n}\n\n/**\n * Options for {@link createSpecializedApp}.\n *\n * @public\n */\nexport type CreateSpecializedAppOptions = {\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 replacement API holder implementation to use.\n *\n * By default, a new API holder will be constructed automatically based on\n * the other inputs. If you pass in a custom one here, none of that\n * automation will take place - so you will have to take care to supply all\n * those APIs yourself.\n */\n apis?: ApiHolder;\n\n /**\n * If set to true, the system will silently accept and move on if\n * encountering config for extensions that do not exist. The default is to\n * reject such config to help catch simple mistakes.\n *\n * This flag can be useful in some scenarios where you have a dynamic set of\n * extensions enabled at different times, but also increases the risk of\n * accidentally missing e.g. simple typos in your config.\n */\n allowUnknownExtensionConfig?: boolean;\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 * Creates an empty app without any default features. This is a low-level API is\n * intended for use in tests or specialized setups. Typically you want to use\n * `createApp` from `@backstage/frontend-defaults` instead.\n *\n * @public\n */\nexport function createSpecializedApp(options?: CreateSpecializedAppOptions): {\n apis: ApiHolder;\n tree: AppTree;\n errors?: AppError[];\n} {\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 factories = createApiFactories({ tree, collector });\n const appBasePath = getBasePath(config);\n const appTreeApi = new AppTreeApiProxy(tree, appBasePath);\n\n const routeRefsById = collectRouteIds(features, collector);\n const routeResolutionApi = new RouteResolutionApiProxy(\n resolveRouteBindings(options?.bindRoutes, config, routeRefsById, collector),\n appBasePath,\n );\n\n const appIdentityProxy = new AppIdentityProxy();\n const apis =\n options?.advanced?.apis ??\n createApiHolder({\n factories,\n staticFactories: [\n createApiFactory(appTreeApiRef, appTreeApi),\n createApiFactory(configApiRef, config),\n createApiFactory(routeResolutionApiRef, routeResolutionApi),\n createApiFactory(identityApiRef, appIdentityProxy),\n ],\n });\n\n const featureFlagApi = apis.get(featureFlagsApiRef);\n if (featureFlagApi) {\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 pluginId: feature.id,\n }),\n );\n }\n if (isInternalFrontendModule(feature)) {\n toInternalFrontendModule(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.pluginId,\n }),\n );\n }\n }\n }\n\n // Now instantiate the entire tree, which will skip anything that's already been instantiated\n instantiateAppNodeTree(\n tree.root,\n apis,\n collector,\n mergeExtensionFactoryMiddleware(\n options?.advanced?.extensionFactoryMiddleware,\n ),\n );\n\n const routeInfo = extractRouteInfoFromAppNode(\n tree.root,\n createRouteAliasResolver(routeRefsById),\n );\n\n routeResolutionApi.initialize(routeInfo, routeRefsById.routes);\n appTreeApi.initialize(routeInfo);\n\n return { apis, tree, errors: collector.collectErrors() };\n}\n\nfunction createApiFactories(options: {\n tree: AppTree;\n collector: ErrorCollector;\n}): AnyApiFactory[] {\n const emptyApiHolder = ApiRegistry.from([]);\n const factories = new Array<AnyApiFactory>();\n\n for (const apiNode of options.tree.root.edges.attachments.get('apis') ?? []) {\n if (!instantiateAppNodeTree(apiNode, emptyApiHolder, options.collector)) {\n continue;\n }\n const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory);\n if (apiFactory) {\n factories.push(apiFactory);\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 factories;\n}\n\nfunction createApiHolder(options: {\n factories: AnyApiFactory[];\n staticFactories: AnyApiFactory[];\n}): ApiHolder {\n const factoryRegistry = new ApiFactoryRegistry();\n\n for (const factory of options.factories.slice().reverse()) {\n factoryRegistry.register('default', factory);\n }\n\n for (const factory of options.staticFactories) {\n factoryRegistry.register('static', factory);\n }\n\n ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());\n\n return new ApiResolver(factoryRegistry);\n}\n\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":"createSpecializedApp.esm.js","sources":["../../src/wiring/createSpecializedApp.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 {\n ApiBlueprint,\n AppTree,\n AppTreeApi,\n appTreeApiRef,\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApi,\n createApiFactory,\n routeResolutionApiRef,\n AppNode,\n ExtensionFactoryMiddleware,\n FrontendFeature,\n} from '@backstage/frontend-plugin-api';\nimport {\n AnyApiFactory,\n ApiHolder,\n ConfigApi,\n configApiRef,\n featureFlagsApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';\nimport {\n createExtensionDataContainer,\n OpaqueFrontendPlugin,\n} from '@internal/frontend';\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 {\n extractRouteInfoFromAppNode,\n RouteInfo,\n} from '../routing/extractRouteInfoFromAppNode';\n\nimport { CreateAppRouteBinder } from '../routing';\nimport { RouteResolver } from '../routing/RouteResolver';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalFrontendModule,\n isInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\nimport { getBasePath } from '../routing/getBasePath';\nimport { Root } from '../extensions/Root';\nimport { resolveAppTree } from '../tree/resolveAppTree';\nimport { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs';\nimport { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig';\nimport { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';\nimport { BackstageRouteObject } from '../routing/types';\nimport { matchRoutes } from 'react-router-dom';\nimport {\n createPluginInfoAttacher,\n FrontendPluginInfoResolver,\n} from './createPluginInfoAttacher';\nimport { createRouteAliasResolver } from '../routing/RouteAliasResolver';\nimport {\n AppError,\n createErrorCollector,\n ErrorCollector,\n} from './createErrorCollector';\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\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass AppTreeApiProxy implements AppTreeApi {\n #routeInfo?: RouteInfo;\n private readonly tree: AppTree;\n private readonly appBasePath: string;\n\n constructor(tree: AppTree, appBasePath: string) {\n this.tree = tree;\n this.appBasePath = appBasePath;\n }\n\n private checkIfInitialized() {\n if (!this.#routeInfo) {\n throw new Error(\n `You can't access the AppTreeApi during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n }\n\n getTree() {\n this.checkIfInitialized();\n\n return { tree: this.tree };\n }\n\n getNodesByRoutePath(routePath: string): { nodes: AppNode[] } {\n this.checkIfInitialized();\n\n let path = routePath;\n if (path.startsWith(this.appBasePath)) {\n path = path.slice(this.appBasePath.length);\n }\n\n const matchedRoutes = matchRoutes(this.#routeInfo!.routeObjects, path);\n\n const matchedAppNodes =\n matchedRoutes\n ?.filter(routeObj => !!routeObj.route.appNode)\n .map(routeObj => routeObj.route.appNode!) || [];\n\n return { nodes: matchedAppNodes };\n }\n\n initialize(routeInfo: RouteInfo) {\n this.#routeInfo = routeInfo;\n }\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass RouteResolutionApiProxy implements RouteResolutionApi {\n #delegate: RouteResolutionApi | undefined;\n #routeObjects: BackstageRouteObject[] | undefined;\n\n private readonly routeBindings: Map<ExternalRouteRef, RouteRef | SubRouteRef>;\n private readonly appBasePath: string;\n\n constructor(\n routeBindings: Map<ExternalRouteRef, RouteRef | SubRouteRef>,\n appBasePath: string,\n ) {\n this.routeBindings = routeBindings;\n this.appBasePath = appBasePath;\n }\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: { sourcePath?: string },\n ): RouteFunc<TParams> | undefined {\n if (!this.#delegate) {\n throw new Error(\n `You can't access the RouteResolver during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n\n return this.#delegate.resolve(anyRouteRef, options);\n }\n\n initialize(\n routeInfo: RouteInfo,\n routeRefsById: Map<string, RouteRef | SubRouteRef>,\n ) {\n this.#delegate = new RouteResolver(\n routeInfo.routePaths,\n routeInfo.routeParents,\n routeInfo.routeObjects,\n this.routeBindings,\n this.appBasePath,\n routeInfo.routeAliasResolver,\n routeRefsById,\n );\n this.#routeObjects = routeInfo.routeObjects;\n\n return routeInfo;\n }\n\n getRouteObjects() {\n return this.#routeObjects;\n }\n}\n\n/**\n * Options for {@link createSpecializedApp}.\n *\n * @public\n */\nexport type CreateSpecializedAppOptions = {\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 replacement API holder implementation to use.\n *\n * By default, a new API holder will be constructed automatically based on\n * the other inputs. If you pass in a custom one here, none of that\n * automation will take place - so you will have to take care to supply all\n * those APIs yourself.\n */\n apis?: ApiHolder;\n\n /**\n * If set to true, the system will silently accept and move on if\n * encountering config for extensions that do not exist. The default is to\n * reject such config to help catch simple mistakes.\n *\n * This flag can be useful in some scenarios where you have a dynamic set of\n * extensions enabled at different times, but also increases the risk of\n * accidentally missing e.g. simple typos in your config.\n */\n allowUnknownExtensionConfig?: boolean;\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 * Creates an empty app without any default features. This is a low-level API is\n * intended for use in tests or specialized setups. Typically you want to use\n * `createApp` from `@backstage/frontend-defaults` instead.\n *\n * @public\n */\nexport function createSpecializedApp(options?: CreateSpecializedAppOptions): {\n apis: ApiHolder;\n tree: AppTree;\n errors?: AppError[];\n} {\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 factories = createApiFactories({ tree, collector });\n const appBasePath = getBasePath(config);\n const appTreeApi = new AppTreeApiProxy(tree, appBasePath);\n\n const routeRefsById = collectRouteIds(features, collector);\n const routeResolutionApi = new RouteResolutionApiProxy(\n resolveRouteBindings(options?.bindRoutes, config, routeRefsById, collector),\n appBasePath,\n );\n\n const appIdentityProxy = new AppIdentityProxy();\n const apis =\n options?.advanced?.apis ??\n createApiHolder({\n factories,\n staticFactories: [\n createApiFactory(appTreeApiRef, appTreeApi),\n createApiFactory(configApiRef, config),\n createApiFactory(routeResolutionApiRef, routeResolutionApi),\n createApiFactory(identityApiRef, appIdentityProxy),\n ],\n });\n\n const featureFlagApi = apis.get(featureFlagsApiRef);\n if (featureFlagApi) {\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 pluginId: feature.id,\n }),\n );\n }\n if (isInternalFrontendModule(feature)) {\n toInternalFrontendModule(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.pluginId,\n }),\n );\n }\n }\n }\n\n // Now instantiate the entire tree, which will skip anything that's already been instantiated\n instantiateAppNodeTree(\n tree.root,\n apis,\n collector,\n mergeExtensionFactoryMiddleware(\n options?.advanced?.extensionFactoryMiddleware,\n ),\n );\n\n const routeInfo = extractRouteInfoFromAppNode(\n tree.root,\n createRouteAliasResolver(routeRefsById),\n );\n\n routeResolutionApi.initialize(routeInfo, routeRefsById.routes);\n appTreeApi.initialize(routeInfo);\n\n return { apis, tree, errors: collector.collectErrors() };\n}\n\nfunction createApiFactories(options: {\n tree: AppTree;\n collector: ErrorCollector;\n}): AnyApiFactory[] {\n const emptyApiHolder = ApiRegistry.from([]);\n const factories = new Array<AnyApiFactory>();\n\n for (const apiNode of options.tree.root.edges.attachments.get('apis') ?? []) {\n if (!instantiateAppNodeTree(apiNode, emptyApiHolder, options.collector)) {\n continue;\n }\n const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory);\n if (apiFactory) {\n factories.push(apiFactory);\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 factories;\n}\n\nfunction createApiHolder(options: {\n factories: AnyApiFactory[];\n staticFactories: AnyApiFactory[];\n}): ApiHolder {\n const factoryRegistry = new ApiFactoryRegistry();\n\n for (const factory of options.factories.slice().reverse()) {\n factoryRegistry.register('default', factory);\n }\n\n for (const factory of options.staticFactories) {\n factoryRegistry.register('static', factory);\n }\n\n ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());\n\n return new ApiResolver(factoryRegistry);\n}\n\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.13.1",
3
+ "version": "0.13.2-next.0",
4
4
  "backstage": {
5
5
  "role": "web-library"
6
6
  },
@@ -32,22 +32,22 @@
32
32
  "test": "backstage-cli package test"
33
33
  },
34
34
  "dependencies": {
35
- "@backstage/config": "^1.3.5",
36
- "@backstage/core-app-api": "^1.19.1",
37
- "@backstage/core-plugin-api": "^1.11.1",
38
- "@backstage/errors": "^1.2.7",
39
- "@backstage/frontend-defaults": "^0.3.2",
40
- "@backstage/frontend-plugin-api": "^0.12.1",
41
- "@backstage/types": "^1.2.2",
42
- "@backstage/version-bridge": "^1.0.11",
35
+ "@backstage/config": "1.3.6-next.0",
36
+ "@backstage/core-app-api": "1.19.2-next.0",
37
+ "@backstage/core-plugin-api": "1.11.2-next.0",
38
+ "@backstage/errors": "1.2.7",
39
+ "@backstage/frontend-defaults": "0.3.3-next.0",
40
+ "@backstage/frontend-plugin-api": "0.12.2-next.0",
41
+ "@backstage/types": "1.2.2",
42
+ "@backstage/version-bridge": "1.0.11",
43
43
  "lodash": "^4.17.21",
44
44
  "zod": "^3.22.4"
45
45
  },
46
46
  "devDependencies": {
47
- "@backstage/cli": "^0.34.4",
48
- "@backstage/frontend-test-utils": "^0.4.0",
49
- "@backstage/plugin-app": "^0.3.1",
50
- "@backstage/test-utils": "^1.7.12",
47
+ "@backstage/cli": "0.34.5-next.0",
48
+ "@backstage/frontend-test-utils": "0.4.1-next.0",
49
+ "@backstage/plugin-app": "0.3.2-next.0",
50
+ "@backstage/test-utils": "1.7.13-next.0",
51
51
  "@testing-library/jest-dom": "^6.0.0",
52
52
  "@testing-library/react": "^16.0.0",
53
53
  "@types/react": "^18.0.0",