@backstage/frontend-plugin-api 0.18.1-next.0 → 0.18.1

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,23 @@
1
1
  # @backstage/frontend-plugin-api
2
2
 
3
+ ## 0.18.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 024c0ef: Updated internal type imports to avoid circular declaration chunks in package builds.
8
+ - bd6cf43: Fixed a bug where overriding a plugin extension with `withOverrides` moved the overridden extension to the end of the plugin's extension list. This caused overridden extensions to lose their original position, for example making an overridden sub page tab move to the end of the tabs on its page. Overridden extensions now keep their original order, while extensions that don't override an existing one are appended at the end.
9
+ - Updated dependencies
10
+ - @backstage/filter-predicates@0.1.5
11
+ - @backstage/config@1.3.9
12
+
13
+ ## 0.18.1-next.1
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies
18
+ - @backstage/config@1.3.9-next.0
19
+ - @backstage/filter-predicates@0.1.5-next.1
20
+
3
21
  ## 0.18.1-next.0
4
22
 
5
23
  ### Patch Changes
package/dist/alpha.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { b as AnyRouteRefParams, d as ApiHolder, e as ApiRef, f as AppNode, g as AppNodeEdges, h as AppNodeInstance, i as AppNodeSpec, j as AppTree, C as ConfigurableExtensionDataRef, E as Extension, p as ExtensionAttachTo, q as ExtensionBlueprint, r as ExtensionBlueprintDefineParams, s as ExtensionBlueprintParameters, t as ExtensionBlueprintParams, u as ExtensionDataContainer, v as ExtensionDataRef, w as ExtensionDataValue, x as ExtensionDefinition, y as ExtensionDefinitionAttachTo, z as ExtensionDefinitionParameters, D as ExtensionInput, F as ExternalRouteRef, J as FrontendPlugin, K as FrontendPluginInfo, N as IconElement, O as OverridableExtensionDefinition, R as PluginWrapperApi, S as PluginWrapperBlueprint, T as PluginWrapperDefinition, U as PortableSchema, V as RouteRef, W as SubRouteRef, $ as createExtensionBlueprintParams, a7 as pluginWrapperApiRef } from './types/alpha.d-gY8B1WFX.js';
1
+ export { b as AnyRouteRefParams, d as ApiHolder, e as ApiRef, f as AppNode, g as AppNodeEdges, h as AppNodeInstance, i as AppNodeSpec, j as AppTree, C as ConfigurableExtensionDataRef, E as Extension, p as ExtensionAttachTo, q as ExtensionBlueprint, r as ExtensionBlueprintDefineParams, s as ExtensionBlueprintParameters, t as ExtensionBlueprintParams, u as ExtensionDataContainer, v as ExtensionDataRef, w as ExtensionDataValue, x as ExtensionDefinition, y as ExtensionDefinitionAttachTo, z as ExtensionDefinitionParameters, D as ExtensionInput, F as ExternalRouteRef, J as FrontendPlugin, K as FrontendPluginInfo, N as IconElement, O as OverridableExtensionDefinition, R as PluginWrapperApi, S as PluginWrapperBlueprint, T as PluginWrapperDefinition, U as PortableSchema, V as RouteRef, W as SubRouteRef, $ as createExtensionBlueprintParams, a7 as pluginWrapperApiRef } from './types/alpha.d-DZG-WVJf.js';
2
2
  export { StandardSchemaV1 } from '@standard-schema/spec';
3
3
  import '@backstage/frontend-plugin-api';
4
4
  import 'react';
package/dist/index.d.ts CHANGED
@@ -2,8 +2,8 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode, JSX as JSX$1, ComponentType, PropsWithChildren } from 'react';
4
4
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
5
- import { N as IconElement, V as RouteRef, b as AnyRouteRefParams, W as SubRouteRef, F as ExternalRouteRef, H as FrontendFeature, M as IconComponent, f as AppNode, J as FrontendPlugin, e as ApiRef, d as ApiHolder, X as TypesToApiRefs, c as ApiFactory, A as AnyApiFactory } from './types/alpha.d-gY8B1WFX.js';
6
- export { a as AnyApiRef, g as AppNodeEdges, h as AppNodeInstance, i as AppNodeSpec, j as AppTree, k as AppTreeApi, C as ConfigurableExtensionDataRef, l as CreateExtensionBlueprintOptions, m as CreateExtensionOptions, n as CreateFrontendModuleOptions, o as CreateFrontendPluginOptions, E as Extension, p as ExtensionAttachTo, q as ExtensionBlueprint, r as ExtensionBlueprintDefineParams, s as ExtensionBlueprintParameters, t as ExtensionBlueprintParams, u as ExtensionDataContainer, v as ExtensionDataRef, w as ExtensionDataValue, x as ExtensionDefinition, y as ExtensionDefinitionAttachTo, z as ExtensionDefinitionParameters, B as ExtensionFactoryMiddleware, D as ExtensionInput, G as FeatureFlagConfig, I as FrontendModule, K as FrontendPluginInfo, L as FrontendPluginInfoOptions, O as OverridableExtensionDefinition, P as OverridableFrontendPlugin, Q as PluginOptions, R as PluginWrapperApi, S as PluginWrapperBlueprint, T as PluginWrapperDefinition, U as PortableSchema, Y as appTreeApiRef, Z as createExtension, _ as createExtensionBlueprint, $ as createExtensionBlueprintParams, a0 as createExtensionDataRef, a1 as createExtensionInput, a2 as createExternalRouteRef, a3 as createFrontendModule, a4 as createFrontendPlugin, a5 as createRouteRef, a6 as createSubRouteRef, a7 as pluginWrapperApiRef } from './types/alpha.d-gY8B1WFX.js';
5
+ import { N as IconElement, V as RouteRef, b as AnyRouteRefParams, W as SubRouteRef, F as ExternalRouteRef, H as FrontendFeature, M as IconComponent, f as AppNode, J as FrontendPlugin, e as ApiRef, d as ApiHolder, X as TypesToApiRefs, c as ApiFactory, A as AnyApiFactory } from './types/alpha.d-DZG-WVJf.js';
6
+ export { a as AnyApiRef, g as AppNodeEdges, h as AppNodeInstance, i as AppNodeSpec, j as AppTree, k as AppTreeApi, C as ConfigurableExtensionDataRef, l as CreateExtensionBlueprintOptions, m as CreateExtensionOptions, n as CreateFrontendModuleOptions, o as CreateFrontendPluginOptions, E as Extension, p as ExtensionAttachTo, q as ExtensionBlueprint, r as ExtensionBlueprintDefineParams, s as ExtensionBlueprintParameters, t as ExtensionBlueprintParams, u as ExtensionDataContainer, v as ExtensionDataRef, w as ExtensionDataValue, x as ExtensionDefinition, y as ExtensionDefinitionAttachTo, z as ExtensionDefinitionParameters, B as ExtensionFactoryMiddleware, D as ExtensionInput, G as FeatureFlagConfig, I as FrontendModule, K as FrontendPluginInfo, L as FrontendPluginInfoOptions, O as OverridableExtensionDefinition, P as OverridableFrontendPlugin, Q as PluginOptions, R as PluginWrapperApi, S as PluginWrapperBlueprint, T as PluginWrapperDefinition, U as PortableSchema, Y as appTreeApiRef, Z as createExtension, _ as createExtensionBlueprint, $ as createExtensionBlueprintParams, a0 as createExtensionDataRef, a1 as createExtensionInput, a2 as createExternalRouteRef, a3 as createFrontendModule, a4 as createFrontendPlugin, a5 as createRouteRef, a6 as createSubRouteRef, a7 as pluginWrapperApiRef } from './types/alpha.d-DZG-WVJf.js';
7
7
  import { Observable, JsonValue, Expand, ExpandRecursive } from '@backstage/types';
8
8
  import { Config } from '@backstage/config';
9
9
  export { StandardSchemaV1 } from '@standard-schema/spec';
@@ -529,6 +529,11 @@ interface OverridableFrontendPlugin<TRoutes extends {
529
529
  }> extends FrontendPlugin<TRoutes, TExternalRoutes> {
530
530
  getExtension<TId extends keyof TExtensionMap>(id: TId): OverridableExtensionDefinition<TExtensionMap[TId]['T']>;
531
531
  withOverrides(options: {
532
+ /**
533
+ * Extensions to add to the plugin. An extension that overrides an existing
534
+ * extension replaces it at its original position in the plugin's list of
535
+ * extensions, while other extensions are appended at the end.
536
+ */
532
537
  extensions?: Array<ExtensionDefinition>;
533
538
  /**
534
539
  * Overrides the shared condition that applies to all extensions in the plugin.
@@ -1,4 +1,4 @@
1
- import { resolveExtensionDefinitions, resolveExtensionDefinition } from './resolveExtensionDefinition.esm.js';
1
+ import { resolveExtensionDefinitions, resolveExtensionDefinition, throwOnDuplicateExtensionIds } from './resolveExtensionDefinition.esm.js';
2
2
  import { ID_PATTERN } from './constants.esm.js';
3
3
  import { OpaqueFrontendPlugin } from '../frontend-internal/src/wiring/InternalFrontendPlugin.esm.js';
4
4
  import '../frontend-internal/src/wiring/InternalExtensionDefinition.esm.js';
@@ -54,23 +54,37 @@ function createFrontendPlugin(options) {
54
54
  ifPredicate = overrides.if;
55
55
  }
56
56
  const overrideExtensions = overrides.extensions ?? [];
57
- const overriddenExtensionIds = new Set(
58
- overrideExtensions.map(
59
- (e) => resolveExtensionDefinition(e, { namespace: pluginId }).id
60
- )
57
+ const overrideExtensionEntries = overrideExtensions.map(
58
+ (extension) => [
59
+ resolveExtensionDefinition(extension, { namespace: pluginId }).id,
60
+ extension
61
+ ]
61
62
  );
62
- const nonOverriddenExtensions = (options.extensions ?? []).filter(
63
- (e) => !overriddenExtensionIds.has(
64
- resolveExtensionDefinition(e, { namespace: pluginId }).id
65
- )
63
+ throwOnDuplicateExtensionIds(
64
+ overrideExtensionEntries.map(([id]) => id),
65
+ { namespace: pluginId, featureType: "Plugin" }
66
66
  );
67
+ const overrideExtensionsById = new Map(overrideExtensionEntries);
68
+ const replacedExtensionIds = /* @__PURE__ */ new Set();
69
+ const mergedExtensions = (options.extensions ?? []).map((extension) => {
70
+ const id = resolveExtensionDefinition(extension, {
71
+ namespace: pluginId
72
+ }).id;
73
+ const overrideExtension = overrideExtensionsById.get(id);
74
+ if (overrideExtension) {
75
+ replacedExtensionIds.add(id);
76
+ return overrideExtension;
77
+ }
78
+ return extension;
79
+ });
80
+ const appendedExtensions = overrideExtensionEntries.filter(([id]) => !replacedExtensionIds.has(id)).map(([, extension]) => extension);
67
81
  return createFrontendPlugin({
68
82
  ...options,
69
83
  pluginId,
70
84
  if: ifPredicate,
71
85
  title: overrides.title ?? options.title,
72
86
  icon: overrides.icon ?? options.icon,
73
- extensions: [...nonOverriddenExtensions, ...overrideExtensions],
87
+ extensions: [...mergedExtensions, ...appendedExtensions],
74
88
  info: {
75
89
  ...options.info,
76
90
  ...overrides.info
@@ -1 +1 @@
1
- {"version":3,"file":"createFrontendPlugin.esm.js","sources":["../../src/wiring/createFrontendPlugin.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 { OpaqueFrontendPlugin } from '@internal/frontend';\nimport {\n ExtensionDefinition,\n OverridableExtensionDefinition,\n} from './createExtension';\nimport {\n resolveExtensionDefinition,\n resolveExtensionDefinitions,\n} from './resolveExtensionDefinition';\nimport { FeatureFlagConfig } from './types';\nimport { MakeSortedExtensionsMap } from './MakeSortedExtensionsMap';\nimport { JsonObject } from '@backstage/types';\nimport { IconElement } from '../icons/types';\nimport { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing';\nimport { ID_PATTERN } from './constants';\nimport { FilterPredicate } from '@backstage/filter-predicates';\n\n/**\n * Information about the plugin.\n *\n * @public\n * @remarks\n *\n * This interface is intended to be extended via [module\n * augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)\n * in order to add fields that are specific to each project.\n *\n * For example, one might add a `slackChannel` field that is read from the\n * opaque manifest file.\n *\n * See the options for `createApp` for more information about how to\n * customize the parsing of manifest files.\n */\nexport interface FrontendPluginInfo {\n /**\n * The name of the package that implements the plugin.\n */\n packageName?: string;\n\n /**\n * The version of the plugin, typically the version of the package.json file.\n */\n version?: string;\n\n /**\n * As short description of the plugin, typically the description field in\n * package.json.\n */\n description?: string;\n\n /**\n * The owner entity references of the plugin.\n */\n ownerEntityRefs?: string[];\n\n /**\n * Links related to the plugin.\n */\n links?: Array<{ title: string; url: string }>;\n}\n\n/**\n * Options for providing information for a plugin.\n *\n * @public\n */\nexport type FrontendPluginInfoOptions = {\n /**\n * A loader function for the package.json file for the plugin.\n */\n packageJson?: () => Promise<{ name: string } & JsonObject>;\n /**\n * A loader function for an opaque manifest file for the plugin.\n */\n manifest?: () => Promise<JsonObject>;\n};\n\n/**\n * A variant of the {@link FrontendPlugin} interface that can also be used to install overrides for the plugin.\n *\n * @public\n */\nexport interface OverridableFrontendPlugin<\n TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {\n [name in string]: RouteRef | SubRouteRef;\n },\n TExternalRoutes extends { [name in string]: ExternalRouteRef } = {\n [name in string]: ExternalRouteRef;\n },\n TExtensionMap extends { [id in string]: ExtensionDefinition } = {\n [id in string]: ExtensionDefinition;\n },\n> extends FrontendPlugin<TRoutes, TExternalRoutes> {\n getExtension<TId extends keyof TExtensionMap>(\n id: TId,\n ): OverridableExtensionDefinition<TExtensionMap[TId]['T']>;\n withOverrides(options: {\n extensions?: Array<ExtensionDefinition>;\n\n /**\n * Overrides the shared condition that applies to all extensions in the plugin.\n */\n if?: FilterPredicate;\n\n /**\n * Overrides the display title of the plugin.\n */\n title?: string;\n\n /**\n * Overrides the display icon of the plugin.\n */\n icon?: IconElement;\n\n /**\n * Overrides the original info loaders of the plugin one by one.\n */\n info?: FrontendPluginInfoOptions;\n }): OverridableFrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;\n}\n\n/** @public */\nexport interface FrontendPlugin<\n TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {\n [name in string]: RouteRef | SubRouteRef;\n },\n TExternalRoutes extends { [name in string]: ExternalRouteRef } = {\n [name in string]: ExternalRouteRef;\n },\n> {\n readonly $$type: '@backstage/FrontendPlugin';\n /**\n * The plugin ID.\n */\n readonly pluginId: string;\n /**\n * Deprecated alias for `pluginId`.\n *\n * @deprecated Use `pluginId` instead.\n */\n readonly id: string;\n /**\n * The display title of the plugin, used in page headers and navigation.\n * Falls back to the plugin ID if not provided.\n */\n readonly title?: string;\n /**\n * The display icon of the plugin, used in page headers and navigation.\n */\n readonly icon?: IconElement;\n readonly routes: TRoutes;\n readonly externalRoutes: TExternalRoutes;\n\n /**\n * Loads the plugin info.\n */\n info(): Promise<FrontendPluginInfo>;\n}\n\n/**\n * Options for {@link createFrontendPlugin}.\n *\n * @public\n */\nexport interface CreateFrontendPluginOptions<\n TId extends string,\n TRoutes extends { [name in string]: RouteRef | SubRouteRef },\n TExternalRoutes extends { [name in string]: ExternalRouteRef },\n TExtensions extends readonly ExtensionDefinition[],\n> {\n pluginId: TId;\n /**\n * The display title of the plugin, used in page headers and navigation.\n * Falls back to the plugin ID if not provided.\n */\n title?: string;\n /**\n * The display icon of the plugin, used in page headers and navigation.\n */\n icon?: IconElement;\n routes?: TRoutes;\n externalRoutes?: TExternalRoutes;\n extensions?: TExtensions;\n featureFlags?: FeatureFlagConfig[];\n if?: FilterPredicate;\n info?: FrontendPluginInfoOptions;\n}\n\n/**\n * @deprecated Use {@link CreateFrontendPluginOptions} instead.\n * @public\n */\nexport type PluginOptions<\n TId extends string,\n TRoutes extends { [name in string]: RouteRef | SubRouteRef },\n TExternalRoutes extends { [name in string]: ExternalRouteRef },\n TExtensions extends readonly ExtensionDefinition[],\n> = CreateFrontendPluginOptions<TId, TRoutes, TExternalRoutes, TExtensions>;\n\n/**\n * Creates a new plugin that can be installed in a Backstage app.\n *\n * @remarks\n *\n * Every plugin is created with a unique ID and a set of extensions\n * that are installed as part of the plugin.\n *\n * For more information on how plugins work, see the\n * {@link https://backstage.io/docs/frontend-system/building-plugins/index | documentation for plugins}\n * in the frontend system documentation.\n *\n * @example\n *\n * ```tsx\n * import { createFrontendPlugin } from '@backstage/frontend-plugin-api';\n *\n * export const examplePlugin = createFrontendPlugin({\n * pluginId: 'example',\n * extensions: [\n * PageBlueprint.make({\n * path: '/example',\n * loader: () => import('./ExamplePage').then(m => <m.ExamplePage />),\n * }),\n * ],\n * });\n * ```\n *\n * @public\n */\nexport function createFrontendPlugin<\n TId extends string,\n TExtensions extends readonly ExtensionDefinition[],\n TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {},\n TExternalRoutes extends { [name in string]: ExternalRouteRef } = {},\n>(\n options: CreateFrontendPluginOptions<\n TId,\n TRoutes,\n TExternalRoutes,\n TExtensions\n >,\n): OverridableFrontendPlugin<\n TRoutes,\n TExternalRoutes,\n MakeSortedExtensionsMap<TExtensions[number], TId>\n> {\n const pluginId = options.pluginId;\n\n if (!ID_PATTERN.test(pluginId)) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: The pluginId '${pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`,\n );\n }\n\n const { extensions, extensionDefinitionsById } = resolveExtensionDefinitions(\n options.extensions ?? [],\n {\n namespace: pluginId,\n featureType: 'Plugin',\n },\n );\n\n return OpaqueFrontendPlugin.createInstance('v1', {\n pluginId,\n id: pluginId,\n title: options.title,\n icon: options.icon,\n routes: options.routes ?? ({} as TRoutes),\n externalRoutes: options.externalRoutes ?? ({} as TExternalRoutes),\n featureFlags: options.featureFlags ?? [],\n if: options.if,\n extensions: extensions,\n infoOptions: options.info,\n\n // This method is overridden when the plugin instance is installed in an app\n async info() {\n throw new Error(\n `Attempted to load plugin info for plugin '${pluginId}', but the plugin instance is not installed in an app`,\n );\n },\n getExtension(id) {\n const ext = extensionDefinitionsById.get(id);\n if (!ext) {\n throw new Error(\n `Attempted to get non-existent extension '${id}' from plugin '${pluginId}'`,\n );\n }\n return ext;\n },\n toString() {\n return `Plugin{id=${pluginId}}`;\n },\n withOverrides(overrides) {\n let ifPredicate = options.if;\n if ('if' in overrides) {\n ifPredicate = overrides.if;\n }\n const overrideExtensions = overrides.extensions ?? [];\n const overriddenExtensionIds = new Set(\n overrideExtensions.map(\n e => resolveExtensionDefinition(e, { namespace: pluginId }).id,\n ),\n );\n const nonOverriddenExtensions = (options.extensions ?? []).filter(\n e =>\n !overriddenExtensionIds.has(\n resolveExtensionDefinition(e, { namespace: pluginId }).id,\n ),\n );\n return createFrontendPlugin({\n ...options,\n pluginId,\n if: ifPredicate,\n title: overrides.title ?? options.title,\n icon: overrides.icon ?? options.icon,\n extensions: [...nonOverriddenExtensions, ...overrideExtensions],\n info: {\n ...options.info,\n ...overrides.info,\n },\n });\n },\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createFrontendPlugin.esm.js","sources":["../../src/wiring/createFrontendPlugin.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 { OpaqueFrontendPlugin } from '@internal/frontend';\nimport {\n ExtensionDefinition,\n OverridableExtensionDefinition,\n} from './createExtension';\nimport {\n resolveExtensionDefinition,\n resolveExtensionDefinitions,\n throwOnDuplicateExtensionIds,\n} from './resolveExtensionDefinition';\nimport { FeatureFlagConfig } from './types';\nimport { MakeSortedExtensionsMap } from './MakeSortedExtensionsMap';\nimport { JsonObject } from '@backstage/types';\nimport { IconElement } from '../icons/types';\nimport { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing';\nimport { ID_PATTERN } from './constants';\nimport { FilterPredicate } from '@backstage/filter-predicates';\n\n/**\n * Information about the plugin.\n *\n * @public\n * @remarks\n *\n * This interface is intended to be extended via [module\n * augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation)\n * in order to add fields that are specific to each project.\n *\n * For example, one might add a `slackChannel` field that is read from the\n * opaque manifest file.\n *\n * See the options for `createApp` for more information about how to\n * customize the parsing of manifest files.\n */\nexport interface FrontendPluginInfo {\n /**\n * The name of the package that implements the plugin.\n */\n packageName?: string;\n\n /**\n * The version of the plugin, typically the version of the package.json file.\n */\n version?: string;\n\n /**\n * As short description of the plugin, typically the description field in\n * package.json.\n */\n description?: string;\n\n /**\n * The owner entity references of the plugin.\n */\n ownerEntityRefs?: string[];\n\n /**\n * Links related to the plugin.\n */\n links?: Array<{ title: string; url: string }>;\n}\n\n/**\n * Options for providing information for a plugin.\n *\n * @public\n */\nexport type FrontendPluginInfoOptions = {\n /**\n * A loader function for the package.json file for the plugin.\n */\n packageJson?: () => Promise<{ name: string } & JsonObject>;\n /**\n * A loader function for an opaque manifest file for the plugin.\n */\n manifest?: () => Promise<JsonObject>;\n};\n\n/**\n * A variant of the {@link FrontendPlugin} interface that can also be used to install overrides for the plugin.\n *\n * @public\n */\nexport interface OverridableFrontendPlugin<\n TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {\n [name in string]: RouteRef | SubRouteRef;\n },\n TExternalRoutes extends { [name in string]: ExternalRouteRef } = {\n [name in string]: ExternalRouteRef;\n },\n TExtensionMap extends { [id in string]: ExtensionDefinition } = {\n [id in string]: ExtensionDefinition;\n },\n> extends FrontendPlugin<TRoutes, TExternalRoutes> {\n getExtension<TId extends keyof TExtensionMap>(\n id: TId,\n ): OverridableExtensionDefinition<TExtensionMap[TId]['T']>;\n withOverrides(options: {\n /**\n * Extensions to add to the plugin. An extension that overrides an existing\n * extension replaces it at its original position in the plugin's list of\n * extensions, while other extensions are appended at the end.\n */\n extensions?: Array<ExtensionDefinition>;\n\n /**\n * Overrides the shared condition that applies to all extensions in the plugin.\n */\n if?: FilterPredicate;\n\n /**\n * Overrides the display title of the plugin.\n */\n title?: string;\n\n /**\n * Overrides the display icon of the plugin.\n */\n icon?: IconElement;\n\n /**\n * Overrides the original info loaders of the plugin one by one.\n */\n info?: FrontendPluginInfoOptions;\n }): OverridableFrontendPlugin<TRoutes, TExternalRoutes, TExtensionMap>;\n}\n\n/** @public */\nexport interface FrontendPlugin<\n TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {\n [name in string]: RouteRef | SubRouteRef;\n },\n TExternalRoutes extends { [name in string]: ExternalRouteRef } = {\n [name in string]: ExternalRouteRef;\n },\n> {\n readonly $$type: '@backstage/FrontendPlugin';\n /**\n * The plugin ID.\n */\n readonly pluginId: string;\n /**\n * Deprecated alias for `pluginId`.\n *\n * @deprecated Use `pluginId` instead.\n */\n readonly id: string;\n /**\n * The display title of the plugin, used in page headers and navigation.\n * Falls back to the plugin ID if not provided.\n */\n readonly title?: string;\n /**\n * The display icon of the plugin, used in page headers and navigation.\n */\n readonly icon?: IconElement;\n readonly routes: TRoutes;\n readonly externalRoutes: TExternalRoutes;\n\n /**\n * Loads the plugin info.\n */\n info(): Promise<FrontendPluginInfo>;\n}\n\n/**\n * Options for {@link createFrontendPlugin}.\n *\n * @public\n */\nexport interface CreateFrontendPluginOptions<\n TId extends string,\n TRoutes extends { [name in string]: RouteRef | SubRouteRef },\n TExternalRoutes extends { [name in string]: ExternalRouteRef },\n TExtensions extends readonly ExtensionDefinition[],\n> {\n pluginId: TId;\n /**\n * The display title of the plugin, used in page headers and navigation.\n * Falls back to the plugin ID if not provided.\n */\n title?: string;\n /**\n * The display icon of the plugin, used in page headers and navigation.\n */\n icon?: IconElement;\n routes?: TRoutes;\n externalRoutes?: TExternalRoutes;\n extensions?: TExtensions;\n featureFlags?: FeatureFlagConfig[];\n if?: FilterPredicate;\n info?: FrontendPluginInfoOptions;\n}\n\n/**\n * @deprecated Use {@link CreateFrontendPluginOptions} instead.\n * @public\n */\nexport type PluginOptions<\n TId extends string,\n TRoutes extends { [name in string]: RouteRef | SubRouteRef },\n TExternalRoutes extends { [name in string]: ExternalRouteRef },\n TExtensions extends readonly ExtensionDefinition[],\n> = CreateFrontendPluginOptions<TId, TRoutes, TExternalRoutes, TExtensions>;\n\n/**\n * Creates a new plugin that can be installed in a Backstage app.\n *\n * @remarks\n *\n * Every plugin is created with a unique ID and a set of extensions\n * that are installed as part of the plugin.\n *\n * For more information on how plugins work, see the\n * {@link https://backstage.io/docs/frontend-system/building-plugins/index | documentation for plugins}\n * in the frontend system documentation.\n *\n * @example\n *\n * ```tsx\n * import { createFrontendPlugin } from '@backstage/frontend-plugin-api';\n *\n * export const examplePlugin = createFrontendPlugin({\n * pluginId: 'example',\n * extensions: [\n * PageBlueprint.make({\n * path: '/example',\n * loader: () => import('./ExamplePage').then(m => <m.ExamplePage />),\n * }),\n * ],\n * });\n * ```\n *\n * @public\n */\nexport function createFrontendPlugin<\n TId extends string,\n TExtensions extends readonly ExtensionDefinition[],\n TRoutes extends { [name in string]: RouteRef | SubRouteRef } = {},\n TExternalRoutes extends { [name in string]: ExternalRouteRef } = {},\n>(\n options: CreateFrontendPluginOptions<\n TId,\n TRoutes,\n TExternalRoutes,\n TExtensions\n >,\n): OverridableFrontendPlugin<\n TRoutes,\n TExternalRoutes,\n MakeSortedExtensionsMap<TExtensions[number], TId>\n> {\n const pluginId = options.pluginId;\n\n if (!ID_PATTERN.test(pluginId)) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: The pluginId '${pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`,\n );\n }\n\n const { extensions, extensionDefinitionsById } = resolveExtensionDefinitions(\n options.extensions ?? [],\n {\n namespace: pluginId,\n featureType: 'Plugin',\n },\n );\n\n return OpaqueFrontendPlugin.createInstance('v1', {\n pluginId,\n id: pluginId,\n title: options.title,\n icon: options.icon,\n routes: options.routes ?? ({} as TRoutes),\n externalRoutes: options.externalRoutes ?? ({} as TExternalRoutes),\n featureFlags: options.featureFlags ?? [],\n if: options.if,\n extensions: extensions,\n infoOptions: options.info,\n\n // This method is overridden when the plugin instance is installed in an app\n async info() {\n throw new Error(\n `Attempted to load plugin info for plugin '${pluginId}', but the plugin instance is not installed in an app`,\n );\n },\n getExtension(id) {\n const ext = extensionDefinitionsById.get(id);\n if (!ext) {\n throw new Error(\n `Attempted to get non-existent extension '${id}' from plugin '${pluginId}'`,\n );\n }\n return ext;\n },\n toString() {\n return `Plugin{id=${pluginId}}`;\n },\n withOverrides(overrides) {\n let ifPredicate = options.if;\n if ('if' in overrides) {\n ifPredicate = overrides.if;\n }\n const overrideExtensions = overrides.extensions ?? [];\n const overrideExtensionEntries = overrideExtensions.map(\n extension =>\n [\n resolveExtensionDefinition(extension, { namespace: pluginId }).id,\n extension,\n ] as const,\n );\n throwOnDuplicateExtensionIds(\n overrideExtensionEntries.map(([id]) => id),\n { namespace: pluginId, featureType: 'Plugin' },\n );\n const overrideExtensionsById = new Map(overrideExtensionEntries);\n // Extensions that override an existing one replace it in place, keeping\n // the original registration order, since the order affects the order of\n // attachments in the app. Remaining extensions are appended at the end.\n const replacedExtensionIds = new Set<string>();\n const mergedExtensions = (options.extensions ?? []).map(extension => {\n const id = resolveExtensionDefinition(extension, {\n namespace: pluginId,\n }).id;\n const overrideExtension = overrideExtensionsById.get(id);\n if (overrideExtension) {\n replacedExtensionIds.add(id);\n return overrideExtension;\n }\n return extension;\n });\n const appendedExtensions = overrideExtensionEntries\n .filter(([id]) => !replacedExtensionIds.has(id))\n .map(([, extension]) => extension);\n return createFrontendPlugin({\n ...options,\n pluginId,\n if: ifPredicate,\n title: overrides.title ?? options.title,\n icon: overrides.icon ?? options.icon,\n extensions: [...mergedExtensions, ...appendedExtensions],\n info: {\n ...options.info,\n ...overrides.info,\n },\n });\n },\n });\n}\n"],"names":[],"mappings":"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"}
@@ -60,18 +60,27 @@ function resolveExtensionDefinitions(definitions, context) {
60
60
  namespace: context.namespace
61
61
  });
62
62
  }
63
- if (extensions.length !== extensionDefinitionsById.size) {
64
- const extensionIds = extensions.map((e) => e.id);
65
- const duplicates = Array.from(
66
- new Set(
67
- extensionIds.filter((id, index) => extensionIds.indexOf(id) !== index)
68
- )
69
- );
63
+ throwOnDuplicateExtensionIds(
64
+ extensions.map((e) => e.id),
65
+ context
66
+ );
67
+ return { extensions, extensionDefinitionsById };
68
+ }
69
+ function throwOnDuplicateExtensionIds(extensionIds, context) {
70
+ const seen = /* @__PURE__ */ new Set();
71
+ const duplicates = /* @__PURE__ */ new Set();
72
+ for (const id of extensionIds) {
73
+ if (seen.has(id)) {
74
+ duplicates.add(id);
75
+ } else {
76
+ seen.add(id);
77
+ }
78
+ }
79
+ if (duplicates.size > 0) {
70
80
  throw new Error(
71
- `${context.featureType} '${context.namespace}' provided duplicate extensions: ${duplicates.join(", ")}`
81
+ `${context.featureType} '${context.namespace}' provided duplicate extensions: ${Array.from(duplicates).join(", ")}`
72
82
  );
73
83
  }
74
- return { extensions, extensionDefinitionsById };
75
84
  }
76
85
  function resolveExtensionDefinition(definition, context) {
77
86
  const internalDefinition = OpaqueExtensionDefinition.toInternal(definition);
@@ -97,5 +106,5 @@ function resolveExtensionDefinition(definition, context) {
97
106
  };
98
107
  }
99
108
 
100
- export { resolveExtensionDefinition, resolveExtensionDefinitions };
109
+ export { resolveExtensionDefinition, resolveExtensionDefinitions, throwOnDuplicateExtensionIds };
101
110
  //# sourceMappingURL=resolveExtensionDefinition.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolveExtensionDefinition.esm.js","sources":["../../src/wiring/resolveExtensionDefinition.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiHolder, AppNode } from '../apis';\nimport {\n ExtensionDefinitionAttachTo,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n ResolvedExtensionInputs,\n} from './createExtension';\nimport { PortableSchema } from '../schema';\nimport { ExtensionInput } from './createExtensionInput';\nimport { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef';\nimport {\n OpaqueExtensionDefinition,\n OpaqueExtensionInput,\n} from '@internal/frontend';\nimport { FilterPredicate } from '@backstage/filter-predicates';\n\n/** @public */\nexport type ExtensionAttachTo = { id: string; input: string };\n\n/** @public */\nexport interface Extension<TConfig, TConfigInput = TConfig> {\n $$type: '@backstage/Extension';\n readonly id: string;\n readonly attachTo: ExtensionAttachTo;\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<TConfig, TConfigInput>;\n}\n\n/** @internal */\nexport type InternalExtension<TConfig, TConfigInput> = Extension<\n TConfig,\n TConfigInput\n> &\n (\n | {\n readonly version: 'v1';\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 apis: ApiHolder;\n node: AppNode;\n config: TConfig;\n inputs: {\n [inputName in string]: unknown;\n };\n }): {\n [inputName in string]: unknown;\n };\n }\n | {\n readonly version: 'v2';\n readonly if?: FilterPredicate;\n readonly inputs: { [inputName in string]: ExtensionInput };\n readonly output: Array<ExtensionDataRef>;\n factory(options: {\n apis: ApiHolder;\n node: AppNode;\n config: TConfig;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput;\n }>;\n }): Iterable<ExtensionDataValue<any, any>>;\n }\n );\n\n/** @internal */\nexport function toInternalExtension<TConfig, TConfigInput>(\n overrides: Extension<TConfig, TConfigInput>,\n): InternalExtension<TConfig, TConfigInput> {\n const internal = overrides as InternalExtension<TConfig, TConfigInput>;\n if (internal.$$type !== '@backstage/Extension') {\n throw new Error(\n `Invalid extension instance, bad type '${internal.$$type}'`,\n );\n }\n const version = internal.version;\n if (version !== 'v1' && version !== 'v2') {\n throw new Error(`Invalid extension instance, bad version '${version}'`);\n }\n return internal;\n}\n\n/** @ignore */\nexport type ResolveExtensionId<\n TExtension extends ExtensionDefinition,\n TNamespace extends string,\n> = TExtension extends ExtensionDefinition<{\n kind: infer IKind extends string | undefined;\n name: infer IName extends string | undefined;\n params: any;\n}>\n ? [string] extends [IKind | IName]\n ? never\n : (\n undefined extends IName ? TNamespace : `${TNamespace}/${IName}`\n ) extends infer INamePart extends string\n ? IKind extends string\n ? `${IKind}:${INamePart}`\n : INamePart\n : never\n : never;\n\nfunction resolveExtensionId(\n kind?: string,\n namespace?: string,\n name?: string,\n): string {\n const namePart =\n name && namespace ? `${namespace}/${name}` : namespace || name;\n if (!namePart) {\n throw new Error(\n `Extension must declare an explicit namespace or name as it could not be resolved from context, kind=${kind} namespace=${namespace} name=${name}`,\n );\n }\n\n return kind ? `${kind}:${namePart}` : namePart;\n}\n\nfunction resolveAttachTo(\n attachTo: ExtensionDefinitionAttachTo | ExtensionDefinitionAttachTo[],\n namespace?: string,\n): ExtensionAttachTo | ExtensionAttachTo[] {\n const resolveSpec = (\n spec: ExtensionDefinitionAttachTo,\n ): { id: string; input: string } => {\n if (OpaqueExtensionInput.isType(spec)) {\n const { context } = OpaqueExtensionInput.toInternal(spec);\n if (!context) {\n throw new Error(\n 'Invalid input object without a parent extension used as attachment point',\n );\n }\n return {\n id: resolveExtensionId(context.kind, namespace, context.name),\n input: context.input,\n };\n }\n if ('relative' in spec && spec.relative) {\n return {\n id: resolveExtensionId(\n spec.relative.kind,\n namespace,\n spec.relative.name,\n ),\n input: spec.input,\n };\n }\n if ('id' in spec) {\n return { id: spec.id, input: spec.input };\n }\n throw new Error('Invalid attachment point specification');\n };\n\n if (Array.isArray(attachTo)) {\n return attachTo.map(resolveSpec);\n }\n\n return resolveSpec(attachTo);\n}\n\n/**\n * Resolves a list of extension definitions into extensions, returning both the\n * resolved extensions and a map of extension definitions keyed by resolved ID.\n * Throws if any two definitions resolve to the same ID.\n *\n * @internal\n */\nexport function resolveExtensionDefinitions(\n definitions: Iterable<ExtensionDefinition>,\n context: { namespace: string; featureType: string },\n): {\n extensions: Extension<any>[];\n extensionDefinitionsById: Map<\n string,\n typeof OpaqueExtensionDefinition.TInternal\n >;\n} {\n const extensions = new Array<Extension<any>>();\n const extensionDefinitionsById = new Map<\n string,\n typeof OpaqueExtensionDefinition.TInternal\n >();\n\n for (const def of definitions) {\n const internal = OpaqueExtensionDefinition.toInternal(def);\n const ext = resolveExtensionDefinition(def, {\n namespace: context.namespace,\n });\n extensions.push(ext);\n extensionDefinitionsById.set(ext.id, {\n ...internal,\n namespace: context.namespace,\n });\n }\n\n if (extensions.length !== extensionDefinitionsById.size) {\n const extensionIds = extensions.map(e => e.id);\n const duplicates = Array.from(\n new Set(\n extensionIds.filter((id, index) => extensionIds.indexOf(id) !== index),\n ),\n );\n throw new Error(\n `${context.featureType} '${\n context.namespace\n }' provided duplicate extensions: ${duplicates.join(', ')}`,\n );\n }\n\n return { extensions, extensionDefinitionsById };\n}\n\n/** @internal */\nexport function resolveExtensionDefinition<\n T extends ExtensionDefinitionParameters,\n>(\n definition: ExtensionDefinition<T>,\n context?: { namespace?: string },\n): Extension<T['config'], T['configInput']> {\n const internalDefinition = OpaqueExtensionDefinition.toInternal(definition);\n\n const {\n name,\n kind,\n namespace: internalNamespace,\n override: _skip2,\n attachTo,\n ...rest\n } = internalDefinition;\n\n const namespace = internalNamespace ?? context?.namespace;\n const id = resolveExtensionId(kind, namespace, name);\n\n return {\n ...rest,\n attachTo: resolveAttachTo(attachTo, namespace) as ExtensionAttachTo,\n $$type: '@backstage/Extension',\n version: internalDefinition.version,\n id,\n toString() {\n return `Extension{id=${id}}`;\n },\n } as InternalExtension<T['config'], T['configInput']> & Object;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolveExtensionDefinition.esm.js","sources":["../../src/wiring/resolveExtensionDefinition.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ApiHolder, AppNode } from '../apis';\nimport {\n ExtensionDefinitionAttachTo,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n ResolvedExtensionInputs,\n} from './createExtension';\nimport { PortableSchema } from '../schema';\nimport { ExtensionInput } from './createExtensionInput';\nimport { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef';\nimport {\n OpaqueExtensionDefinition,\n OpaqueExtensionInput,\n} from '@internal/frontend';\nimport { FilterPredicate } from '@backstage/filter-predicates';\n\n/** @public */\nexport type ExtensionAttachTo = { id: string; input: string };\n\n/** @public */\nexport interface Extension<TConfig, TConfigInput = TConfig> {\n $$type: '@backstage/Extension';\n readonly id: string;\n readonly attachTo: ExtensionAttachTo;\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<TConfig, TConfigInput>;\n}\n\n/** @internal */\nexport type InternalExtension<TConfig, TConfigInput> = Extension<\n TConfig,\n TConfigInput\n> &\n (\n | {\n readonly version: 'v1';\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 apis: ApiHolder;\n node: AppNode;\n config: TConfig;\n inputs: {\n [inputName in string]: unknown;\n };\n }): {\n [inputName in string]: unknown;\n };\n }\n | {\n readonly version: 'v2';\n readonly if?: FilterPredicate;\n readonly inputs: { [inputName in string]: ExtensionInput };\n readonly output: Array<ExtensionDataRef>;\n factory(options: {\n apis: ApiHolder;\n node: AppNode;\n config: TConfig;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput;\n }>;\n }): Iterable<ExtensionDataValue<any, any>>;\n }\n );\n\n/** @internal */\nexport function toInternalExtension<TConfig, TConfigInput>(\n overrides: Extension<TConfig, TConfigInput>,\n): InternalExtension<TConfig, TConfigInput> {\n const internal = overrides as InternalExtension<TConfig, TConfigInput>;\n if (internal.$$type !== '@backstage/Extension') {\n throw new Error(\n `Invalid extension instance, bad type '${internal.$$type}'`,\n );\n }\n const version = internal.version;\n if (version !== 'v1' && version !== 'v2') {\n throw new Error(`Invalid extension instance, bad version '${version}'`);\n }\n return internal;\n}\n\n/** @ignore */\nexport type ResolveExtensionId<\n TExtension extends ExtensionDefinition,\n TNamespace extends string,\n> = TExtension extends ExtensionDefinition<{\n kind: infer IKind extends string | undefined;\n name: infer IName extends string | undefined;\n params: any;\n}>\n ? [string] extends [IKind | IName]\n ? never\n : (\n undefined extends IName ? TNamespace : `${TNamespace}/${IName}`\n ) extends infer INamePart extends string\n ? IKind extends string\n ? `${IKind}:${INamePart}`\n : INamePart\n : never\n : never;\n\nfunction resolveExtensionId(\n kind?: string,\n namespace?: string,\n name?: string,\n): string {\n const namePart =\n name && namespace ? `${namespace}/${name}` : namespace || name;\n if (!namePart) {\n throw new Error(\n `Extension must declare an explicit namespace or name as it could not be resolved from context, kind=${kind} namespace=${namespace} name=${name}`,\n );\n }\n\n return kind ? `${kind}:${namePart}` : namePart;\n}\n\nfunction resolveAttachTo(\n attachTo: ExtensionDefinitionAttachTo | ExtensionDefinitionAttachTo[],\n namespace?: string,\n): ExtensionAttachTo | ExtensionAttachTo[] {\n const resolveSpec = (\n spec: ExtensionDefinitionAttachTo,\n ): { id: string; input: string } => {\n if (OpaqueExtensionInput.isType(spec)) {\n const { context } = OpaqueExtensionInput.toInternal(spec);\n if (!context) {\n throw new Error(\n 'Invalid input object without a parent extension used as attachment point',\n );\n }\n return {\n id: resolveExtensionId(context.kind, namespace, context.name),\n input: context.input,\n };\n }\n if ('relative' in spec && spec.relative) {\n return {\n id: resolveExtensionId(\n spec.relative.kind,\n namespace,\n spec.relative.name,\n ),\n input: spec.input,\n };\n }\n if ('id' in spec) {\n return { id: spec.id, input: spec.input };\n }\n throw new Error('Invalid attachment point specification');\n };\n\n if (Array.isArray(attachTo)) {\n return attachTo.map(resolveSpec);\n }\n\n return resolveSpec(attachTo);\n}\n\n/**\n * Resolves a list of extension definitions into extensions, returning both the\n * resolved extensions and a map of extension definitions keyed by resolved ID.\n * Throws if any two definitions resolve to the same ID.\n *\n * @internal\n */\nexport function resolveExtensionDefinitions(\n definitions: Iterable<ExtensionDefinition>,\n context: { namespace: string; featureType: string },\n): {\n extensions: Extension<any>[];\n extensionDefinitionsById: Map<\n string,\n typeof OpaqueExtensionDefinition.TInternal\n >;\n} {\n const extensions = new Array<Extension<any>>();\n const extensionDefinitionsById = new Map<\n string,\n typeof OpaqueExtensionDefinition.TInternal\n >();\n\n for (const def of definitions) {\n const internal = OpaqueExtensionDefinition.toInternal(def);\n const ext = resolveExtensionDefinition(def, {\n namespace: context.namespace,\n });\n extensions.push(ext);\n extensionDefinitionsById.set(ext.id, {\n ...internal,\n namespace: context.namespace,\n });\n }\n\n throwOnDuplicateExtensionIds(\n extensions.map(e => e.id),\n context,\n );\n\n return { extensions, extensionDefinitionsById };\n}\n\n/**\n * Throws if the provided list of resolved extension IDs contains duplicates.\n *\n * @internal\n */\nexport function throwOnDuplicateExtensionIds(\n extensionIds: string[],\n context: { namespace: string; featureType: string },\n): void {\n const seen = new Set<string>();\n const duplicates = new Set<string>();\n for (const id of extensionIds) {\n if (seen.has(id)) {\n duplicates.add(id);\n } else {\n seen.add(id);\n }\n }\n if (duplicates.size > 0) {\n throw new Error(\n `${context.featureType} '${\n context.namespace\n }' provided duplicate extensions: ${Array.from(duplicates).join(', ')}`,\n );\n }\n}\n\n/** @internal */\nexport function resolveExtensionDefinition<\n T extends ExtensionDefinitionParameters,\n>(\n definition: ExtensionDefinition<T>,\n context?: { namespace?: string },\n): Extension<T['config'], T['configInput']> {\n const internalDefinition = OpaqueExtensionDefinition.toInternal(definition);\n\n const {\n name,\n kind,\n namespace: internalNamespace,\n override: _skip2,\n attachTo,\n ...rest\n } = internalDefinition;\n\n const namespace = internalNamespace ?? context?.namespace;\n const id = resolveExtensionId(kind, namespace, name);\n\n return {\n ...rest,\n attachTo: resolveAttachTo(attachTo, namespace) as ExtensionAttachTo,\n $$type: '@backstage/Extension',\n version: internalDefinition.version,\n id,\n toString() {\n return `Extension{id=${id}}`;\n },\n } as InternalExtension<T['config'], T['configInput']> & Object;\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/frontend-plugin-api",
3
- "version": "0.18.1-next.0",
3
+ "version": "0.18.1",
4
4
  "backstage": {
5
5
  "role": "web-library"
6
6
  },
@@ -52,18 +52,18 @@
52
52
  "test": "backstage-cli package test"
53
53
  },
54
54
  "dependencies": {
55
- "@backstage/config": "1.3.8",
56
- "@backstage/errors": "1.3.1",
57
- "@backstage/filter-predicates": "0.1.5-next.0",
58
- "@backstage/types": "1.2.2",
59
- "@backstage/version-bridge": "1.0.12",
55
+ "@backstage/config": "^1.3.9",
56
+ "@backstage/errors": "^1.3.1",
57
+ "@backstage/filter-predicates": "^0.1.5",
58
+ "@backstage/types": "^1.2.2",
59
+ "@backstage/version-bridge": "^1.0.12",
60
60
  "@standard-schema/spec": "^1.1.0"
61
61
  },
62
62
  "devDependencies": {
63
- "@backstage/cli": "0.36.6-next.0",
64
- "@backstage/frontend-app-api": "0.16.8-next.1",
65
- "@backstage/frontend-test-utils": "0.6.4-next.1",
66
- "@backstage/test-utils": "1.7.22-next.1",
63
+ "@backstage/cli": "^0.36.6",
64
+ "@backstage/frontend-app-api": "^0.16.8",
65
+ "@backstage/frontend-test-utils": "^0.6.4",
66
+ "@backstage/test-utils": "^1.7.22",
67
67
  "@testing-library/dom": "^10.0.0",
68
68
  "@testing-library/jest-dom": "^6.0.0",
69
69
  "@testing-library/react": "^16.0.0",