@backstage/frontend-app-api 0.12.0-next.3 → 0.12.1-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/dist/catalog-model/src/entity/constants.esm.js.map +1 -1
- package/dist/catalog-model/src/entity/ref.esm.js.map +1 -1
- package/dist/core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.esm.js.map +1 -1
- package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js.map +1 -1
- package/dist/core-app-api/src/apis/system/ApiRegistry.esm.js.map +1 -1
- package/dist/extensions/Root.esm.js +1 -1
- package/dist/extensions/Root.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalExtensionDefinition.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalFrontendPlugin.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/InternalSwappableComponentRef.esm.js.map +1 -1
- package/dist/frontend-internal/src/wiring/createExtensionDataContainer.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/routing/ExternalRouteRef.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/routing/RouteRef.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/routing/SubRouteRef.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/wiring/createFrontendModule.esm.js.map +1 -1
- package/dist/frontend-plugin-api/src/wiring/resolveExtensionDefinition.esm.js.map +1 -1
- package/dist/opaque-internal/src/OpaqueType.esm.js.map +1 -1
- package/dist/routing/RouteAliasResolver.esm.js.map +1 -1
- package/dist/routing/RouteResolver.esm.js +30 -33
- package/dist/routing/RouteResolver.esm.js.map +1 -1
- package/dist/routing/collectRouteIds.esm.js.map +1 -1
- package/dist/routing/extractRouteInfoFromAppNode.esm.js +1 -7
- package/dist/routing/extractRouteInfoFromAppNode.esm.js.map +1 -1
- package/dist/routing/getBasePath.esm.js.map +1 -1
- package/dist/routing/resolveRouteBindings.esm.js.map +1 -1
- package/dist/tree/instantiateAppNodeTree.esm.js.map +1 -1
- package/dist/tree/readAppExtensionsConfig.esm.js.map +1 -1
- package/dist/tree/resolveAppNodeSpecs.esm.js.map +1 -1
- package/dist/tree/resolveAppTree.esm.js.map +1 -1
- package/dist/wiring/createPluginInfoAttacher.esm.js.map +1 -1
- package/dist/wiring/createSpecializedApp.esm.js +4 -3
- package/dist/wiring/createSpecializedApp.esm.js.map +1 -1
- package/package.json +7 -7
- package/dist/routing/toLegacyPlugin.esm.js +0 -35
- package/dist/routing/toLegacyPlugin.esm.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,73 @@
|
|
|
1
1
|
# @backstage/frontend-app-api
|
|
2
2
|
|
|
3
|
+
## 0.12.1-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/frontend-plugin-api@0.11.1-next.0
|
|
9
|
+
- @backstage/frontend-defaults@0.3.1-next.0
|
|
10
|
+
|
|
11
|
+
## 0.12.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 8e21c4d: Use an app plugin for built-in extension app node specs.
|
|
16
|
+
- df7bd3b: **BREAKING**: Removed the deprecated `FrontendFeature` type, import it from `@backstage/frontend-plugin-api` instead.
|
|
17
|
+
- 8e21c4d: The `AppNodeSpec.plugin` property is now required.
|
|
18
|
+
- 5e12252: **BREAKING**: Restructured some of option fields of `createApp` and `createSpecializedApp`.
|
|
19
|
+
|
|
20
|
+
- For `createApp`, all option fields _except_ `features` and `bindRoutes` have been moved into a new `advanced` object field.
|
|
21
|
+
- For `createSpecializedApp`, all option fields _except_ `features`, `config`, and `bindRoutes` have been moved into a new `advanced` object field.
|
|
22
|
+
|
|
23
|
+
This helps highlight that some options are meant to rarely be needed or used, and simplifies the usage of those options that are almost always required.
|
|
24
|
+
|
|
25
|
+
As an example, if you used to supply a custom config loader, you would update your code as follows:
|
|
26
|
+
|
|
27
|
+
```diff
|
|
28
|
+
createApp({
|
|
29
|
+
features: [...],
|
|
30
|
+
- configLoader: new MyCustomLoader(),
|
|
31
|
+
+ advanced: {
|
|
32
|
+
+ configLoader: new MyCustomLoader(),
|
|
33
|
+
+ },
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Patch Changes
|
|
38
|
+
|
|
39
|
+
- d9e00e3: Add support for a new `aliasFor` option for `createRouteRef`. This allows for the creation of a new route ref that acts as an alias for an existing route ref that is installed in the app. This is particularly useful when creating modules that override existing plugin pages, without referring to the existing plugin. For example:
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
export default createFrontendModule({
|
|
43
|
+
pluginId: 'catalog',
|
|
44
|
+
extensions: [
|
|
45
|
+
PageBlueprint.make({
|
|
46
|
+
params: {
|
|
47
|
+
defaultPath: '/catalog',
|
|
48
|
+
routeRef: createRouteRef({ aliasFor: 'catalog.catalogIndex' }),
|
|
49
|
+
loader: () =>
|
|
50
|
+
import('./CustomCatalogIndexPage').then(m => (
|
|
51
|
+
<m.CustomCatalogIndexPage />
|
|
52
|
+
)),
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- f2f133c: Internal update to use the new variant of `ApiBlueprint`.
|
|
60
|
+
- ef54427: Internal cleanup of routing system data.
|
|
61
|
+
- 391f0ca: External route references are no longer required to be exported via a plugin instance to function. The default target will still be resolved even if the external route reference is not included in `externalRoutes` of a plugin, but users of the plugin will not be able to configure the target of the route. This is particularly useful when building modules or overrides for existing plugins, allowing you add external routes both within and out from the plugin.
|
|
62
|
+
- f3f9d57: Renaming the `getNodesByRoutePath` parameter from `sourcePath` to `routePath`
|
|
63
|
+
- 8b1bf6e: Deprecated new frontend system config setting `app.experimental.packages` to just `app.packages`. The old config will continue working for the time being, but may be removed in a future release.
|
|
64
|
+
- fda1bbc: Added a default implementation of the `SwappableComponentsApi` and removing the legacy `ComponentsApi` implementation
|
|
65
|
+
- 1c2cc37: Improved runtime error message clarity when extension factories don't return an iterable object.
|
|
66
|
+
- 3d2499f: Moved `createSpecializedApp` options to a new `CreateSpecializedAppOptions` type.
|
|
67
|
+
- Updated dependencies
|
|
68
|
+
- @backstage/frontend-defaults@0.3.0
|
|
69
|
+
- @backstage/frontend-plugin-api@0.11.0
|
|
70
|
+
|
|
3
71
|
## 0.12.0-next.3
|
|
4
72
|
|
|
5
73
|
### Minor Changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.esm.js","sources":["../../../../../catalog-model/src/entity/constants.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\n/**\n * The namespace that entities without an explicit namespace fall into.\n *\n * @public\n */\nexport const DEFAULT_NAMESPACE = 'default';\n\n/**\n * Annotation for linking to entity page from catalog pages.\n *\n * @public\n */\nexport const ANNOTATION_VIEW_URL = 'backstage.io/view-url';\n\n/**\n * Annotation for linking to entity edit page from catalog pages.\n *\n * @public\n */\nexport const ANNOTATION_EDIT_URL = 'backstage.io/edit-url';\n\n/**\n * Annotation for specifying the API server of a Kubernetes cluster\n *\n * @deprecated Import this constant from `@backstage/plugin-kubernetes-common` instead\n * @public\n */\nexport const ANNOTATION_KUBERNETES_API_SERVER = 'kubernetes.io/api-server';\n\n/**\n * Annotation for specifying the Certificate Authority of an API server for a Kubernetes cluster\n *\n * @deprecated Import this constant from `@backstage/plugin-kubernetes-common` instead\n * @public\n */\nexport const ANNOTATION_KUBERNETES_API_SERVER_CA =\n 'kubernetes.io/api-server-certificate-authority';\n\n/**\n * Annotation for specifying the auth provider for a Kubernetes cluster\n *\n * @deprecated Import this constant from `@backstage/plugin-kubernetes-common` instead\n * @public\n */\nexport const ANNOTATION_KUBERNETES_AUTH_PROVIDER =\n 'kubernetes.io/auth-provider';\n"],"names":[],"mappings":"AAqBO,MAAM,
|
|
1
|
+
{"version":3,"file":"constants.esm.js","sources":["../../../../../catalog-model/src/entity/constants.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\n/**\n * The namespace that entities without an explicit namespace fall into.\n *\n * @public\n */\nexport const DEFAULT_NAMESPACE = 'default';\n\n/**\n * Annotation for linking to entity page from catalog pages.\n *\n * @public\n */\nexport const ANNOTATION_VIEW_URL = 'backstage.io/view-url';\n\n/**\n * Annotation for linking to entity edit page from catalog pages.\n *\n * @public\n */\nexport const ANNOTATION_EDIT_URL = 'backstage.io/edit-url';\n\n/**\n * Annotation for specifying the API server of a Kubernetes cluster\n *\n * @deprecated Import this constant from `@backstage/plugin-kubernetes-common` instead\n * @public\n */\nexport const ANNOTATION_KUBERNETES_API_SERVER = 'kubernetes.io/api-server';\n\n/**\n * Annotation for specifying the Certificate Authority of an API server for a Kubernetes cluster\n *\n * @deprecated Import this constant from `@backstage/plugin-kubernetes-common` instead\n * @public\n */\nexport const ANNOTATION_KUBERNETES_API_SERVER_CA =\n 'kubernetes.io/api-server-certificate-authority';\n\n/**\n * Annotation for specifying the auth provider for a Kubernetes cluster\n *\n * @deprecated Import this constant from `@backstage/plugin-kubernetes-common` instead\n * @public\n */\nexport const ANNOTATION_KUBERNETES_AUTH_PROVIDER =\n 'kubernetes.io/auth-provider';\n"],"names":[],"mappings":"AAqBO,MAAM,iBAAA,GAAoB;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ref.esm.js","sources":["../../../../../catalog-model/src/entity/ref.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 { DEFAULT_NAMESPACE } from './constants';\nimport { CompoundEntityRef } from '../types';\nimport { Entity } from './Entity';\n\nfunction parseRefString(ref: string): {\n kind?: string;\n namespace?: string;\n name: string;\n} {\n let colonI = ref.indexOf(':');\n const slashI = ref.indexOf('/');\n\n // If the / is ahead of the :, treat the rest as the name\n if (slashI !== -1 && slashI < colonI) {\n colonI = -1;\n }\n\n const kind = colonI === -1 ? undefined : ref.slice(0, colonI);\n const namespace = slashI === -1 ? undefined : ref.slice(colonI + 1, slashI);\n const name = ref.slice(Math.max(colonI + 1, slashI + 1));\n\n if (kind === '' || namespace === '' || name === '') {\n throw new TypeError(\n `Entity reference \"${ref}\" was not on the form [<kind>:][<namespace>/]<name>`,\n );\n }\n\n return { kind, namespace, name };\n}\n\n/**\n * Extracts the kind, namespace and name that form the compound entity ref\n * triplet of the given entity.\n *\n * @public\n * @param entity - An entity\n * @returns The compound entity ref\n */\nexport function getCompoundEntityRef(entity: Entity): CompoundEntityRef {\n return {\n kind: entity.kind,\n namespace: entity.metadata.namespace || DEFAULT_NAMESPACE,\n name: entity.metadata.name,\n };\n}\n\n/**\n * Parses an entity reference, either on string or compound form, and returns\n * a structure with a name, and optional kind and namespace.\n *\n * @remarks\n *\n * The context object can contain default values for the kind and namespace,\n * that will be used if the input reference did not specify any.\n *\n * @public\n * @param ref - The reference to parse\n * @param context - The context of defaults that the parsing happens within\n * @returns The compound form of the reference\n */\nexport function parseEntityRef(\n ref: string | { kind?: string; namespace?: string; name: string },\n context?: {\n /** The default kind, if none is given in the reference */\n defaultKind?: string;\n /** The default namespace, if none is given in the reference */\n defaultNamespace?: string;\n },\n): CompoundEntityRef {\n if (!ref) {\n throw new Error(`Entity reference must not be empty`);\n }\n\n const defaultKind = context?.defaultKind;\n const defaultNamespace = context?.defaultNamespace || DEFAULT_NAMESPACE;\n\n let kind: string | undefined;\n let namespace: string | undefined;\n let name: string | undefined;\n\n if (typeof ref === 'string') {\n const parsed = parseRefString(ref);\n kind = parsed.kind ?? defaultKind;\n namespace = parsed.namespace ?? defaultNamespace;\n name = parsed.name;\n } else {\n kind = ref.kind ?? defaultKind;\n namespace = ref.namespace ?? defaultNamespace;\n name = ref.name;\n }\n\n if (!kind) {\n const textual = JSON.stringify(ref);\n throw new Error(\n `Entity reference ${textual} had missing or empty kind (e.g. did not start with \"component:\" or similar)`,\n );\n } else if (!namespace) {\n const textual = JSON.stringify(ref);\n throw new Error(\n `Entity reference ${textual} had missing or empty namespace`,\n );\n } else if (!name) {\n const textual = JSON.stringify(ref);\n throw new Error(`Entity reference ${textual} had missing or empty name`);\n }\n\n return { kind, namespace, name };\n}\n\n/**\n * Takes an entity or entity name/reference, and returns the string form of an\n * entity ref.\n *\n * @remarks\n *\n * This function creates a canonical and unique reference to the entity, converting\n * all parts of the name to lowercase and inserts the default namespace if needed.\n * It is typically not the best way to represent the entity reference to the user.\n *\n * @public\n * @param ref - The reference to serialize\n * @returns The same reference on either string or compound form\n */\nexport function stringifyEntityRef(\n ref: Entity | { kind: string; namespace?: string; name: string },\n): string {\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in ref) {\n kind = ref.kind;\n namespace = ref.metadata.namespace ?? DEFAULT_NAMESPACE;\n name = ref.metadata.name;\n } else {\n kind = ref.kind;\n namespace = ref.namespace ?? DEFAULT_NAMESPACE;\n name = ref.name;\n }\n\n return `${kind.toLocaleLowerCase('en-US')}:${namespace.toLocaleLowerCase(\n 'en-US',\n )}/${name.toLocaleLowerCase('en-US')}`;\n}\n"],"names":[],"mappings":";;AAoBA,SAAS,eAAe,
|
|
1
|
+
{"version":3,"file":"ref.esm.js","sources":["../../../../../catalog-model/src/entity/ref.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 { DEFAULT_NAMESPACE } from './constants';\nimport { CompoundEntityRef } from '../types';\nimport { Entity } from './Entity';\n\nfunction parseRefString(ref: string): {\n kind?: string;\n namespace?: string;\n name: string;\n} {\n let colonI = ref.indexOf(':');\n const slashI = ref.indexOf('/');\n\n // If the / is ahead of the :, treat the rest as the name\n if (slashI !== -1 && slashI < colonI) {\n colonI = -1;\n }\n\n const kind = colonI === -1 ? undefined : ref.slice(0, colonI);\n const namespace = slashI === -1 ? undefined : ref.slice(colonI + 1, slashI);\n const name = ref.slice(Math.max(colonI + 1, slashI + 1));\n\n if (kind === '' || namespace === '' || name === '') {\n throw new TypeError(\n `Entity reference \"${ref}\" was not on the form [<kind>:][<namespace>/]<name>`,\n );\n }\n\n return { kind, namespace, name };\n}\n\n/**\n * Extracts the kind, namespace and name that form the compound entity ref\n * triplet of the given entity.\n *\n * @public\n * @param entity - An entity\n * @returns The compound entity ref\n */\nexport function getCompoundEntityRef(entity: Entity): CompoundEntityRef {\n return {\n kind: entity.kind,\n namespace: entity.metadata.namespace || DEFAULT_NAMESPACE,\n name: entity.metadata.name,\n };\n}\n\n/**\n * Parses an entity reference, either on string or compound form, and returns\n * a structure with a name, and optional kind and namespace.\n *\n * @remarks\n *\n * The context object can contain default values for the kind and namespace,\n * that will be used if the input reference did not specify any.\n *\n * @public\n * @param ref - The reference to parse\n * @param context - The context of defaults that the parsing happens within\n * @returns The compound form of the reference\n */\nexport function parseEntityRef(\n ref: string | { kind?: string; namespace?: string; name: string },\n context?: {\n /** The default kind, if none is given in the reference */\n defaultKind?: string;\n /** The default namespace, if none is given in the reference */\n defaultNamespace?: string;\n },\n): CompoundEntityRef {\n if (!ref) {\n throw new Error(`Entity reference must not be empty`);\n }\n\n const defaultKind = context?.defaultKind;\n const defaultNamespace = context?.defaultNamespace || DEFAULT_NAMESPACE;\n\n let kind: string | undefined;\n let namespace: string | undefined;\n let name: string | undefined;\n\n if (typeof ref === 'string') {\n const parsed = parseRefString(ref);\n kind = parsed.kind ?? defaultKind;\n namespace = parsed.namespace ?? defaultNamespace;\n name = parsed.name;\n } else {\n kind = ref.kind ?? defaultKind;\n namespace = ref.namespace ?? defaultNamespace;\n name = ref.name;\n }\n\n if (!kind) {\n const textual = JSON.stringify(ref);\n throw new Error(\n `Entity reference ${textual} had missing or empty kind (e.g. did not start with \"component:\" or similar)`,\n );\n } else if (!namespace) {\n const textual = JSON.stringify(ref);\n throw new Error(\n `Entity reference ${textual} had missing or empty namespace`,\n );\n } else if (!name) {\n const textual = JSON.stringify(ref);\n throw new Error(`Entity reference ${textual} had missing or empty name`);\n }\n\n return { kind, namespace, name };\n}\n\n/**\n * Takes an entity or entity name/reference, and returns the string form of an\n * entity ref.\n *\n * @remarks\n *\n * This function creates a canonical and unique reference to the entity, converting\n * all parts of the name to lowercase and inserts the default namespace if needed.\n * It is typically not the best way to represent the entity reference to the user.\n *\n * @public\n * @param ref - The reference to serialize\n * @returns The same reference on either string or compound form\n */\nexport function stringifyEntityRef(\n ref: Entity | { kind: string; namespace?: string; name: string },\n): string {\n let kind;\n let namespace;\n let name;\n\n if ('metadata' in ref) {\n kind = ref.kind;\n namespace = ref.metadata.namespace ?? DEFAULT_NAMESPACE;\n name = ref.metadata.name;\n } else {\n kind = ref.kind;\n namespace = ref.namespace ?? DEFAULT_NAMESPACE;\n name = ref.name;\n }\n\n return `${kind.toLocaleLowerCase('en-US')}:${namespace.toLocaleLowerCase(\n 'en-US',\n )}/${name.toLocaleLowerCase('en-US')}`;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppIdentityProxy.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.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 {\n IdentityApi,\n ProfileInfo,\n BackstageUserIdentity,\n ErrorApi,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport { startCookieAuthRefresh } from './startCookieAuthRefresh';\n\nfunction mkError(thing: string) {\n return new Error(\n `Tried to access IdentityApi ${thing} before app was loaded`,\n );\n}\n\nfunction logDeprecation(thing: string) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: Call to ${thing} is deprecated and will break in the future`,\n );\n}\n\n// We use this for a period of backwards compatibility. It is a hidden\n// compatibility that will allow old plugins to continue working for a limited time.\ntype CompatibilityIdentityApi = IdentityApi & {\n getUserId?(): string;\n getIdToken?(): Promise<string | undefined>;\n getProfile?(): ProfileInfo;\n};\n\n/**\n * Implementation of the connection between the App-wide IdentityApi\n * and sign-in page.\n */\nexport class AppIdentityProxy implements IdentityApi {\n private target?: CompatibilityIdentityApi;\n private waitForTarget: Promise<CompatibilityIdentityApi>;\n private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {};\n private signOutTargetUrl = '/';\n\n #cookieAuthSignOut?: () => Promise<void>;\n\n constructor() {\n this.waitForTarget = new Promise<CompatibilityIdentityApi>(resolve => {\n this.resolveTarget = resolve;\n });\n }\n\n // This is called by the app manager once the sign-in page provides us with an implementation\n setTarget(\n identityApi: CompatibilityIdentityApi,\n targetOptions: { signOutTargetUrl: string },\n ) {\n this.target = identityApi;\n this.signOutTargetUrl = targetOptions.signOutTargetUrl;\n this.resolveTarget(identityApi);\n }\n\n getUserId(): string {\n if (!this.target) {\n throw mkError('getUserId');\n }\n if (!this.target.getUserId) {\n throw new Error('IdentityApi does not implement getUserId');\n }\n logDeprecation('getUserId');\n return this.target.getUserId();\n }\n\n getProfile(): ProfileInfo {\n if (!this.target) {\n throw mkError('getProfile');\n }\n if (!this.target.getProfile) {\n throw new Error('IdentityApi does not implement getProfile');\n }\n logDeprecation('getProfile');\n return this.target.getProfile();\n }\n\n async getProfileInfo(): Promise<ProfileInfo> {\n return this.waitForTarget.then(target => target.getProfileInfo());\n }\n\n async getBackstageIdentity(): Promise<BackstageUserIdentity> {\n const identity = await this.waitForTarget.then(target =>\n target.getBackstageIdentity(),\n );\n if (!identity.userEntityRef.match(/^.*:.*\\/.*$/)) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. ` +\n `It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`,\n );\n }\n\n return identity;\n }\n\n async getCredentials(): Promise<{ token?: string | undefined }> {\n return this.waitForTarget.then(target => target.getCredentials());\n }\n\n async getIdToken(): Promise<string | undefined> {\n return this.waitForTarget.then(target => {\n if (!target.getIdToken) {\n throw new Error('IdentityApi does not implement getIdToken');\n }\n logDeprecation('getIdToken');\n return target.getIdToken();\n });\n }\n\n async signOut(): Promise<void> {\n await this.waitForTarget.then(target => target.signOut());\n\n await this.#cookieAuthSignOut?.();\n\n window.location.href = this.signOutTargetUrl;\n }\n\n enableCookieAuth(ctx: {\n errorApi: ErrorApi;\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n }) {\n if (this.#cookieAuthSignOut) {\n return;\n }\n\n const stopRefresh = startCookieAuthRefresh(ctx);\n\n this.#cookieAuthSignOut = async () => {\n stopRefresh();\n\n // It is fine if we do NOT worry yet about deleting cookies for OTHER backends like techdocs\n const appBaseUrl = await ctx.discoveryApi.getBaseUrl('app');\n try {\n await fetch(`${appBaseUrl}/.backstage/auth/v1/cookie`, {\n method: 'DELETE',\n credentials: 'include',\n });\n } catch {\n // Ignore the error for those who use static serving of the frontend\n }\n };\n }\n}\n"],"names":[],"mappings":";;AA0BA,SAAS,QAAQ,
|
|
1
|
+
{"version":3,"file":"AppIdentityProxy.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy.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 {\n IdentityApi,\n ProfileInfo,\n BackstageUserIdentity,\n ErrorApi,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport { startCookieAuthRefresh } from './startCookieAuthRefresh';\n\nfunction mkError(thing: string) {\n return new Error(\n `Tried to access IdentityApi ${thing} before app was loaded`,\n );\n}\n\nfunction logDeprecation(thing: string) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: Call to ${thing} is deprecated and will break in the future`,\n );\n}\n\n// We use this for a period of backwards compatibility. It is a hidden\n// compatibility that will allow old plugins to continue working for a limited time.\ntype CompatibilityIdentityApi = IdentityApi & {\n getUserId?(): string;\n getIdToken?(): Promise<string | undefined>;\n getProfile?(): ProfileInfo;\n};\n\n/**\n * Implementation of the connection between the App-wide IdentityApi\n * and sign-in page.\n */\nexport class AppIdentityProxy implements IdentityApi {\n private target?: CompatibilityIdentityApi;\n private waitForTarget: Promise<CompatibilityIdentityApi>;\n private resolveTarget: (api: CompatibilityIdentityApi) => void = () => {};\n private signOutTargetUrl = '/';\n\n #cookieAuthSignOut?: () => Promise<void>;\n\n constructor() {\n this.waitForTarget = new Promise<CompatibilityIdentityApi>(resolve => {\n this.resolveTarget = resolve;\n });\n }\n\n // This is called by the app manager once the sign-in page provides us with an implementation\n setTarget(\n identityApi: CompatibilityIdentityApi,\n targetOptions: { signOutTargetUrl: string },\n ) {\n this.target = identityApi;\n this.signOutTargetUrl = targetOptions.signOutTargetUrl;\n this.resolveTarget(identityApi);\n }\n\n getUserId(): string {\n if (!this.target) {\n throw mkError('getUserId');\n }\n if (!this.target.getUserId) {\n throw new Error('IdentityApi does not implement getUserId');\n }\n logDeprecation('getUserId');\n return this.target.getUserId();\n }\n\n getProfile(): ProfileInfo {\n if (!this.target) {\n throw mkError('getProfile');\n }\n if (!this.target.getProfile) {\n throw new Error('IdentityApi does not implement getProfile');\n }\n logDeprecation('getProfile');\n return this.target.getProfile();\n }\n\n async getProfileInfo(): Promise<ProfileInfo> {\n return this.waitForTarget.then(target => target.getProfileInfo());\n }\n\n async getBackstageIdentity(): Promise<BackstageUserIdentity> {\n const identity = await this.waitForTarget.then(target =>\n target.getBackstageIdentity(),\n );\n if (!identity.userEntityRef.match(/^.*:.*\\/.*$/)) {\n // eslint-disable-next-line no-console\n console.warn(\n `WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. ` +\n `It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`,\n );\n }\n\n return identity;\n }\n\n async getCredentials(): Promise<{ token?: string | undefined }> {\n return this.waitForTarget.then(target => target.getCredentials());\n }\n\n async getIdToken(): Promise<string | undefined> {\n return this.waitForTarget.then(target => {\n if (!target.getIdToken) {\n throw new Error('IdentityApi does not implement getIdToken');\n }\n logDeprecation('getIdToken');\n return target.getIdToken();\n });\n }\n\n async signOut(): Promise<void> {\n await this.waitForTarget.then(target => target.signOut());\n\n await this.#cookieAuthSignOut?.();\n\n window.location.href = this.signOutTargetUrl;\n }\n\n enableCookieAuth(ctx: {\n errorApi: ErrorApi;\n fetchApi: FetchApi;\n discoveryApi: DiscoveryApi;\n }) {\n if (this.#cookieAuthSignOut) {\n return;\n }\n\n const stopRefresh = startCookieAuthRefresh(ctx);\n\n this.#cookieAuthSignOut = async () => {\n stopRefresh();\n\n // It is fine if we do NOT worry yet about deleting cookies for OTHER backends like techdocs\n const appBaseUrl = await ctx.discoveryApi.getBaseUrl('app');\n try {\n await fetch(`${appBaseUrl}/.backstage/auth/v1/cookie`, {\n method: 'DELETE',\n credentials: 'include',\n });\n } catch {\n // Ignore the error for those who use static serving of the frontend\n }\n };\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"startCookieAuthRefresh.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.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 { DiscoveryApi, ErrorApi, FetchApi } from '@backstage/core-plugin-api';\n\nconst PLUGIN_ID = 'app';\nconst CHANNEL_ID = `${PLUGIN_ID}-auth-cookie-expires-at`;\n\nconst MIN_BASE_DELAY_MS = 5 * 60_000;\n\nconst ERROR_BACKOFF_START = 5_000;\nconst ERROR_BACKOFF_FACTOR = 2;\nconst ERROR_BACKOFF_MAX = 5 * 60_000;\n\n// Messaging implementation and IDs must match\n// plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx\nexport function startCookieAuthRefresh({\n discoveryApi,\n fetchApi,\n errorApi,\n}: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n errorApi: ErrorApi;\n}) {\n let stopped = false;\n let timeout: NodeJS.Timeout | undefined;\n let firstError = true;\n let errorBackoff = ERROR_BACKOFF_START;\n\n const channel =\n 'BroadcastChannel' in window ? new BroadcastChannel(CHANNEL_ID) : undefined;\n\n // Randomize the refreshing margin with a margin of 1-4 minutes to avoid all tabs refreshing at the same time\n const getDelay = (expiresAt: number) => {\n const margin = (1 + 3 * Math.random()) * 60000;\n const delay = Math.max(expiresAt - Date.now(), MIN_BASE_DELAY_MS) - margin;\n return delay;\n };\n\n const refresh = async () => {\n try {\n const baseUrl = await discoveryApi.getBaseUrl(PLUGIN_ID);\n const requestUrl = `${baseUrl}/.backstage/auth/v1/cookie`;\n const res = await fetchApi.fetch(requestUrl, {\n credentials: 'include',\n });\n\n if (!res.ok) {\n throw new Error(\n `Request failed with status ${res.status} ${res.statusText}, see request towards ${requestUrl} for more details`,\n );\n }\n\n const data = await res.json();\n if (!data.expiresAt) {\n throw new Error('No expiration date in response');\n }\n\n const expiresAt = Date.parse(data.expiresAt);\n if (Number.isNaN(expiresAt)) {\n throw new Error('Invalid expiration date in response');\n }\n\n firstError = true;\n\n channel?.postMessage({\n action: 'COOKIE_REFRESH_SUCCESS',\n payload: { expiresAt: new Date(expiresAt).toISOString() },\n });\n\n scheduleRefresh(getDelay(expiresAt));\n } catch (error) {\n // Ignore the first error after successful requests\n if (firstError) {\n firstError = false;\n errorBackoff = ERROR_BACKOFF_START;\n } else {\n errorBackoff = Math.min(\n ERROR_BACKOFF_MAX,\n errorBackoff * ERROR_BACKOFF_FACTOR,\n );\n // eslint-disable-next-line no-console\n console.error('Session cookie refresh failed', error);\n errorApi.post(\n new Error(\n `Session refresh failed, see developer console for details`,\n ),\n );\n }\n\n scheduleRefresh(errorBackoff);\n }\n };\n\n const onMessage = (\n event: MessageEvent<\n | {\n action: 'COOKIE_REFRESH_SUCCESS';\n payload: { expiresAt: string };\n }\n | object\n >,\n ) => {\n const { data } = event;\n if (data === null || typeof data !== 'object') {\n return;\n }\n if ('action' in data && data.action === 'COOKIE_REFRESH_SUCCESS') {\n const expiresAt = Date.parse(data.payload.expiresAt);\n if (Number.isNaN(expiresAt)) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Received invalid expiration from session refresh channel',\n );\n return;\n }\n\n scheduleRefresh(getDelay(expiresAt));\n }\n };\n\n function scheduleRefresh(delayMs: number) {\n if (stopped) {\n return;\n }\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(refresh, delayMs);\n }\n\n channel?.addEventListener('message', onMessage);\n refresh();\n\n return () => {\n stopped = true;\n if (timeout) {\n clearTimeout(timeout);\n }\n channel?.removeEventListener('message', onMessage);\n channel?.close();\n };\n}\n"],"names":[],"mappings":"AAkBA,MAAM,
|
|
1
|
+
{"version":3,"file":"startCookieAuthRefresh.esm.js","sources":["../../../../../../../core-app-api/src/apis/implementations/IdentityApi/startCookieAuthRefresh.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 { DiscoveryApi, ErrorApi, FetchApi } from '@backstage/core-plugin-api';\n\nconst PLUGIN_ID = 'app';\nconst CHANNEL_ID = `${PLUGIN_ID}-auth-cookie-expires-at`;\n\nconst MIN_BASE_DELAY_MS = 5 * 60_000;\n\nconst ERROR_BACKOFF_START = 5_000;\nconst ERROR_BACKOFF_FACTOR = 2;\nconst ERROR_BACKOFF_MAX = 5 * 60_000;\n\n// Messaging implementation and IDs must match\n// plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.tsx\nexport function startCookieAuthRefresh({\n discoveryApi,\n fetchApi,\n errorApi,\n}: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n errorApi: ErrorApi;\n}) {\n let stopped = false;\n let timeout: NodeJS.Timeout | undefined;\n let firstError = true;\n let errorBackoff = ERROR_BACKOFF_START;\n\n const channel =\n 'BroadcastChannel' in window ? new BroadcastChannel(CHANNEL_ID) : undefined;\n\n // Randomize the refreshing margin with a margin of 1-4 minutes to avoid all tabs refreshing at the same time\n const getDelay = (expiresAt: number) => {\n const margin = (1 + 3 * Math.random()) * 60000;\n const delay = Math.max(expiresAt - Date.now(), MIN_BASE_DELAY_MS) - margin;\n return delay;\n };\n\n const refresh = async () => {\n try {\n const baseUrl = await discoveryApi.getBaseUrl(PLUGIN_ID);\n const requestUrl = `${baseUrl}/.backstage/auth/v1/cookie`;\n const res = await fetchApi.fetch(requestUrl, {\n credentials: 'include',\n });\n\n if (!res.ok) {\n throw new Error(\n `Request failed with status ${res.status} ${res.statusText}, see request towards ${requestUrl} for more details`,\n );\n }\n\n const data = await res.json();\n if (!data.expiresAt) {\n throw new Error('No expiration date in response');\n }\n\n const expiresAt = Date.parse(data.expiresAt);\n if (Number.isNaN(expiresAt)) {\n throw new Error('Invalid expiration date in response');\n }\n\n firstError = true;\n\n channel?.postMessage({\n action: 'COOKIE_REFRESH_SUCCESS',\n payload: { expiresAt: new Date(expiresAt).toISOString() },\n });\n\n scheduleRefresh(getDelay(expiresAt));\n } catch (error) {\n // Ignore the first error after successful requests\n if (firstError) {\n firstError = false;\n errorBackoff = ERROR_BACKOFF_START;\n } else {\n errorBackoff = Math.min(\n ERROR_BACKOFF_MAX,\n errorBackoff * ERROR_BACKOFF_FACTOR,\n );\n // eslint-disable-next-line no-console\n console.error('Session cookie refresh failed', error);\n errorApi.post(\n new Error(\n `Session refresh failed, see developer console for details`,\n ),\n );\n }\n\n scheduleRefresh(errorBackoff);\n }\n };\n\n const onMessage = (\n event: MessageEvent<\n | {\n action: 'COOKIE_REFRESH_SUCCESS';\n payload: { expiresAt: string };\n }\n | object\n >,\n ) => {\n const { data } = event;\n if (data === null || typeof data !== 'object') {\n return;\n }\n if ('action' in data && data.action === 'COOKIE_REFRESH_SUCCESS') {\n const expiresAt = Date.parse(data.payload.expiresAt);\n if (Number.isNaN(expiresAt)) {\n // eslint-disable-next-line no-console\n console.warn(\n 'Received invalid expiration from session refresh channel',\n );\n return;\n }\n\n scheduleRefresh(getDelay(expiresAt));\n }\n };\n\n function scheduleRefresh(delayMs: number) {\n if (stopped) {\n return;\n }\n if (timeout) {\n clearTimeout(timeout);\n }\n timeout = setTimeout(refresh, delayMs);\n }\n\n channel?.addEventListener('message', onMessage);\n refresh();\n\n return () => {\n stopped = true;\n if (timeout) {\n clearTimeout(timeout);\n }\n channel?.removeEventListener('message', onMessage);\n channel?.close();\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiRegistry.esm.js","sources":["../../../../../../core-app-api/src/apis/system/ApiRegistry.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 { ApiRef, ApiHolder } from '@backstage/core-plugin-api';\n\ntype ApiImpl<T = unknown> = readonly [ApiRef<T>, T];\n\n/** @internal */\nclass ApiRegistryBuilder {\n private apis: [string, unknown][] = [];\n\n add<T, I extends T>(api: ApiRef<T>, impl: I): I {\n this.apis.push([api.id, impl]);\n return impl;\n }\n\n build(): ApiRegistry {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n return new ApiRegistry(new Map(this.apis));\n }\n}\n\n/**\n * A registry for utility APIs.\n *\n * @internal\n */\nexport class ApiRegistry implements ApiHolder {\n static builder() {\n return new ApiRegistryBuilder();\n }\n\n /**\n * Creates a new ApiRegistry with a list of API implementations.\n *\n * @param apis - A list of pairs mapping an ApiRef to its respective implementation\n */\n static from(apis: ApiImpl[]) {\n return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));\n }\n\n /**\n * Creates a new ApiRegistry with a single API implementation.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n static with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([[api.id, impl]]));\n }\n\n constructor(private readonly apis: Map<string, unknown>) {}\n\n /**\n * Returns a new ApiRegistry with the provided API added to the existing ones.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));\n }\n\n get<T>(api: ApiRef<T>): T | undefined {\n return this.apis.get(api.id) as T | undefined;\n }\n}\n"],"names":[],"mappings":"AAqBA,MAAM,
|
|
1
|
+
{"version":3,"file":"ApiRegistry.esm.js","sources":["../../../../../../core-app-api/src/apis/system/ApiRegistry.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 { ApiRef, ApiHolder } from '@backstage/core-plugin-api';\n\ntype ApiImpl<T = unknown> = readonly [ApiRef<T>, T];\n\n/** @internal */\nclass ApiRegistryBuilder {\n private apis: [string, unknown][] = [];\n\n add<T, I extends T>(api: ApiRef<T>, impl: I): I {\n this.apis.push([api.id, impl]);\n return impl;\n }\n\n build(): ApiRegistry {\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n return new ApiRegistry(new Map(this.apis));\n }\n}\n\n/**\n * A registry for utility APIs.\n *\n * @internal\n */\nexport class ApiRegistry implements ApiHolder {\n static builder() {\n return new ApiRegistryBuilder();\n }\n\n /**\n * Creates a new ApiRegistry with a list of API implementations.\n *\n * @param apis - A list of pairs mapping an ApiRef to its respective implementation\n */\n static from(apis: ApiImpl[]) {\n return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));\n }\n\n /**\n * Creates a new ApiRegistry with a single API implementation.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n static with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([[api.id, impl]]));\n }\n\n constructor(private readonly apis: Map<string, unknown>) {}\n\n /**\n * Returns a new ApiRegistry with the provided API added to the existing ones.\n *\n * @param api - ApiRef for the API to add\n * @param impl - Implementation of the API to add\n */\n with<T>(api: ApiRef<T>, impl: T): ApiRegistry {\n return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));\n }\n\n get<T>(api: ApiRef<T>): T | undefined {\n return this.apis.get(api.id) as T | undefined;\n }\n}\n"],"names":[],"mappings":"AAqBA,MAAM,kBAAA,CAAmB;AAAA,EACf,OAA4B,EAAC;AAAA,EAErC,GAAA,CAAoB,KAAgB,IAAA,EAAY;AAC9C,IAAA,IAAA,CAAK,KAAK,IAAA,CAAK,CAAC,GAAA,CAAI,EAAA,EAAI,IAAI,CAAC,CAAA;AAC7B,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,KAAA,GAAqB;AAEnB,IAAA,OAAO,IAAI,WAAA,CAAY,IAAI,GAAA,CAAI,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,EAC3C;AACF;AAOO,MAAM,WAAA,CAAiC;AAAA,EAwB5C,YAA6B,IAAA,EAA4B;AAA5B,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA,EAA6B;AAAA,EAvB1D,OAAO,OAAA,GAAU;AACf,IAAA,OAAO,IAAI,kBAAA,EAAmB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,KAAK,IAAA,EAAiB;AAC3B,IAAA,OAAO,IAAI,WAAA,CAAY,IAAI,GAAA,CAAI,IAAA,CAAK,IAAI,CAAC,CAAC,GAAA,EAAK,IAAI,MAAM,CAAC,GAAA,CAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,IAAA,CAAQ,GAAA,EAAgB,IAAA,EAAsB;AACnD,IAAA,OAAO,IAAI,WAAA,iBAAY,IAAI,GAAA,CAAI,CAAC,CAAC,GAAA,CAAI,EAAA,EAAI,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAA,CAAQ,KAAgB,IAAA,EAAsB;AAC5C,IAAA,OAAO,IAAI,WAAA,CAAY,IAAI,GAAA,CAAI,CAAC,GAAG,IAAA,CAAK,IAAA,EAAM,CAAC,GAAA,CAAI,EAAA,EAAI,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA,EAChE;AAAA,EAEA,IAAO,GAAA,EAA+B;AACpC,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA;AAAA,EAC7B;AACF;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createExtension,
|
|
1
|
+
import { createExtension, coreExtensionData, createExtensionInput, ApiBlueprint } from '@backstage/frontend-plugin-api';
|
|
2
2
|
|
|
3
3
|
const Root = createExtension({
|
|
4
4
|
attachTo: { id: "ignored", input: "ignored" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Root.esm.js","sources":["../../src/extensions/Root.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 ApiBlueprint,\n coreExtensionData,\n createExtension,\n createExtensionInput,\n} from '@backstage/frontend-plugin-api';\n\nexport const Root = createExtension({\n attachTo: { id: 'ignored', input: 'ignored' },\n inputs: {\n app: createExtensionInput([coreExtensionData.reactElement], {\n singleton: true,\n }),\n apis: createExtensionInput([ApiBlueprint.dataRefs.factory], {\n replaces: [{ id: 'app', input: 'apis' }],\n }),\n },\n output: [coreExtensionData.reactElement],\n factory: ({ inputs }) => inputs.app,\n});\n"],"names":[],"mappings":";;AAuBO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"Root.esm.js","sources":["../../src/extensions/Root.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 ApiBlueprint,\n coreExtensionData,\n createExtension,\n createExtensionInput,\n} from '@backstage/frontend-plugin-api';\n\nexport const Root = createExtension({\n attachTo: { id: 'ignored', input: 'ignored' },\n inputs: {\n app: createExtensionInput([coreExtensionData.reactElement], {\n singleton: true,\n }),\n apis: createExtensionInput([ApiBlueprint.dataRefs.factory], {\n replaces: [{ id: 'app', input: 'apis' }],\n }),\n },\n output: [coreExtensionData.reactElement],\n factory: ({ inputs }) => inputs.app,\n});\n"],"names":[],"mappings":";;AAuBO,MAAM,OAAO,eAAA,CAAgB;AAAA,EAClC,QAAA,EAAU,EAAE,EAAA,EAAI,SAAA,EAAW,OAAO,SAAA,EAAU;AAAA,EAC5C,MAAA,EAAQ;AAAA,IACN,GAAA,EAAK,oBAAA,CAAqB,CAAC,iBAAA,CAAkB,YAAY,CAAA,EAAG;AAAA,MAC1D,SAAA,EAAW;AAAA,KACZ,CAAA;AAAA,IACD,MAAM,oBAAA,CAAqB,CAAC,YAAA,CAAa,QAAA,CAAS,OAAO,CAAA,EAAG;AAAA,MAC1D,UAAU,CAAC,EAAE,IAAI,KAAA,EAAO,KAAA,EAAO,QAAQ;AAAA,KACxC;AAAA,GACH;AAAA,EACA,MAAA,EAAQ,CAAC,iBAAA,CAAkB,YAAY,CAAA;AAAA,EACvC,OAAA,EAAS,CAAC,EAAE,MAAA,OAAa,MAAA,CAAO;AAClC,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: {\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\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 ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n }>;\n }): Iterable<ExtensionDataValue<any, any>>;\n };\n}>({\n type: '@backstage/ExtensionDefinition',\n versions: ['v1', 'v2'],\n});\n"],"names":[],"mappings":";;
|
|
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: {\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\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 ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\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,CA6DjD;AAAA,EACD,IAAA,EAAM,gCAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAA,EAAM,IAAI;AACvB,CAAC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InternalFrontendPlugin.esm.js","sources":["../../../../../frontend-internal/src/wiring/InternalFrontendPlugin.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 Extension,\n FeatureFlagConfig,\n
|
|
1
|
+
{"version":3,"file":"InternalFrontendPlugin.esm.js","sources":["../../../../../frontend-internal/src/wiring/InternalFrontendPlugin.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 Extension,\n FeatureFlagConfig,\n OverridableFrontendPlugin,\n} from '@backstage/frontend-plugin-api';\nimport { JsonObject } from '@backstage/types';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueFrontendPlugin = OpaqueType.create<{\n public: OverridableFrontendPlugin;\n versions: {\n readonly version: 'v1';\n readonly extensions: Extension<unknown>[];\n readonly featureFlags: FeatureFlagConfig[];\n readonly infoOptions?: {\n packageJson?: () => Promise<JsonObject>;\n manifest?: () => Promise<JsonObject>;\n };\n };\n}>({\n type: '@backstage/FrontendPlugin',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAwBO,MAAM,oBAAA,GAAuB,WAAW,MAAA,CAW5C;AAAA,EACD,IAAA,EAAM,2BAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InternalSwappableComponentRef.esm.js","sources":["../../../../../frontend-internal/src/wiring/InternalSwappableComponentRef.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 { SwappableComponentRef } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueSwappableComponentRef = OpaqueType.create<{\n public: SwappableComponentRef;\n versions: {\n readonly version: 'v1';\n readonly transformProps?: (props: object) => object;\n readonly defaultComponent: (props: object) => JSX.Element | null;\n };\n}>({\n versions: ['v1'],\n type: '@backstage/SwappableComponentRef',\n});\n"],"names":[],"mappings":";;AAmB2C,WAAW,
|
|
1
|
+
{"version":3,"file":"InternalSwappableComponentRef.esm.js","sources":["../../../../../frontend-internal/src/wiring/InternalSwappableComponentRef.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 { SwappableComponentRef } from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueSwappableComponentRef = OpaqueType.create<{\n public: SwappableComponentRef;\n versions: {\n readonly version: 'v1';\n readonly transformProps?: (props: object) => object;\n readonly defaultComponent: (props: object) => JSX.Element | null;\n };\n}>({\n versions: ['v1'],\n type: '@backstage/SwappableComponentRef',\n});\n"],"names":[],"mappings":";;AAmB2C,WAAW,MAAA,CAOnD;AAAA,EACD,QAAA,EAAU,CAAC,IAAI,CAAA;AAAA,EACf,IAAA,EAAM;AACR,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createExtensionDataContainer.esm.js","sources":["../../../../../frontend-internal/src/wiring/createExtensionDataContainer.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 ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionDataValue,\n} from '@backstage/frontend-plugin-api';\n\nexport function createExtensionDataContainer<UData extends ExtensionDataRef>(\n values: Iterable<\n UData extends ExtensionDataRef<infer IData, infer IId>\n ? ExtensionDataValue<IData, IId>\n : never\n >,\n contextName: string,\n declaredRefs?: ExtensionDataRef<any, any, any>[],\n): ExtensionDataContainer<UData> {\n if (typeof values !== 'object' || !values?.[Symbol.iterator]) {\n throw new Error(`${contextName} did not provide an iterable object`);\n }\n\n const container = new Map<string, ExtensionDataValue<any, any>>();\n const verifyRefs =\n declaredRefs && new Map(declaredRefs.map(ref => [ref.id, ref]));\n\n for (const output of values) {\n if (verifyRefs) {\n if (!verifyRefs.delete(output.id)) {\n throw new Error(\n `extension data '${output.id}' was provided but not declared`,\n );\n }\n }\n container.set(output.id, output);\n }\n\n const remainingRefs =\n verifyRefs &&\n Array.from(verifyRefs.values()).filter(ref => !ref.config.optional);\n if (remainingRefs && remainingRefs.length > 0) {\n throw new Error(\n `missing required extension data value(s) '${remainingRefs\n .map(ref => ref.id)\n .join(', ')}'`,\n );\n }\n\n return {\n get(ref) {\n return container.get(ref.id)?.value;\n },\n [Symbol.iterator]() {\n return container.values();\n },\n } as ExtensionDataContainer<UData>;\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"createExtensionDataContainer.esm.js","sources":["../../../../../frontend-internal/src/wiring/createExtensionDataContainer.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 ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionDataValue,\n} from '@backstage/frontend-plugin-api';\n\nexport function createExtensionDataContainer<UData extends ExtensionDataRef>(\n values: Iterable<\n UData extends ExtensionDataRef<infer IData, infer IId>\n ? ExtensionDataValue<IData, IId>\n : never\n >,\n contextName: string,\n declaredRefs?: ExtensionDataRef<any, any, any>[],\n): ExtensionDataContainer<UData> {\n if (typeof values !== 'object' || !values?.[Symbol.iterator]) {\n throw new Error(`${contextName} did not provide an iterable object`);\n }\n\n const container = new Map<string, ExtensionDataValue<any, any>>();\n const verifyRefs =\n declaredRefs && new Map(declaredRefs.map(ref => [ref.id, ref]));\n\n for (const output of values) {\n if (verifyRefs) {\n if (!verifyRefs.delete(output.id)) {\n throw new Error(\n `extension data '${output.id}' was provided but not declared`,\n );\n }\n }\n container.set(output.id, output);\n }\n\n const remainingRefs =\n verifyRefs &&\n Array.from(verifyRefs.values()).filter(ref => !ref.config.optional);\n if (remainingRefs && remainingRefs.length > 0) {\n throw new Error(\n `missing required extension data value(s) '${remainingRefs\n .map(ref => ref.id)\n .join(', ')}'`,\n );\n }\n\n return {\n get(ref) {\n return container.get(ref.id)?.value;\n },\n [Symbol.iterator]() {\n return container.values();\n },\n } as ExtensionDataContainer<UData>;\n}\n"],"names":[],"mappings":"AAsBO,SAAS,4BAAA,CACd,MAAA,EAKA,WAAA,EACA,YAAA,EAC+B;AAC/B,EAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,CAAC,MAAA,GAAS,MAAA,CAAO,QAAQ,CAAA,EAAG;AAC5D,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,EAAG,WAAW,CAAA,mCAAA,CAAqC,CAAA;AAAA,EACrE;AAEA,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAA0C;AAIhE,EAAA,KAAA,MAAW,UAAU,MAAA,EAAQ;AAQ3B,IAAA,SAAA,CAAU,GAAA,CAAI,MAAA,CAAO,EAAA,EAAI,MAAM,CAAA;AAAA,EACjC;AAaA,EAAA,OAAO;AAAA,IACL,IAAI,GAAA,EAAK;AACP,MAAA,OAAO,SAAA,CAAU,GAAA,CAAI,GAAA,CAAI,EAAE,CAAA,EAAG,KAAA;AAAA,IAChC,CAAA;AAAA,IACA,CAAC,MAAA,CAAO,QAAQ,CAAA,GAAI;AAClB,MAAA,OAAO,UAAU,MAAA,EAAO;AAAA,IAC1B;AAAA,GACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExternalRouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/ExternalRouteRef.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RouteRefImpl } from './RouteRef';\nimport { describeParentCallSite } from './describeParentCallSite';\nimport { AnyRouteRefParams } from './types';\n\n/**\n * Route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface ExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/ExternalRouteRef';\n readonly T: TParams;\n}\n\n/** @internal */\nexport interface InternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends ExternalRouteRef<TParams> {\n readonly version: 'v1';\n getParams(): string[];\n getDescription(): string;\n getDefaultTarget(): string | undefined;\n\n setId(id: string): void;\n}\n\n/** @internal */\nexport function toInternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: ExternalRouteRef<TParams>): InternalExternalRouteRef<TParams> {\n const r = resource as InternalExternalRouteRef<TParams>;\n if (r.$$type !== '@backstage/ExternalRouteRef') {\n throw new Error(`Invalid ExternalRouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isExternalRouteRef(opaque: {\n $$type: string;\n}): opaque is ExternalRouteRef {\n return opaque.$$type === '@backstage/ExternalRouteRef';\n}\n\n/** @internal */\nclass ExternalRouteRefImpl\n extends RouteRefImpl\n implements InternalExternalRouteRef\n{\n readonly $$type = '@backstage/ExternalRouteRef' as any;\n\n constructor(\n readonly params: string[] = [],\n readonly defaultTarget: string | undefined,\n creationSite: string,\n ) {\n super(params, creationSite);\n }\n\n getDefaultTarget() {\n return this.defaultTarget;\n }\n}\n\n/**\n * Creates a route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @param options - Description of the route reference to be created.\n * @public\n */\nexport function createExternalRouteRef<\n TParams extends { [param in TParamKeys]: string } | undefined = undefined,\n TParamKeys extends string = string,\n>(options?: {\n /**\n * The parameters that will be provided to the external route reference.\n */\n readonly params?: string extends TParamKeys\n ? (keyof TParams)[]\n : TParamKeys[];\n\n /**\n * The route (typically in another plugin) that this should map to by default.\n *\n * The string is expected to be on the standard `<plugin id>.<route id>` form,\n * for example `techdocs.docRoot`.\n */\n defaultTarget?: string;\n}): ExternalRouteRef<\n keyof TParams extends never\n ? undefined\n : string extends TParamKeys\n ? TParams\n : { [param in TParamKeys]: string }\n> {\n return new ExternalRouteRefImpl(\n options?.params as string[] | undefined,\n options?.defaultTarget,\n describeParentCallSite(),\n );\n}\n"],"names":[],"mappings":"AAiDO,SAAS,2BAEd,
|
|
1
|
+
{"version":3,"file":"ExternalRouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/ExternalRouteRef.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RouteRefImpl } from './RouteRef';\nimport { describeParentCallSite } from './describeParentCallSite';\nimport { AnyRouteRefParams } from './types';\n\n/**\n * Route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface ExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/ExternalRouteRef';\n readonly T: TParams;\n}\n\n/** @internal */\nexport interface InternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends ExternalRouteRef<TParams> {\n readonly version: 'v1';\n getParams(): string[];\n getDescription(): string;\n getDefaultTarget(): string | undefined;\n\n setId(id: string): void;\n}\n\n/** @internal */\nexport function toInternalExternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: ExternalRouteRef<TParams>): InternalExternalRouteRef<TParams> {\n const r = resource as InternalExternalRouteRef<TParams>;\n if (r.$$type !== '@backstage/ExternalRouteRef') {\n throw new Error(`Invalid ExternalRouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isExternalRouteRef(opaque: {\n $$type: string;\n}): opaque is ExternalRouteRef {\n return opaque.$$type === '@backstage/ExternalRouteRef';\n}\n\n/** @internal */\nclass ExternalRouteRefImpl\n extends RouteRefImpl\n implements InternalExternalRouteRef\n{\n readonly $$type = '@backstage/ExternalRouteRef' as any;\n\n constructor(\n readonly params: string[] = [],\n readonly defaultTarget: string | undefined,\n creationSite: string,\n ) {\n super(params, creationSite);\n }\n\n getDefaultTarget() {\n return this.defaultTarget;\n }\n}\n\n/**\n * Creates a route descriptor, to be later bound to a concrete route by the app. Used to implement cross-plugin route references.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @param options - Description of the route reference to be created.\n * @public\n */\nexport function createExternalRouteRef<\n TParams extends { [param in TParamKeys]: string } | undefined = undefined,\n TParamKeys extends string = string,\n>(options?: {\n /**\n * The parameters that will be provided to the external route reference.\n */\n readonly params?: string extends TParamKeys\n ? (keyof TParams)[]\n : TParamKeys[];\n\n /**\n * The route (typically in another plugin) that this should map to by default.\n *\n * The string is expected to be on the standard `<plugin id>.<route id>` form,\n * for example `techdocs.docRoot`.\n */\n defaultTarget?: string;\n}): ExternalRouteRef<\n keyof TParams extends never\n ? undefined\n : string extends TParamKeys\n ? TParams\n : { [param in TParamKeys]: string }\n> {\n return new ExternalRouteRefImpl(\n options?.params as string[] | undefined,\n options?.defaultTarget,\n describeParentCallSite(),\n );\n}\n"],"names":[],"mappings":"AAiDO,SAAS,2BAEd,QAAA,EAAwE;AACxE,EAAA,MAAM,CAAA,GAAI,QAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,6BAAA,EAA+B;AAC9C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oCAAA,EAAuC,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EACpE;AAEA,EAAA,OAAO,CAAA;AACT;AAGO,SAAS,mBAAmB,MAAA,EAEJ;AAC7B,EAAA,OAAO,OAAO,MAAA,KAAW,6BAAA;AAC3B;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/RouteRef.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 { describeParentCallSite } from './describeParentCallSite';\nimport { AnyRouteRefParams } from './types';\n\n/**\n * Absolute route reference.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface RouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/RouteRef';\n readonly T: TParams;\n}\n\n/** @internal */\nexport interface InternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends RouteRef<TParams> {\n readonly version: 'v1';\n getParams(): string[];\n getDescription(): string;\n\n alias: string | undefined;\n\n setId(id: string): void;\n}\n\n/** @internal */\nexport function toInternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: RouteRef<TParams>): InternalRouteRef<TParams> {\n const r = resource as InternalRouteRef<TParams>;\n if (r.$$type !== '@backstage/RouteRef') {\n throw new Error(`Invalid RouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isRouteRef(opaque: { $$type: string }): opaque is RouteRef {\n return opaque.$$type === '@backstage/RouteRef';\n}\n\n/** @internal */\nexport class RouteRefImpl implements InternalRouteRef {\n readonly $$type = '@backstage/RouteRef';\n readonly version = 'v1';\n declare readonly T: never;\n\n #id?: string;\n readonly #params: string[];\n readonly #creationSite: string;\n readonly #alias?: string;\n\n constructor(\n readonly params: string[] = [],\n creationSite: string,\n alias?: string,\n ) {\n this.#params = params;\n this.#creationSite = creationSite;\n this.#alias = alias;\n }\n\n getParams(): string[] {\n return this.#params;\n }\n\n get alias(): string | undefined {\n return this.#alias;\n }\n\n getDescription(): string {\n if (this.#id) {\n return this.#id;\n }\n return `created at '${this.#creationSite}'`;\n }\n\n get #name() {\n return this.$$type.slice('@backstage/'.length);\n }\n\n setId(id: string): void {\n if (!id) {\n throw new Error(`${this.#name} id must be a non-empty string`);\n }\n if (this.#id && this.#id !== id) {\n throw new Error(\n `${this.#name} was referenced twice as both '${this.#id}' and '${id}'`,\n );\n }\n this.#id = id;\n }\n\n toString(): string {\n return `${this.#name}{${this.getDescription()}}`;\n }\n}\n\n/**\n * Create a {@link RouteRef} from a route descriptor.\n *\n * @param config - Description of the route reference to be created.\n * @public\n */\nexport function createRouteRef<\n // Params is the type that we care about and the one to be embedded in the route ref.\n // For example, given the params ['name', 'kind'], Params will be {name: string, kind: string}\n TParams extends { [param in TParamKeys]: string } | undefined = undefined,\n TParamKeys extends string = string,\n>(config?: {\n /** A list of parameter names that the path that this route ref is bound to must contain */\n readonly params?: string extends TParamKeys\n ? (keyof TParams)[]\n : TParamKeys[];\n\n aliasFor?: string;\n}): RouteRef<\n keyof TParams extends never\n ? undefined\n : string extends TParamKeys\n ? TParams\n : { [param in TParamKeys]: string }\n> {\n return new RouteRefImpl(\n config?.params as string[] | undefined,\n describeParentCallSite(),\n config?.aliasFor,\n ) as RouteRef<any>;\n}\n"],"names":[],"mappings":"AAiDO,SAAS,mBAEd,
|
|
1
|
+
{"version":3,"file":"RouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/RouteRef.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 { describeParentCallSite } from './describeParentCallSite';\nimport { AnyRouteRefParams } from './types';\n\n/**\n * Absolute route reference.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface RouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/RouteRef';\n readonly T: TParams;\n}\n\n/** @internal */\nexport interface InternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends RouteRef<TParams> {\n readonly version: 'v1';\n getParams(): string[];\n getDescription(): string;\n\n alias: string | undefined;\n\n setId(id: string): void;\n}\n\n/** @internal */\nexport function toInternalRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: RouteRef<TParams>): InternalRouteRef<TParams> {\n const r = resource as InternalRouteRef<TParams>;\n if (r.$$type !== '@backstage/RouteRef') {\n throw new Error(`Invalid RouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isRouteRef(opaque: { $$type: string }): opaque is RouteRef {\n return opaque.$$type === '@backstage/RouteRef';\n}\n\n/** @internal */\nexport class RouteRefImpl implements InternalRouteRef {\n readonly $$type = '@backstage/RouteRef';\n readonly version = 'v1';\n declare readonly T: never;\n\n #id?: string;\n readonly #params: string[];\n readonly #creationSite: string;\n readonly #alias?: string;\n\n constructor(\n readonly params: string[] = [],\n creationSite: string,\n alias?: string,\n ) {\n this.#params = params;\n this.#creationSite = creationSite;\n this.#alias = alias;\n }\n\n getParams(): string[] {\n return this.#params;\n }\n\n get alias(): string | undefined {\n return this.#alias;\n }\n\n getDescription(): string {\n if (this.#id) {\n return this.#id;\n }\n return `created at '${this.#creationSite}'`;\n }\n\n get #name() {\n return this.$$type.slice('@backstage/'.length);\n }\n\n setId(id: string): void {\n if (!id) {\n throw new Error(`${this.#name} id must be a non-empty string`);\n }\n if (this.#id && this.#id !== id) {\n throw new Error(\n `${this.#name} was referenced twice as both '${this.#id}' and '${id}'`,\n );\n }\n this.#id = id;\n }\n\n toString(): string {\n return `${this.#name}{${this.getDescription()}}`;\n }\n}\n\n/**\n * Create a {@link RouteRef} from a route descriptor.\n *\n * @param config - Description of the route reference to be created.\n * @public\n */\nexport function createRouteRef<\n // Params is the type that we care about and the one to be embedded in the route ref.\n // For example, given the params ['name', 'kind'], Params will be {name: string, kind: string}\n TParams extends { [param in TParamKeys]: string } | undefined = undefined,\n TParamKeys extends string = string,\n>(config?: {\n /** A list of parameter names that the path that this route ref is bound to must contain */\n readonly params?: string extends TParamKeys\n ? (keyof TParams)[]\n : TParamKeys[];\n\n aliasFor?: string;\n}): RouteRef<\n keyof TParams extends never\n ? undefined\n : string extends TParamKeys\n ? TParams\n : { [param in TParamKeys]: string }\n> {\n return new RouteRefImpl(\n config?.params as string[] | undefined,\n describeParentCallSite(),\n config?.aliasFor,\n ) as RouteRef<any>;\n}\n"],"names":[],"mappings":"AAiDO,SAAS,mBAEd,QAAA,EAAwD;AACxD,EAAA,MAAM,CAAA,GAAI,QAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,qBAAA,EAAuB;AACtC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+B,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EAC5D;AAEA,EAAA,OAAO,CAAA;AACT;AAGO,SAAS,WAAW,MAAA,EAAgD;AACzE,EAAA,OAAO,OAAO,MAAA,KAAW,qBAAA;AAC3B;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubRouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/SubRouteRef.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 { RouteRef, toInternalRouteRef } from './RouteRef';\nimport { AnyRouteRefParams } from './types';\n\n// Should match the pattern in react-router\nconst PARAM_PATTERN = /^\\w+$/;\n\n/**\n * Descriptor of a route relative to an absolute {@link RouteRef}.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface SubRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/SubRouteRef';\n\n readonly T: TParams;\n\n readonly path: string;\n}\n\n/** @internal */\nexport interface InternalSubRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends SubRouteRef<TParams> {\n readonly version: 'v1';\n\n getParams(): string[];\n getParent(): RouteRef;\n getDescription(): string;\n}\n\n/** @internal */\nexport function toInternalSubRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: SubRouteRef<TParams>): InternalSubRouteRef<TParams> {\n const r = resource as InternalSubRouteRef<TParams>;\n if (r.$$type !== '@backstage/SubRouteRef') {\n throw new Error(`Invalid SubRouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isSubRouteRef(opaque: {\n $$type: string;\n}): opaque is SubRouteRef {\n return opaque.$$type === '@backstage/SubRouteRef';\n}\n\n/** @internal */\nexport class SubRouteRefImpl<TParams extends AnyRouteRefParams>\n implements SubRouteRef<TParams>\n{\n readonly $$type = '@backstage/SubRouteRef';\n readonly version = 'v1';\n declare readonly T: never;\n\n #params: string[];\n #parent: RouteRef;\n\n constructor(readonly path: string, params: string[], parent: RouteRef) {\n this.#params = params;\n this.#parent = parent;\n }\n\n getParams(): string[] {\n return this.#params;\n }\n\n getParent(): RouteRef {\n return this.#parent;\n }\n\n getDescription(): string {\n const parent = toInternalRouteRef(this.#parent);\n return `at ${this.path} with parent ${parent.getDescription()}`;\n }\n\n toString(): string {\n return `SubRouteRef{${this.getDescription()}}`;\n }\n}\n\n/**\n * Used in {@link PathParams} type declaration.\n * @ignore\n */\ntype ParamPart<S extends string> = S extends `:${infer Param}` ? Param : never;\n\n/**\n * Used in {@link PathParams} type declaration.\n * @ignore\n */\ntype ParamNames<S extends string> = S extends `${infer Part}/${infer Rest}`\n ? ParamPart<Part> | ParamNames<Rest>\n : ParamPart<S>;\n/**\n * This utility type helps us infer a Param object type from a string path\n * For example, `/foo/:bar/:baz` inferred to `{ bar: string, baz: string }`\n * @ignore\n */\ntype PathParams<S extends string> = { [name in ParamNames<S>]: string };\n\n/**\n * Merges a param object type with an optional params type into a params object.\n * @ignore\n */\ntype MergeParams<\n P1 extends { [param in string]: string },\n P2 extends AnyRouteRefParams,\n> = (P1[keyof P1] extends never ? {} : P1) & (P2 extends undefined ? {} : P2);\n\n/**\n * Convert empty params to undefined.\n * @ignore\n */\ntype TrimEmptyParams<Params extends { [param in string]: string }> =\n keyof Params extends never ? undefined : Params;\n\n/**\n * Creates a SubRouteRef type given the desired parameters and parent route parameters.\n * The parameters types are merged together while ensuring that there is no overlap between the two.\n *\n * @ignore\n */\ntype MakeSubRouteRef<\n Params extends { [param in string]: string },\n ParentParams extends AnyRouteRefParams,\n> = keyof Params & keyof ParentParams extends never\n ? SubRouteRef<TrimEmptyParams<MergeParams<Params, ParentParams>>>\n : never;\n\n/**\n * Create a {@link SubRouteRef} from a route descriptor.\n *\n * @param config - Description of the route reference to be created.\n * @public\n */\nexport function createSubRouteRef<\n Path extends string,\n ParentParams extends AnyRouteRefParams = never,\n>(config: {\n path: Path;\n parent: RouteRef<ParentParams>;\n}): MakeSubRouteRef<PathParams<Path>, ParentParams> {\n const { path, parent } = config;\n type Params = PathParams<Path>;\n\n const internalParent = toInternalRouteRef(parent);\n const parentParams = internalParent.getParams();\n\n // Collect runtime parameters from the path, e.g. ['bar', 'baz'] from '/foo/:bar/:baz'\n const pathParams = path\n .split('/')\n .filter(p => p.startsWith(':'))\n .map(p => p.substring(1));\n const params = [...parentParams, ...pathParams];\n\n if (parentParams.some(p => pathParams.includes(p as string))) {\n throw new Error(\n 'SubRouteRef may not have params that overlap with its parent',\n );\n }\n if (!path.startsWith('/')) {\n throw new Error(`SubRouteRef path must start with '/', got '${path}'`);\n }\n if (path.endsWith('/')) {\n throw new Error(`SubRouteRef path must not end with '/', got '${path}'`);\n }\n for (const param of pathParams) {\n if (!PARAM_PATTERN.test(param)) {\n throw new Error(`SubRouteRef path has invalid param, got '${param}'`);\n }\n }\n\n // We ensure that the type of the return type is sane here\n const subRouteRef = new SubRouteRefImpl(\n path,\n params as string[],\n parent,\n ) as SubRouteRef<TrimEmptyParams<MergeParams<Params, ParentParams>>>;\n\n // But skip type checking of the return value itself, because the conditional\n // type checking of the parent parameter overlap is tricky to express.\n return subRouteRef as any;\n}\n"],"names":[],"mappings":"AAqDO,SAAS,sBAEd,
|
|
1
|
+
{"version":3,"file":"SubRouteRef.esm.js","sources":["../../../../../frontend-plugin-api/src/routing/SubRouteRef.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 { RouteRef, toInternalRouteRef } from './RouteRef';\nimport { AnyRouteRefParams } from './types';\n\n// Should match the pattern in react-router\nconst PARAM_PATTERN = /^\\w+$/;\n\n/**\n * Descriptor of a route relative to an absolute {@link RouteRef}.\n *\n * @remarks\n *\n * See {@link https://backstage.io/docs/plugins/composability#routing-system}.\n *\n * @public\n */\nexport interface SubRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> {\n readonly $$type: '@backstage/SubRouteRef';\n\n readonly T: TParams;\n\n readonly path: string;\n}\n\n/** @internal */\nexport interface InternalSubRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n> extends SubRouteRef<TParams> {\n readonly version: 'v1';\n\n getParams(): string[];\n getParent(): RouteRef;\n getDescription(): string;\n}\n\n/** @internal */\nexport function toInternalSubRouteRef<\n TParams extends AnyRouteRefParams = AnyRouteRefParams,\n>(resource: SubRouteRef<TParams>): InternalSubRouteRef<TParams> {\n const r = resource as InternalSubRouteRef<TParams>;\n if (r.$$type !== '@backstage/SubRouteRef') {\n throw new Error(`Invalid SubRouteRef, bad type '${r.$$type}'`);\n }\n\n return r;\n}\n\n/** @internal */\nexport function isSubRouteRef(opaque: {\n $$type: string;\n}): opaque is SubRouteRef {\n return opaque.$$type === '@backstage/SubRouteRef';\n}\n\n/** @internal */\nexport class SubRouteRefImpl<TParams extends AnyRouteRefParams>\n implements SubRouteRef<TParams>\n{\n readonly $$type = '@backstage/SubRouteRef';\n readonly version = 'v1';\n declare readonly T: never;\n\n #params: string[];\n #parent: RouteRef;\n\n constructor(readonly path: string, params: string[], parent: RouteRef) {\n this.#params = params;\n this.#parent = parent;\n }\n\n getParams(): string[] {\n return this.#params;\n }\n\n getParent(): RouteRef {\n return this.#parent;\n }\n\n getDescription(): string {\n const parent = toInternalRouteRef(this.#parent);\n return `at ${this.path} with parent ${parent.getDescription()}`;\n }\n\n toString(): string {\n return `SubRouteRef{${this.getDescription()}}`;\n }\n}\n\n/**\n * Used in {@link PathParams} type declaration.\n * @ignore\n */\ntype ParamPart<S extends string> = S extends `:${infer Param}` ? Param : never;\n\n/**\n * Used in {@link PathParams} type declaration.\n * @ignore\n */\ntype ParamNames<S extends string> = S extends `${infer Part}/${infer Rest}`\n ? ParamPart<Part> | ParamNames<Rest>\n : ParamPart<S>;\n/**\n * This utility type helps us infer a Param object type from a string path\n * For example, `/foo/:bar/:baz` inferred to `{ bar: string, baz: string }`\n * @ignore\n */\ntype PathParams<S extends string> = { [name in ParamNames<S>]: string };\n\n/**\n * Merges a param object type with an optional params type into a params object.\n * @ignore\n */\ntype MergeParams<\n P1 extends { [param in string]: string },\n P2 extends AnyRouteRefParams,\n> = (P1[keyof P1] extends never ? {} : P1) & (P2 extends undefined ? {} : P2);\n\n/**\n * Convert empty params to undefined.\n * @ignore\n */\ntype TrimEmptyParams<Params extends { [param in string]: string }> =\n keyof Params extends never ? undefined : Params;\n\n/**\n * Creates a SubRouteRef type given the desired parameters and parent route parameters.\n * The parameters types are merged together while ensuring that there is no overlap between the two.\n *\n * @ignore\n */\ntype MakeSubRouteRef<\n Params extends { [param in string]: string },\n ParentParams extends AnyRouteRefParams,\n> = keyof Params & keyof ParentParams extends never\n ? SubRouteRef<TrimEmptyParams<MergeParams<Params, ParentParams>>>\n : never;\n\n/**\n * Create a {@link SubRouteRef} from a route descriptor.\n *\n * @param config - Description of the route reference to be created.\n * @public\n */\nexport function createSubRouteRef<\n Path extends string,\n ParentParams extends AnyRouteRefParams = never,\n>(config: {\n path: Path;\n parent: RouteRef<ParentParams>;\n}): MakeSubRouteRef<PathParams<Path>, ParentParams> {\n const { path, parent } = config;\n type Params = PathParams<Path>;\n\n const internalParent = toInternalRouteRef(parent);\n const parentParams = internalParent.getParams();\n\n // Collect runtime parameters from the path, e.g. ['bar', 'baz'] from '/foo/:bar/:baz'\n const pathParams = path\n .split('/')\n .filter(p => p.startsWith(':'))\n .map(p => p.substring(1));\n const params = [...parentParams, ...pathParams];\n\n if (parentParams.some(p => pathParams.includes(p as string))) {\n throw new Error(\n 'SubRouteRef may not have params that overlap with its parent',\n );\n }\n if (!path.startsWith('/')) {\n throw new Error(`SubRouteRef path must start with '/', got '${path}'`);\n }\n if (path.endsWith('/')) {\n throw new Error(`SubRouteRef path must not end with '/', got '${path}'`);\n }\n for (const param of pathParams) {\n if (!PARAM_PATTERN.test(param)) {\n throw new Error(`SubRouteRef path has invalid param, got '${param}'`);\n }\n }\n\n // We ensure that the type of the return type is sane here\n const subRouteRef = new SubRouteRefImpl(\n path,\n params as string[],\n parent,\n ) as SubRouteRef<TrimEmptyParams<MergeParams<Params, ParentParams>>>;\n\n // But skip type checking of the return value itself, because the conditional\n // type checking of the parent parameter overlap is tricky to express.\n return subRouteRef as any;\n}\n"],"names":[],"mappings":"AAqDO,SAAS,sBAEd,QAAA,EAA8D;AAC9D,EAAA,MAAM,CAAA,GAAI,QAAA;AACV,EAAA,IAAI,CAAA,CAAE,WAAW,wBAAA,EAA0B;AACzC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,+BAAA,EAAkC,CAAA,CAAE,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EAC/D;AAEA,EAAA,OAAO,CAAA;AACT;AAGO,SAAS,cAAc,MAAA,EAEJ;AACxB,EAAA,OAAO,OAAO,MAAA,KAAW,wBAAA;AAC3B;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createFrontendModule.esm.js","sources":["../../../../../frontend-plugin-api/src/wiring/createFrontendModule.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 { OpaqueExtensionDefinition } from '@internal/frontend';\nimport { ExtensionDefinition } from './createExtension';\nimport {\n Extension,\n resolveExtensionDefinition,\n} from './resolveExtensionDefinition';\nimport { FeatureFlagConfig } from './types';\n\n/** @public */\nexport interface CreateFrontendModuleOptions<\n TPluginId extends string,\n TExtensions extends readonly ExtensionDefinition[],\n> {\n pluginId: TPluginId;\n extensions?: TExtensions;\n featureFlags?: FeatureFlagConfig[];\n}\n\n/** @public */\nexport interface FrontendModule {\n readonly $$type: '@backstage/FrontendModule';\n readonly pluginId: string;\n}\n\n/** @internal */\nexport interface InternalFrontendModule extends FrontendModule {\n readonly version: 'v1';\n readonly extensions: Extension<unknown>[];\n readonly featureFlags: FeatureFlagConfig[];\n}\n\n/**\n * Creates a new module that can be installed in a Backstage app.\n *\n * @remarks\n *\n * Modules are used to add or override extensions for an existing plugin. If a\n * module provides an extension with the same ID as one provided by the plugin,\n * the extension provided by the module will always take precedence.\n *\n * Every module is created for a specific plugin by providing the\n * unique ID of the plugin that the module should be installed for. If that\n * plugin is not present in the app, the module will be ignored and have no\n * effect.\n *\n * For more information on how modules work, see the\n * {@link https://backstage.io/docs/frontend-system/architecture/extension-overrides#creating-a-frontend-module | documentation for modules}\n * in the frontend system documentation.\n *\n * It is recommended to name the module variable of the form `<pluginId>Module<ModuleName>`.\n *\n * @example\n *\n * ```tsx\n * import { createFrontendModule } from '@backstage/frontend-plugin-api';\n *\n * export const exampleModuleCustomPage = createFrontendModule({\n * pluginId: 'example',\n * extensions: [\n * // Overrides the default page for the 'example' plugin\n * PageBlueprint.make({\n * path: '/example',\n * loader: () => import('./CustomPage').then(m => <m.CustomPage />),\n * }),\n * ],\n * });\n * ```\n *\n * @public\n */\nexport function createFrontendModule<\n TId extends string,\n TExtensions extends readonly ExtensionDefinition[]
|
|
1
|
+
{"version":3,"file":"createFrontendModule.esm.js","sources":["../../../../../frontend-plugin-api/src/wiring/createFrontendModule.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 { OpaqueExtensionDefinition } from '@internal/frontend';\nimport { ExtensionDefinition } from './createExtension';\nimport {\n Extension,\n resolveExtensionDefinition,\n} from './resolveExtensionDefinition';\nimport { FeatureFlagConfig } from './types';\n\n/** @public */\nexport interface CreateFrontendModuleOptions<\n TPluginId extends string,\n TExtensions extends readonly ExtensionDefinition[],\n> {\n pluginId: TPluginId;\n extensions?: TExtensions;\n featureFlags?: FeatureFlagConfig[];\n}\n\n/** @public */\nexport interface FrontendModule {\n readonly $$type: '@backstage/FrontendModule';\n readonly pluginId: string;\n}\n\n/** @internal */\nexport interface InternalFrontendModule extends FrontendModule {\n readonly version: 'v1';\n readonly extensions: Extension<unknown>[];\n readonly featureFlags: FeatureFlagConfig[];\n}\n\n/**\n * Creates a new module that can be installed in a Backstage app.\n *\n * @remarks\n *\n * Modules are used to add or override extensions for an existing plugin. If a\n * module provides an extension with the same ID as one provided by the plugin,\n * the extension provided by the module will always take precedence.\n *\n * Every module is created for a specific plugin by providing the\n * unique ID of the plugin that the module should be installed for. If that\n * plugin is not present in the app, the module will be ignored and have no\n * effect.\n *\n * For more information on how modules work, see the\n * {@link https://backstage.io/docs/frontend-system/architecture/extension-overrides#creating-a-frontend-module | documentation for modules}\n * in the frontend system documentation.\n *\n * It is recommended to name the module variable of the form `<pluginId>Module<ModuleName>`.\n *\n * @example\n *\n * ```tsx\n * import { createFrontendModule } from '@backstage/frontend-plugin-api';\n *\n * export const exampleModuleCustomPage = createFrontendModule({\n * pluginId: 'example',\n * extensions: [\n * // Overrides the default page for the 'example' plugin\n * PageBlueprint.make({\n * path: '/example',\n * loader: () => import('./CustomPage').then(m => <m.CustomPage />),\n * }),\n * ],\n * });\n * ```\n *\n * @public\n */\nexport function createFrontendModule<\n TId extends string,\n TExtensions extends readonly ExtensionDefinition[],\n>(options: CreateFrontendModuleOptions<TId, TExtensions>): FrontendModule {\n const { pluginId } = options;\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 options.extensions ?? []) {\n const internal = OpaqueExtensionDefinition.toInternal(def);\n const ext = resolveExtensionDefinition(def, { namespace: pluginId });\n extensions.push(ext);\n extensionDefinitionsById.set(ext.id, {\n ...internal,\n namespace: pluginId,\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 // TODO(Rugvip): This could provide some more information about the kind + name of the extensions\n throw new Error(\n `Plugin '${pluginId}' provided duplicate extensions: ${duplicates.join(\n ', ',\n )}`,\n );\n }\n\n return {\n $$type: '@backstage/FrontendModule',\n version: 'v1',\n pluginId,\n featureFlags: options.featureFlags ?? [],\n extensions,\n toString() {\n return `Module{pluginId=${pluginId}}`;\n },\n } as InternalFrontendModule;\n}\n\n/** @internal */\nexport function isInternalFrontendModule(opaque: {\n $$type: string;\n}): opaque is InternalFrontendModule {\n if (opaque.$$type === '@backstage/FrontendModule') {\n // Make sure we throw if invalid\n toInternalFrontendModule(opaque as FrontendModule);\n return true;\n }\n return false;\n}\n\n/** @internal */\nexport function toInternalFrontendModule(\n plugin: FrontendModule,\n): InternalFrontendModule {\n const internal = plugin as InternalFrontendModule;\n if (internal.$$type !== '@backstage/FrontendModule') {\n throw new Error(`Invalid plugin instance, bad type '${internal.$$type}'`);\n }\n if (internal.version !== 'v1') {\n throw new Error(\n `Invalid plugin instance, bad version '${internal.version}'`,\n );\n }\n return internal;\n}\n"],"names":[],"mappings":"AAwIO,SAAS,yBAAyB,MAAA,EAEJ;AACnC,EAAA,IAAI,MAAA,CAAO,WAAW,2BAAA,EAA6B;AAEjD,IAAA,wBAAA,CAAyB,MAAwB,CAAA;AACjD,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,yBACd,MAAA,EACwB;AACxB,EAAA,MAAM,QAAA,GAAW,MAAA;AACjB,EAAA,IAAI,QAAA,CAAS,WAAW,2BAAA,EAA6B;AACnD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,QAAA,CAAS,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EAC1E;AACA,EAAA,IAAI,QAAA,CAAS,YAAY,IAAA,EAAM;AAC7B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,sCAAA,EAAyC,SAAS,OAAO,CAAA,CAAA;AAAA,KAC3D;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveExtensionDefinition.esm.js","sources":["../../../../../frontend-plugin-api/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 ExtensionAttachToSpec,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n ResolvedExtensionInputs,\n} from './createExtension';\nimport { PortableSchema } from '../schema';\nimport { ExtensionInput } from './createExtensionInput';\nimport { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\n\n/** @public */\nexport interface Extension<TConfig, TConfigInput = TConfig> {\n $$type: '@backstage/Extension';\n readonly id: string;\n readonly attachTo: ExtensionAttachToSpec;\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 inputs: {\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\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 ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\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\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 const {\n name,\n kind,\n namespace: _skip1,\n override: _skip2,\n ...rest\n } = internalDefinition;\n\n const namespace = internalDefinition.namespace ?? context?.namespace;\n\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 const id = kind ? `${kind}:${namePart}` : namePart;\n\n return {\n ...rest,\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":";;;;AA4FO,SAAS,oBACd,
|
|
1
|
+
{"version":3,"file":"resolveExtensionDefinition.esm.js","sources":["../../../../../frontend-plugin-api/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 ExtensionAttachToSpec,\n ExtensionDefinition,\n ExtensionDefinitionParameters,\n ResolvedExtensionInputs,\n} from './createExtension';\nimport { PortableSchema } from '../schema';\nimport { ExtensionInput } from './createExtensionInput';\nimport { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef';\nimport { OpaqueExtensionDefinition } from '@internal/frontend';\n\n/** @public */\nexport interface Extension<TConfig, TConfigInput = TConfig> {\n $$type: '@backstage/Extension';\n readonly id: string;\n readonly attachTo: ExtensionAttachToSpec;\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 inputs: {\n [inputName in string]: ExtensionInput<\n ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\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 ExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\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\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 const {\n name,\n kind,\n namespace: _skip1,\n override: _skip2,\n ...rest\n } = internalDefinition;\n\n const namespace = internalDefinition.namespace ?? context?.namespace;\n\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 const id = kind ? `${kind}:${namePart}` : namePart;\n\n return {\n ...rest,\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":";;;;AA4FO,SAAS,oBACd,SAAA,EAC0C;AAC1C,EAAA,MAAM,QAAA,GAAW,SAAA;AACjB,EAAA,IAAI,QAAA,CAAS,WAAW,sBAAA,EAAwB;AAC9C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,sCAAA,EAAyC,SAAS,MAAM,CAAA,CAAA;AAAA,KAC1D;AAAA,EACF;AACA,EAAA,MAAM,UAAU,QAAA,CAAS,OAAA;AACzB,EAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,OAAA,KAAY,IAAA,EAAM;AACxC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,yCAAA,EAA4C,OAAO,CAAA,CAAA,CAAG,CAAA;AAAA,EACxE;AACA,EAAA,OAAO,QAAA;AACT;AAuBO,SAAS,0BAAA,CAGd,YACA,OAAA,EAC0C;AAC1C,EAAA,MAAM,kBAAA,GAAqB,yBAAA,CAA0B,UAAA,CAAW,UAAU,CAAA;AAC1E,EAAA,MAAM;AAAA,IACJ,IAAA;AAAA,IACA,IAAA;AAAA,IACA,SAAA,EAAW,MAAA;AAAA,IACX,QAAA,EAAU,MAAA;AAAA,IACV,GAAG;AAAA,GACL,GAAI,kBAAA;AAEJ,EAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,SAAA,IAAa,OAAA,EAAS,SAAA;AAE3D,EAAA,MAAM,QAAA,GACJ,QAAQ,SAAA,GAAY,CAAA,EAAG,SAAS,CAAA,CAAA,EAAI,IAAI,KAAK,SAAA,IAAa,IAAA;AAC5D,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,oGAAA,EAAuG,IAAI,CAAA,WAAA,EAAc,SAAS,SAAS,IAAI,CAAA;AAAA,KACjJ;AAAA,EACF;AAEA,EAAA,MAAM,KAAK,IAAA,GAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAA,GAAK,QAAA;AAE1C,EAAA,OAAO;AAAA,IACL,GAAG,IAAA;AAAA,IACH,MAAA,EAAQ,sBAAA;AAAA,IACR,SAAS,kBAAA,CAAmB,OAAA;AAAA,IAC5B,EAAA;AAAA,IACA,QAAA,GAAW;AACT,MAAA,OAAO,gBAAgB,EAAE,CAAA,CAAA,CAAA;AAAA,IAC3B;AAAA,GACF;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OpaqueType.esm.js","sources":["../../../../opaque-internal/src/OpaqueType.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\n// TODO(Rugvip): This lives here temporarily, but should be moved to a more\n// central location. It's useful for backend packages too so we'll need to have\n// it in a common package, but it might also be that we want to make it\n// available publicly too in which case it would make sense to have this be part\n// of @backstage/version-bridge. The problem with exporting it from there is\n// that it would need to be very stable at that point, so it might be a bit\n// early to put it there already.\n\n/**\n * A helper for working with opaque types.\n */\nexport class OpaqueType<\n T extends {\n public: { $$type: string };\n versions: { version: string | undefined };\n },\n> {\n /**\n * Creates a new opaque type.\n *\n * @param options.type The type identifier of the opaque type\n * @param options.versions The available versions of the opaque type\n * @returns A new opaque type helper\n */\n static create<\n T extends {\n public: { $$type: string };\n versions: { version: string | undefined };\n },\n >(options: {\n type: T['public']['$$type'];\n versions: Array<T['versions']['version']>;\n }) {\n return new OpaqueType<T>(options.type, new Set(options.versions));\n }\n\n #type: string;\n #versions: Set<string | undefined>;\n\n private constructor(type: string, versions: Set<string | undefined>) {\n this.#type = type;\n this.#versions = versions;\n }\n\n /**\n * The internal version of the opaque type, used like this: `typeof MyOpaqueType.TPublic`\n *\n * @remarks\n *\n * This property is only useful for type checking, its runtime value is `undefined`.\n */\n TPublic: T['public'] = undefined as any;\n\n /**\n * The internal version of the opaque type, used like this: `typeof MyOpaqueType.TInternal`\n *\n * @remarks\n *\n * This property is only useful for type checking, its runtime value is `undefined`.\n */\n TInternal: T['public'] & T['versions'] = undefined as any;\n\n /**\n * @param value Input value expected to be an instance of this opaque type\n * @returns True if the value matches this opaque type\n */\n isType = (value: unknown): value is T['public'] => {\n return this.#isThisInternalType(value);\n };\n\n /**\n * @param value Input value expected to be an instance of this opaque type\n * @throws If the value is not an instance of this opaque type or is of an unsupported version\n * @returns The internal version of the opaque type\n */\n toInternal = (value: unknown): T['public'] & T['versions'] => {\n if (!this.#isThisInternalType(value)) {\n throw new TypeError(\n `Invalid opaque type, expected '${\n this.#type\n }', but got '${this.#stringifyUnknown(value)}'`,\n );\n }\n\n if (!this.#versions.has(value.version)) {\n const versions = Array.from(this.#versions).map(this.#stringifyVersion);\n if (versions.length > 1) {\n versions[versions.length - 1] = `or ${versions[versions.length - 1]}`;\n }\n const expected =\n versions.length > 2 ? versions.join(', ') : versions.join(' ');\n throw new TypeError(\n `Invalid opaque type instance, got version ${this.#stringifyVersion(\n value.version,\n )}, expected ${expected}`,\n );\n }\n\n return value;\n };\n\n /**\n * Creates an instance of the opaque type, returning the public type.\n *\n * @param version The version of the instance to create\n * @param value The remaining public and internal properties of the instance\n * @returns An instance of the opaque type\n */\n createInstance<\n TVersion extends T['versions']['version'],\n TPublic extends T['public'],\n >(\n version: TVersion,\n props: Omit<T['public'], '$$type'> &\n (T['versions'] extends infer UVersion\n ? UVersion extends { version: TVersion }\n ? Omit<UVersion, 'version'>\n : never\n : never) &\n Object, // & Object to allow for object properties too, e.g. toString()\n ): TPublic {\n return Object.assign(props as object, {\n $$type: this.#type,\n ...(version && { version }),\n }) as unknown as TPublic;\n }\n\n #isThisInternalType(value: unknown): value is T['public'] & T['versions'] {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n return (value as T['public']).$$type === this.#type;\n }\n\n #stringifyUnknown(value: unknown) {\n if (typeof value !== 'object') {\n return `<${typeof value}>`;\n }\n if (value === null) {\n return '<null>';\n }\n if ('$$type' in value) {\n return String(value.$$type);\n }\n return String(value);\n }\n\n #stringifyVersion = (version: string | undefined) => {\n return version ? `'${version}'` : 'undefined';\n };\n}\n"],"names":[],"mappings":"AA2BO,MAAM,
|
|
1
|
+
{"version":3,"file":"OpaqueType.esm.js","sources":["../../../../opaque-internal/src/OpaqueType.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\n// TODO(Rugvip): This lives here temporarily, but should be moved to a more\n// central location. It's useful for backend packages too so we'll need to have\n// it in a common package, but it might also be that we want to make it\n// available publicly too in which case it would make sense to have this be part\n// of @backstage/version-bridge. The problem with exporting it from there is\n// that it would need to be very stable at that point, so it might be a bit\n// early to put it there already.\n\n/**\n * A helper for working with opaque types.\n */\nexport class OpaqueType<\n T extends {\n public: { $$type: string };\n versions: { version: string | undefined };\n },\n> {\n /**\n * Creates a new opaque type.\n *\n * @param options.type The type identifier of the opaque type\n * @param options.versions The available versions of the opaque type\n * @returns A new opaque type helper\n */\n static create<\n T extends {\n public: { $$type: string };\n versions: { version: string | undefined };\n },\n >(options: {\n type: T['public']['$$type'];\n versions: Array<T['versions']['version']>;\n }) {\n return new OpaqueType<T>(options.type, new Set(options.versions));\n }\n\n #type: string;\n #versions: Set<string | undefined>;\n\n private constructor(type: string, versions: Set<string | undefined>) {\n this.#type = type;\n this.#versions = versions;\n }\n\n /**\n * The internal version of the opaque type, used like this: `typeof MyOpaqueType.TPublic`\n *\n * @remarks\n *\n * This property is only useful for type checking, its runtime value is `undefined`.\n */\n TPublic: T['public'] = undefined as any;\n\n /**\n * The internal version of the opaque type, used like this: `typeof MyOpaqueType.TInternal`\n *\n * @remarks\n *\n * This property is only useful for type checking, its runtime value is `undefined`.\n */\n TInternal: T['public'] & T['versions'] = undefined as any;\n\n /**\n * @param value Input value expected to be an instance of this opaque type\n * @returns True if the value matches this opaque type\n */\n isType = (value: unknown): value is T['public'] => {\n return this.#isThisInternalType(value);\n };\n\n /**\n * @param value Input value expected to be an instance of this opaque type\n * @throws If the value is not an instance of this opaque type or is of an unsupported version\n * @returns The internal version of the opaque type\n */\n toInternal = (value: unknown): T['public'] & T['versions'] => {\n if (!this.#isThisInternalType(value)) {\n throw new TypeError(\n `Invalid opaque type, expected '${\n this.#type\n }', but got '${this.#stringifyUnknown(value)}'`,\n );\n }\n\n if (!this.#versions.has(value.version)) {\n const versions = Array.from(this.#versions).map(this.#stringifyVersion);\n if (versions.length > 1) {\n versions[versions.length - 1] = `or ${versions[versions.length - 1]}`;\n }\n const expected =\n versions.length > 2 ? versions.join(', ') : versions.join(' ');\n throw new TypeError(\n `Invalid opaque type instance, got version ${this.#stringifyVersion(\n value.version,\n )}, expected ${expected}`,\n );\n }\n\n return value;\n };\n\n /**\n * Creates an instance of the opaque type, returning the public type.\n *\n * @param version The version of the instance to create\n * @param value The remaining public and internal properties of the instance\n * @returns An instance of the opaque type\n */\n createInstance<\n TVersion extends T['versions']['version'],\n TPublic extends T['public'],\n >(\n version: TVersion,\n props: Omit<T['public'], '$$type'> &\n (T['versions'] extends infer UVersion\n ? UVersion extends { version: TVersion }\n ? Omit<UVersion, 'version'>\n : never\n : never) &\n Object, // & Object to allow for object properties too, e.g. toString()\n ): TPublic {\n return Object.assign(props as object, {\n $$type: this.#type,\n ...(version && { version }),\n }) as unknown as TPublic;\n }\n\n #isThisInternalType(value: unknown): value is T['public'] & T['versions'] {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n return (value as T['public']).$$type === this.#type;\n }\n\n #stringifyUnknown(value: unknown) {\n if (typeof value !== 'object') {\n return `<${typeof value}>`;\n }\n if (value === null) {\n return '<null>';\n }\n if ('$$type' in value) {\n return String(value.$$type);\n }\n return String(value);\n }\n\n #stringifyVersion = (version: string | undefined) => {\n return version ? `'${version}'` : 'undefined';\n };\n}\n"],"names":[],"mappings":"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"}
|