@backstage/frontend-plugin-api 0.12.2-next.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/apis/definitions/AppTreeApi.esm.js.map +1 -1
  3. package/dist/components/createSwappableComponent.esm.js +1 -0
  4. package/dist/components/createSwappableComponent.esm.js.map +1 -1
  5. package/dist/frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js +9 -0
  6. package/dist/frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js.map +1 -0
  7. package/dist/frontend-internal/src/routing/OpaqueRouteRef.esm.js +9 -0
  8. package/dist/frontend-internal/src/routing/OpaqueRouteRef.esm.js.map +1 -0
  9. package/dist/frontend-internal/src/routing/OpaqueSubRouteRef.esm.js +9 -0
  10. package/dist/frontend-internal/src/routing/OpaqueSubRouteRef.esm.js.map +1 -0
  11. package/dist/frontend-internal/src/wiring/InternalExtensionDefinition.esm.js.map +1 -1
  12. package/dist/frontend-internal/src/wiring/InternalExtensionInput.esm.js +9 -0
  13. package/dist/frontend-internal/src/wiring/InternalExtensionInput.esm.js.map +1 -0
  14. package/dist/index.d.ts +107 -32
  15. package/dist/routing/ExternalRouteRef.esm.js +36 -20
  16. package/dist/routing/ExternalRouteRef.esm.js.map +1 -1
  17. package/dist/routing/RouteRef.esm.js +34 -56
  18. package/dist/routing/RouteRef.esm.js.map +1 -1
  19. package/dist/routing/SubRouteRef.esm.js +21 -32
  20. package/dist/routing/SubRouteRef.esm.js.map +1 -1
  21. package/dist/wiring/createExtension.esm.js +58 -6
  22. package/dist/wiring/createExtension.esm.js.map +1 -1
  23. package/dist/wiring/createExtensionBlueprint.esm.js.map +1 -1
  24. package/dist/wiring/createExtensionInput.esm.js +11 -2
  25. package/dist/wiring/createExtensionInput.esm.js.map +1 -1
  26. package/dist/wiring/createFrontendPlugin.esm.js +2 -1
  27. package/dist/wiring/createFrontendPlugin.esm.js.map +1 -1
  28. package/dist/wiring/resolveExtensionDefinition.esm.js +49 -9
  29. package/dist/wiring/resolveExtensionDefinition.esm.js.map +1 -1
  30. package/dist/wiring/resolveInputOverrides.esm.js +2 -1
  31. package/dist/wiring/resolveInputOverrides.esm.js.map +1 -1
  32. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # @backstage/frontend-plugin-api
2
2
 
3
+ ## 0.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7d87b4f: Renamed `ExtensionDefinition` to `OverridableExtensionDefinition` and introduced a slimmer `ExtensionDefinition` type that does not include override methods. The overridable type is generally used as an output type, while plain `ExtensionDefinition`s are used for input. This reduces type conflicts across different of `@backstage/frontend-plugin-api`, improving long-term compatibility.
8
+
9
+ ### Patch Changes
10
+
11
+ - 4d03f08: Internal refactor of route reference implementations with minor updates to the `toString` implementations.
12
+ - 7c6a66d: Added support for plugin-relative `attachTo` declarations for extension definitions. This allows for the creation of extension and extension blueprints that attach to other extensions of a particular `kind` in the same plugin, rather than needing to provide the exact extension ID. This is particularly useful when wanting to provide extension blueprints with a built-in hierarchy where the extensions created from one blueprint attach to extensions created from the other blueprint, for example:
13
+
14
+ ```ts
15
+ // kind: 'tabbed-page'
16
+ const parentPage = TabbedPageBlueprint.make({
17
+ params: {....}
18
+ })
19
+ // attachTo: { kind: 'tabbed-page', input: 'tabs' }
20
+ const child1 = TabContentBlueprint.make({
21
+ name: 'tab1',
22
+ params: {....}
23
+ })
24
+ ```
25
+
26
+ - 878c251: Updated to `ExtensionInput` to make all type parameters optional.
27
+ - 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.
28
+ - Updated dependencies
29
+ - @backstage/core-components@0.18.3
30
+ - @backstage/core-plugin-api@1.12.0
31
+
32
+ ## 0.12.2-next.2
33
+
34
+ ### Patch Changes
35
+
36
+ - 7c6a66d: Added support for plugin-relative `attachTo` declarations for extension definitions. This allows for the creation of extension and extension blueprints that attach to other extensions of a particular `kind` in the same plugin, rather than needing to provide the exact extension ID. This is particularly useful when wanting to provide extension blueprints with a built-in hierarchy where the extensions created from one blueprint attach to extensions created from the other blueprint, for example:
37
+
38
+ ```ts
39
+ // kind: 'tabbed-page'
40
+ const parentPage = TabbedPageBlueprint.make({
41
+ params: {....}
42
+ })
43
+ // attachTo: { kind: 'tabbed-page', input: 'tabs' }
44
+ const child1 = TabContentBlueprint.make({
45
+ name: 'tab1',
46
+ params: {....}
47
+ })
48
+ ```
49
+
50
+ - Updated dependencies
51
+ - @backstage/core-components@0.18.3-next.2
52
+
3
53
  ## 0.12.2-next.1
4
54
 
5
55
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"AppTreeApi.esm.js","sources":["../../../src/apis/definitions/AppTreeApi.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 { createApiRef } from '@backstage/core-plugin-api';\nimport {\n FrontendPlugin,\n Extension,\n ExtensionDataRef,\n ExtensionAttachToSpec,\n} from '../../wiring';\n\n/**\n * The specification for this {@link AppNode} in the {@link AppTree}.\n *\n * @public\n * @remarks\n *\n * The specifications for a collection of app nodes is all the information needed\n * to build the tree and instantiate the nodes.\n */\nexport interface AppNodeSpec {\n readonly id: string;\n readonly attachTo: ExtensionAttachToSpec;\n readonly extension: Extension<unknown, unknown>;\n readonly disabled: boolean;\n readonly config?: unknown;\n readonly plugin: FrontendPlugin;\n}\n\n/**\n * The connections from this {@link AppNode} to other nodes.\n *\n * @public\n * @remarks\n *\n * The app node edges are resolved based on the app node specs, regardless of whether\n * adjacent nodes are disabled or not. If no parent attachment is present or\n */\nexport interface AppNodeEdges {\n readonly attachedTo?: { node: AppNode; input: string };\n readonly attachments: ReadonlyMap<string, AppNode[]>;\n}\n\n/**\n * The instance of this {@link AppNode} in the {@link AppTree}.\n *\n * @public\n * @remarks\n *\n * The app node instance is created when the `factory` function of an extension is called.\n * Instances will only be present for nodes in the app that are connected to the root\n * node and not disabled\n */\nexport interface AppNodeInstance {\n /** Returns a sequence of all extension data refs that were output by this instance */\n getDataRefs(): Iterable<ExtensionDataRef<unknown>>;\n /** Get the output data for a single extension data ref */\n getData<T>(ref: ExtensionDataRef<T>): T | undefined;\n}\n\n/**\n * A node in the {@link AppTree}.\n *\n * @public\n */\nexport interface AppNode {\n /** The specification for how this node should be instantiated */\n readonly spec: AppNodeSpec;\n /** The edges from this node to other nodes in the app tree */\n readonly edges: AppNodeEdges;\n /** The instance of this node, if it was instantiated */\n readonly instance?: AppNodeInstance;\n}\n\n/**\n * The app tree containing all {@link AppNode}s of the app.\n *\n * @public\n */\nexport interface AppTree {\n /** The root node of the app */\n readonly root: AppNode;\n /** A map of all nodes in the app by ID, including orphaned or disabled nodes */\n readonly nodes: ReadonlyMap<string /* id */, AppNode>;\n /** A sequence of all nodes with a parent that is not reachable from the app root node */\n readonly orphans: Iterable<AppNode>;\n}\n\n/**\n * The API for interacting with the {@link AppTree}.\n *\n * @public\n */\nexport interface AppTreeApi {\n /**\n * Get the {@link AppTree} for the app.\n */\n getTree(): { tree: AppTree };\n\n /**\n * Get all nodes in the app that are mounted at a given route path.\n */\n getNodesByRoutePath(routePath: string): { nodes: AppNode[] };\n}\n\n/**\n * The `ApiRef` of {@link AppTreeApi}.\n *\n * @public\n */\nexport const appTreeApiRef = createApiRef<AppTreeApi>({ id: 'core.app-tree' });\n"],"names":[],"mappings":";;AA2HO,MAAM,aAAA,GAAgB,YAAA,CAAyB,EAAE,EAAA,EAAI,iBAAiB;;;;"}
1
+ {"version":3,"file":"AppTreeApi.esm.js","sources":["../../../src/apis/definitions/AppTreeApi.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 { createApiRef } from '@backstage/core-plugin-api';\nimport { FrontendPlugin, Extension, ExtensionDataRef } from '../../wiring';\nimport { ExtensionAttachTo } from '../../wiring/resolveExtensionDefinition';\n\n/**\n * The specification for this {@link AppNode} in the {@link AppTree}.\n *\n * @public\n * @remarks\n *\n * The specifications for a collection of app nodes is all the information needed\n * to build the tree and instantiate the nodes.\n */\nexport interface AppNodeSpec {\n readonly id: string;\n readonly attachTo: ExtensionAttachTo;\n readonly extension: Extension<unknown, unknown>;\n readonly disabled: boolean;\n readonly config?: unknown;\n readonly plugin: FrontendPlugin;\n}\n\n/**\n * The connections from this {@link AppNode} to other nodes.\n *\n * @public\n * @remarks\n *\n * The app node edges are resolved based on the app node specs, regardless of whether\n * adjacent nodes are disabled or not. If no parent attachment is present or\n */\nexport interface AppNodeEdges {\n readonly attachedTo?: { node: AppNode; input: string };\n readonly attachments: ReadonlyMap<string, AppNode[]>;\n}\n\n/**\n * The instance of this {@link AppNode} in the {@link AppTree}.\n *\n * @public\n * @remarks\n *\n * The app node instance is created when the `factory` function of an extension is called.\n * Instances will only be present for nodes in the app that are connected to the root\n * node and not disabled\n */\nexport interface AppNodeInstance {\n /** Returns a sequence of all extension data refs that were output by this instance */\n getDataRefs(): Iterable<ExtensionDataRef<unknown>>;\n /** Get the output data for a single extension data ref */\n getData<T>(ref: ExtensionDataRef<T>): T | undefined;\n}\n\n/**\n * A node in the {@link AppTree}.\n *\n * @public\n */\nexport interface AppNode {\n /** The specification for how this node should be instantiated */\n readonly spec: AppNodeSpec;\n /** The edges from this node to other nodes in the app tree */\n readonly edges: AppNodeEdges;\n /** The instance of this node, if it was instantiated */\n readonly instance?: AppNodeInstance;\n}\n\n/**\n * The app tree containing all {@link AppNode}s of the app.\n *\n * @public\n */\nexport interface AppTree {\n /** The root node of the app */\n readonly root: AppNode;\n /** A map of all nodes in the app by ID, including orphaned or disabled nodes */\n readonly nodes: ReadonlyMap<string /* id */, AppNode>;\n /** A sequence of all nodes with a parent that is not reachable from the app root node */\n readonly orphans: Iterable<AppNode>;\n}\n\n/**\n * The API for interacting with the {@link AppTree}.\n *\n * @public\n */\nexport interface AppTreeApi {\n /**\n * Get the {@link AppTree} for the app.\n */\n getTree(): { tree: AppTree };\n\n /**\n * Get all nodes in the app that are mounted at a given route path.\n */\n getNodesByRoutePath(routePath: string): { nodes: AppNode[] };\n}\n\n/**\n * The `ApiRef` of {@link AppTreeApi}.\n *\n * @public\n */\nexport const appTreeApiRef = createApiRef<AppTreeApi>({ id: 'core.app-tree' });\n"],"names":[],"mappings":";;AAuHO,MAAM,aAAA,GAAgB,YAAA,CAAyB,EAAE,EAAA,EAAI,iBAAiB;;;;"}
@@ -9,6 +9,7 @@ import '../apis/definitions/AnalyticsApi.esm.js';
9
9
  import { lazy } from 'react';
10
10
  import { OpaqueSwappableComponentRef } from '../frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js';
11
11
  import '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
12
+ import '../frontend-internal/src/wiring/InternalExtensionInput.esm.js';
12
13
  import '../frontend-internal/src/wiring/InternalFrontendPlugin.esm.js';
13
14
 
14
15
  const useComponentRefApi = () => {
@@ -1 +1 @@
1
- {"version":3,"file":"createSwappableComponent.esm.js","sources":["../../src/components/createSwappableComponent.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 { OpaqueSwappableComponentRef } from '@internal/frontend';\nimport { swappableComponentsApiRef, useApi } from '../apis';\nimport { lazy } from 'react';\n\n/** @public */\nexport type SwappableComponentRef<\n TInnerComponentProps extends {} = {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n TProps: TInnerComponentProps;\n TExternalProps: TExternalComponentProps;\n $$type: '@backstage/SwappableComponentRef';\n};\n\n/**\n * Options for creating an SwappableComponent.\n *\n * @public\n */\nexport type CreateSwappableComponentOptions<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n loader?:\n | (() => (props: TInnerComponentProps) => JSX.Element | null)\n | (() => Promise<(props: TInnerComponentProps) => JSX.Element | null>);\n transformProps?: (props: TExternalComponentProps) => TInnerComponentProps;\n};\n\nconst useComponentRefApi = () => {\n try {\n return useApi(swappableComponentsApiRef);\n } catch (e) {\n return undefined;\n }\n};\n\n/**\n * Creates a SwappableComponent that can be used to render the component, optionally overridden by the app.\n *\n * @public\n */\nexport function createSwappableComponent<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n>(\n options: CreateSwappableComponentOptions<\n TInnerComponentProps,\n TExternalComponentProps\n >,\n): {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n} {\n const FallbackComponent = (p: JSX.IntrinsicAttributes) => (\n <div data-testid={options.id} {...p} />\n );\n\n const ref = OpaqueSwappableComponentRef.createInstance('v1', {\n id: options.id,\n TProps: null as unknown as TInnerComponentProps,\n TExternalProps: null as unknown as TExternalComponentProps,\n toString() {\n return `SwappableComponentRef{id=${options.id}}`;\n },\n defaultComponent: lazy(async () => {\n const Component = (await options.loader?.()) ?? FallbackComponent;\n return { default: Component };\n }) as (typeof OpaqueSwappableComponentRef.TInternal)['defaultComponent'],\n transformProps:\n options.transformProps as (typeof OpaqueSwappableComponentRef.TInternal)['transformProps'],\n });\n\n const ComponentRefImpl = (props: TExternalComponentProps) => {\n const api = useComponentRefApi();\n\n if (!api) {\n const internalRef = OpaqueSwappableComponentRef.toInternal(ref);\n const Component = internalRef.defaultComponent;\n const innerProps = internalRef.transformProps?.(props) ?? props;\n return <Component {...innerProps} />;\n }\n\n const Component = api.getComponent<any>(ref);\n return <Component {...props} />;\n };\n\n Object.assign(ComponentRefImpl, { ref });\n\n return ComponentRefImpl as {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n };\n}\n"],"names":["Component"],"mappings":";;;;;;;;;;;;;AA+CA,MAAM,qBAAqB,MAAM;AAC/B,EAAA,IAAI;AACF,IAAA,OAAO,OAAO,yBAAyB,CAAA;AAAA,EACzC,SAAS,CAAA,EAAG;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;AAOO,SAAS,yBAId,OAAA,EAOA;AACA,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,qBACzB,GAAA,CAAC,SAAI,aAAA,EAAa,OAAA,CAAQ,EAAA,EAAK,GAAG,CAAA,EAAG,CAAA;AAGvC,EAAA,MAAM,GAAA,GAAM,2BAAA,CAA4B,cAAA,CAAe,IAAA,EAAM;AAAA,IAC3D,IAAI,OAAA,CAAQ,EAAA;AAAA,IACZ,MAAA,EAAQ,IAAA;AAAA,IACR,cAAA,EAAgB,IAAA;AAAA,IAChB,QAAA,GAAW;AACT,MAAA,OAAO,CAAA,yBAAA,EAA4B,QAAQ,EAAE,CAAA,CAAA,CAAA;AAAA,IAC/C,CAAA;AAAA,IACA,gBAAA,EAAkB,KAAK,YAAY;AACjC,MAAA,MAAM,SAAA,GAAa,MAAM,OAAA,CAAQ,MAAA,IAAS,IAAM,iBAAA;AAChD,MAAA,OAAO,EAAE,SAAS,SAAA,EAAU;AAAA,IAC9B,CAAC,CAAA;AAAA,IACD,gBACE,OAAA,CAAQ;AAAA,GACX,CAAA;AAED,EAAA,MAAM,gBAAA,GAAmB,CAAC,KAAA,KAAmC;AAC3D,IAAA,MAAM,MAAM,kBAAA,EAAmB;AAE/B,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,MAAM,WAAA,GAAc,2BAAA,CAA4B,UAAA,CAAW,GAAG,CAAA;AAC9D,MAAA,MAAMA,aAAY,WAAA,CAAY,gBAAA;AAC9B,MAAA,MAAM,UAAA,GAAa,WAAA,CAAY,cAAA,GAAiB,KAAK,CAAA,IAAK,KAAA;AAC1D,MAAA,uBAAO,GAAA,CAACA,UAAAA,EAAA,EAAW,GAAG,UAAA,EAAY,CAAA;AAAA,IACpC;AAEA,IAAA,MAAM,SAAA,GAAY,GAAA,CAAI,YAAA,CAAkB,GAAG,CAAA;AAC3C,IAAA,uBAAO,GAAA,CAAC,SAAA,EAAA,EAAW,GAAG,KAAA,EAAO,CAAA;AAAA,EAC/B,CAAA;AAEA,EAAA,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,EAAE,GAAA,EAAK,CAAA;AAEvC,EAAA,OAAO,gBAAA;AAIT;;;;"}
1
+ {"version":3,"file":"createSwappableComponent.esm.js","sources":["../../src/components/createSwappableComponent.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 { OpaqueSwappableComponentRef } from '@internal/frontend';\nimport { swappableComponentsApiRef, useApi } from '../apis';\nimport { lazy } from 'react';\n\n/** @public */\nexport type SwappableComponentRef<\n TInnerComponentProps extends {} = {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n TProps: TInnerComponentProps;\n TExternalProps: TExternalComponentProps;\n $$type: '@backstage/SwappableComponentRef';\n};\n\n/**\n * Options for creating an SwappableComponent.\n *\n * @public\n */\nexport type CreateSwappableComponentOptions<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n> = {\n id: string;\n loader?:\n | (() => (props: TInnerComponentProps) => JSX.Element | null)\n | (() => Promise<(props: TInnerComponentProps) => JSX.Element | null>);\n transformProps?: (props: TExternalComponentProps) => TInnerComponentProps;\n};\n\nconst useComponentRefApi = () => {\n try {\n return useApi(swappableComponentsApiRef);\n } catch (e) {\n return undefined;\n }\n};\n\n/**\n * Creates a SwappableComponent that can be used to render the component, optionally overridden by the app.\n *\n * @public\n */\nexport function createSwappableComponent<\n TInnerComponentProps extends {},\n TExternalComponentProps extends {} = TInnerComponentProps,\n>(\n options: CreateSwappableComponentOptions<\n TInnerComponentProps,\n TExternalComponentProps\n >,\n): {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n} {\n const FallbackComponent = (p: JSX.IntrinsicAttributes) => (\n <div data-testid={options.id} {...p} />\n );\n\n const ref = OpaqueSwappableComponentRef.createInstance('v1', {\n id: options.id,\n TProps: null as unknown as TInnerComponentProps,\n TExternalProps: null as unknown as TExternalComponentProps,\n toString() {\n return `SwappableComponentRef{id=${options.id}}`;\n },\n defaultComponent: lazy(async () => {\n const Component = (await options.loader?.()) ?? FallbackComponent;\n return { default: Component };\n }) as (typeof OpaqueSwappableComponentRef.TInternal)['defaultComponent'],\n transformProps:\n options.transformProps as (typeof OpaqueSwappableComponentRef.TInternal)['transformProps'],\n });\n\n const ComponentRefImpl = (props: TExternalComponentProps) => {\n const api = useComponentRefApi();\n\n if (!api) {\n const internalRef = OpaqueSwappableComponentRef.toInternal(ref);\n const Component = internalRef.defaultComponent;\n const innerProps = internalRef.transformProps?.(props) ?? props;\n return <Component {...innerProps} />;\n }\n\n const Component = api.getComponent<any>(ref);\n return <Component {...props} />;\n };\n\n Object.assign(ComponentRefImpl, { ref });\n\n return ComponentRefImpl as {\n (props: TExternalComponentProps): JSX.Element | null;\n ref: SwappableComponentRef<TInnerComponentProps, TExternalComponentProps>;\n };\n}\n"],"names":["Component"],"mappings":";;;;;;;;;;;;;;AA+CA,MAAM,qBAAqB,MAAM;AAC/B,EAAA,IAAI;AACF,IAAA,OAAO,OAAO,yBAAyB,CAAA;AAAA,EACzC,SAAS,CAAA,EAAG;AACV,IAAA,OAAO,MAAA;AAAA,EACT;AACF,CAAA;AAOO,SAAS,yBAId,OAAA,EAOA;AACA,EAAA,MAAM,iBAAA,GAAoB,CAAC,CAAA,qBACzB,GAAA,CAAC,SAAI,aAAA,EAAa,OAAA,CAAQ,EAAA,EAAK,GAAG,CAAA,EAAG,CAAA;AAGvC,EAAA,MAAM,GAAA,GAAM,2BAAA,CAA4B,cAAA,CAAe,IAAA,EAAM;AAAA,IAC3D,IAAI,OAAA,CAAQ,EAAA;AAAA,IACZ,MAAA,EAAQ,IAAA;AAAA,IACR,cAAA,EAAgB,IAAA;AAAA,IAChB,QAAA,GAAW;AACT,MAAA,OAAO,CAAA,yBAAA,EAA4B,QAAQ,EAAE,CAAA,CAAA,CAAA;AAAA,IAC/C,CAAA;AAAA,IACA,gBAAA,EAAkB,KAAK,YAAY;AACjC,MAAA,MAAM,SAAA,GAAa,MAAM,OAAA,CAAQ,MAAA,IAAS,IAAM,iBAAA;AAChD,MAAA,OAAO,EAAE,SAAS,SAAA,EAAU;AAAA,IAC9B,CAAC,CAAA;AAAA,IACD,gBACE,OAAA,CAAQ;AAAA,GACX,CAAA;AAED,EAAA,MAAM,gBAAA,GAAmB,CAAC,KAAA,KAAmC;AAC3D,IAAA,MAAM,MAAM,kBAAA,EAAmB;AAE/B,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,MAAM,WAAA,GAAc,2BAAA,CAA4B,UAAA,CAAW,GAAG,CAAA;AAC9D,MAAA,MAAMA,aAAY,WAAA,CAAY,gBAAA;AAC9B,MAAA,MAAM,UAAA,GAAa,WAAA,CAAY,cAAA,GAAiB,KAAK,CAAA,IAAK,KAAA;AAC1D,MAAA,uBAAO,GAAA,CAACA,UAAAA,EAAA,EAAW,GAAG,UAAA,EAAY,CAAA;AAAA,IACpC;AAEA,IAAA,MAAM,SAAA,GAAY,GAAA,CAAI,YAAA,CAAkB,GAAG,CAAA;AAC3C,IAAA,uBAAO,GAAA,CAAC,SAAA,EAAA,EAAW,GAAG,KAAA,EAAO,CAAA;AAAA,EAC/B,CAAA;AAEA,EAAA,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,EAAE,GAAA,EAAK,CAAA;AAEvC,EAAA,OAAO,gBAAA;AAIT;;;;"}
@@ -0,0 +1,9 @@
1
+ import { OpaqueType } from '../../../opaque-internal/src/OpaqueType.esm.js';
2
+
3
+ const OpaqueExternalRouteRef = OpaqueType.create({
4
+ type: "@backstage/ExternalRouteRef",
5
+ versions: ["v1"]
6
+ });
7
+
8
+ export { OpaqueExternalRouteRef };
9
+ //# sourceMappingURL=OpaqueExternalRouteRef.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpaqueExternalRouteRef.esm.js","sources":["../../../../../frontend-internal/src/routing/OpaqueExternalRouteRef.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 { ExternalRouteRef } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueExternalRouteRef = OpaqueType.create<{\n public: ExternalRouteRef;\n versions: {\n readonly version: 'v1';\n\n getParams(): string[];\n getDescription(): string;\n getDefaultTarget(): string | undefined;\n\n setId(id: string): void;\n };\n}>({\n type: '@backstage/ExternalRouteRef',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAmBO,MAAM,sBAAA,GAAyB,WAAW,MAAA,CAW9C;AAAA,EACD,IAAA,EAAM,6BAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC;;;;"}
@@ -0,0 +1,9 @@
1
+ import { OpaqueType } from '../../../opaque-internal/src/OpaqueType.esm.js';
2
+
3
+ const OpaqueRouteRef = OpaqueType.create({
4
+ type: "@backstage/RouteRef",
5
+ versions: ["v1"]
6
+ });
7
+
8
+ export { OpaqueRouteRef };
9
+ //# sourceMappingURL=OpaqueRouteRef.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpaqueRouteRef.esm.js","sources":["../../../../../frontend-internal/src/routing/OpaqueRouteRef.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 { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueRouteRef = OpaqueType.create<{\n public: RouteRef;\n versions: {\n readonly version: 'v1';\n\n getParams(): string[];\n getDescription(): string;\n\n alias: string | undefined;\n\n setId(id: string): void;\n };\n}>({\n type: '@backstage/RouteRef',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAmBO,MAAM,cAAA,GAAiB,WAAW,MAAA,CAYtC;AAAA,EACD,IAAA,EAAM,qBAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC;;;;"}
@@ -0,0 +1,9 @@
1
+ import { OpaqueType } from '../../../opaque-internal/src/OpaqueType.esm.js';
2
+
3
+ const OpaqueSubRouteRef = OpaqueType.create({
4
+ type: "@backstage/SubRouteRef",
5
+ versions: ["v1"]
6
+ });
7
+
8
+ export { OpaqueSubRouteRef };
9
+ //# sourceMappingURL=OpaqueSubRouteRef.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpaqueSubRouteRef.esm.js","sources":["../../../../../frontend-internal/src/routing/OpaqueSubRouteRef.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, SubRouteRef } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueSubRouteRef = OpaqueType.create<{\n public: SubRouteRef;\n versions: {\n readonly version: 'v1';\n\n getParams(): string[];\n getParent(): RouteRef;\n getDescription(): string;\n };\n}>({\n type: '@backstage/SubRouteRef',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAmBO,MAAM,iBAAA,GAAoB,WAAW,MAAA,CASzC;AAAA,EACD,IAAA,EAAM,wBAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"InternalExtensionDefinition.esm.js","sources":["../../../../../frontend-internal/src/wiring/InternalExtensionDefinition.ts"],"sourcesContent":["/*\n * Copyright 2024 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 AppNode,\n ExtensionAttachToSpec,\n ExtensionDataValue,\n ExtensionDataRef,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n ExtensionInput,\n PortableSchema,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueExtensionDefinition = OpaqueType.create<{\n public: ExtensionDefinition<ExtensionDefinitionParameters>;\n versions:\n | {\n readonly version: 'v1';\n readonly kind?: string;\n readonly namespace?: string;\n readonly name?: string;\n readonly attachTo: ExtensionAttachToSpec;\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<any, any>;\n readonly inputs: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: ExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n };\n readonly output: {\n [name in string]: ExtensionDataRef;\n };\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: object;\n inputs: {\n [inputName in string]: unknown;\n };\n }): {\n [inputName in string]: unknown;\n };\n }\n | {\n readonly version: 'v2';\n readonly kind?: string;\n readonly namespace?: string;\n readonly name?: string;\n readonly attachTo: ExtensionAttachToSpec;\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<any, any>;\n readonly inputs: { [inputName in string]: ExtensionInput };\n readonly output: Array<ExtensionDataRef>;\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: object;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput;\n }>;\n }): Iterable<ExtensionDataValue<any, any>>;\n };\n}>({\n type: '@backstage/ExtensionDefinition',\n versions: ['v1', 'v2'],\n});\n"],"names":[],"mappings":";;AA8BO,MAAM,yBAAA,GAA4B,WAAW,MAAA,CAqDjD;AAAA,EACD,IAAA,EAAM,gCAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAA,EAAM,IAAI;AACvB,CAAC;;;;"}
1
+ {"version":3,"file":"InternalExtensionDefinition.esm.js","sources":["../../../../../frontend-internal/src/wiring/InternalExtensionDefinition.ts"],"sourcesContent":["/*\n * Copyright 2024 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 AppNode,\n ExtensionDefinitionAttachTo,\n ExtensionDataValue,\n ExtensionDataRef,\n OverridableExtensionDefinition,\n ExtensionDefinitionParameters,\n ExtensionInput,\n PortableSchema,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueExtensionDefinition = OpaqueType.create<{\n public: OverridableExtensionDefinition<ExtensionDefinitionParameters>;\n versions:\n | {\n readonly version: 'v1';\n readonly kind?: string;\n readonly namespace?: string;\n readonly name?: string;\n readonly attachTo: ExtensionDefinitionAttachTo;\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<any, any>;\n readonly inputs: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: ExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n };\n readonly output: {\n [name in string]: ExtensionDataRef;\n };\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: object;\n inputs: {\n [inputName in string]: unknown;\n };\n }): {\n [inputName in string]: unknown;\n };\n }\n | {\n readonly version: 'v2';\n readonly kind?: string;\n readonly namespace?: string;\n readonly name?: string;\n readonly attachTo: ExtensionDefinitionAttachTo;\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<any, any>;\n readonly inputs: { [inputName in string]: ExtensionInput };\n readonly output: Array<ExtensionDataRef>;\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: object;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput;\n }>;\n }): Iterable<ExtensionDataValue<any, any>>;\n };\n}>({\n type: '@backstage/ExtensionDefinition',\n versions: ['v1', 'v2'],\n});\n"],"names":[],"mappings":";;AA8BO,MAAM,yBAAA,GAA4B,WAAW,MAAA,CAqDjD;AAAA,EACD,IAAA,EAAM,gCAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAA,EAAM,IAAI;AACvB,CAAC;;;;"}
@@ -0,0 +1,9 @@
1
+ import { OpaqueType } from '../../../opaque-internal/src/OpaqueType.esm.js';
2
+
3
+ const OpaqueExtensionInput = OpaqueType.create({
4
+ type: "@backstage/ExtensionInput",
5
+ versions: [void 0]
6
+ });
7
+
8
+ export { OpaqueExtensionInput };
9
+ //# sourceMappingURL=InternalExtensionInput.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InternalExtensionInput.esm.js","sources":["../../../../../frontend-internal/src/wiring/InternalExtensionInput.ts"],"sourcesContent":["/*\n * Copyright 2024 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 { ExtensionInput } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport type ExtensionInputContext = {\n input: string;\n kind?: string;\n name?: string;\n};\n\nexport const OpaqueExtensionInput = OpaqueType.create<{\n public: ExtensionInput;\n versions: {\n readonly version: undefined;\n readonly context?: ExtensionInputContext;\n withContext(context: ExtensionInputContext): ExtensionInput;\n };\n}>({\n type: '@backstage/ExtensionInput',\n versions: [undefined],\n});\n"],"names":[],"mappings":";;AAyBO,MAAM,oBAAA,GAAuB,WAAW,MAAA,CAO5C;AAAA,EACD,IAAA,EAAM,2BAAA;AAAA,EACN,QAAA,EAAU,CAAC,MAAS;AACtB,CAAC;;;;"}
package/dist/index.d.ts CHANGED
@@ -140,10 +140,10 @@ interface ExtensionInput<UExtensionData extends ExtensionDataRef<unknown, string
140
140
  singleton: boolean;
141
141
  optional: boolean;
142
142
  }> {
143
- $$type: '@backstage/ExtensionInput';
144
- extensionData: Array<UExtensionData>;
145
- config: TConfig;
146
- replaces?: Array<{
143
+ readonly $$type: '@backstage/ExtensionInput';
144
+ readonly extensionData: Array<UExtensionData>;
145
+ readonly config: TConfig;
146
+ readonly replaces?: Array<{
147
147
  id: string;
148
148
  input: string;
149
149
  }>;
@@ -245,6 +245,19 @@ type PortableSchema<TOutput, TInput = TOutput> = {
245
245
  schema: JsonObject;
246
246
  };
247
247
 
248
+ /** @public */
249
+ type ExtensionAttachTo = {
250
+ id: string;
251
+ input: string;
252
+ } | Array<{
253
+ id: string;
254
+ input: string;
255
+ }>;
256
+ /**
257
+ * @deprecated Use {@link ExtensionAttachTo} instead.
258
+ * @public
259
+ */
260
+ type ExtensionAttachToSpec = ExtensionAttachTo;
248
261
  /** @public */
249
262
  interface Extension<TConfig, TConfigInput = TConfig> {
250
263
  $$type: '@backstage/Extension';
@@ -387,7 +400,7 @@ interface ExternalRouteRef<TParams extends AnyRouteRefParams = AnyRouteRefParams
387
400
  */
388
401
  declare function createExternalRouteRef<TParams extends {
389
402
  [param in TParamKeys]: string;
390
- } | undefined = undefined, TParamKeys extends string = string>(options?: {
403
+ } | undefined = undefined, TParamKeys extends string = string>(config?: {
391
404
  /**
392
405
  * The parameters that will be provided to the external route reference.
393
406
  */
@@ -500,7 +513,7 @@ interface OverridableFrontendPlugin<TRoutes extends {
500
513
  } = {
501
514
  [id in string]: ExtensionDefinition;
502
515
  }> extends FrontendPlugin<TRoutes, TExternalRoutes> {
503
- getExtension<TId extends keyof TExtensionMap>(id: TId): TExtensionMap[TId];
516
+ getExtension<TId extends keyof TExtensionMap>(id: TId): OverridableExtensionDefinition<TExtensionMap[TId]['T']>;
504
517
  withOverrides(options: {
505
518
  extensions: Array<ExtensionDefinition>;
506
519
  /**
@@ -649,9 +662,9 @@ type CreateExtensionBlueprintOptions<TKind extends string, TParams extends objec
649
662
  [key in string]: (zImpl: typeof z) => z.ZodType;
650
663
  }, UFactoryOutput extends ExtensionDataValue<any, any>, TDataRefs extends {
651
664
  [name in string]: ExtensionDataRef;
652
- }> = {
665
+ }, UParentInputs extends ExtensionDataRef> = {
653
666
  kind: TKind;
654
- attachTo: ExtensionAttachToSpec;
667
+ attachTo: ExtensionDefinitionAttachTo<UParentInputs> & VerifyExtensionAttachTo<UOutput, UParentInputs>;
655
668
  disabled?: boolean;
656
669
  inputs?: TInputs;
657
670
  output: Array<UOutput>;
@@ -739,12 +752,12 @@ type AnyParamsInput$1<TParams extends object | ExtensionBlueprintDefineParams> =
739
752
  */
740
753
  interface ExtensionBlueprint<T extends ExtensionBlueprintParameters = ExtensionBlueprintParameters> {
741
754
  dataRefs: T['dataRefs'];
742
- make<TName extends string | undefined, TParamsInput extends AnyParamsInput$1<NonNullable<T['params']>>>(args: {
755
+ make<TName extends string | undefined, TParamsInput extends AnyParamsInput$1<NonNullable<T['params']>>, UParentInputs extends ExtensionDataRef>(args: {
743
756
  name?: TName;
744
- attachTo?: ExtensionAttachToSpec;
757
+ attachTo?: ExtensionDefinitionAttachTo<UParentInputs> & VerifyExtensionAttachTo<NonNullable<T['output']>, UParentInputs>;
745
758
  disabled?: boolean;
746
759
  params: TParamsInput extends ExtensionBlueprintDefineParams ? TParamsInput : T['params'] extends ExtensionBlueprintDefineParams ? 'Error: This blueprint uses advanced parameter types and requires you to pass parameters as using the following callback syntax: `<blueprint>.make({ params: defineParams => defineParams(<params>) })`' : T['params'];
747
- }): ExtensionDefinition<{
760
+ }): OverridableExtensionDefinition<{
748
761
  kind: T['kind'];
749
762
  name: string | undefined extends TName ? undefined : TName;
750
763
  config: T['config'];
@@ -763,9 +776,9 @@ interface ExtensionBlueprint<T extends ExtensionBlueprintParameters = ExtensionB
763
776
  [key in string]: (zImpl: typeof z) => z.ZodType;
764
777
  }, UFactoryOutput extends ExtensionDataValue<any, any>, UNewOutput extends ExtensionDataRef, TExtraInputs extends {
765
778
  [inputName in string]: ExtensionInput;
766
- }>(args: {
779
+ }, UParentInputs extends ExtensionDataRef>(args: {
767
780
  name?: TName;
768
- attachTo?: ExtensionAttachToSpec;
781
+ attachTo?: ExtensionDefinitionAttachTo<UParentInputs> & VerifyExtensionAttachTo<ExtensionDataRef extends UNewOutput ? NonNullable<T['output']> : UNewOutput, UParentInputs>;
769
782
  disabled?: boolean;
770
783
  inputs?: TExtraInputs & {
771
784
  [KName in keyof T['inputs']]?: `Error: Input '${KName & string}' is already defined in parent definition`;
@@ -787,7 +800,7 @@ interface ExtensionBlueprint<T extends ExtensionBlueprintParameters = ExtensionB
787
800
  };
788
801
  inputs: Expand<ResolvedExtensionInputs<T['inputs'] & TExtraInputs>>;
789
802
  }): Iterable<UFactoryOutput> & VerifyExtensionFactoryOutput<ExtensionDataRef extends UNewOutput ? NonNullable<T['output']> : UNewOutput, UFactoryOutput>;
790
- }): ExtensionDefinition<{
803
+ }): OverridableExtensionDefinition<{
791
804
  config: (string extends keyof TExtensionConfigSchema ? {} : {
792
805
  [key in keyof TExtensionConfigSchema]: z.infer<ReturnType<TExtensionConfigSchema[key]>>;
793
806
  }) & T['config'];
@@ -854,9 +867,9 @@ declare function createExtensionBlueprint<TParams extends object | ExtensionBlue
854
867
  [inputName in string]: ExtensionInput;
855
868
  }, TConfigSchema extends {
856
869
  [key in string]: (zImpl: typeof z) => z.ZodType;
857
- }, UFactoryOutput extends ExtensionDataValue<any, any>, TKind extends string, TDataRefs extends {
870
+ }, UFactoryOutput extends ExtensionDataValue<any, any>, TKind extends string, UParentInputs extends ExtensionDataRef, TDataRefs extends {
858
871
  [name in string]: ExtensionDataRef;
859
- } = never>(options: CreateExtensionBlueprintOptions<TKind, TParams, UOutput, TInputs, TConfigSchema, UFactoryOutput, TDataRefs>): ExtensionBlueprint<{
872
+ } = never>(options: CreateExtensionBlueprintOptions<TKind, TParams, UOutput, TInputs, TConfigSchema, UFactoryOutput, TDataRefs, UParentInputs>): ExtensionBlueprint<{
860
873
  kind: TKind;
861
874
  params: TParams;
862
875
  output: UOutput extends ExtensionDataRef<infer IData, infer IId, infer IConfig> ? ExtensionDataRef<IData, IId, IConfig> : never;
@@ -891,24 +904,77 @@ type PopUnion<U> = ToIntersection<U extends any ? () => U : never> extends () =>
891
904
  /** @ignore */
892
905
  type JoinStringUnion<U, TDiv extends string = ', ', TResult extends string = ''> = PopUnion<U> extends [infer IRest extends string, infer INext extends string] ? TResult extends '' ? JoinStringUnion<IRest, TDiv, INext> : JoinStringUnion<IRest, TDiv, `${TResult}${TDiv}${INext}`> : TResult;
893
906
  /** @ignore */
894
- type VerifyExtensionFactoryOutput<UDeclaredOutput extends ExtensionDataRef, UFactoryOutput extends ExtensionDataValue<any, any>> = (UDeclaredOutput extends any ? UDeclaredOutput['config']['optional'] extends true ? never : UDeclaredOutput['id'] : never) extends infer IRequiredOutputIds ? [IRequiredOutputIds] extends [UFactoryOutput['id']] ? [UFactoryOutput['id']] extends [UDeclaredOutput['id']] ? {} : `Error: The extension factory has undeclared output(s): ${JoinStringUnion<Exclude<UFactoryOutput['id'], UDeclaredOutput['id']>>}` : `Error: The extension factory is missing the following output(s): ${JoinStringUnion<Exclude<IRequiredOutputIds, UFactoryOutput['id']>>}` : never;
895
- /** @public */
896
- type ExtensionAttachToSpec = {
907
+ type RequiredExtensionIds<UExtensionData extends ExtensionDataRef> = UExtensionData extends any ? UExtensionData['config']['optional'] extends true ? never : UExtensionData['id'] : never;
908
+ /** @ignore */
909
+ type VerifyExtensionFactoryOutput<UDeclaredOutput extends ExtensionDataRef, UFactoryOutput extends ExtensionDataValue<any, any>> = [RequiredExtensionIds<UDeclaredOutput>] extends [UFactoryOutput['id']] ? [UFactoryOutput['id']] extends [UDeclaredOutput['id']] ? {} : `Error: The extension factory has undeclared output(s): ${JoinStringUnion<Exclude<UFactoryOutput['id'], UDeclaredOutput['id']>>}` : `Error: The extension factory is missing the following output(s): ${JoinStringUnion<Exclude<RequiredExtensionIds<UDeclaredOutput>, UFactoryOutput['id']>>}`;
910
+ /** @ignore */
911
+ type VerifyExtensionAttachTo<UOutput extends ExtensionDataRef, UParentInput extends ExtensionDataRef> = ExtensionDataRef extends UParentInput ? {} : [RequiredExtensionIds<UParentInput>] extends [RequiredExtensionIds<UOutput>] ? {} : `Error: This parent extension input requires the following extension data, but it is not declared as guaranteed output of this extension: ${JoinStringUnion<Exclude<RequiredExtensionIds<UParentInput>, RequiredExtensionIds<UOutput>>>}`;
912
+ /**
913
+ * Specifies where an extension should attach in the extension tree.
914
+ *
915
+ * @remarks
916
+ *
917
+ * A standard attachment point declaration will specify the ID of the parent extension, as well as the name of the input to attach to.
918
+ *
919
+ * There are three more advanced forms that are available for more complex use-cases:
920
+ *
921
+ * 1. Relative attachment points: using the `relative` property instead of `id`, the attachment point is resolved relative to the current plugin.
922
+ * 2. Extension input references: using a reference in code to another extension's input in the same plugin. These references are always relative.
923
+ * 3. Array of attachment points: an array of attachment points can be used to clone and attach to multiple extensions at once.
924
+ *
925
+ * @example
926
+ * ```ts
927
+ * // Attach to a specific extension by full ID
928
+ * { id: 'app/routes', input: 'routes' }
929
+ *
930
+ * // Attach to an extension in the same plugin by kind
931
+ * { relative: { kind: 'page' }, input: 'actions' }
932
+ *
933
+ * // Attach to a specific input of another extension
934
+ * const page = ParentBlueprint.make({ ... });
935
+ * const child = ChildBlueprint.make({ attachTo: page.inputs.children });
936
+ *
937
+ * // Attach to multiple parents at once
938
+ * [
939
+ * { id: 'page/home', input: 'widgets' },
940
+ * { relative: { kind: 'page' }, input: 'widgets' },
941
+ * ]
942
+ * ```
943
+ *
944
+ * @public
945
+ */
946
+ type ExtensionDefinitionAttachTo<UParentInputs extends ExtensionDataRef = ExtensionDataRef> = {
897
947
  id: string;
898
948
  input: string;
899
- } | Array<{
949
+ relative?: never;
950
+ } | {
951
+ relative: {
952
+ kind?: string;
953
+ name?: string;
954
+ };
955
+ input: string;
956
+ id?: never;
957
+ } | ExtensionInput<UParentInputs> | Array<{
900
958
  id: string;
901
959
  input: string;
902
- }>;
960
+ relative?: never;
961
+ } | {
962
+ relative: {
963
+ kind?: string;
964
+ name?: string;
965
+ };
966
+ input: string;
967
+ id?: never;
968
+ } | ExtensionInput<UParentInputs>>;
903
969
  /** @public */
904
970
  type CreateExtensionOptions<TKind extends string | undefined, TName extends string | undefined, UOutput extends ExtensionDataRef, TInputs extends {
905
971
  [inputName in string]: ExtensionInput;
906
972
  }, TConfigSchema extends {
907
973
  [key: string]: (zImpl: typeof z) => z.ZodType;
908
- }, UFactoryOutput extends ExtensionDataValue<any, any>> = {
974
+ }, UFactoryOutput extends ExtensionDataValue<any, any>, UParentInputs extends ExtensionDataRef> = {
909
975
  kind?: TKind;
910
976
  name?: TName;
911
- attachTo: ExtensionAttachToSpec;
977
+ attachTo: ExtensionDefinitionAttachTo<UParentInputs> & VerifyExtensionAttachTo<UOutput, UParentInputs>;
912
978
  disabled?: boolean;
913
979
  inputs?: TInputs;
914
980
  output: Array<UOutput>;
@@ -947,15 +1013,24 @@ type ExtensionDefinitionParameters = {
947
1013
  */
948
1014
  type AnyParamsInput<TParams extends object | ExtensionBlueprintDefineParams> = TParams extends ExtensionBlueprintDefineParams<infer IParams> ? IParams | ((define: TParams) => ReturnType<TParams>) : TParams | ((define: ExtensionBlueprintDefineParams<TParams, TParams>) => ReturnType<ExtensionBlueprintDefineParams<TParams, TParams>>);
949
1015
  /** @public */
950
- type ExtensionDefinition<T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters> = {
1016
+ interface ExtensionDefinition<TParams extends ExtensionDefinitionParameters = ExtensionDefinitionParameters> {
951
1017
  $$type: '@backstage/ExtensionDefinition';
952
- readonly T: T;
1018
+ readonly T: TParams;
1019
+ }
1020
+ /** @public */
1021
+ interface OverridableExtensionDefinition<T extends ExtensionDefinitionParameters = ExtensionDefinitionParameters> extends ExtensionDefinition<T> {
1022
+ /**
1023
+ * References to the inputs of this extension, which can be used to attach child extensions.
1024
+ */
1025
+ readonly inputs: {
1026
+ [K in keyof T['inputs']]: ExtensionInput<T['inputs'][K] extends ExtensionInput<infer IData> ? IData : never>;
1027
+ };
953
1028
  override<TExtensionConfigSchema extends {
954
1029
  [key in string]: (zImpl: typeof z) => z.ZodType;
955
1030
  }, UFactoryOutput extends ExtensionDataValue<any, any>, UNewOutput extends ExtensionDataRef, TExtraInputs extends {
956
1031
  [inputName in string]: ExtensionInput;
957
- }, TParamsInput extends AnyParamsInput<NonNullable<T['params']>>>(args: Expand<{
958
- attachTo?: ExtensionAttachToSpec;
1032
+ }, TParamsInput extends AnyParamsInput<NonNullable<T['params']>>, UParentInputs extends ExtensionDataRef>(args: Expand<{
1033
+ attachTo?: ExtensionDefinitionAttachTo<UParentInputs> & VerifyExtensionAttachTo<ExtensionDataRef extends UNewOutput ? NonNullable<T['output']> : UNewOutput, UParentInputs>;
959
1034
  disabled?: boolean;
960
1035
  inputs?: TExtraInputs & {
961
1036
  [KName in keyof T['inputs']]?: `Error: Input '${KName & string}' is already defined in parent definition`;
@@ -981,7 +1056,7 @@ type ExtensionDefinition<T extends ExtensionDefinitionParameters = ExtensionDefi
981
1056
  }): Iterable<UFactoryOutput>;
982
1057
  } & ([T['params']] extends [never] ? {} : {
983
1058
  params?: TParamsInput extends ExtensionBlueprintDefineParams ? TParamsInput : T['params'] extends ExtensionBlueprintDefineParams ? 'Error: This blueprint uses advanced parameter types and requires you to pass parameters as using the following callback syntax: `originalFactory(defineParams => defineParams(<params>))`' : Partial<T['params']>;
984
- })> & VerifyExtensionFactoryOutput<ExtensionDataRef extends UNewOutput ? NonNullable<T['output']> : UNewOutput, UFactoryOutput>): ExtensionDefinition<{
1059
+ })> & VerifyExtensionFactoryOutput<ExtensionDataRef extends UNewOutput ? NonNullable<T['output']> : UNewOutput, UFactoryOutput>): OverridableExtensionDefinition<{
985
1060
  kind: T['kind'];
986
1061
  name: T['name'];
987
1062
  output: ExtensionDataRef extends UNewOutput ? T['output'] : UNewOutput;
@@ -993,7 +1068,7 @@ type ExtensionDefinition<T extends ExtensionDefinitionParameters = ExtensionDefi
993
1068
  [key in keyof TExtensionConfigSchema]: ReturnType<TExtensionConfigSchema[key]>;
994
1069
  }>>;
995
1070
  }>;
996
- };
1071
+ }
997
1072
  /**
998
1073
  * Creates a new extension definition for installation in a Backstage app.
999
1074
  *
@@ -1033,7 +1108,7 @@ declare function createExtension<UOutput extends ExtensionDataRef, TInputs exten
1033
1108
  [inputName in string]: ExtensionInput;
1034
1109
  }, TConfigSchema extends {
1035
1110
  [key: string]: (zImpl: typeof z) => z.ZodType;
1036
- }, UFactoryOutput extends ExtensionDataValue<any, any>, const TKind extends string | undefined = undefined, const TName extends string | undefined = undefined>(options: CreateExtensionOptions<TKind, TName, UOutput, TInputs, TConfigSchema, UFactoryOutput>): ExtensionDefinition<{
1111
+ }, UFactoryOutput extends ExtensionDataValue<any, any>, const TKind extends string | undefined = undefined, const TName extends string | undefined = undefined, UParentInputs extends ExtensionDataRef = ExtensionDataRef>(options: CreateExtensionOptions<TKind, TName, UOutput, TInputs, TConfigSchema, UFactoryOutput, UParentInputs>): OverridableExtensionDefinition<{
1037
1112
  config: string extends keyof TConfigSchema ? {} : {
1038
1113
  [key in keyof TConfigSchema]: z.infer<ReturnType<TConfigSchema[key]>>;
1039
1114
  };
@@ -1077,7 +1152,7 @@ declare function createFrontendFeatureLoader(options: CreateFrontendFeatureLoade
1077
1152
  */
1078
1153
  interface AppNodeSpec {
1079
1154
  readonly id: string;
1080
- readonly attachTo: ExtensionAttachToSpec;
1155
+ readonly attachTo: ExtensionAttachTo;
1081
1156
  readonly extension: Extension<unknown, unknown>;
1082
1157
  readonly disabled: boolean;
1083
1158
  readonly config?: unknown;
@@ -1944,4 +2019,4 @@ declare const SwappableComponentBlueprint: ExtensionBlueprint<{
1944
2019
  };
1945
2020
  }>;
1946
2021
 
1947
- export { type AnalyticsApi, AnalyticsContext, type AnalyticsContextValue, type AnalyticsEvent, type AnalyticsEventAttributes, type AnalyticsImplementation, AnalyticsImplementationBlueprint, type AnalyticsImplementationFactory, type AnalyticsTracker, type AnyExtensionDataRef, type AnyRouteRefParams, ApiBlueprint, type AppNode, type AppNodeEdges, type AppNodeInstance, type AppNodeSpec, AppRootElementBlueprint, AppRootWrapperBlueprint, type AppTree, type AppTreeApi, type ConfigurableExtensionDataRef, type CreateExtensionBlueprintOptions, type CreateExtensionOptions, type CreateFrontendFeatureLoaderOptions, type CreateFrontendModuleOptions, type CreateSwappableComponentOptions, type DialogApi, type DialogApiDialog, ErrorDisplay, type ErrorDisplayProps, type Extension, type ExtensionAttachToSpec, type ExtensionBlueprint, type ExtensionBlueprintDefineParams, type ExtensionBlueprintParameters, type ExtensionBlueprintParams, ExtensionBoundary, type ExtensionBoundaryProps, type ExtensionDataContainer, type ExtensionDataRef, type ExtensionDataRefToValue, type ExtensionDataValue, type ExtensionDefinition, type ExtensionDefinitionParameters, type ExtensionFactoryMiddleware, type ExtensionInput, type ExternalRouteRef, type FeatureFlagConfig, type FrontendFeature, type FrontendFeatureLoader, type FrontendModule, type FrontendPlugin, type FrontendPluginInfo, type FrontendPluginInfoOptions, IconBundleBlueprint, type IconComponent, type IconsApi, NavContentBlueprint, type NavContentComponent, type NavContentComponentProps, NavItemBlueprint, NotFoundErrorPage, type NotFoundErrorPageProps, type OverridableFrontendPlugin, PageBlueprint, type PluginOptions, type PortableSchema, Progress, type ProgressProps, type ResolvedExtensionInput, type ResolvedExtensionInputs, type RouteFunc, type RouteRef, type RouteResolutionApi, RouterBlueprint, SignInPageBlueprint, type SubRouteRef, SwappableComponentBlueprint, type SwappableComponentRef, type SwappableComponentsApi, ThemeBlueprint, TranslationBlueprint, analyticsApiRef, appTreeApiRef, coreExtensionData, createExtension, createExtensionBlueprint, createExtensionBlueprintParams, createExtensionDataRef, createExtensionInput, createExternalRouteRef, createFrontendFeatureLoader, createFrontendModule, createFrontendPlugin, createRouteRef, createSubRouteRef, createSwappableComponent, dialogApiRef, iconsApiRef, routeResolutionApiRef, swappableComponentsApiRef, useAnalytics, useAppNode, useRouteRef, useRouteRefParams };
2022
+ export { type AnalyticsApi, AnalyticsContext, type AnalyticsContextValue, type AnalyticsEvent, type AnalyticsEventAttributes, type AnalyticsImplementation, AnalyticsImplementationBlueprint, type AnalyticsImplementationFactory, type AnalyticsTracker, type AnyExtensionDataRef, type AnyRouteRefParams, ApiBlueprint, type AppNode, type AppNodeEdges, type AppNodeInstance, type AppNodeSpec, AppRootElementBlueprint, AppRootWrapperBlueprint, type AppTree, type AppTreeApi, type ConfigurableExtensionDataRef, type CreateExtensionBlueprintOptions, type CreateExtensionOptions, type CreateFrontendFeatureLoaderOptions, type CreateFrontendModuleOptions, type CreateSwappableComponentOptions, type DialogApi, type DialogApiDialog, ErrorDisplay, type ErrorDisplayProps, type Extension, type ExtensionAttachTo, type ExtensionAttachToSpec, type ExtensionBlueprint, type ExtensionBlueprintDefineParams, type ExtensionBlueprintParameters, type ExtensionBlueprintParams, ExtensionBoundary, type ExtensionBoundaryProps, type ExtensionDataContainer, type ExtensionDataRef, type ExtensionDataRefToValue, type ExtensionDataValue, type ExtensionDefinition, type ExtensionDefinitionAttachTo, type ExtensionDefinitionParameters, type ExtensionFactoryMiddleware, type ExtensionInput, type ExternalRouteRef, type FeatureFlagConfig, type FrontendFeature, type FrontendFeatureLoader, type FrontendModule, type FrontendPlugin, type FrontendPluginInfo, type FrontendPluginInfoOptions, IconBundleBlueprint, type IconComponent, type IconsApi, NavContentBlueprint, type NavContentComponent, type NavContentComponentProps, NavItemBlueprint, NotFoundErrorPage, type NotFoundErrorPageProps, type OverridableExtensionDefinition, type OverridableFrontendPlugin, PageBlueprint, type PluginOptions, type PortableSchema, Progress, type ProgressProps, type ResolvedExtensionInput, type ResolvedExtensionInputs, type RouteFunc, type RouteRef, type RouteResolutionApi, RouterBlueprint, SignInPageBlueprint, type SubRouteRef, SwappableComponentBlueprint, type SwappableComponentRef, type SwappableComponentsApi, ThemeBlueprint, TranslationBlueprint, analyticsApiRef, appTreeApiRef, coreExtensionData, createExtension, createExtensionBlueprint, createExtensionBlueprintParams, createExtensionDataRef, createExtensionInput, createExternalRouteRef, createFrontendFeatureLoader, createFrontendModule, createFrontendPlugin, createRouteRef, createSubRouteRef, createSwappableComponent, dialogApiRef, iconsApiRef, routeResolutionApiRef, swappableComponentsApiRef, useAnalytics, useAppNode, useRouteRef, useRouteRefParams };
@@ -1,25 +1,41 @@
1
- import { RouteRefImpl } from './RouteRef.esm.js';
2
1
  import { describeParentCallSite } from './describeParentCallSite.esm.js';
2
+ import { OpaqueExternalRouteRef } from '../frontend-internal/src/routing/OpaqueExternalRouteRef.esm.js';
3
+ import '../frontend-internal/src/routing/OpaqueRouteRef.esm.js';
4
+ import '../frontend-internal/src/routing/OpaqueSubRouteRef.esm.js';
3
5
 
4
- class ExternalRouteRefImpl extends RouteRefImpl {
5
- $$type = "@backstage/ExternalRouteRef";
6
- params;
7
- defaultTarget;
8
- constructor(params = [], defaultTarget, creationSite) {
9
- super(params, creationSite);
10
- this.params = params;
11
- this.defaultTarget = defaultTarget;
12
- }
13
- getDefaultTarget() {
14
- return this.defaultTarget;
15
- }
16
- }
17
- function createExternalRouteRef(options) {
18
- return new ExternalRouteRefImpl(
19
- options?.params,
20
- options?.defaultTarget,
21
- describeParentCallSite()
22
- );
6
+ function createExternalRouteRef(config) {
7
+ const params = config?.params ?? [];
8
+ const creationSite = describeParentCallSite();
9
+ let id = void 0;
10
+ return OpaqueExternalRouteRef.createInstance("v1", {
11
+ T: void 0,
12
+ getParams() {
13
+ return params;
14
+ },
15
+ getDescription() {
16
+ if (id) {
17
+ return id;
18
+ }
19
+ return `created at '${creationSite}'`;
20
+ },
21
+ getDefaultTarget() {
22
+ return config?.defaultTarget;
23
+ },
24
+ setId(newId) {
25
+ if (!newId) {
26
+ throw new Error(`ExternalRouteRef id must be a non-empty string`);
27
+ }
28
+ if (id && id !== newId) {
29
+ throw new Error(
30
+ `ExternalRouteRef was referenced twice as both '${id}' and '${newId}'`
31
+ );
32
+ }
33
+ id = newId;
34
+ },
35
+ toString() {
36
+ return `externalRouteRef{id=${id},at='${creationSite}'}`;
37
+ }
38
+ });
23
39
  }
24
40
 
25
41
  export { createExternalRouteRef };
@@ -1 +1 @@
1
- {"version":3,"file":"ExternalRouteRef.esm.js","sources":["../../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":";;;AAoEA,MAAM,6BACI,YAAA,CAEV;AAAA,EACW,MAAA,GAAS,6BAAA;AAAA,EACT,MAAA;AAAA,EACA,aAAA;AAAA,EAET,WAAA,CACE,MAAA,GAAmB,EAAC,EACpB,eACA,YAAA,EACA;AACA,IAAA,KAAA,CAAM,QAAQ,YAAY,CAAA;AAC1B,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,aAAA,GAAgB,aAAA;AAAA,EACvB;AAAA,EAEA,gBAAA,GAAmB;AACjB,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd;AACF;AAYO,SAAS,uBAGd,OAAA,EAqBA;AACA,EAAA,OAAO,IAAI,oBAAA;AAAA,IACT,OAAA,EAAS,MAAA;AAAA,IACT,OAAA,EAAS,aAAA;AAAA,IACT,sBAAA;AAAuB,GACzB;AACF;;;;"}
1
+ {"version":3,"file":"ExternalRouteRef.esm.js","sources":["../../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 { OpaqueExternalRouteRef } from '@internal/frontend';\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/**\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>(config?: {\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 const params = (config?.params ?? []) as string[];\n const creationSite = describeParentCallSite();\n\n let id: string | undefined = undefined;\n\n return OpaqueExternalRouteRef.createInstance('v1', {\n T: undefined as unknown as TParams,\n getParams() {\n return params;\n },\n getDescription() {\n if (id) {\n return id;\n }\n return `created at '${creationSite}'`;\n },\n getDefaultTarget() {\n return config?.defaultTarget;\n },\n setId(newId: string) {\n if (!newId) {\n throw new Error(`ExternalRouteRef id must be a non-empty string`);\n }\n if (id && id !== newId) {\n throw new Error(\n `ExternalRouteRef was referenced twice as both '${id}' and '${newId}'`,\n );\n }\n id = newId;\n },\n toString(): string {\n return `externalRouteRef{id=${id},at='${creationSite}'}`;\n },\n });\n}\n"],"names":[],"mappings":";;;;;AAsEO,SAAS,uBAGd,MAAA,EAqBA;AACA,EAAA,MAAM,MAAA,GAAU,MAAA,EAAQ,MAAA,IAAU,EAAC;AACnC,EAAA,MAAM,eAAe,sBAAA,EAAuB;AAE5C,EAAA,IAAI,EAAA,GAAyB,MAAA;AAE7B,EAAA,OAAO,sBAAA,CAAuB,eAAe,IAAA,EAAM;AAAA,IACjD,CAAA,EAAG,MAAA;AAAA,IACH,SAAA,GAAY;AACV,MAAA,OAAO,MAAA;AAAA,IACT,CAAA;AAAA,IACA,cAAA,GAAiB;AACf,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,OAAO,EAAA;AAAA,MACT;AACA,MAAA,OAAO,eAAe,YAAY,CAAA,CAAA,CAAA;AAAA,IACpC,CAAA;AAAA,IACA,gBAAA,GAAmB;AACjB,MAAA,OAAO,MAAA,EAAQ,aAAA;AAAA,IACjB,CAAA;AAAA,IACA,MAAM,KAAA,EAAe;AACnB,MAAA,IAAI,CAAC,KAAA,EAAO;AACV,QAAA,MAAM,IAAI,MAAM,CAAA,8CAAA,CAAgD,CAAA;AAAA,MAClE;AACA,MAAA,IAAI,EAAA,IAAM,OAAO,KAAA,EAAO;AACtB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,+CAAA,EAAkD,EAAE,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,SACrE;AAAA,MACF;AACA,MAAA,EAAA,GAAK,KAAA;AAAA,IACP,CAAA;AAAA,IACA,QAAA,GAAmB;AACjB,MAAA,OAAO,CAAA,oBAAA,EAAuB,EAAE,CAAA,KAAA,EAAQ,YAAY,CAAA,EAAA,CAAA;AAAA,IACtD;AAAA,GACD,CAAA;AACH;;;;"}