@backstage/frontend-app-api 0.10.1-next.1 → 0.10.1-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- 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.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-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/RouteResolver.esm.js.map +1 -1
- package/dist/routing/collectRouteIds.esm.js.map +1 -1
- 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/routing/toLegacyPlugin.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/createSpecializedApp.esm.js.map +1 -1
- package/package.json +12 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @backstage/frontend-app-api
|
|
2
2
|
|
|
3
|
+
## 0.10.1-next.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/config@1.2.0
|
|
9
|
+
- @backstage/core-app-api@1.15.1
|
|
10
|
+
- @backstage/core-plugin-api@1.10.0
|
|
11
|
+
- @backstage/errors@1.2.4
|
|
12
|
+
- @backstage/frontend-defaults@0.1.2-next.2
|
|
13
|
+
- @backstage/frontend-plugin-api@0.9.1-next.2
|
|
14
|
+
- @backstage/types@1.1.1
|
|
15
|
+
- @backstage/version-bridge@1.0.10
|
|
16
|
+
|
|
3
17
|
## 0.10.1-next.1
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -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,KAAe,EAAA;AAC9B,EAAA,OAAO,IAAI,KAAA;AAAA,IACT,+BAA+B,KAAK,CAAA,sBAAA
|
|
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,SAAY,GAAA,KAAA
|
|
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,kBAAmB,CAAA;AAAA,EACf,OAA4B,EAAC
|
|
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,kBAAmB,CAAA;AAAA,EACf,OAA4B,EAAC;AAAA,EAErC,GAAA,CAAoB,KAAgB,IAAY,EAAA;AAC9C,IAAA,IAAA,CAAK,KAAK,IAAK,CAAA,CAAC,GAAI,CAAA,EAAA,EAAI,IAAI,CAAC,CAAA;AAC7B,IAAO,OAAA,IAAA;AAAA;AACT,EAEA,KAAqB,GAAA;AAEnB,IAAA,OAAO,IAAI,WAAY,CAAA,IAAI,GAAI,CAAA,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA;AAE7C;AAOO,MAAM,WAAiC,CAAA;AAAA,EAwB5C,YAA6B,IAA4B,EAAA;AAA5B,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAA6B,EAvB1D,OAAO,OAAU,GAAA;AACf,IAAA,OAAO,IAAI,kBAAmB,EAAA;AAAA;AAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,KAAK,IAAiB,EAAA;AAC3B,IAAA,OAAO,IAAI,WAAY,CAAA,IAAI,GAAI,CAAA,IAAA,CAAK,IAAI,CAAC,CAAC,GAAK,EAAA,IAAI,MAAM,CAAC,GAAA,CAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA;AAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,IAAQ,CAAA,GAAA,EAAgB,IAAsB,EAAA;AACnD,IAAO,OAAA,IAAI,WAAY,iBAAA,IAAI,GAAI,CAAA,CAAC,CAAC,GAAA,CAAI,EAAI,EAAA,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA;AAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAA,CAAQ,KAAgB,IAAsB,EAAA;AAC5C,IAAA,OAAO,IAAI,WAAA,CAAY,IAAI,GAAA,CAAI,CAAC,GAAG,IAAA,CAAK,IAAM,EAAA,CAAC,GAAI,CAAA,EAAA,EAAI,IAAI,CAAC,CAAC,CAAC,CAAA;AAAA;AAChE,EAEA,IAAO,GAA+B,EAAA;AACpC,IAAA,OAAO,IAAK,CAAA,IAAA,CAAK,GAAI,CAAA,GAAA,CAAI,EAAE,CAAA;AAAA;AAE/B;;;;"}
|
|
@@ -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,eAAgB,CAAA;AAAA,EAClC,QAAU,EAAA,EAAE,EAAI,EAAA,SAAA,EAAW,OAAO,SAAU,EAAA;AAAA,EAC5C,MAAQ,EAAA;AAAA,IACN,GAAK,EAAA,oBAAA,CAAqB,CAAC,iBAAA,CAAkB,YAAY,CAAG,EAAA;AAAA,MAC1D,SAAW,EAAA
|
|
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,eAAgB,CAAA;AAAA,EAClC,QAAU,EAAA,EAAE,EAAI,EAAA,SAAA,EAAW,OAAO,SAAU,EAAA;AAAA,EAC5C,MAAQ,EAAA;AAAA,IACN,GAAK,EAAA,oBAAA,CAAqB,CAAC,iBAAA,CAAkB,YAAY,CAAG,EAAA;AAAA,MAC1D,SAAW,EAAA;AAAA,KACZ,CAAA;AAAA,IACD,MAAM,oBAAqB,CAAA,CAAC,YAAa,CAAA,QAAA,CAAS,OAAO,CAAG,EAAA;AAAA,MAC1D,UAAU,CAAC,EAAE,IAAI,KAAO,EAAA,KAAA,EAAO,QAAQ;AAAA,KACxC;AAAA,GACH;AAAA,EACA,MAAA,EAAQ,CAAC,iBAAA,CAAkB,YAAY,CAAA;AAAA,EACvC,OAAS,EAAA,CAAC,EAAE,MAAA,OAAa,MAAO,CAAA;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 AnyExtensionDataRef,\n ApiHolder,\n AppNode,\n ExtensionDataValue,\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: { id: string; input: string };\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]: AnyExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n };\n readonly output: {\n [name in string]: AnyExtensionDataRef;\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: { id: string; input: string };\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<any, any>;\n readonly inputs: {\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\n readonly output: Array<AnyExtensionDataRef>;\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: object;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\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":";;AA6Ba,MAAA,yBAAA,GAA4B,WAAW,MA6DjD,CAAA;AAAA,EACD,IAAM,EAAA,gCAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAA,EAAM,IAAI
|
|
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 AnyExtensionDataRef,\n ApiHolder,\n AppNode,\n ExtensionDataValue,\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: { id: string; input: string };\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]: AnyExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n };\n readonly output: {\n [name in string]: AnyExtensionDataRef;\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: { id: string; input: string };\n readonly disabled: boolean;\n readonly configSchema?: PortableSchema<any, any>;\n readonly inputs: {\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\n readonly output: Array<AnyExtensionDataRef>;\n factory(context: {\n node: AppNode;\n apis: ApiHolder;\n config: object;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\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":";;AA6Ba,MAAA,yBAAA,GAA4B,WAAW,MA6DjD,CAAA;AAAA,EACD,IAAM,EAAA,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 FrontendPlugin,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueFrontendPlugin = OpaqueType.create<{\n public: FrontendPlugin;\n versions: {\n readonly version: 'v1';\n readonly extensions: Extension<unknown>[];\n readonly featureFlags: FeatureFlagConfig[];\n };\n}>({\n type: '@backstage/FrontendPlugin',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAuBa,MAAA,oBAAA,GAAuB,WAAW,MAO5C,CAAA;AAAA,EACD,IAAM,EAAA,2BAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI
|
|
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 FrontendPlugin,\n} from '@backstage/frontend-plugin-api';\nimport { OpaqueType } from '@internal/opaque';\n\nexport const OpaqueFrontendPlugin = OpaqueType.create<{\n public: FrontendPlugin;\n versions: {\n readonly version: 'v1';\n readonly extensions: Extension<unknown>[];\n readonly featureFlags: FeatureFlagConfig[];\n };\n}>({\n type: '@backstage/FrontendPlugin',\n versions: ['v1'],\n});\n"],"names":[],"mappings":";;AAuBa,MAAA,oBAAA,GAAuB,WAAW,MAO5C,CAAA;AAAA,EACD,IAAM,EAAA,2BAAA;AAAA,EACN,QAAA,EAAU,CAAC,IAAI;AACjB,CAAC;;;;"}
|
|
@@ -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,QAAwE,EAAA;AACxE,EAAA,MAAM,CAAI,GAAA,QAAA
|
|
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,QAAwE,EAAA;AACxE,EAAA,MAAM,CAAI,GAAA,QAAA;AACV,EAAI,IAAA,CAAA,CAAE,WAAW,6BAA+B,EAAA;AAC9C,IAAA,MAAM,IAAI,KAAA,CAAM,CAAuC,oCAAA,EAAA,CAAA,CAAE,MAAM,CAAG,CAAA,CAAA,CAAA;AAAA;AAGpE,EAAO,OAAA,CAAA;AACT;AAGO,SAAS,mBAAmB,MAEJ,EAAA;AAC7B,EAAA,OAAO,OAAO,MAAW,KAAA,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 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 #params: string[];\n #creationSite: string;\n\n constructor(readonly params: string[] = [], creationSite: string) {\n this.#params = params;\n this.#creationSite = creationSite;\n }\n\n getParams(): string[] {\n return this.#params;\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) {\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 ? (keyof TParams)[] : TParamKeys[];\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 ) as RouteRef<any>;\n}\n"],"names":[],"mappings":"AA+CO,SAAS,mBAEd,QAAwD,EAAA;AACxD,EAAA,MAAM,CAAI,GAAA,QAAA
|
|
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 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 #params: string[];\n #creationSite: string;\n\n constructor(readonly params: string[] = [], creationSite: string) {\n this.#params = params;\n this.#creationSite = creationSite;\n }\n\n getParams(): string[] {\n return this.#params;\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) {\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 ? (keyof TParams)[] : TParamKeys[];\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 ) as RouteRef<any>;\n}\n"],"names":[],"mappings":"AA+CO,SAAS,mBAEd,QAAwD,EAAA;AACxD,EAAA,MAAM,CAAI,GAAA,QAAA;AACV,EAAI,IAAA,CAAA,CAAE,WAAW,qBAAuB,EAAA;AACtC,IAAA,MAAM,IAAI,KAAA,CAAM,CAA+B,4BAAA,EAAA,CAAA,CAAE,MAAM,CAAG,CAAA,CAAA,CAAA;AAAA;AAG5D,EAAO,OAAA,CAAA;AACT;AAGO,SAAS,WAAW,MAAgD,EAAA;AACzE,EAAA,OAAO,OAAO,MAAW,KAAA,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,QAA8D,EAAA;AAC9D,EAAA,MAAM,CAAI,GAAA,QAAA
|
|
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,QAA8D,EAAA;AAC9D,EAAA,MAAM,CAAI,GAAA,QAAA;AACV,EAAI,IAAA,CAAA,CAAE,WAAW,wBAA0B,EAAA;AACzC,IAAA,MAAM,IAAI,KAAA,CAAM,CAAkC,+BAAA,EAAA,CAAA,CAAE,MAAM,CAAG,CAAA,CAAA,CAAA;AAAA;AAG/D,EAAO,OAAA,CAAA;AACT;AAGO,SAAS,cAAc,MAEJ,EAAA;AACxB,EAAA,OAAO,OAAO,MAAW,KAAA,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/** @public */\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":"AAkGO,SAAS,yBAAyB,MAEJ,EAAA;AACnC,EAAI,IAAA,MAAA,CAAO,WAAW,2BAA6B,EAAA;AAEjD,IAAA,wBAAA,CAAyB,MAAwB,CAAA
|
|
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/** @public */\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":"AAkGO,SAAS,yBAAyB,MAEJ,EAAA;AACnC,EAAI,IAAA,MAAA,CAAO,WAAW,2BAA6B,EAAA;AAEjD,IAAA,wBAAA,CAAyB,MAAwB,CAAA;AACjD,IAAO,OAAA,IAAA;AAAA;AAET,EAAO,OAAA,KAAA;AACT;AAGO,SAAS,yBACd,MACwB,EAAA;AACxB,EAAA,MAAM,QAAW,GAAA,MAAA;AACjB,EAAI,IAAA,QAAA,CAAS,WAAW,2BAA6B,EAAA;AACnD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAsC,mCAAA,EAAA,QAAA,CAAS,MAAM,CAAG,CAAA,CAAA,CAAA;AAAA;AAE1E,EAAI,IAAA,QAAA,CAAS,YAAY,IAAM,EAAA;AAC7B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,sCAAA,EAAyC,SAAS,OAAO,CAAA,CAAA;AAAA,KAC3D;AAAA;AAEF,EAAO,OAAA,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 ExtensionDefinition,\n ExtensionDefinitionParameters,\n ResolvedExtensionInputs,\n} from './createExtension';\nimport { PortableSchema } from '../schema';\nimport { ExtensionInput } from './createExtensionInput';\nimport {\n AnyExtensionDataRef,\n ExtensionDataValue,\n} 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: { id: string; input: string };\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]: AnyExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n };\n readonly output: {\n [name in string]: AnyExtensionDataRef;\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 AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\n readonly output: Array<AnyExtensionDataRef>;\n factory(options: {\n apis: ApiHolder;\n node: AppNode;\n config: TConfig;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\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}>\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":";;;AA8FO,SAAS,oBACd,SAC0C,EAAA;AAC1C,EAAA,MAAM,QAAW,GAAA,SAAA
|
|
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 ExtensionDefinition,\n ExtensionDefinitionParameters,\n ResolvedExtensionInputs,\n} from './createExtension';\nimport { PortableSchema } from '../schema';\nimport { ExtensionInput } from './createExtensionInput';\nimport {\n AnyExtensionDataRef,\n ExtensionDataValue,\n} 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: { id: string; input: string };\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]: AnyExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n };\n readonly output: {\n [name in string]: AnyExtensionDataRef;\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 AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n };\n readonly output: Array<AnyExtensionDataRef>;\n factory(options: {\n apis: ApiHolder;\n node: AppNode;\n config: TConfig;\n inputs: ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\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}>\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":";;;AA8FO,SAAS,oBACd,SAC0C,EAAA;AAC1C,EAAA,MAAM,QAAW,GAAA,SAAA;AACjB,EAAI,IAAA,QAAA,CAAS,WAAW,sBAAwB,EAAA;AAC9C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,sCAAA,EAAyC,SAAS,MAAM,CAAA,CAAA;AAAA,KAC1D;AAAA;AAEF,EAAA,MAAM,UAAU,QAAS,CAAA,OAAA;AACzB,EAAI,IAAA,OAAA,KAAY,IAAQ,IAAA,OAAA,KAAY,IAAM,EAAA;AACxC,IAAA,MAAM,IAAI,KAAA,CAAM,CAA4C,yCAAA,EAAA,OAAO,CAAG,CAAA,CAAA,CAAA;AAAA;AAExE,EAAO,OAAA,QAAA;AACT;AAsBgB,SAAA,0BAAA,CAGd,YACA,OAC0C,EAAA;AAC1C,EAAM,MAAA,kBAAA,GAAqB,yBAA0B,CAAA,UAAA,CAAW,UAAU,CAAA;AAC1E,EAAM,MAAA;AAAA,IACJ,IAAA;AAAA,IACA,IAAA;AAAA,IACA,SAAW,EAAA,MAAA;AAAA,IACX,QAAU,EAAA,MAAA;AAAA,IACV,GAAG;AAAA,GACD,GAAA,kBAAA;AAEJ,EAAM,MAAA,SAAA,GAAY,kBAAmB,CAAA,SAAA,IAAa,OAAS,EAAA,SAAA;AAE3D,EAAM,MAAA,QAAA,GACJ,QAAQ,SAAY,GAAA,CAAA,EAAG,SAAS,CAAI,CAAA,EAAA,IAAI,KAAK,SAAa,IAAA,IAAA;AAC5D,EAAA,IAAI,CAAC,QAAU,EAAA;AACb,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAuG,oGAAA,EAAA,IAAI,CAAc,WAAA,EAAA,SAAS,SAAS,IAAI,CAAA;AAAA,KACjJ;AAAA;AAGF,EAAA,MAAM,KAAK,IAAO,GAAA,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,QAAQ,CAAK,CAAA,GAAA,QAAA;AAE1C,EAAO,OAAA;AAAA,IACL,GAAG,IAAA;AAAA,IACH,MAAQ,EAAA,sBAAA;AAAA,IACR,SAAS,kBAAmB,CAAA,OAAA;AAAA,IAC5B,EAAA;AAAA,IACA,QAAW,GAAA;AACT,MAAA,OAAO,gBAAgB,EAAE,CAAA,CAAA,CAAA;AAAA;AAC3B,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,UAKX,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,OAKL,OAGC,EAAA;AACD,IAAO,OAAA,IAAI,WAAc,OAAQ,CAAA,IAAA,EAAM,IAAI,GAAI,CAAA,OAAA,CAAQ,QAAQ,CAAC,CAAA
|
|
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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RouteResolver.esm.js","sources":["../../src/routing/RouteResolver.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApiResolveOptions,\n RouteResolutionApi,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AnyRouteRef, BackstageRouteObject } from './types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isSubRouteRef,\n toInternalSubRouteRef,\n} from '../../../frontend-plugin-api/src/routing/SubRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n anyRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let targetRef: RouteRef;\n let subRoutePath = '';\n if (isRouteRef(anyRouteRef)) {\n targetRef = anyRouteRef;\n } else if (isSubRouteRef(anyRouteRef)) {\n const internal = toInternalSubRouteRef(anyRouteRef);\n targetRef = internal.getParent();\n subRoutePath = internal.path;\n } else if (isExternalRouteRef(anyRouteRef)) {\n const resolvedRoute = routeBindings.get(anyRouteRef);\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n if (isRouteRef(resolvedRoute)) {\n targetRef = resolvedRoute;\n } else if (isSubRouteRef(resolvedRoute)) {\n const internal = toInternalSubRouteRef(resolvedRoute);\n targetRef = internal.getParent();\n subRoutePath = resolvedRoute.path;\n } else {\n throw new Error(\n `ExternalRouteRef was bound to invalid target, ${resolvedRoute}`,\n );\n }\n } else {\n throw new Error(`Unknown object passed to useRouteRef, got ${anyRouteRef}`);\n }\n\n // Bail if no absolute path could be resolved\n if (!targetRef) {\n return [undefined, ''];\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(targetRef);\n if (resolvedPath === undefined) {\n return [undefined, ''];\n }\n\n // SubRouteRefs join the path from the parent route with its own path\n const targetPath = joinPaths(resolvedPath, subRoutePath);\n return [targetRef, targetPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPaths = refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (path === undefined) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n });\n\n return `${joinPaths(parentPath, ...diffPaths)}/`;\n}\n\nexport class RouteResolver implements RouteResolutionApi {\n constructor(\n private readonly routePaths: Map<RouteRef, string>,\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>,\n private readonly routeObjects: BackstageRouteObject[],\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string, // base path without a trailing slash\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: RouteResolutionApiResolveOptions,\n ): RouteFunc<TParams> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef,\n this.routePaths,\n this.routeBindings,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n const relativeSourceLocation = this.trimPath(options?.sourcePath ?? '');\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath = resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<TParams> = (...[params]) => {\n // We selectively encode some some known-dangerous characters in the\n // params. The reason that we don't perform a blanket `encodeURIComponent`\n // here is that this encoding was added defensively long after the initial\n // release of this code. There's likely to be many users of this code that\n // already encode their parameters knowing that this code didn't do this\n // for them in the past. Therefore, we are extra careful NOT to include\n // the percent character in this set, even though that might seem like a\n // bad idea.\n const encodedParams =\n params &&\n mapValues(params, value => {\n if (typeof value === 'string') {\n return value.replaceAll(/[&?#;\\/]/g, c => encodeURIComponent(c));\n }\n return value;\n });\n return joinPaths(basePath, generatePath(targetPath, encodedParams));\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RouteResolver.esm.js","sources":["../../src/routing/RouteResolver.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { generatePath, matchRoutes } from 'react-router-dom';\nimport {\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApiResolveOptions,\n RouteResolutionApi,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AnyRouteRef, BackstageRouteObject } from './types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isRouteRef } from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isSubRouteRef,\n toInternalSubRouteRef,\n} from '../../../frontend-plugin-api/src/routing/SubRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\n/**\n * Resolves the absolute route ref that our target route ref is pointing pointing to, as well\n * as the relative target path.\n *\n * Returns an undefined target ref if one could not be fully resolved.\n */\nfunction resolveTargetRef(\n anyRouteRef: AnyRouteRef,\n routePaths: Map<RouteRef, string>,\n routeBindings: Map<AnyRouteRef, AnyRouteRef | undefined>,\n): readonly [RouteRef | undefined, string] {\n // First we figure out which absolute route ref we're dealing with, an if there was an sub route path to append.\n // For sub routes it will be the parent path, while for external routes it will be the bound route.\n let targetRef: RouteRef;\n let subRoutePath = '';\n if (isRouteRef(anyRouteRef)) {\n targetRef = anyRouteRef;\n } else if (isSubRouteRef(anyRouteRef)) {\n const internal = toInternalSubRouteRef(anyRouteRef);\n targetRef = internal.getParent();\n subRoutePath = internal.path;\n } else if (isExternalRouteRef(anyRouteRef)) {\n const resolvedRoute = routeBindings.get(anyRouteRef);\n if (!resolvedRoute) {\n return [undefined, ''];\n }\n if (isRouteRef(resolvedRoute)) {\n targetRef = resolvedRoute;\n } else if (isSubRouteRef(resolvedRoute)) {\n const internal = toInternalSubRouteRef(resolvedRoute);\n targetRef = internal.getParent();\n subRoutePath = resolvedRoute.path;\n } else {\n throw new Error(\n `ExternalRouteRef was bound to invalid target, ${resolvedRoute}`,\n );\n }\n } else {\n throw new Error(`Unknown object passed to useRouteRef, got ${anyRouteRef}`);\n }\n\n // Bail if no absolute path could be resolved\n if (!targetRef) {\n return [undefined, ''];\n }\n\n // Find the path that our target route is bound to\n const resolvedPath = routePaths.get(targetRef);\n if (resolvedPath === undefined) {\n return [undefined, ''];\n }\n\n // SubRouteRefs join the path from the parent route with its own path\n const targetPath = joinPaths(resolvedPath, subRoutePath);\n return [targetRef, targetPath];\n}\n\n/**\n * Resolves the complete base path for navigating to the target RouteRef.\n */\nfunction resolveBasePath(\n targetRef: RouteRef,\n sourceLocation: Parameters<typeof matchRoutes>[1],\n routePaths: Map<RouteRef, string>,\n routeParents: Map<RouteRef, RouteRef | undefined>,\n routeObjects: BackstageRouteObject[],\n) {\n // While traversing the app element tree we build up the routeObjects structure\n // used here. It is the same kind of structure that react-router creates, with the\n // addition that associated route refs are stored throughout the tree. This lets\n // us look up all route refs that can be reached from our source location.\n // Because of the similar route object structure, we can use `matchRoutes` from\n // react-router to do the lookup of our current location.\n const match = matchRoutes(routeObjects, sourceLocation) ?? [];\n\n // While we search for a common routing root between our current location and\n // the target route, we build a list of all route refs we find that we need\n // to traverse to reach the target.\n const refDiffList = Array<RouteRef>();\n\n let matchIndex = -1;\n for (\n let targetSearchRef: RouteRef | undefined = targetRef;\n targetSearchRef;\n targetSearchRef = routeParents.get(targetSearchRef)\n ) {\n // The match contains a list of all ancestral route refs present at our current location\n // Starting at the desired target ref and traversing back through its parents, we search\n // for a target ref that is present in the match for our current location. When a match\n // is found it means we have found a common base to resolve the route from.\n matchIndex = match.findIndex(m =>\n (m.route as BackstageRouteObject).routeRefs.has(targetSearchRef!),\n );\n if (matchIndex !== -1) {\n break;\n }\n\n // Every time we move a step up in the ancestry of the target ref, we add the current ref\n // to the diff list, which ends up being the list of route refs to traverse form the common base\n // in order to reach our target.\n refDiffList.unshift(targetSearchRef);\n }\n\n // If our target route is present in the initial match we need to construct the final path\n // from the parent of the matched route segment. That's to allow the caller of the route\n // function to supply their own params.\n if (refDiffList.length === 0) {\n matchIndex -= 1;\n }\n\n // This is the part of the route tree that the target and source locations have in common.\n // We re-use the existing pathname directly along with all params.\n const parentPath = matchIndex === -1 ? '' : match[matchIndex].pathname;\n\n // This constructs the mid section of the path using paths resolved from all route refs\n // we need to traverse to reach our target except for the very last one. None of these\n // paths are allowed to require any parameters, as the caller would have no way of knowing\n // what parameters those are.\n const diffPaths = refDiffList.slice(0, -1).map(ref => {\n const path = routePaths.get(ref);\n if (path === undefined) {\n throw new Error(`No path for ${ref}`);\n }\n if (path.includes(':')) {\n throw new Error(\n `Cannot route to ${targetRef} with parent ${ref} as it has parameters`,\n );\n }\n return path;\n });\n\n return `${joinPaths(parentPath, ...diffPaths)}/`;\n}\n\nexport class RouteResolver implements RouteResolutionApi {\n constructor(\n private readonly routePaths: Map<RouteRef, string>,\n private readonly routeParents: Map<RouteRef, RouteRef | undefined>,\n private readonly routeObjects: BackstageRouteObject[],\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly appBasePath: string, // base path without a trailing slash\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: RouteResolutionApiResolveOptions,\n ): RouteFunc<TParams> | undefined {\n // First figure out what our target absolute ref is, as well as our target path.\n const [targetRef, targetPath] = resolveTargetRef(\n anyRouteRef,\n this.routePaths,\n this.routeBindings,\n );\n if (!targetRef) {\n return undefined;\n }\n\n // The location that we get passed in uses the full path, so start by trimming off\n // the app base path prefix in case we're running the app on a sub-path.\n const relativeSourceLocation = this.trimPath(options?.sourcePath ?? '');\n\n // Next we figure out the base path, which is the combination of the common parent path\n // between our current location and our target location, as well as the additional path\n // that is the difference between the parent path and the base of our target location.\n const basePath = resolveBasePath(\n targetRef,\n relativeSourceLocation,\n this.routePaths,\n this.routeParents,\n this.routeObjects,\n );\n\n const routeFunc: RouteFunc<TParams> = (...[params]) => {\n // We selectively encode some some known-dangerous characters in the\n // params. The reason that we don't perform a blanket `encodeURIComponent`\n // here is that this encoding was added defensively long after the initial\n // release of this code. There's likely to be many users of this code that\n // already encode their parameters knowing that this code didn't do this\n // for them in the past. Therefore, we are extra careful NOT to include\n // the percent character in this set, even though that might seem like a\n // bad idea.\n const encodedParams =\n params &&\n mapValues(params, value => {\n if (typeof value === 'string') {\n return value.replaceAll(/[&?#;\\/]/g, c => encodeURIComponent(c));\n }\n return value;\n });\n return joinPaths(basePath, generatePath(targetPath, encodedParams));\n };\n return routeFunc;\n }\n\n private trimPath(targetPath: string) {\n if (!targetPath) {\n return targetPath;\n }\n\n if (targetPath.startsWith(this.appBasePath)) {\n return targetPath.slice(this.appBasePath.length);\n }\n return targetPath;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collectRouteIds.esm.js","sources":["../../src/routing/collectRouteIds.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isRouteRef,\n toInternalRouteRef,\n} from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalSubRouteRef } from '../../../frontend-plugin-api/src/routing/SubRouteRef';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\nimport { FrontendFeature } from '../wiring';\n\n/** @internal */\nexport interface RouteRefsById {\n routes: Map<string, RouteRef | SubRouteRef>;\n externalRoutes: Map<string, ExternalRouteRef>;\n}\n\n/** @internal */\nexport function collectRouteIds(features: FrontendFeature[]): RouteRefsById {\n const routesById = new Map<string, RouteRef | SubRouteRef>();\n const externalRoutesById = new Map<string, ExternalRouteRef>();\n\n for (const feature of features) {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n continue;\n }\n\n for (const [name, ref] of Object.entries(feature.routes)) {\n const refId = `${feature.id}.${name}`;\n if (routesById.has(refId)) {\n throw new Error(`Unexpected duplicate route '${refId}'`);\n }\n\n if (isRouteRef(ref)) {\n const internalRef = toInternalRouteRef(ref);\n internalRef.setId(refId);\n routesById.set(refId, ref);\n } else {\n const internalRef = toInternalSubRouteRef(ref);\n routesById.set(refId, internalRef);\n }\n }\n for (const [name, ref] of Object.entries(feature.externalRoutes)) {\n const refId = `${feature.id}.${name}`;\n if (externalRoutesById.has(refId)) {\n throw new Error(`Unexpected duplicate external route '${refId}'`);\n }\n\n const internalRef = toInternalExternalRouteRef(ref);\n internalRef.setId(refId);\n externalRoutesById.set(refId, ref);\n }\n }\n\n return { routes: routesById, externalRoutes: externalRoutesById };\n}\n"],"names":[],"mappings":";;;;;;AAwCO,SAAS,gBAAgB,QAA4C,EAAA;AAC1E,EAAM,MAAA,UAAA,uBAAiB,GAAoC,EAAA
|
|
1
|
+
{"version":3,"file":"collectRouteIds.esm.js","sources":["../../src/routing/collectRouteIds.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isRouteRef,\n toInternalRouteRef,\n} from '../../../frontend-plugin-api/src/routing/RouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalSubRouteRef } from '../../../frontend-plugin-api/src/routing/SubRouteRef';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\nimport { FrontendFeature } from '../wiring';\n\n/** @internal */\nexport interface RouteRefsById {\n routes: Map<string, RouteRef | SubRouteRef>;\n externalRoutes: Map<string, ExternalRouteRef>;\n}\n\n/** @internal */\nexport function collectRouteIds(features: FrontendFeature[]): RouteRefsById {\n const routesById = new Map<string, RouteRef | SubRouteRef>();\n const externalRoutesById = new Map<string, ExternalRouteRef>();\n\n for (const feature of features) {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n continue;\n }\n\n for (const [name, ref] of Object.entries(feature.routes)) {\n const refId = `${feature.id}.${name}`;\n if (routesById.has(refId)) {\n throw new Error(`Unexpected duplicate route '${refId}'`);\n }\n\n if (isRouteRef(ref)) {\n const internalRef = toInternalRouteRef(ref);\n internalRef.setId(refId);\n routesById.set(refId, ref);\n } else {\n const internalRef = toInternalSubRouteRef(ref);\n routesById.set(refId, internalRef);\n }\n }\n for (const [name, ref] of Object.entries(feature.externalRoutes)) {\n const refId = `${feature.id}.${name}`;\n if (externalRoutesById.has(refId)) {\n throw new Error(`Unexpected duplicate external route '${refId}'`);\n }\n\n const internalRef = toInternalExternalRouteRef(ref);\n internalRef.setId(refId);\n externalRoutesById.set(refId, ref);\n }\n }\n\n return { routes: routesById, externalRoutes: externalRoutesById };\n}\n"],"names":[],"mappings":";;;;;;AAwCO,SAAS,gBAAgB,QAA4C,EAAA;AAC1E,EAAM,MAAA,UAAA,uBAAiB,GAAoC,EAAA;AAC3D,EAAM,MAAA,kBAAA,uBAAyB,GAA8B,EAAA;AAE7D,EAAA,KAAA,MAAW,WAAW,QAAU,EAAA;AAC9B,IAAA,IAAI,CAAC,oBAAA,CAAqB,MAAO,CAAA,OAAO,CAAG,EAAA;AACzC,MAAA;AAAA;AAGF,IAAW,KAAA,MAAA,CAAC,MAAM,GAAG,CAAA,IAAK,OAAO,OAAQ,CAAA,OAAA,CAAQ,MAAM,CAAG,EAAA;AACxD,MAAA,MAAM,KAAQ,GAAA,CAAA,EAAG,OAAQ,CAAA,EAAE,IAAI,IAAI,CAAA,CAAA;AACnC,MAAI,IAAA,UAAA,CAAW,GAAI,CAAA,KAAK,CAAG,EAAA;AACzB,QAAA,MAAM,IAAI,KAAA,CAAM,CAA+B,4BAAA,EAAA,KAAK,CAAG,CAAA,CAAA,CAAA;AAAA;AAGzD,MAAI,IAAA,UAAA,CAAW,GAAG,CAAG,EAAA;AACnB,QAAM,MAAA,WAAA,GAAc,mBAAmB,GAAG,CAAA;AAC1C,QAAA,WAAA,CAAY,MAAM,KAAK,CAAA;AACvB,QAAW,UAAA,CAAA,GAAA,CAAI,OAAO,GAAG,CAAA;AAAA,OACpB,MAAA;AACL,QAAM,MAAA,WAAA,GAAc,sBAAsB,GAAG,CAAA;AAC7C,QAAW,UAAA,CAAA,GAAA,CAAI,OAAO,WAAW,CAAA;AAAA;AACnC;AAEF,IAAW,KAAA,MAAA,CAAC,MAAM,GAAG,CAAA,IAAK,OAAO,OAAQ,CAAA,OAAA,CAAQ,cAAc,CAAG,EAAA;AAChE,MAAA,MAAM,KAAQ,GAAA,CAAA,EAAG,OAAQ,CAAA,EAAE,IAAI,IAAI,CAAA,CAAA;AACnC,MAAI,IAAA,kBAAA,CAAmB,GAAI,CAAA,KAAK,CAAG,EAAA;AACjC,QAAA,MAAM,IAAI,KAAA,CAAM,CAAwC,qCAAA,EAAA,KAAK,CAAG,CAAA,CAAA,CAAA;AAAA;AAGlE,MAAM,MAAA,WAAA,GAAc,2BAA2B,GAAG,CAAA;AAClD,MAAA,WAAA,CAAY,MAAM,KAAK,CAAA;AACvB,MAAmB,kBAAA,CAAA,GAAA,CAAI,OAAO,GAAG,CAAA;AAAA;AACnC;AAGF,EAAA,OAAO,EAAE,MAAA,EAAQ,UAAY,EAAA,cAAA,EAAgB,kBAAmB,EAAA;AAClE;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extractRouteInfoFromAppNode.esm.js","sources":["../../src/routing/extractRouteInfoFromAppNode.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 { RouteRef, coreExtensionData } from '@backstage/frontend-plugin-api';\nimport { BackstageRouteObject } from './types';\nimport { AppNode } from '@backstage/frontend-plugin-api';\nimport { toLegacyPlugin } from './toLegacyPlugin';\n\n// We always add a child that matches all subroutes but without any route refs. This makes\n// sure that we're always able to match each route no matter how deep the navigation goes.\n// The route resolver then takes care of selecting the most specific match in order to find\n// mount points that are as deep in the routing tree as possible.\nexport const MATCH_ALL_ROUTE: BackstageRouteObject = {\n caseSensitive: false,\n path: '*',\n element: 'match-all', // These elements aren't used, so we add in a bit of debug information\n routeRefs: new Set(),\n plugins: new Set(),\n};\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function extractRouteInfoFromAppNode(node: AppNode): {\n routePaths: Map<RouteRef, string>;\n routeParents: Map<RouteRef, RouteRef | undefined>;\n routeObjects: BackstageRouteObject[];\n} {\n // This tracks the route path for each route ref, the value is the route path relative to the parent ref\n const routePaths = new Map<RouteRef, string>();\n // This tracks the parents of each route ref. To find the full path of any route ref you traverse\n // upwards in this tree and substitute each route ref for its route path along then way.\n const routeParents = new Map<RouteRef, RouteRef | undefined>();\n // This route object tree is passed to react-router in order to be able to look up the current route\n // ref or extension/source based on our current location.\n const routeObjects = new Array<BackstageRouteObject>();\n\n function visit(\n current: AppNode,\n collectedPath?: string,\n foundRefForCollectedPath: boolean = false,\n parentRef?: RouteRef,\n candidateParentRef?: RouteRef,\n parentObj?: BackstageRouteObject,\n ) {\n const routePath = current.instance\n ?.getData(coreExtensionData.routePath)\n ?.replace(/^\\//, '');\n const routeRef = current.instance?.getData(coreExtensionData.routeRef);\n const parentChildren = parentObj?.children ?? routeObjects;\n let currentObj = parentObj;\n\n let newCollectedPath = collectedPath;\n let newFoundRefForCollectedPath = foundRefForCollectedPath;\n\n let newParentRef = parentRef;\n let newCandidateParentRef = candidateParentRef;\n\n // Whenever a route path is encountered, a new node is created in the routing tree.\n if (routePath !== undefined) {\n currentObj = {\n path: routePath,\n element: 'mounted',\n routeRefs: new Set<RouteRef>(),\n caseSensitive: false,\n children: [MATCH_ALL_ROUTE],\n plugins: new Set(),\n appNode: current,\n };\n parentChildren.push(currentObj);\n\n // Each route path that we discover creates a new node in the routing tree, at that point\n // we also switch out our candidate parent ref to be the active one.\n newParentRef = candidateParentRef;\n newCandidateParentRef = undefined;\n\n // We need to collect and concatenate route paths until the path has been assigned a route ref:\n // Once we find a route ref the collection starts over from an empty path, that way each route\n // path assignment only contains the diff from the parent ref.\n if (newFoundRefForCollectedPath) {\n newCollectedPath = routePath;\n newFoundRefForCollectedPath = false;\n } else {\n newCollectedPath = collectedPath\n ? joinPaths(collectedPath, routePath)\n : routePath;\n }\n }\n\n // Whenever a route ref is encountered, we need to give it a route path and position in the ref tree.\n if (routeRef) {\n // The first route ref we find after encountering a route path is selected to be used as the\n // parent ref further down the tree. We don't start using this candidate ref until we encounter\n // another route path though, at which point we repeat the process and select another candidate.\n if (!newCandidateParentRef) {\n newCandidateParentRef = routeRef;\n }\n\n // Check if we've encountered any route paths since the closest route ref, in that case we assign\n // that path to this and following route refs until we encounter another route path.\n if (newCollectedPath !== undefined) {\n routePaths.set(routeRef, newCollectedPath);\n newFoundRefForCollectedPath = true;\n }\n\n routeParents.set(routeRef, newParentRef);\n currentObj?.routeRefs.add(routeRef);\n if (current.spec.source) {\n currentObj?.plugins.add(toLegacyPlugin(current.spec.source));\n }\n }\n\n for (const children of current.edges.attachments.values()) {\n for (const child of children) {\n visit(\n child,\n newCollectedPath,\n newFoundRefForCollectedPath,\n newParentRef,\n newCandidateParentRef,\n currentObj,\n );\n }\n }\n }\n\n visit(node);\n\n return { routePaths, routeParents, routeObjects };\n}\n"],"names":[],"mappings":";;;AAyBO,MAAM,eAAwC,GAAA;AAAA,EACnD,aAAe,EAAA,KAAA;AAAA,EACf,IAAM,EAAA,GAAA;AAAA,EACN,OAAS,EAAA,WAAA;AAAA;AAAA,EACT,SAAA,sBAAe,GAAI,EAAA;AAAA,EACnB,OAAA,sBAAa,GAAI
|
|
1
|
+
{"version":3,"file":"extractRouteInfoFromAppNode.esm.js","sources":["../../src/routing/extractRouteInfoFromAppNode.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 { RouteRef, coreExtensionData } from '@backstage/frontend-plugin-api';\nimport { BackstageRouteObject } from './types';\nimport { AppNode } from '@backstage/frontend-plugin-api';\nimport { toLegacyPlugin } from './toLegacyPlugin';\n\n// We always add a child that matches all subroutes but without any route refs. This makes\n// sure that we're always able to match each route no matter how deep the navigation goes.\n// The route resolver then takes care of selecting the most specific match in order to find\n// mount points that are as deep in the routing tree as possible.\nexport const MATCH_ALL_ROUTE: BackstageRouteObject = {\n caseSensitive: false,\n path: '*',\n element: 'match-all', // These elements aren't used, so we add in a bit of debug information\n routeRefs: new Set(),\n plugins: new Set(),\n};\n\n// Joins a list of paths together, avoiding trailing and duplicate slashes\nexport function joinPaths(...paths: string[]): string {\n const normalized = paths.join('/').replace(/\\/\\/+/g, '/');\n if (normalized !== '/' && normalized.endsWith('/')) {\n return normalized.slice(0, -1);\n }\n return normalized;\n}\n\nexport function extractRouteInfoFromAppNode(node: AppNode): {\n routePaths: Map<RouteRef, string>;\n routeParents: Map<RouteRef, RouteRef | undefined>;\n routeObjects: BackstageRouteObject[];\n} {\n // This tracks the route path for each route ref, the value is the route path relative to the parent ref\n const routePaths = new Map<RouteRef, string>();\n // This tracks the parents of each route ref. To find the full path of any route ref you traverse\n // upwards in this tree and substitute each route ref for its route path along then way.\n const routeParents = new Map<RouteRef, RouteRef | undefined>();\n // This route object tree is passed to react-router in order to be able to look up the current route\n // ref or extension/source based on our current location.\n const routeObjects = new Array<BackstageRouteObject>();\n\n function visit(\n current: AppNode,\n collectedPath?: string,\n foundRefForCollectedPath: boolean = false,\n parentRef?: RouteRef,\n candidateParentRef?: RouteRef,\n parentObj?: BackstageRouteObject,\n ) {\n const routePath = current.instance\n ?.getData(coreExtensionData.routePath)\n ?.replace(/^\\//, '');\n const routeRef = current.instance?.getData(coreExtensionData.routeRef);\n const parentChildren = parentObj?.children ?? routeObjects;\n let currentObj = parentObj;\n\n let newCollectedPath = collectedPath;\n let newFoundRefForCollectedPath = foundRefForCollectedPath;\n\n let newParentRef = parentRef;\n let newCandidateParentRef = candidateParentRef;\n\n // Whenever a route path is encountered, a new node is created in the routing tree.\n if (routePath !== undefined) {\n currentObj = {\n path: routePath,\n element: 'mounted',\n routeRefs: new Set<RouteRef>(),\n caseSensitive: false,\n children: [MATCH_ALL_ROUTE],\n plugins: new Set(),\n appNode: current,\n };\n parentChildren.push(currentObj);\n\n // Each route path that we discover creates a new node in the routing tree, at that point\n // we also switch out our candidate parent ref to be the active one.\n newParentRef = candidateParentRef;\n newCandidateParentRef = undefined;\n\n // We need to collect and concatenate route paths until the path has been assigned a route ref:\n // Once we find a route ref the collection starts over from an empty path, that way each route\n // path assignment only contains the diff from the parent ref.\n if (newFoundRefForCollectedPath) {\n newCollectedPath = routePath;\n newFoundRefForCollectedPath = false;\n } else {\n newCollectedPath = collectedPath\n ? joinPaths(collectedPath, routePath)\n : routePath;\n }\n }\n\n // Whenever a route ref is encountered, we need to give it a route path and position in the ref tree.\n if (routeRef) {\n // The first route ref we find after encountering a route path is selected to be used as the\n // parent ref further down the tree. We don't start using this candidate ref until we encounter\n // another route path though, at which point we repeat the process and select another candidate.\n if (!newCandidateParentRef) {\n newCandidateParentRef = routeRef;\n }\n\n // Check if we've encountered any route paths since the closest route ref, in that case we assign\n // that path to this and following route refs until we encounter another route path.\n if (newCollectedPath !== undefined) {\n routePaths.set(routeRef, newCollectedPath);\n newFoundRefForCollectedPath = true;\n }\n\n routeParents.set(routeRef, newParentRef);\n currentObj?.routeRefs.add(routeRef);\n if (current.spec.source) {\n currentObj?.plugins.add(toLegacyPlugin(current.spec.source));\n }\n }\n\n for (const children of current.edges.attachments.values()) {\n for (const child of children) {\n visit(\n child,\n newCollectedPath,\n newFoundRefForCollectedPath,\n newParentRef,\n newCandidateParentRef,\n currentObj,\n );\n }\n }\n }\n\n visit(node);\n\n return { routePaths, routeParents, routeObjects };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getBasePath.esm.js","sources":["../../src/routing/getBasePath.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 { ConfigApi } from '@backstage/frontend-plugin-api';\n\n/** @internal */\nexport function getBasePath(configApi: ConfigApi) {\n let { pathname } = new URL(\n configApi.getOptionalString('app.baseUrl') ?? '/',\n 'http://sample.dev', // baseUrl can be specified as just a path\n );\n pathname = pathname.replace(/\\/*$/, '');\n return pathname;\n}\n"],"names":[],"mappings":"AAmBO,SAAS,YAAY,SAAsB,EAAA;AAChD,EAAI,IAAA,EAAE,QAAS,EAAA,GAAI,IAAI,GAAA;AAAA,IACrB,SAAA,CAAU,iBAAkB,CAAA,aAAa,CAAK,IAAA,GAAA;AAAA,IAC9C
|
|
1
|
+
{"version":3,"file":"getBasePath.esm.js","sources":["../../src/routing/getBasePath.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 { ConfigApi } from '@backstage/frontend-plugin-api';\n\n/** @internal */\nexport function getBasePath(configApi: ConfigApi) {\n let { pathname } = new URL(\n configApi.getOptionalString('app.baseUrl') ?? '/',\n 'http://sample.dev', // baseUrl can be specified as just a path\n );\n pathname = pathname.replace(/\\/*$/, '');\n return pathname;\n}\n"],"names":[],"mappings":"AAmBO,SAAS,YAAY,SAAsB,EAAA;AAChD,EAAI,IAAA,EAAE,QAAS,EAAA,GAAI,IAAI,GAAA;AAAA,IACrB,SAAA,CAAU,iBAAkB,CAAA,aAAa,CAAK,IAAA,GAAA;AAAA,IAC9C;AAAA;AAAA,GACF;AACA,EAAW,QAAA,GAAA,QAAA,CAAS,OAAQ,CAAA,MAAA,EAAQ,EAAE,CAAA;AACtC,EAAO,OAAA,QAAA;AACT;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveRouteBindings.esm.js","sources":["../../src/routing/resolveRouteBindings.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 RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\nimport { Config } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n\n/**\n * Extracts a union of the keys in a map whose value extends the given type\n *\n * @ignore\n */\ntype KeysWithType<Obj extends { [key in string]: any }, Type> = {\n [key in keyof Obj]: Obj[key] extends Type ? key : never;\n}[keyof Obj];\n\n/**\n * Takes a map Map required values and makes all keys matching Keys optional\n *\n * @ignore\n */\ntype PartialKeys<\n Map extends { [name in string]: any },\n Keys extends keyof Map,\n> = Partial<Pick<Map, Keys>> & Required<Omit<Map, Keys>>;\n\n/**\n * Creates a map of target routes with matching parameters based on a map of external routes.\n *\n * @ignore\n */\ntype TargetRouteMap<\n ExternalRoutes extends { [name: string]: ExternalRouteRef },\n> = {\n [name in keyof ExternalRoutes]: ExternalRoutes[name] extends ExternalRouteRef<\n infer Params\n >\n ? RouteRef<Params> | SubRouteRef<Params> | false\n : never;\n};\n\n/**\n * A function that can bind from external routes of a given plugin, to concrete\n * routes of other plugins. See {@link @backstage/frontend-defaults#createApp}.\n *\n * @public\n */\nexport type CreateAppRouteBinder = <\n TExternalRoutes extends { [name: string]: ExternalRouteRef },\n>(\n externalRoutes: TExternalRoutes,\n targetRoutes: PartialKeys<\n TargetRouteMap<TExternalRoutes>,\n KeysWithType<TExternalRoutes, ExternalRouteRef<any>>\n >,\n) => void;\n\n/** @internal */\nexport function resolveRouteBindings(\n bindRoutes: ((context: { bind: CreateAppRouteBinder }) => void) | undefined,\n config: Config,\n routesById: RouteRefsById,\n): Map<ExternalRouteRef, RouteRef | SubRouteRef> {\n const result = new Map<ExternalRouteRef, RouteRef | SubRouteRef>();\n const disabledExternalRefs = new Set<ExternalRouteRef>();\n\n // Perform callback bindings first with highest priority\n if (bindRoutes) {\n const bind: CreateAppRouteBinder = (\n externalRoutes,\n targetRoutes: { [name: string]: RouteRef | SubRouteRef },\n ) => {\n for (const [key, value] of Object.entries(targetRoutes)) {\n const externalRoute = externalRoutes[key];\n if (!externalRoute) {\n throw new Error(`Key ${key} is not an existing external route`);\n }\n if (value) {\n result.set(externalRoute, value);\n } else if (value === false) {\n disabledExternalRefs.add(externalRoute);\n }\n }\n };\n bindRoutes({ bind });\n }\n\n // Then perform config based bindings with lower priority\n const bindings = config\n .getOptionalConfig('app.routes.bindings')\n ?.get<JsonObject>();\n if (bindings) {\n for (const [externalRefId, targetRefId] of Object.entries(bindings)) {\n if (!isValidTargetRefId(targetRefId)) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string or false`,\n );\n }\n\n const externalRef = routesById.externalRoutes.get(externalRefId);\n if (!externalRef) {\n throw new Error(\n `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`,\n );\n }\n\n // Skip if binding was already defined in code\n if (result.has(externalRef) || disabledExternalRefs.has(externalRef)) {\n continue;\n }\n\n if (targetRefId === false) {\n disabledExternalRefs.add(externalRef);\n } else {\n const targetRef = routesById.routes.get(targetRefId);\n if (!targetRef) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], '${targetRefId}' is not a valid route`,\n );\n }\n\n result.set(externalRef, targetRef);\n }\n }\n }\n\n // Finally fall back to attempting to map defaults, at lowest priority\n for (const externalRef of routesById.externalRoutes.values()) {\n if (!result.has(externalRef) && !disabledExternalRefs.has(externalRef)) {\n const defaultRefId =\n toInternalExternalRouteRef(externalRef).getDefaultTarget();\n if (defaultRefId) {\n const defaultRef = routesById.routes.get(defaultRefId);\n if (defaultRef) {\n result.set(externalRef, defaultRef);\n }\n }\n }\n }\n\n return result;\n}\n\nfunction isValidTargetRefId(value: unknown): value is string | false {\n if (value === false) {\n return true;\n }\n\n if (typeof value === 'string' && value) {\n return true;\n }\n\n return false;\n}\n"],"names":[],"mappings":";;AA8EgB,SAAA,oBAAA,CACd,UACA,EAAA,MAAA,EACA,UAC+C,EAAA;AAC/C,EAAM,MAAA,MAAA,uBAAa,GAA8C,EAAA
|
|
1
|
+
{"version":3,"file":"resolveRouteBindings.esm.js","sources":["../../src/routing/resolveRouteBindings.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 RouteRef,\n SubRouteRef,\n ExternalRouteRef,\n} from '@backstage/frontend-plugin-api';\nimport { RouteRefsById } from './collectRouteIds';\nimport { Config } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/routing/ExternalRouteRef';\n\n/**\n * Extracts a union of the keys in a map whose value extends the given type\n *\n * @ignore\n */\ntype KeysWithType<Obj extends { [key in string]: any }, Type> = {\n [key in keyof Obj]: Obj[key] extends Type ? key : never;\n}[keyof Obj];\n\n/**\n * Takes a map Map required values and makes all keys matching Keys optional\n *\n * @ignore\n */\ntype PartialKeys<\n Map extends { [name in string]: any },\n Keys extends keyof Map,\n> = Partial<Pick<Map, Keys>> & Required<Omit<Map, Keys>>;\n\n/**\n * Creates a map of target routes with matching parameters based on a map of external routes.\n *\n * @ignore\n */\ntype TargetRouteMap<\n ExternalRoutes extends { [name: string]: ExternalRouteRef },\n> = {\n [name in keyof ExternalRoutes]: ExternalRoutes[name] extends ExternalRouteRef<\n infer Params\n >\n ? RouteRef<Params> | SubRouteRef<Params> | false\n : never;\n};\n\n/**\n * A function that can bind from external routes of a given plugin, to concrete\n * routes of other plugins. See {@link @backstage/frontend-defaults#createApp}.\n *\n * @public\n */\nexport type CreateAppRouteBinder = <\n TExternalRoutes extends { [name: string]: ExternalRouteRef },\n>(\n externalRoutes: TExternalRoutes,\n targetRoutes: PartialKeys<\n TargetRouteMap<TExternalRoutes>,\n KeysWithType<TExternalRoutes, ExternalRouteRef<any>>\n >,\n) => void;\n\n/** @internal */\nexport function resolveRouteBindings(\n bindRoutes: ((context: { bind: CreateAppRouteBinder }) => void) | undefined,\n config: Config,\n routesById: RouteRefsById,\n): Map<ExternalRouteRef, RouteRef | SubRouteRef> {\n const result = new Map<ExternalRouteRef, RouteRef | SubRouteRef>();\n const disabledExternalRefs = new Set<ExternalRouteRef>();\n\n // Perform callback bindings first with highest priority\n if (bindRoutes) {\n const bind: CreateAppRouteBinder = (\n externalRoutes,\n targetRoutes: { [name: string]: RouteRef | SubRouteRef },\n ) => {\n for (const [key, value] of Object.entries(targetRoutes)) {\n const externalRoute = externalRoutes[key];\n if (!externalRoute) {\n throw new Error(`Key ${key} is not an existing external route`);\n }\n if (value) {\n result.set(externalRoute, value);\n } else if (value === false) {\n disabledExternalRefs.add(externalRoute);\n }\n }\n };\n bindRoutes({ bind });\n }\n\n // Then perform config based bindings with lower priority\n const bindings = config\n .getOptionalConfig('app.routes.bindings')\n ?.get<JsonObject>();\n if (bindings) {\n for (const [externalRefId, targetRefId] of Object.entries(bindings)) {\n if (!isValidTargetRefId(targetRefId)) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], value must be a non-empty string or false`,\n );\n }\n\n const externalRef = routesById.externalRoutes.get(externalRefId);\n if (!externalRef) {\n throw new Error(\n `Invalid config at app.routes.bindings, '${externalRefId}' is not a valid external route`,\n );\n }\n\n // Skip if binding was already defined in code\n if (result.has(externalRef) || disabledExternalRefs.has(externalRef)) {\n continue;\n }\n\n if (targetRefId === false) {\n disabledExternalRefs.add(externalRef);\n } else {\n const targetRef = routesById.routes.get(targetRefId);\n if (!targetRef) {\n throw new Error(\n `Invalid config at app.routes.bindings['${externalRefId}'], '${targetRefId}' is not a valid route`,\n );\n }\n\n result.set(externalRef, targetRef);\n }\n }\n }\n\n // Finally fall back to attempting to map defaults, at lowest priority\n for (const externalRef of routesById.externalRoutes.values()) {\n if (!result.has(externalRef) && !disabledExternalRefs.has(externalRef)) {\n const defaultRefId =\n toInternalExternalRouteRef(externalRef).getDefaultTarget();\n if (defaultRefId) {\n const defaultRef = routesById.routes.get(defaultRefId);\n if (defaultRef) {\n result.set(externalRef, defaultRef);\n }\n }\n }\n }\n\n return result;\n}\n\nfunction isValidTargetRefId(value: unknown): value is string | false {\n if (value === false) {\n return true;\n }\n\n if (typeof value === 'string' && value) {\n return true;\n }\n\n return false;\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toLegacyPlugin.esm.js","sources":["../../src/routing/toLegacyPlugin.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 { FrontendPlugin } from '@backstage/frontend-plugin-api';\nimport { BackstagePlugin as LegacyBackstagePlugin } from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\n// Make sure that we only convert each new plugin instance to its legacy equivalent once\nconst legacyPluginStore = getOrCreateGlobalSingleton(\n 'legacy-plugin-compatibility-store',\n () => new WeakMap<FrontendPlugin, LegacyBackstagePlugin>(),\n);\n\nexport function toLegacyPlugin(plugin: FrontendPlugin): LegacyBackstagePlugin {\n let legacy = legacyPluginStore.get(plugin);\n if (legacy) {\n return legacy;\n }\n\n const errorMsg = 'Not implemented in legacy plugin compatibility layer';\n const notImplemented = () => {\n throw new Error(errorMsg);\n };\n\n legacy = {\n getId(): string {\n return plugin.id;\n },\n get routes() {\n return {};\n },\n get externalRoutes() {\n return {};\n },\n getApis: notImplemented,\n getFeatureFlags: notImplemented,\n provide: notImplemented,\n };\n\n legacyPluginStore.set(plugin, legacy);\n return legacy;\n}\n"],"names":[],"mappings":";;AAqBA,MAAM,iBAAoB,GAAA,0BAAA;AAAA,EACxB,mCAAA;AAAA,EACA,0BAAU,OAA+C
|
|
1
|
+
{"version":3,"file":"toLegacyPlugin.esm.js","sources":["../../src/routing/toLegacyPlugin.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 { FrontendPlugin } from '@backstage/frontend-plugin-api';\nimport { BackstagePlugin as LegacyBackstagePlugin } from '@backstage/core-plugin-api';\nimport { getOrCreateGlobalSingleton } from '@backstage/version-bridge';\n\n// Make sure that we only convert each new plugin instance to its legacy equivalent once\nconst legacyPluginStore = getOrCreateGlobalSingleton(\n 'legacy-plugin-compatibility-store',\n () => new WeakMap<FrontendPlugin, LegacyBackstagePlugin>(),\n);\n\nexport function toLegacyPlugin(plugin: FrontendPlugin): LegacyBackstagePlugin {\n let legacy = legacyPluginStore.get(plugin);\n if (legacy) {\n return legacy;\n }\n\n const errorMsg = 'Not implemented in legacy plugin compatibility layer';\n const notImplemented = () => {\n throw new Error(errorMsg);\n };\n\n legacy = {\n getId(): string {\n return plugin.id;\n },\n get routes() {\n return {};\n },\n get externalRoutes() {\n return {};\n },\n getApis: notImplemented,\n getFeatureFlags: notImplemented,\n provide: notImplemented,\n };\n\n legacyPluginStore.set(plugin, legacy);\n return legacy;\n}\n"],"names":[],"mappings":";;AAqBA,MAAM,iBAAoB,GAAA,0BAAA;AAAA,EACxB,mCAAA;AAAA,EACA,0BAAU,OAA+C;AAC3D,CAAA;AAEO,SAAS,eAAe,MAA+C,EAAA;AAC5E,EAAI,IAAA,MAAA,GAAS,iBAAkB,CAAA,GAAA,CAAI,MAAM,CAAA;AACzC,EAAA,IAAI,MAAQ,EAAA;AACV,IAAO,OAAA,MAAA;AAAA;AAGT,EAAA,MAAM,QAAW,GAAA,sDAAA;AACjB,EAAA,MAAM,iBAAiB,MAAM;AAC3B,IAAM,MAAA,IAAI,MAAM,QAAQ,CAAA;AAAA,GAC1B;AAEA,EAAS,MAAA,GAAA;AAAA,IACP,KAAgB,GAAA;AACd,MAAA,OAAO,MAAO,CAAA,EAAA;AAAA,KAChB;AAAA,IACA,IAAI,MAAS,GAAA;AACX,MAAA,OAAO,EAAC;AAAA,KACV;AAAA,IACA,IAAI,cAAiB,GAAA;AACnB,MAAA,OAAO,EAAC;AAAA,KACV;AAAA,IACA,OAAS,EAAA,cAAA;AAAA,IACT,eAAiB,EAAA,cAAA;AAAA,IACjB,OAAS,EAAA;AAAA,GACX;AAEA,EAAkB,iBAAA,CAAA,GAAA,CAAI,QAAQ,MAAM,CAAA;AACpC,EAAO,OAAA,MAAA;AACT;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instantiateAppNodeTree.esm.js","sources":["../../src/tree/instantiateAppNodeTree.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AnyExtensionDataRef,\n ApiHolder,\n ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionInput,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nfunction resolveV1InputDataMap(\n dataMap: {\n [name in string]: AnyExtensionDataRef;\n },\n attachment: AppNode,\n inputName: string,\n) {\n return mapValues(dataMap, ref => {\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = Object.values(dataMap)\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n return value;\n });\n}\n\nfunction resolveInputDataContainer(\n extensionData: Array<AnyExtensionDataRef>,\n attachment: AppNode,\n inputName: string,\n): { node: AppNode } & ExtensionDataContainer<AnyExtensionDataRef> {\n const dataMap = new Map<string, unknown>();\n\n for (const ref of extensionData) {\n if (dataMap.has(ref.id)) {\n throw new Error(`Unexpected duplicate input data '${ref.id}'`);\n }\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = extensionData\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n\n dataMap.set(ref.id, value);\n }\n\n return {\n node: attachment,\n get(ref) {\n return dataMap.get(ref.id);\n },\n *[Symbol.iterator]() {\n for (const [id, value] of dataMap) {\n // TODO: Would be better to be able to create a new instance using the ref here instead\n yield {\n $$type: '@backstage/ExtensionDataValue',\n id,\n value,\n };\n }\n },\n } as { node: AppNode } & ExtensionDataContainer<AnyExtensionDataRef>;\n}\n\nfunction reportUndeclaredAttachments(\n id: string,\n inputMap: { [name in string]: unknown },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n const undeclaredAttachments = Array.from(attachments.entries()).filter(\n ([inputName]) => inputMap[inputName] === undefined,\n );\n\n const inputNames = Object.keys(inputMap);\n\n for (const [name, nodes] of undeclaredAttachments) {\n const pl = nodes.length > 1;\n // eslint-disable-next-line no-console\n console.warn(\n [\n `The extension${pl ? 's' : ''} '${nodes\n .map(n => n.spec.id)\n .join(\"', '\")}' ${pl ? 'are' : 'is'}`,\n `attached to the input '${name}' of the extension '${id}', but it`,\n inputNames.length === 0\n ? 'has no inputs'\n : `has no such input (candidates are '${inputNames.join(\"', '\")}')`,\n ].join(' '),\n );\n }\n}\n\nfunction resolveV1Inputs(\n inputMap: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: AnyExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return {\n node: attachedNodes[0],\n output: resolveV1InputDataMap(\n input.extensionData,\n attachedNodes[0],\n inputName,\n ),\n };\n }\n\n return attachedNodes.map(attachment => ({\n node: attachment,\n output: resolveV1InputDataMap(input.extensionData, attachment, inputName),\n }));\n }) as {\n [inputName in string]: {\n node: AppNode;\n output: {\n [name in string]: unknown;\n };\n };\n };\n}\n\nfunction resolveV2Inputs(\n inputMap: {\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n): ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n}> {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return resolveInputDataContainer(\n input.extensionData,\n attachedNodes[0],\n inputName,\n );\n }\n\n return attachedNodes.map(attachment =>\n resolveInputDataContainer(input.extensionData, attachment, inputName),\n );\n }) as ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n }>;\n}\n\n/** @internal */\nexport function createAppNodeInstance(options: {\n node: AppNode;\n apis: ApiHolder;\n attachments: ReadonlyMap<string, AppNode[]>;\n}): AppNodeInstance {\n const { node, apis, attachments } = options;\n const { id, extension, config } = node.spec;\n const extensionData = new Map<string, unknown>();\n const extensionDataRefs = new Set<ExtensionDataRef<unknown>>();\n\n let parsedConfig: unknown;\n try {\n parsedConfig = extension.configSchema?.parse(config ?? {});\n } catch (e) {\n throw new Error(\n `Invalid configuration for extension '${id}'; caused by ${e}`,\n );\n }\n\n try {\n const internalExtension = toInternalExtension(extension);\n\n if (process.env.NODE_ENV !== 'production') {\n reportUndeclaredAttachments(id, internalExtension.inputs, attachments);\n }\n\n if (internalExtension.version === 'v1') {\n const namedOutputs = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV1Inputs(internalExtension.inputs, attachments),\n });\n\n for (const [name, output] of Object.entries(namedOutputs)) {\n const ref = internalExtension.output[name];\n if (!ref) {\n throw new Error(`unknown output provided via '${name}'`);\n }\n if (extensionData.has(ref.id)) {\n throw new Error(\n `duplicate extension data '${ref.id}' received via output '${name}'`,\n );\n }\n extensionData.set(ref.id, output);\n extensionDataRefs.add(ref);\n }\n } else if (internalExtension.version === 'v2') {\n const outputDataValues = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV2Inputs(internalExtension.inputs, attachments),\n });\n\n const outputDataMap = new Map<string, unknown>();\n for (const value of outputDataValues) {\n if (outputDataMap.has(value.id)) {\n throw new Error(`duplicate extension data output '${value.id}'`);\n }\n outputDataMap.set(value.id, value.value);\n }\n\n for (const ref of internalExtension.output) {\n const value = outputDataMap.get(ref.id);\n outputDataMap.delete(ref.id);\n if (value === undefined) {\n if (!ref.config.optional) {\n throw new Error(\n `missing required extension data output '${ref.id}'`,\n );\n }\n } else {\n extensionData.set(ref.id, value);\n extensionDataRefs.add(ref);\n }\n }\n\n if (outputDataMap.size > 0) {\n throw new Error(\n `unexpected output '${Array.from(outputDataMap.keys()).join(\n \"', '\",\n )}'`,\n );\n }\n } else {\n throw new Error(\n `unexpected extension version '${(internalExtension as any).version}'`,\n );\n }\n } catch (e) {\n throw new Error(\n `Failed to instantiate extension '${id}'${\n e.name === 'Error' ? `, ${e.message}` : `; caused by ${e.stack}`\n }`,\n );\n }\n\n return {\n getDataRefs() {\n return extensionDataRefs.values();\n },\n getData<T>(ref: ExtensionDataRef<T>): T | undefined {\n return extensionData.get(ref.id) as T | undefined;\n },\n };\n}\n\n/**\n * Starting at the provided node, instantiate all reachable nodes in the tree that have not been disabled.\n * @internal\n */\nexport function instantiateAppNodeTree(\n rootNode: AppNode,\n apis: ApiHolder,\n): void {\n function createInstance(node: AppNode): AppNodeInstance | undefined {\n if (node.instance) {\n return node.instance;\n }\n if (node.spec.disabled) {\n return undefined;\n }\n\n const instantiatedAttachments = new Map<string, AppNode[]>();\n\n for (const [input, children] of node.edges.attachments) {\n const instantiatedChildren = children.flatMap(child => {\n const childInstance = createInstance(child);\n if (!childInstance) {\n return [];\n }\n return [child];\n });\n if (instantiatedChildren.length > 0) {\n instantiatedAttachments.set(input, instantiatedChildren);\n }\n }\n\n (node as Mutable<AppNode>).instance = createAppNodeInstance({\n node,\n apis,\n attachments: instantiatedAttachments,\n });\n\n return node.instance;\n }\n\n createInstance(rootNode);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"instantiateAppNodeTree.esm.js","sources":["../../src/tree/instantiateAppNodeTree.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AnyExtensionDataRef,\n ApiHolder,\n ExtensionDataContainer,\n ExtensionDataRef,\n ExtensionInput,\n ResolvedExtensionInputs,\n} from '@backstage/frontend-plugin-api';\nimport mapValues from 'lodash/mapValues';\nimport { AppNode, AppNodeInstance } from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\ntype Mutable<T> = {\n -readonly [P in keyof T]: T[P];\n};\n\nfunction resolveV1InputDataMap(\n dataMap: {\n [name in string]: AnyExtensionDataRef;\n },\n attachment: AppNode,\n inputName: string,\n) {\n return mapValues(dataMap, ref => {\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = Object.values(dataMap)\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n return value;\n });\n}\n\nfunction resolveInputDataContainer(\n extensionData: Array<AnyExtensionDataRef>,\n attachment: AppNode,\n inputName: string,\n): { node: AppNode } & ExtensionDataContainer<AnyExtensionDataRef> {\n const dataMap = new Map<string, unknown>();\n\n for (const ref of extensionData) {\n if (dataMap.has(ref.id)) {\n throw new Error(`Unexpected duplicate input data '${ref.id}'`);\n }\n const value = attachment.instance?.getData(ref);\n if (value === undefined && !ref.config.optional) {\n const expected = extensionData\n .filter(r => !r.config.optional)\n .map(r => `'${r.id}'`)\n .join(', ');\n\n const provided = [...(attachment.instance?.getDataRefs() ?? [])]\n .map(r => `'${r.id}'`)\n .join(', ');\n\n throw new Error(\n `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`,\n );\n }\n\n dataMap.set(ref.id, value);\n }\n\n return {\n node: attachment,\n get(ref) {\n return dataMap.get(ref.id);\n },\n *[Symbol.iterator]() {\n for (const [id, value] of dataMap) {\n // TODO: Would be better to be able to create a new instance using the ref here instead\n yield {\n $$type: '@backstage/ExtensionDataValue',\n id,\n value,\n };\n }\n },\n } as { node: AppNode } & ExtensionDataContainer<AnyExtensionDataRef>;\n}\n\nfunction reportUndeclaredAttachments(\n id: string,\n inputMap: { [name in string]: unknown },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n const undeclaredAttachments = Array.from(attachments.entries()).filter(\n ([inputName]) => inputMap[inputName] === undefined,\n );\n\n const inputNames = Object.keys(inputMap);\n\n for (const [name, nodes] of undeclaredAttachments) {\n const pl = nodes.length > 1;\n // eslint-disable-next-line no-console\n console.warn(\n [\n `The extension${pl ? 's' : ''} '${nodes\n .map(n => n.spec.id)\n .join(\"', '\")}' ${pl ? 'are' : 'is'}`,\n `attached to the input '${name}' of the extension '${id}', but it`,\n inputNames.length === 0\n ? 'has no inputs'\n : `has no such input (candidates are '${inputNames.join(\"', '\")}')`,\n ].join(' '),\n );\n }\n}\n\nfunction resolveV1Inputs(\n inputMap: {\n [inputName in string]: {\n $$type: '@backstage/ExtensionInput';\n extensionData: {\n [name in string]: AnyExtensionDataRef;\n };\n config: { optional: boolean; singleton: boolean };\n };\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n) {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return {\n node: attachedNodes[0],\n output: resolveV1InputDataMap(\n input.extensionData,\n attachedNodes[0],\n inputName,\n ),\n };\n }\n\n return attachedNodes.map(attachment => ({\n node: attachment,\n output: resolveV1InputDataMap(input.extensionData, attachment, inputName),\n }));\n }) as {\n [inputName in string]: {\n node: AppNode;\n output: {\n [name in string]: unknown;\n };\n };\n };\n}\n\nfunction resolveV2Inputs(\n inputMap: {\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n },\n attachments: ReadonlyMap<string, AppNode[]>,\n): ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n}> {\n return mapValues(inputMap, (input, inputName) => {\n const attachedNodes = attachments.get(inputName) ?? [];\n\n if (input.config.singleton) {\n if (attachedNodes.length > 1) {\n const attachedNodeIds = attachedNodes.map(e => e.spec.id);\n throw Error(\n `expected ${\n input.config.optional ? 'at most' : 'exactly'\n } one '${inputName}' input but received multiple: '${attachedNodeIds.join(\n \"', '\",\n )}'`,\n );\n } else if (attachedNodes.length === 0) {\n if (input.config.optional) {\n return undefined;\n }\n throw Error(`input '${inputName}' is required but was not received`);\n }\n return resolveInputDataContainer(\n input.extensionData,\n attachedNodes[0],\n inputName,\n );\n }\n\n return attachedNodes.map(attachment =>\n resolveInputDataContainer(input.extensionData, attachment, inputName),\n );\n }) as ResolvedExtensionInputs<{\n [inputName in string]: ExtensionInput<\n AnyExtensionDataRef,\n { optional: boolean; singleton: boolean }\n >;\n }>;\n}\n\n/** @internal */\nexport function createAppNodeInstance(options: {\n node: AppNode;\n apis: ApiHolder;\n attachments: ReadonlyMap<string, AppNode[]>;\n}): AppNodeInstance {\n const { node, apis, attachments } = options;\n const { id, extension, config } = node.spec;\n const extensionData = new Map<string, unknown>();\n const extensionDataRefs = new Set<ExtensionDataRef<unknown>>();\n\n let parsedConfig: unknown;\n try {\n parsedConfig = extension.configSchema?.parse(config ?? {});\n } catch (e) {\n throw new Error(\n `Invalid configuration for extension '${id}'; caused by ${e}`,\n );\n }\n\n try {\n const internalExtension = toInternalExtension(extension);\n\n if (process.env.NODE_ENV !== 'production') {\n reportUndeclaredAttachments(id, internalExtension.inputs, attachments);\n }\n\n if (internalExtension.version === 'v1') {\n const namedOutputs = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV1Inputs(internalExtension.inputs, attachments),\n });\n\n for (const [name, output] of Object.entries(namedOutputs)) {\n const ref = internalExtension.output[name];\n if (!ref) {\n throw new Error(`unknown output provided via '${name}'`);\n }\n if (extensionData.has(ref.id)) {\n throw new Error(\n `duplicate extension data '${ref.id}' received via output '${name}'`,\n );\n }\n extensionData.set(ref.id, output);\n extensionDataRefs.add(ref);\n }\n } else if (internalExtension.version === 'v2') {\n const outputDataValues = internalExtension.factory({\n node,\n apis,\n config: parsedConfig,\n inputs: resolveV2Inputs(internalExtension.inputs, attachments),\n });\n\n const outputDataMap = new Map<string, unknown>();\n for (const value of outputDataValues) {\n if (outputDataMap.has(value.id)) {\n throw new Error(`duplicate extension data output '${value.id}'`);\n }\n outputDataMap.set(value.id, value.value);\n }\n\n for (const ref of internalExtension.output) {\n const value = outputDataMap.get(ref.id);\n outputDataMap.delete(ref.id);\n if (value === undefined) {\n if (!ref.config.optional) {\n throw new Error(\n `missing required extension data output '${ref.id}'`,\n );\n }\n } else {\n extensionData.set(ref.id, value);\n extensionDataRefs.add(ref);\n }\n }\n\n if (outputDataMap.size > 0) {\n throw new Error(\n `unexpected output '${Array.from(outputDataMap.keys()).join(\n \"', '\",\n )}'`,\n );\n }\n } else {\n throw new Error(\n `unexpected extension version '${(internalExtension as any).version}'`,\n );\n }\n } catch (e) {\n throw new Error(\n `Failed to instantiate extension '${id}'${\n e.name === 'Error' ? `, ${e.message}` : `; caused by ${e.stack}`\n }`,\n );\n }\n\n return {\n getDataRefs() {\n return extensionDataRefs.values();\n },\n getData<T>(ref: ExtensionDataRef<T>): T | undefined {\n return extensionData.get(ref.id) as T | undefined;\n },\n };\n}\n\n/**\n * Starting at the provided node, instantiate all reachable nodes in the tree that have not been disabled.\n * @internal\n */\nexport function instantiateAppNodeTree(\n rootNode: AppNode,\n apis: ApiHolder,\n): void {\n function createInstance(node: AppNode): AppNodeInstance | undefined {\n if (node.instance) {\n return node.instance;\n }\n if (node.spec.disabled) {\n return undefined;\n }\n\n const instantiatedAttachments = new Map<string, AppNode[]>();\n\n for (const [input, children] of node.edges.attachments) {\n const instantiatedChildren = children.flatMap(child => {\n const childInstance = createInstance(child);\n if (!childInstance) {\n return [];\n }\n return [child];\n });\n if (instantiatedChildren.length > 0) {\n instantiatedAttachments.set(input, instantiatedChildren);\n }\n }\n\n (node as Mutable<AppNode>).instance = createAppNodeInstance({\n node,\n apis,\n attachments: instantiatedAttachments,\n });\n\n return node.instance;\n }\n\n createInstance(rootNode);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readAppExtensionsConfig.esm.js","sources":["../../src/tree/readAppExtensionsConfig.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 { Config } from '@backstage/config';\nimport { JsonValue } from '@backstage/types';\n\nexport interface ExtensionParameters {\n id: string;\n attachTo?: { id: string; input: string };\n disabled?: boolean;\n config?: unknown;\n}\n\nconst knownExtensionParameters = ['attachTo', 'disabled', 'config'];\n\n// Since we'll never merge arrays in config the config reader context\n// isn't too much of a help. Fall back to manual config reading logic\n// as the Config interface makes it quite hard for us otherwise.\n/** @internal */\nexport function readAppExtensionsConfig(\n rootConfig: Config,\n): ExtensionParameters[] {\n const arr = rootConfig.getOptional('app.extensions');\n if (!Array.isArray(arr)) {\n if (arr === undefined) {\n return [];\n }\n // This will throw, and show which part of config had the wrong type\n rootConfig.getConfigArray('app.extensions');\n return [];\n }\n\n return arr.map((arrayEntry, arrayIndex) =>\n expandShorthandExtensionParameters(arrayEntry, arrayIndex),\n );\n}\n\n/** @internal */\nexport function expandShorthandExtensionParameters(\n arrayEntry: JsonValue,\n arrayIndex: number,\n): ExtensionParameters {\n function errorMsg(msg: string, key?: string, prop?: string) {\n return `Invalid extension configuration at app.extensions[${arrayIndex}]${\n key ? `[${key}]` : ''\n }${prop ? `.${prop}` : ''}, ${msg}`;\n }\n\n // NOTE(freben): This check is intentionally not complete and doesn't check\n // whether letters and digits are used, etc. It's not up to the config reading\n // logic to decide what constitutes a valid extension ID; that should be\n // decided by the logic that loads and instantiates the extensions. This check\n // is just here to catch real mistakes or truly conceptually wrong input.\n function assertValidId(id: string) {\n if (!id || id !== id.trim()) {\n throw new Error(\n errorMsg('extension ID must not be empty or contain whitespace'),\n );\n }\n }\n\n // Example YAML:\n // - entity.card.about\n if (typeof arrayEntry === 'string') {\n assertValidId(arrayEntry);\n return {\n id: arrayEntry,\n disabled: false,\n };\n }\n\n // All remaining cases are single-key objects\n if (\n typeof arrayEntry !== 'object' ||\n arrayEntry === null ||\n Array.isArray(arrayEntry)\n ) {\n throw new Error(errorMsg('must be a string or an object'));\n }\n const keys = Object.keys(arrayEntry);\n if (keys.length !== 1) {\n const joinedKeys = keys.length ? `'${keys.join(\"', '\")}'` : 'none';\n throw new Error(errorMsg(`must have exactly one key, got ${joinedKeys}`));\n }\n\n const id = String(keys[0]);\n const value = arrayEntry[id];\n assertValidId(id);\n\n // This example covers a potentially common mistake in the syntax\n // Example YAML:\n // - entity.card.about:\n if (value === null) {\n return {\n id,\n disabled: false,\n };\n }\n\n // Example YAML:\n // - catalog.page.cicd: false\n if (typeof value === 'boolean') {\n return {\n id,\n disabled: !value,\n };\n }\n\n // The remaining case is the generic object. Example YAML:\n // - tech-radar.page:\n // at: core.router/routes\n // disabled: false\n // config:\n // path: /tech-radar\n // width: 1500\n // height: 800\n if (typeof value !== 'object' || Array.isArray(value)) {\n // We don't mention null here - we don't want people to explicitly enter\n // - entity.card.about: null\n throw new Error(errorMsg('value must be a boolean or object', id));\n }\n\n const attachTo = value.attachTo as { id: string; input: string } | undefined;\n const disabled = value.disabled;\n const config = value.config;\n\n if (attachTo !== undefined) {\n if (\n attachTo === null ||\n typeof attachTo !== 'object' ||\n Array.isArray(attachTo)\n ) {\n throw new Error(errorMsg('must be an object', id, 'attachTo'));\n }\n if (typeof attachTo.id !== 'string' || attachTo.id === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.id'),\n );\n }\n if (typeof attachTo.input !== 'string' || attachTo.input === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.input'),\n );\n }\n }\n if (disabled !== undefined && typeof disabled !== 'boolean') {\n throw new Error(errorMsg('must be a boolean', id, 'disabled'));\n }\n if (\n config !== undefined &&\n (typeof config !== 'object' || config === null || Array.isArray(config))\n ) {\n throw new Error(errorMsg('must be an object', id, 'config'));\n }\n\n const unknownKeys = Object.keys(value).filter(\n k => !knownExtensionParameters.includes(k),\n );\n if (unknownKeys.length > 0) {\n throw new Error(\n errorMsg(\n `unknown parameter; expected one of '${knownExtensionParameters.join(\n \"', '\",\n )}'`,\n id,\n unknownKeys.join(', '),\n ),\n );\n }\n\n return {\n id,\n attachTo,\n disabled,\n config,\n };\n}\n"],"names":["id"],"mappings":"AA0BA,MAAM,wBAA2B,GAAA,CAAC,UAAY,EAAA,UAAA,EAAY,QAAQ,CAAA
|
|
1
|
+
{"version":3,"file":"readAppExtensionsConfig.esm.js","sources":["../../src/tree/readAppExtensionsConfig.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 { Config } from '@backstage/config';\nimport { JsonValue } from '@backstage/types';\n\nexport interface ExtensionParameters {\n id: string;\n attachTo?: { id: string; input: string };\n disabled?: boolean;\n config?: unknown;\n}\n\nconst knownExtensionParameters = ['attachTo', 'disabled', 'config'];\n\n// Since we'll never merge arrays in config the config reader context\n// isn't too much of a help. Fall back to manual config reading logic\n// as the Config interface makes it quite hard for us otherwise.\n/** @internal */\nexport function readAppExtensionsConfig(\n rootConfig: Config,\n): ExtensionParameters[] {\n const arr = rootConfig.getOptional('app.extensions');\n if (!Array.isArray(arr)) {\n if (arr === undefined) {\n return [];\n }\n // This will throw, and show which part of config had the wrong type\n rootConfig.getConfigArray('app.extensions');\n return [];\n }\n\n return arr.map((arrayEntry, arrayIndex) =>\n expandShorthandExtensionParameters(arrayEntry, arrayIndex),\n );\n}\n\n/** @internal */\nexport function expandShorthandExtensionParameters(\n arrayEntry: JsonValue,\n arrayIndex: number,\n): ExtensionParameters {\n function errorMsg(msg: string, key?: string, prop?: string) {\n return `Invalid extension configuration at app.extensions[${arrayIndex}]${\n key ? `[${key}]` : ''\n }${prop ? `.${prop}` : ''}, ${msg}`;\n }\n\n // NOTE(freben): This check is intentionally not complete and doesn't check\n // whether letters and digits are used, etc. It's not up to the config reading\n // logic to decide what constitutes a valid extension ID; that should be\n // decided by the logic that loads and instantiates the extensions. This check\n // is just here to catch real mistakes or truly conceptually wrong input.\n function assertValidId(id: string) {\n if (!id || id !== id.trim()) {\n throw new Error(\n errorMsg('extension ID must not be empty or contain whitespace'),\n );\n }\n }\n\n // Example YAML:\n // - entity.card.about\n if (typeof arrayEntry === 'string') {\n assertValidId(arrayEntry);\n return {\n id: arrayEntry,\n disabled: false,\n };\n }\n\n // All remaining cases are single-key objects\n if (\n typeof arrayEntry !== 'object' ||\n arrayEntry === null ||\n Array.isArray(arrayEntry)\n ) {\n throw new Error(errorMsg('must be a string or an object'));\n }\n const keys = Object.keys(arrayEntry);\n if (keys.length !== 1) {\n const joinedKeys = keys.length ? `'${keys.join(\"', '\")}'` : 'none';\n throw new Error(errorMsg(`must have exactly one key, got ${joinedKeys}`));\n }\n\n const id = String(keys[0]);\n const value = arrayEntry[id];\n assertValidId(id);\n\n // This example covers a potentially common mistake in the syntax\n // Example YAML:\n // - entity.card.about:\n if (value === null) {\n return {\n id,\n disabled: false,\n };\n }\n\n // Example YAML:\n // - catalog.page.cicd: false\n if (typeof value === 'boolean') {\n return {\n id,\n disabled: !value,\n };\n }\n\n // The remaining case is the generic object. Example YAML:\n // - tech-radar.page:\n // at: core.router/routes\n // disabled: false\n // config:\n // path: /tech-radar\n // width: 1500\n // height: 800\n if (typeof value !== 'object' || Array.isArray(value)) {\n // We don't mention null here - we don't want people to explicitly enter\n // - entity.card.about: null\n throw new Error(errorMsg('value must be a boolean or object', id));\n }\n\n const attachTo = value.attachTo as { id: string; input: string } | undefined;\n const disabled = value.disabled;\n const config = value.config;\n\n if (attachTo !== undefined) {\n if (\n attachTo === null ||\n typeof attachTo !== 'object' ||\n Array.isArray(attachTo)\n ) {\n throw new Error(errorMsg('must be an object', id, 'attachTo'));\n }\n if (typeof attachTo.id !== 'string' || attachTo.id === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.id'),\n );\n }\n if (typeof attachTo.input !== 'string' || attachTo.input === '') {\n throw new Error(\n errorMsg('must be a non-empty string', id, 'attachTo.input'),\n );\n }\n }\n if (disabled !== undefined && typeof disabled !== 'boolean') {\n throw new Error(errorMsg('must be a boolean', id, 'disabled'));\n }\n if (\n config !== undefined &&\n (typeof config !== 'object' || config === null || Array.isArray(config))\n ) {\n throw new Error(errorMsg('must be an object', id, 'config'));\n }\n\n const unknownKeys = Object.keys(value).filter(\n k => !knownExtensionParameters.includes(k),\n );\n if (unknownKeys.length > 0) {\n throw new Error(\n errorMsg(\n `unknown parameter; expected one of '${knownExtensionParameters.join(\n \"', '\",\n )}'`,\n id,\n unknownKeys.join(', '),\n ),\n );\n }\n\n return {\n id,\n attachTo,\n disabled,\n config,\n };\n}\n"],"names":["id"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveAppNodeSpecs.esm.js","sources":["../../src/tree/resolveAppNodeSpecs.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Extension } from '@backstage/frontend-plugin-api';\nimport { ExtensionParameters } from './readAppExtensionsConfig';\nimport { AppNodeSpec } from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isInternalFrontendModule,\n toInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { FrontendFeature } from '../wiring';\n\n/** @internal */\nexport function resolveAppNodeSpecs(options: {\n features?: FrontendFeature[];\n builtinExtensions?: Extension<any, any>[];\n parameters?: Array<ExtensionParameters>;\n forbidden?: Set<string>;\n}): AppNodeSpec[] {\n const {\n builtinExtensions = [],\n parameters = [],\n forbidden = new Set(),\n features = [],\n } = options;\n\n const plugins = features.filter(OpaqueFrontendPlugin.isType);\n const modules = features.filter(isInternalFrontendModule);\n\n const pluginExtensions = plugins.flatMap(source => {\n return OpaqueFrontendPlugin.toInternal(source).extensions.map(\n extension => ({\n ...extension,\n source,\n }),\n );\n });\n const moduleExtensions = modules.flatMap(mod =>\n toInternalFrontendModule(mod).extensions.flatMap(extension => {\n // Modules for plugins that are not installed are ignored\n const source = plugins.find(p => p.id === mod.pluginId);\n if (!source) {\n return [];\n }\n\n return [{ ...extension, source }];\n }),\n );\n\n // Prevent core override\n if (pluginExtensions.some(({ id }) => forbidden.has(id))) {\n const pluginsStr = pluginExtensions\n .filter(({ id }) => forbidden.has(id))\n .map(({ source }) => `'${source.id}'`)\n .join(', ');\n const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', ');\n throw new Error(\n `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by the following plugin(s): ${pluginsStr}`,\n );\n }\n if (moduleExtensions.some(({ id }) => forbidden.has(id))) {\n const pluginsStr = moduleExtensions\n .filter(({ id }) => forbidden.has(id))\n .map(({ source }) => `'${source.id}'`)\n .join(', ');\n const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', ');\n throw new Error(\n `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by a module for the following plugin(s): ${pluginsStr}`,\n );\n }\n\n const configuredExtensions = [\n ...pluginExtensions.map(({ source, ...extension }) => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n source,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n config: undefined as unknown,\n },\n };\n }),\n ...builtinExtensions.map(extension => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n source: undefined,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n config: undefined as unknown,\n },\n };\n }),\n ];\n\n // Install all module overrides\n for (const extension of moduleExtensions) {\n const internalExtension = toInternalExtension(extension);\n\n // Check if our override is overriding an extension that already exists\n const index = configuredExtensions.findIndex(\n e => e.extension.id === extension.id,\n );\n if (index !== -1) {\n // Only implementation, attachment point and default disabled status are overridden, the source is kept\n configuredExtensions[index].extension = internalExtension;\n configuredExtensions[index].params.attachTo = internalExtension.attachTo;\n configuredExtensions[index].params.disabled = internalExtension.disabled;\n } else {\n // Add the extension as a new one when not overriding an existing one\n configuredExtensions.push({\n extension: internalExtension,\n params: {\n source: extension.source,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n config: undefined,\n },\n });\n }\n }\n\n const duplicatedExtensionIds = new Set<string>();\n const duplicatedExtensionData = configuredExtensions.reduce<\n Record<string, Record<string, number>>\n >((data, { extension, params }) => {\n const extensionId = extension.id;\n const extensionData = data?.[extensionId];\n if (extensionData) duplicatedExtensionIds.add(extensionId);\n const pluginId = params.source?.id ?? 'internal';\n const pluginCount = extensionData?.[pluginId] ?? 0;\n return {\n ...data,\n [extensionId]: { ...extensionData, [pluginId]: pluginCount + 1 },\n };\n }, {});\n\n if (duplicatedExtensionIds.size > 0) {\n throw new Error(\n `The following extensions are duplicated: ${Array.from(\n duplicatedExtensionIds,\n )\n .map(\n extensionId =>\n `The extension '${extensionId}' was provided ${Object.keys(\n duplicatedExtensionData[extensionId],\n )\n .map(\n pluginId =>\n `${duplicatedExtensionData[extensionId][pluginId]} time(s) by the plugin '${pluginId}'`,\n )\n .join(' and ')}`,\n )\n .join(', ')}`,\n );\n }\n\n const order = new Map<string, (typeof configuredExtensions)[number]>();\n for (const overrideParam of parameters) {\n const extensionId = overrideParam.id;\n\n if (forbidden.has(extensionId)) {\n throw new Error(\n `Configuration of the '${extensionId}' extension is forbidden`,\n );\n }\n\n const existing = configuredExtensions.find(\n e => e.extension.id === extensionId,\n );\n if (existing) {\n if (overrideParam.attachTo) {\n existing.params.attachTo = overrideParam.attachTo;\n }\n if (overrideParam.config) {\n // TODO: merge config?\n existing.params.config = overrideParam.config;\n }\n if (\n Boolean(existing.params.disabled) !== Boolean(overrideParam.disabled)\n ) {\n existing.params.disabled = Boolean(overrideParam.disabled);\n }\n order.set(extensionId, existing);\n } else {\n throw new Error(`Extension ${extensionId} does not exist`);\n }\n }\n\n const orderedExtensions = [\n ...order.values(),\n ...configuredExtensions.filter(e => !order.has(e.extension.id)),\n ];\n\n return orderedExtensions.map(param => ({\n id: param.extension.id,\n attachTo: param.params.attachTo,\n extension: param.extension,\n disabled: param.params.disabled,\n source: param.params.source,\n config: param.params.config,\n }));\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolveAppNodeSpecs.esm.js","sources":["../../src/tree/resolveAppNodeSpecs.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Extension } from '@backstage/frontend-plugin-api';\nimport { ExtensionParameters } from './readAppExtensionsConfig';\nimport { AppNodeSpec } from '@backstage/frontend-plugin-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n isInternalFrontendModule,\n toInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\nimport { FrontendFeature } from '../wiring';\n\n/** @internal */\nexport function resolveAppNodeSpecs(options: {\n features?: FrontendFeature[];\n builtinExtensions?: Extension<any, any>[];\n parameters?: Array<ExtensionParameters>;\n forbidden?: Set<string>;\n}): AppNodeSpec[] {\n const {\n builtinExtensions = [],\n parameters = [],\n forbidden = new Set(),\n features = [],\n } = options;\n\n const plugins = features.filter(OpaqueFrontendPlugin.isType);\n const modules = features.filter(isInternalFrontendModule);\n\n const pluginExtensions = plugins.flatMap(source => {\n return OpaqueFrontendPlugin.toInternal(source).extensions.map(\n extension => ({\n ...extension,\n source,\n }),\n );\n });\n const moduleExtensions = modules.flatMap(mod =>\n toInternalFrontendModule(mod).extensions.flatMap(extension => {\n // Modules for plugins that are not installed are ignored\n const source = plugins.find(p => p.id === mod.pluginId);\n if (!source) {\n return [];\n }\n\n return [{ ...extension, source }];\n }),\n );\n\n // Prevent core override\n if (pluginExtensions.some(({ id }) => forbidden.has(id))) {\n const pluginsStr = pluginExtensions\n .filter(({ id }) => forbidden.has(id))\n .map(({ source }) => `'${source.id}'`)\n .join(', ');\n const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', ');\n throw new Error(\n `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by the following plugin(s): ${pluginsStr}`,\n );\n }\n if (moduleExtensions.some(({ id }) => forbidden.has(id))) {\n const pluginsStr = moduleExtensions\n .filter(({ id }) => forbidden.has(id))\n .map(({ source }) => `'${source.id}'`)\n .join(', ');\n const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', ');\n throw new Error(\n `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by a module for the following plugin(s): ${pluginsStr}`,\n );\n }\n\n const configuredExtensions = [\n ...pluginExtensions.map(({ source, ...extension }) => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n source,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n config: undefined as unknown,\n },\n };\n }),\n ...builtinExtensions.map(extension => {\n const internalExtension = toInternalExtension(extension);\n return {\n extension: internalExtension,\n params: {\n source: undefined,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n config: undefined as unknown,\n },\n };\n }),\n ];\n\n // Install all module overrides\n for (const extension of moduleExtensions) {\n const internalExtension = toInternalExtension(extension);\n\n // Check if our override is overriding an extension that already exists\n const index = configuredExtensions.findIndex(\n e => e.extension.id === extension.id,\n );\n if (index !== -1) {\n // Only implementation, attachment point and default disabled status are overridden, the source is kept\n configuredExtensions[index].extension = internalExtension;\n configuredExtensions[index].params.attachTo = internalExtension.attachTo;\n configuredExtensions[index].params.disabled = internalExtension.disabled;\n } else {\n // Add the extension as a new one when not overriding an existing one\n configuredExtensions.push({\n extension: internalExtension,\n params: {\n source: extension.source,\n attachTo: internalExtension.attachTo,\n disabled: internalExtension.disabled,\n config: undefined,\n },\n });\n }\n }\n\n const duplicatedExtensionIds = new Set<string>();\n const duplicatedExtensionData = configuredExtensions.reduce<\n Record<string, Record<string, number>>\n >((data, { extension, params }) => {\n const extensionId = extension.id;\n const extensionData = data?.[extensionId];\n if (extensionData) duplicatedExtensionIds.add(extensionId);\n const pluginId = params.source?.id ?? 'internal';\n const pluginCount = extensionData?.[pluginId] ?? 0;\n return {\n ...data,\n [extensionId]: { ...extensionData, [pluginId]: pluginCount + 1 },\n };\n }, {});\n\n if (duplicatedExtensionIds.size > 0) {\n throw new Error(\n `The following extensions are duplicated: ${Array.from(\n duplicatedExtensionIds,\n )\n .map(\n extensionId =>\n `The extension '${extensionId}' was provided ${Object.keys(\n duplicatedExtensionData[extensionId],\n )\n .map(\n pluginId =>\n `${duplicatedExtensionData[extensionId][pluginId]} time(s) by the plugin '${pluginId}'`,\n )\n .join(' and ')}`,\n )\n .join(', ')}`,\n );\n }\n\n const order = new Map<string, (typeof configuredExtensions)[number]>();\n for (const overrideParam of parameters) {\n const extensionId = overrideParam.id;\n\n if (forbidden.has(extensionId)) {\n throw new Error(\n `Configuration of the '${extensionId}' extension is forbidden`,\n );\n }\n\n const existing = configuredExtensions.find(\n e => e.extension.id === extensionId,\n );\n if (existing) {\n if (overrideParam.attachTo) {\n existing.params.attachTo = overrideParam.attachTo;\n }\n if (overrideParam.config) {\n // TODO: merge config?\n existing.params.config = overrideParam.config;\n }\n if (\n Boolean(existing.params.disabled) !== Boolean(overrideParam.disabled)\n ) {\n existing.params.disabled = Boolean(overrideParam.disabled);\n }\n order.set(extensionId, existing);\n } else {\n throw new Error(`Extension ${extensionId} does not exist`);\n }\n }\n\n const orderedExtensions = [\n ...order.values(),\n ...configuredExtensions.filter(e => !order.has(e.extension.id)),\n ];\n\n return orderedExtensions.map(param => ({\n id: param.extension.id,\n attachTo: param.params.attachTo,\n extension: param.extension,\n disabled: param.params.disabled,\n source: param.params.source,\n config: param.params.config,\n }));\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolveAppTree.esm.js","sources":["../../src/tree/resolveAppTree.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AppTree,\n AppNode,\n AppNodeInstance,\n AppNodeSpec,\n} from '@backstage/frontend-plugin-api';\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nfunction indent(str: string) {\n return str.replace(/^/gm, ' ');\n}\n\n/** @internal */\nclass SerializableAppNode implements AppNode {\n public readonly spec: AppNodeSpec;\n public readonly edges = {\n attachedTo: undefined as { node: AppNode; input: string } | undefined,\n attachments: new Map<string, SerializableAppNode[]>(),\n };\n public readonly instance?: AppNodeInstance;\n\n constructor(spec: AppNodeSpec) {\n this.spec = spec;\n }\n\n setParent(parent: SerializableAppNode, input: string) {\n this.edges.attachedTo = { node: parent, input };\n\n const parentInputEdges = parent.edges.attachments.get(input);\n if (parentInputEdges) {\n parentInputEdges.push(this);\n } else {\n parent.edges.attachments.set(input, [this]);\n }\n }\n\n toJSON() {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n return {\n id: this.spec.id,\n output:\n dataRefs && dataRefs.length > 0\n ? dataRefs.map(ref => ref.id)\n : undefined,\n attachments:\n this.edges.attachments.size > 0\n ? Object.fromEntries(this.edges.attachments)\n : undefined,\n };\n }\n\n toString(): string {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n const out =\n dataRefs && dataRefs.length > 0\n ? ` out=[${[...dataRefs].map(r => r.id).join(', ')}]`\n : '';\n\n if (this.edges.attachments.size === 0) {\n return `<${this.spec.id}${out} />`;\n }\n\n return [\n `<${this.spec.id}${out}>`,\n ...[...this.edges.attachments.entries()].map(([k, v]) =>\n indent([`${k} [`, ...v.map(e => indent(e.toString())), `]`].join('\\n')),\n ),\n `</${this.spec.id}>`,\n ].join('\\n');\n }\n}\n\nfunction makeRedirectKey(attachTo: { id: string; input: string }) {\n return `${attachTo.id}%${attachTo.input}`;\n}\n\nconst isValidAttachmentPoint = (\n attachTo: { id: string; input: string },\n nodes: Map<string, SerializableAppNode>,\n) => {\n if (!nodes.has(attachTo.id)) {\n return false;\n }\n\n return (\n attachTo.input in\n toInternalExtension(nodes.get(attachTo.id)!.spec.extension).inputs\n );\n};\n\n/**\n * Build the app tree by iterating through all node specs and constructing the app\n * tree with all attachments in the same order as they appear in the input specs array.\n * @internal\n */\nexport function resolveAppTree(\n rootNodeId: string,\n specs: AppNodeSpec[],\n): AppTree {\n const nodes = new Map<string, SerializableAppNode>();\n\n const redirectTargetsByKey = new Map<string, { id: string; input: string }>();\n\n for (const spec of specs) {\n // The main check with a more helpful error message happens in resolveAppNodeSpecs\n if (nodes.has(spec.id)) {\n throw new Error(`Unexpected duplicate extension id '${spec.id}'`);\n }\n\n const node = new SerializableAppNode(spec);\n nodes.set(spec.id, node);\n\n const internal = toInternalExtension(spec.extension);\n for (const [inputName, input] of Object.entries(internal.inputs)) {\n if (input.replaces) {\n for (const replace of input.replaces) {\n const key = makeRedirectKey(replace);\n if (redirectTargetsByKey.has(key)) {\n throw new Error(\n `Duplicate redirect target for input '${inputName}' in extension '${spec.id}'`,\n );\n }\n redirectTargetsByKey.set(key, { id: spec.id, input: inputName });\n }\n }\n }\n }\n\n const orphans = new Array<SerializableAppNode>();\n\n // A node with the provided rootNodeId must be found in the tree, and it must not be attached to anything\n let rootNode: AppNode | undefined = undefined;\n\n for (const node of nodes.values()) {\n const spec = node.spec;\n\n // TODO: For now we simply ignore the attachTo spec of the root node, but it'd be cleaner if we could avoid defining it\n if (spec.id === rootNodeId) {\n rootNode = node;\n } else {\n let attachTo = node.spec.attachTo;\n\n if (!isValidAttachmentPoint(attachTo, nodes)) {\n attachTo =\n redirectTargetsByKey.get(makeRedirectKey(attachTo)) ?? attachTo;\n }\n\n const parent = nodes.get(attachTo.id);\n if (parent) {\n node.setParent(parent, attachTo.input);\n } else {\n orphans.push(node);\n }\n }\n }\n\n if (!rootNode) {\n throw new Error(`No root node with id '${rootNodeId}' found in app tree`);\n }\n\n return {\n root: rootNode,\n nodes,\n orphans,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"resolveAppTree.esm.js","sources":["../../src/tree/resolveAppTree.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AppTree,\n AppNode,\n AppNodeInstance,\n AppNodeSpec,\n} from '@backstage/frontend-plugin-api';\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nfunction indent(str: string) {\n return str.replace(/^/gm, ' ');\n}\n\n/** @internal */\nclass SerializableAppNode implements AppNode {\n public readonly spec: AppNodeSpec;\n public readonly edges = {\n attachedTo: undefined as { node: AppNode; input: string } | undefined,\n attachments: new Map<string, SerializableAppNode[]>(),\n };\n public readonly instance?: AppNodeInstance;\n\n constructor(spec: AppNodeSpec) {\n this.spec = spec;\n }\n\n setParent(parent: SerializableAppNode, input: string) {\n this.edges.attachedTo = { node: parent, input };\n\n const parentInputEdges = parent.edges.attachments.get(input);\n if (parentInputEdges) {\n parentInputEdges.push(this);\n } else {\n parent.edges.attachments.set(input, [this]);\n }\n }\n\n toJSON() {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n return {\n id: this.spec.id,\n output:\n dataRefs && dataRefs.length > 0\n ? dataRefs.map(ref => ref.id)\n : undefined,\n attachments:\n this.edges.attachments.size > 0\n ? Object.fromEntries(this.edges.attachments)\n : undefined,\n };\n }\n\n toString(): string {\n const dataRefs = this.instance && [...this.instance.getDataRefs()];\n const out =\n dataRefs && dataRefs.length > 0\n ? ` out=[${[...dataRefs].map(r => r.id).join(', ')}]`\n : '';\n\n if (this.edges.attachments.size === 0) {\n return `<${this.spec.id}${out} />`;\n }\n\n return [\n `<${this.spec.id}${out}>`,\n ...[...this.edges.attachments.entries()].map(([k, v]) =>\n indent([`${k} [`, ...v.map(e => indent(e.toString())), `]`].join('\\n')),\n ),\n `</${this.spec.id}>`,\n ].join('\\n');\n }\n}\n\nfunction makeRedirectKey(attachTo: { id: string; input: string }) {\n return `${attachTo.id}%${attachTo.input}`;\n}\n\nconst isValidAttachmentPoint = (\n attachTo: { id: string; input: string },\n nodes: Map<string, SerializableAppNode>,\n) => {\n if (!nodes.has(attachTo.id)) {\n return false;\n }\n\n return (\n attachTo.input in\n toInternalExtension(nodes.get(attachTo.id)!.spec.extension).inputs\n );\n};\n\n/**\n * Build the app tree by iterating through all node specs and constructing the app\n * tree with all attachments in the same order as they appear in the input specs array.\n * @internal\n */\nexport function resolveAppTree(\n rootNodeId: string,\n specs: AppNodeSpec[],\n): AppTree {\n const nodes = new Map<string, SerializableAppNode>();\n\n const redirectTargetsByKey = new Map<string, { id: string; input: string }>();\n\n for (const spec of specs) {\n // The main check with a more helpful error message happens in resolveAppNodeSpecs\n if (nodes.has(spec.id)) {\n throw new Error(`Unexpected duplicate extension id '${spec.id}'`);\n }\n\n const node = new SerializableAppNode(spec);\n nodes.set(spec.id, node);\n\n const internal = toInternalExtension(spec.extension);\n for (const [inputName, input] of Object.entries(internal.inputs)) {\n if (input.replaces) {\n for (const replace of input.replaces) {\n const key = makeRedirectKey(replace);\n if (redirectTargetsByKey.has(key)) {\n throw new Error(\n `Duplicate redirect target for input '${inputName}' in extension '${spec.id}'`,\n );\n }\n redirectTargetsByKey.set(key, { id: spec.id, input: inputName });\n }\n }\n }\n }\n\n const orphans = new Array<SerializableAppNode>();\n\n // A node with the provided rootNodeId must be found in the tree, and it must not be attached to anything\n let rootNode: AppNode | undefined = undefined;\n\n for (const node of nodes.values()) {\n const spec = node.spec;\n\n // TODO: For now we simply ignore the attachTo spec of the root node, but it'd be cleaner if we could avoid defining it\n if (spec.id === rootNodeId) {\n rootNode = node;\n } else {\n let attachTo = node.spec.attachTo;\n\n if (!isValidAttachmentPoint(attachTo, nodes)) {\n attachTo =\n redirectTargetsByKey.get(makeRedirectKey(attachTo)) ?? attachTo;\n }\n\n const parent = nodes.get(attachTo.id);\n if (parent) {\n node.setParent(parent, attachTo.input);\n } else {\n orphans.push(node);\n }\n }\n }\n\n if (!rootNode) {\n throw new Error(`No root node with id '${rootNodeId}' found in app tree`);\n }\n\n return {\n root: rootNode,\n nodes,\n orphans,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createSpecializedApp.esm.js","sources":["../../src/wiring/createSpecializedApp.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { JSX } from 'react';\nimport { ConfigReader } from '@backstage/config';\nimport {\n ApiBlueprint,\n AppTree,\n AppTreeApi,\n appTreeApiRef,\n coreExtensionData,\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApiResolveOptions,\n RouteResolutionApi,\n createApiFactory,\n routeResolutionApiRef,\n} from '@backstage/frontend-plugin-api';\nimport {\n AnyApiFactory,\n ApiHolder,\n ConfigApi,\n configApiRef,\n featureFlagsApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nimport { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode';\n\nimport { CreateAppRouteBinder } from '../routing';\nimport { RouteResolver } from '../routing/RouteResolver';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalFrontendModule,\n isInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\nimport { getBasePath } from '../routing/getBasePath';\nimport { Root } from '../extensions/Root';\nimport { resolveAppTree } from '../tree/resolveAppTree';\nimport { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs';\nimport { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig';\nimport { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';\nimport { BackstageRouteObject } from '../routing/types';\nimport { FrontendFeature } from './types';\n\nfunction deduplicateFeatures(\n allFeatures: FrontendFeature[],\n): FrontendFeature[] {\n // Start by removing duplicates by reference\n const features = Array.from(new Set(allFeatures));\n\n // Plugins are deduplicated by ID, last one wins\n const seenIds = new Set<string>();\n return features\n .reverse()\n .filter(feature => {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n return true;\n }\n if (seenIds.has(feature.id)) {\n return false;\n }\n seenIds.add(feature.id);\n return true;\n })\n .reverse();\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass AppTreeApiProxy implements AppTreeApi {\n #safeToUse: boolean = false;\n\n constructor(private readonly tree: AppTree) {}\n\n getTree() {\n if (!this.#safeToUse) {\n throw new Error(\n `You can't access the AppTreeApi during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n return { tree: this.tree };\n }\n\n initialize() {\n this.#safeToUse = true;\n }\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass RouteResolutionApiProxy implements RouteResolutionApi {\n #delegate: RouteResolutionApi | undefined;\n #routeObjects: BackstageRouteObject[] | undefined;\n\n constructor(\n private readonly tree: AppTree,\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly basePath: string,\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: RouteResolutionApiResolveOptions,\n ): RouteFunc<TParams> | undefined {\n if (!this.#delegate) {\n throw new Error(\n `You can't access the RouteResolver during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n\n return this.#delegate.resolve(anyRouteRef, options);\n }\n\n initialize() {\n const routeInfo = extractRouteInfoFromAppNode(this.tree.root);\n\n this.#delegate = new RouteResolver(\n routeInfo.routePaths,\n routeInfo.routeParents,\n routeInfo.routeObjects,\n this.routeBindings,\n this.basePath,\n );\n this.#routeObjects = routeInfo.routeObjects;\n\n return routeInfo;\n }\n\n getRouteObjects() {\n return this.#routeObjects;\n }\n}\n/**\n * Creates an empty app without any default features. This is a low-level API is\n * intended for use in tests or specialized setups. Typically wou want to use\n * `createApp` from `@backstage/frontend-defaults` instead.\n *\n * @public\n */\nexport function createSpecializedApp(options?: {\n features?: FrontendFeature[];\n config?: ConfigApi;\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n}): { createRoot(): JSX.Element } {\n const config = options?.config ?? new ConfigReader({}, 'empty-config');\n const features = deduplicateFeatures(options?.features ?? []);\n\n const tree = resolveAppTree(\n 'root',\n resolveAppNodeSpecs({\n features,\n builtinExtensions: [\n resolveExtensionDefinition(Root, { namespace: 'root' }),\n ],\n parameters: readAppExtensionsConfig(config),\n forbidden: new Set(['root']),\n }),\n );\n\n const factories = createApiFactories({ tree });\n const appTreeApi = new AppTreeApiProxy(tree);\n const routeResolutionApi = new RouteResolutionApiProxy(\n tree,\n resolveRouteBindings(\n options?.bindRoutes,\n config,\n collectRouteIds(features),\n ),\n getBasePath(config),\n );\n\n const appIdentityProxy = new AppIdentityProxy();\n const apiHolder = createApiHolder({\n factories,\n staticFactories: [\n createApiFactory(appTreeApiRef, appTreeApi),\n createApiFactory(configApiRef, config),\n createApiFactory(routeResolutionApiRef, routeResolutionApi),\n createApiFactory(identityApiRef, appIdentityProxy),\n ],\n });\n\n const featureFlagApi = apiHolder.get(featureFlagsApiRef);\n if (featureFlagApi) {\n for (const feature of features) {\n if (OpaqueFrontendPlugin.isType(feature)) {\n OpaqueFrontendPlugin.toInternal(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.id,\n }),\n );\n }\n if (isInternalFrontendModule(feature)) {\n toInternalFrontendModule(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.pluginId,\n }),\n );\n }\n }\n }\n\n // Now instantiate the entire tree, which will skip anything that's already been instantiated\n instantiateAppNodeTree(tree.root, apiHolder);\n\n routeResolutionApi.initialize();\n appTreeApi.initialize();\n\n const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement);\n\n const AppComponent = () => rootEl;\n\n return {\n createRoot() {\n return <AppComponent />;\n },\n };\n}\n\nfunction createApiFactories(options: { tree: AppTree }): AnyApiFactory[] {\n const emptyApiHolder = ApiRegistry.from([]);\n const factories = new Array<AnyApiFactory>();\n\n for (const apiNode of options.tree.root.edges.attachments.get('apis') ?? []) {\n instantiateAppNodeTree(apiNode, emptyApiHolder);\n const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory);\n if (!apiFactory) {\n throw new Error(\n `No API factory found in for extension ${apiNode.spec.id}`,\n );\n }\n factories.push(apiFactory);\n }\n\n return factories;\n}\n\nfunction createApiHolder(options: {\n factories: AnyApiFactory[];\n staticFactories: AnyApiFactory[];\n}): ApiHolder {\n const factoryRegistry = new ApiFactoryRegistry();\n\n for (const factory of options.factories.slice().reverse()) {\n factoryRegistry.register('default', factory);\n }\n\n for (const factory of options.staticFactories) {\n factoryRegistry.register('static', factory);\n }\n\n ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());\n\n return new ApiResolver(factoryRegistry);\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwEA,SAAS,oBACP,WACmB,EAAA;AAEnB,EAAA,MAAM,WAAW,KAAM,CAAA,IAAA,CAAK,IAAI,GAAA,CAAI,WAAW,CAAC,CAAA,CAAA;AAGhD,EAAM,MAAA,OAAA,uBAAc,GAAY,EAAA,CAAA;AAChC,EAAA,OAAO,QACJ,CAAA,OAAA,EACA,CAAA,MAAA,CAAO,CAAW,OAAA,KAAA;AACjB,IAAA,IAAI,CAAC,oBAAA,CAAqB,MAAO,CAAA,OAAO,CAAG,EAAA;AACzC,MAAO,OAAA,IAAA,CAAA;AAAA,KACT;AACA,IAAA,IAAI,OAAQ,CAAA,GAAA,CAAI,OAAQ,CAAA,EAAE,CAAG,EAAA;AAC3B,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AACA,IAAQ,OAAA,CAAA,GAAA,CAAI,QAAQ,EAAE,CAAA,CAAA;AACtB,IAAO,OAAA,IAAA,CAAA;AAAA,GACR,EACA,OAAQ,EAAA,CAAA;AACb,CAAA;AAGA,MAAM,eAAsC,CAAA;AAAA,EAG1C,YAA6B,IAAe,EAAA;AAAf,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA,CAAA;AAAA,GAAgB;AAAA,EAF7C,UAAsB,GAAA,KAAA,CAAA;AAAA,EAItB,OAAU,GAAA;AACR,IAAI,IAAA,CAAC,KAAK,UAAY,EAAA;AACpB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,+IAAA,CAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAO,OAAA,EAAE,IAAM,EAAA,IAAA,CAAK,IAAK,EAAA,CAAA;AAAA,GAC3B;AAAA,EAEA,UAAa,GAAA;AACX,IAAA,IAAA,CAAK,UAAa,GAAA,IAAA,CAAA;AAAA,GACpB;AACF,CAAA;AAGA,MAAM,uBAAsD,CAAA;AAAA,EAI1D,WAAA,CACmB,IACA,EAAA,aAAA,EAIA,QACjB,EAAA;AANiB,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA,CAAA;AACA,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA,CAAA;AAIA,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA,CAAA;AAAA,GAChB;AAAA,EAVH,SAAA,CAAA;AAAA,EACA,aAAA,CAAA;AAAA,EAWA,OAAA,CACE,aAIA,OACgC,EAAA;AAChC,IAAI,IAAA,CAAC,KAAK,SAAW,EAAA;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,kJAAA,CAAA;AAAA,OACF,CAAA;AAAA,KACF;AAEA,IAAA,OAAO,IAAK,CAAA,SAAA,CAAU,OAAQ,CAAA,WAAA,EAAa,OAAO,CAAA,CAAA;AAAA,GACpD;AAAA,EAEA,UAAa,GAAA;AACX,IAAA,MAAM,SAAY,GAAA,2BAAA,CAA4B,IAAK,CAAA,IAAA,CAAK,IAAI,CAAA,CAAA;AAE5D,IAAA,IAAA,CAAK,YAAY,IAAI,aAAA;AAAA,MACnB,SAAU,CAAA,UAAA;AAAA,MACV,SAAU,CAAA,YAAA;AAAA,MACV,SAAU,CAAA,YAAA;AAAA,MACV,IAAK,CAAA,aAAA;AAAA,MACL,IAAK,CAAA,QAAA;AAAA,KACP,CAAA;AACA,IAAA,IAAA,CAAK,gBAAgB,SAAU,CAAA,YAAA,CAAA;AAE/B,IAAO,OAAA,SAAA,CAAA;AAAA,GACT;AAAA,EAEA,eAAkB,GAAA;AAChB,IAAA,OAAO,IAAK,CAAA,aAAA,CAAA;AAAA,GACd;AACF,CAAA;AAQO,SAAS,qBAAqB,OAIH,EAAA;AAChC,EAAA,MAAM,SAAS,OAAS,EAAA,MAAA,IAAU,IAAI,YAAa,CAAA,IAAI,cAAc,CAAA,CAAA;AACrE,EAAA,MAAM,QAAW,GAAA,mBAAA,CAAoB,OAAS,EAAA,QAAA,IAAY,EAAE,CAAA,CAAA;AAE5D,EAAA,MAAM,IAAO,GAAA,cAAA;AAAA,IACX,MAAA;AAAA,IACA,mBAAoB,CAAA;AAAA,MAClB,QAAA;AAAA,MACA,iBAAmB,EAAA;AAAA,QACjB,0BAA2B,CAAA,IAAA,EAAM,EAAE,SAAA,EAAW,QAAQ,CAAA;AAAA,OACxD;AAAA,MACA,UAAA,EAAY,wBAAwB,MAAM,CAAA;AAAA,MAC1C,SAAW,kBAAA,IAAI,GAAI,CAAA,CAAC,MAAM,CAAC,CAAA;AAAA,KAC5B,CAAA;AAAA,GACH,CAAA;AAEA,EAAA,MAAM,SAAY,GAAA,kBAAA,CAAmB,EAAE,IAAA,EAAM,CAAA,CAAA;AAC7C,EAAM,MAAA,UAAA,GAAa,IAAI,eAAA,CAAgB,IAAI,CAAA,CAAA;AAC3C,EAAA,MAAM,qBAAqB,IAAI,uBAAA;AAAA,IAC7B,IAAA;AAAA,IACA,oBAAA;AAAA,MACE,OAAS,EAAA,UAAA;AAAA,MACT,MAAA;AAAA,MACA,gBAAgB,QAAQ,CAAA;AAAA,KAC1B;AAAA,IACA,YAAY,MAAM,CAAA;AAAA,GACpB,CAAA;AAEA,EAAM,MAAA,gBAAA,GAAmB,IAAI,gBAAiB,EAAA,CAAA;AAC9C,EAAA,MAAM,YAAY,eAAgB,CAAA;AAAA,IAChC,SAAA;AAAA,IACA,eAAiB,EAAA;AAAA,MACf,gBAAA,CAAiB,eAAe,UAAU,CAAA;AAAA,MAC1C,gBAAA,CAAiB,cAAc,MAAM,CAAA;AAAA,MACrC,gBAAA,CAAiB,uBAAuB,kBAAkB,CAAA;AAAA,MAC1D,gBAAA,CAAiB,gBAAgB,gBAAgB,CAAA;AAAA,KACnD;AAAA,GACD,CAAA,CAAA;AAED,EAAM,MAAA,cAAA,GAAiB,SAAU,CAAA,GAAA,CAAI,kBAAkB,CAAA,CAAA;AACvD,EAAA,IAAI,cAAgB,EAAA;AAClB,IAAA,KAAA,MAAW,WAAW,QAAU,EAAA;AAC9B,MAAI,IAAA,oBAAA,CAAqB,MAAO,CAAA,OAAO,CAAG,EAAA;AACxC,QAAqB,oBAAA,CAAA,UAAA,CAAW,OAAO,CAAA,CAAE,YAAa,CAAA,OAAA;AAAA,UAAQ,CAAA,IAAA,KAC5D,eAAe,YAAa,CAAA;AAAA,YAC1B,MAAM,IAAK,CAAA,IAAA;AAAA,YACX,UAAU,OAAQ,CAAA,EAAA;AAAA,WACnB,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AACA,MAAI,IAAA,wBAAA,CAAyB,OAAO,CAAG,EAAA;AACrC,QAAyB,wBAAA,CAAA,OAAO,EAAE,YAAa,CAAA,OAAA;AAAA,UAAQ,CAAA,IAAA,KACrD,eAAe,YAAa,CAAA;AAAA,YAC1B,MAAM,IAAK,CAAA,IAAA;AAAA,YACX,UAAU,OAAQ,CAAA,QAAA;AAAA,WACnB,CAAA;AAAA,SACH,CAAA;AAAA,OACF;AAAA,KACF;AAAA,GACF;AAGA,EAAuB,sBAAA,CAAA,IAAA,CAAK,MAAM,SAAS,CAAA,CAAA;AAE3C,EAAA,kBAAA,CAAmB,UAAW,EAAA,CAAA;AAC9B,EAAA,UAAA,CAAW,UAAW,EAAA,CAAA;AAEtB,EAAA,MAAM,SAAS,IAAK,CAAA,IAAA,CAAK,QAAU,CAAA,OAAA,CAAQ,kBAAkB,YAAY,CAAA,CAAA;AAEzE,EAAA,MAAM,eAAe,MAAM,MAAA,CAAA;AAE3B,EAAO,OAAA;AAAA,IACL,UAAa,GAAA;AACX,MAAA,2CAAQ,YAAa,EAAA,IAAA,CAAA,CAAA;AAAA,KACvB;AAAA,GACF,CAAA;AACF,CAAA;AAEA,SAAS,mBAAmB,OAA6C,EAAA;AACvE,EAAA,MAAM,cAAiB,GAAA,WAAA,CAAY,IAAK,CAAA,EAAE,CAAA,CAAA;AAC1C,EAAM,MAAA,SAAA,GAAY,IAAI,KAAqB,EAAA,CAAA;AAE3C,EAAW,KAAA,MAAA,OAAA,IAAW,OAAQ,CAAA,IAAA,CAAK,IAAK,CAAA,KAAA,CAAM,YAAY,GAAI,CAAA,MAAM,CAAK,IAAA,EAAI,EAAA;AAC3E,IAAA,sBAAA,CAAuB,SAAS,cAAc,CAAA,CAAA;AAC9C,IAAA,MAAM,aAAa,OAAQ,CAAA,QAAA,EAAU,OAAQ,CAAA,YAAA,CAAa,SAAS,OAAO,CAAA,CAAA;AAC1E,IAAA,IAAI,CAAC,UAAY,EAAA;AACf,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,sCAAA,EAAyC,OAAQ,CAAA,IAAA,CAAK,EAAE,CAAA,CAAA;AAAA,OAC1D,CAAA;AAAA,KACF;AACA,IAAA,SAAA,CAAU,KAAK,UAAU,CAAA,CAAA;AAAA,GAC3B;AAEA,EAAO,OAAA,SAAA,CAAA;AACT,CAAA;AAEA,SAAS,gBAAgB,OAGX,EAAA;AACZ,EAAM,MAAA,eAAA,GAAkB,IAAI,kBAAmB,EAAA,CAAA;AAE/C,EAAA,KAAA,MAAW,WAAW,OAAQ,CAAA,SAAA,CAAU,KAAM,EAAA,CAAE,SAAW,EAAA;AACzD,IAAgB,eAAA,CAAA,QAAA,CAAS,WAAW,OAAO,CAAA,CAAA;AAAA,GAC7C;AAEA,EAAW,KAAA,MAAA,OAAA,IAAW,QAAQ,eAAiB,EAAA;AAC7C,IAAgB,eAAA,CAAA,QAAA,CAAS,UAAU,OAAO,CAAA,CAAA;AAAA,GAC5C;AAEA,EAAA,WAAA,CAAY,iBAAkB,CAAA,eAAA,EAAiB,eAAgB,CAAA,UAAA,EAAY,CAAA,CAAA;AAE3E,EAAO,OAAA,IAAI,YAAY,eAAe,CAAA,CAAA;AACxC;;;;"}
|
|
1
|
+
{"version":3,"file":"createSpecializedApp.esm.js","sources":["../../src/wiring/createSpecializedApp.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport React, { JSX } from 'react';\nimport { ConfigReader } from '@backstage/config';\nimport {\n ApiBlueprint,\n AppTree,\n AppTreeApi,\n appTreeApiRef,\n coreExtensionData,\n RouteRef,\n ExternalRouteRef,\n SubRouteRef,\n AnyRouteRefParams,\n RouteFunc,\n RouteResolutionApiResolveOptions,\n RouteResolutionApi,\n createApiFactory,\n routeResolutionApiRef,\n} from '@backstage/frontend-plugin-api';\nimport {\n AnyApiFactory,\n ApiHolder,\n ConfigApi,\n configApiRef,\n featureFlagsApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\nimport { ApiFactoryRegistry, ApiResolver } from '@backstage/core-app-api';\nimport { OpaqueFrontendPlugin } from '@internal/frontend';\n\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';\n\nimport { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode';\n\nimport { CreateAppRouteBinder } from '../routing';\nimport { RouteResolver } from '../routing/RouteResolver';\nimport { resolveRouteBindings } from '../routing/resolveRouteBindings';\nimport { collectRouteIds } from '../routing/collectRouteIds';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport {\n toInternalFrontendModule,\n isInternalFrontendModule,\n} from '../../../frontend-plugin-api/src/wiring/createFrontendModule';\nimport { getBasePath } from '../routing/getBasePath';\nimport { Root } from '../extensions/Root';\nimport { resolveAppTree } from '../tree/resolveAppTree';\nimport { resolveAppNodeSpecs } from '../tree/resolveAppNodeSpecs';\nimport { readAppExtensionsConfig } from '../tree/readAppExtensionsConfig';\nimport { instantiateAppNodeTree } from '../tree/instantiateAppNodeTree';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { ApiRegistry } from '../../../core-app-api/src/apis/system/ApiRegistry';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { AppIdentityProxy } from '../../../core-app-api/src/apis/implementations/IdentityApi/AppIdentityProxy';\nimport { BackstageRouteObject } from '../routing/types';\nimport { FrontendFeature } from './types';\n\nfunction deduplicateFeatures(\n allFeatures: FrontendFeature[],\n): FrontendFeature[] {\n // Start by removing duplicates by reference\n const features = Array.from(new Set(allFeatures));\n\n // Plugins are deduplicated by ID, last one wins\n const seenIds = new Set<string>();\n return features\n .reverse()\n .filter(feature => {\n if (!OpaqueFrontendPlugin.isType(feature)) {\n return true;\n }\n if (seenIds.has(feature.id)) {\n return false;\n }\n seenIds.add(feature.id);\n return true;\n })\n .reverse();\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass AppTreeApiProxy implements AppTreeApi {\n #safeToUse: boolean = false;\n\n constructor(private readonly tree: AppTree) {}\n\n getTree() {\n if (!this.#safeToUse) {\n throw new Error(\n `You can't access the AppTreeApi during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n return { tree: this.tree };\n }\n\n initialize() {\n this.#safeToUse = true;\n }\n}\n\n// Helps delay callers from reaching out to the API before the app tree has been materialized\nclass RouteResolutionApiProxy implements RouteResolutionApi {\n #delegate: RouteResolutionApi | undefined;\n #routeObjects: BackstageRouteObject[] | undefined;\n\n constructor(\n private readonly tree: AppTree,\n private readonly routeBindings: Map<\n ExternalRouteRef,\n RouteRef | SubRouteRef\n >,\n private readonly basePath: string,\n ) {}\n\n resolve<TParams extends AnyRouteRefParams>(\n anyRouteRef:\n | RouteRef<TParams>\n | SubRouteRef<TParams>\n | ExternalRouteRef<TParams>,\n options?: RouteResolutionApiResolveOptions,\n ): RouteFunc<TParams> | undefined {\n if (!this.#delegate) {\n throw new Error(\n `You can't access the RouteResolver during initialization of the app tree. Please move occurrences of this out of the initialization of the factory`,\n );\n }\n\n return this.#delegate.resolve(anyRouteRef, options);\n }\n\n initialize() {\n const routeInfo = extractRouteInfoFromAppNode(this.tree.root);\n\n this.#delegate = new RouteResolver(\n routeInfo.routePaths,\n routeInfo.routeParents,\n routeInfo.routeObjects,\n this.routeBindings,\n this.basePath,\n );\n this.#routeObjects = routeInfo.routeObjects;\n\n return routeInfo;\n }\n\n getRouteObjects() {\n return this.#routeObjects;\n }\n}\n/**\n * Creates an empty app without any default features. This is a low-level API is\n * intended for use in tests or specialized setups. Typically wou want to use\n * `createApp` from `@backstage/frontend-defaults` instead.\n *\n * @public\n */\nexport function createSpecializedApp(options?: {\n features?: FrontendFeature[];\n config?: ConfigApi;\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n}): { createRoot(): JSX.Element } {\n const config = options?.config ?? new ConfigReader({}, 'empty-config');\n const features = deduplicateFeatures(options?.features ?? []);\n\n const tree = resolveAppTree(\n 'root',\n resolveAppNodeSpecs({\n features,\n builtinExtensions: [\n resolveExtensionDefinition(Root, { namespace: 'root' }),\n ],\n parameters: readAppExtensionsConfig(config),\n forbidden: new Set(['root']),\n }),\n );\n\n const factories = createApiFactories({ tree });\n const appTreeApi = new AppTreeApiProxy(tree);\n const routeResolutionApi = new RouteResolutionApiProxy(\n tree,\n resolveRouteBindings(\n options?.bindRoutes,\n config,\n collectRouteIds(features),\n ),\n getBasePath(config),\n );\n\n const appIdentityProxy = new AppIdentityProxy();\n const apiHolder = createApiHolder({\n factories,\n staticFactories: [\n createApiFactory(appTreeApiRef, appTreeApi),\n createApiFactory(configApiRef, config),\n createApiFactory(routeResolutionApiRef, routeResolutionApi),\n createApiFactory(identityApiRef, appIdentityProxy),\n ],\n });\n\n const featureFlagApi = apiHolder.get(featureFlagsApiRef);\n if (featureFlagApi) {\n for (const feature of features) {\n if (OpaqueFrontendPlugin.isType(feature)) {\n OpaqueFrontendPlugin.toInternal(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.id,\n }),\n );\n }\n if (isInternalFrontendModule(feature)) {\n toInternalFrontendModule(feature).featureFlags.forEach(flag =>\n featureFlagApi.registerFlag({\n name: flag.name,\n pluginId: feature.pluginId,\n }),\n );\n }\n }\n }\n\n // Now instantiate the entire tree, which will skip anything that's already been instantiated\n instantiateAppNodeTree(tree.root, apiHolder);\n\n routeResolutionApi.initialize();\n appTreeApi.initialize();\n\n const rootEl = tree.root.instance!.getData(coreExtensionData.reactElement);\n\n const AppComponent = () => rootEl;\n\n return {\n createRoot() {\n return <AppComponent />;\n },\n };\n}\n\nfunction createApiFactories(options: { tree: AppTree }): AnyApiFactory[] {\n const emptyApiHolder = ApiRegistry.from([]);\n const factories = new Array<AnyApiFactory>();\n\n for (const apiNode of options.tree.root.edges.attachments.get('apis') ?? []) {\n instantiateAppNodeTree(apiNode, emptyApiHolder);\n const apiFactory = apiNode.instance?.getData(ApiBlueprint.dataRefs.factory);\n if (!apiFactory) {\n throw new Error(\n `No API factory found in for extension ${apiNode.spec.id}`,\n );\n }\n factories.push(apiFactory);\n }\n\n return factories;\n}\n\nfunction createApiHolder(options: {\n factories: AnyApiFactory[];\n staticFactories: AnyApiFactory[];\n}): ApiHolder {\n const factoryRegistry = new ApiFactoryRegistry();\n\n for (const factory of options.factories.slice().reverse()) {\n factoryRegistry.register('default', factory);\n }\n\n for (const factory of options.staticFactories) {\n factoryRegistry.register('static', factory);\n }\n\n ApiResolver.validateFactories(factoryRegistry, factoryRegistry.getAllApis());\n\n return new ApiResolver(factoryRegistry);\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/frontend-app-api",
|
|
3
|
-
"version": "0.10.1-next.
|
|
3
|
+
"version": "0.10.1-next.2",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "web-library"
|
|
6
6
|
},
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
"@backstage/core-app-api": "1.15.1",
|
|
37
37
|
"@backstage/core-plugin-api": "1.10.0",
|
|
38
38
|
"@backstage/errors": "1.2.4",
|
|
39
|
-
"@backstage/frontend-defaults": "0.1.2-next.
|
|
40
|
-
"@backstage/frontend-plugin-api": "0.9.1-next.
|
|
39
|
+
"@backstage/frontend-defaults": "0.1.2-next.2",
|
|
40
|
+
"@backstage/frontend-plugin-api": "0.9.1-next.2",
|
|
41
41
|
"@backstage/types": "1.1.1",
|
|
42
42
|
"@backstage/version-bridge": "1.0.10",
|
|
43
43
|
"lodash": "^4.17.21",
|
|
44
44
|
"zod": "^3.22.4"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@backstage/cli": "0.29.0-next.
|
|
48
|
-
"@backstage/plugin-app": "0.1.2-next.
|
|
47
|
+
"@backstage/cli": "0.29.0-next.3",
|
|
48
|
+
"@backstage/plugin-app": "0.1.2-next.2",
|
|
49
49
|
"@backstage/test-utils": "1.7.1-next.0",
|
|
50
50
|
"@testing-library/jest-dom": "^6.0.0",
|
|
51
51
|
"@testing-library/react": "^16.0.0",
|
|
@@ -66,5 +66,12 @@
|
|
|
66
66
|
}
|
|
67
67
|
},
|
|
68
68
|
"configSchema": "config.d.ts",
|
|
69
|
+
"typesVersions": {
|
|
70
|
+
"*": {
|
|
71
|
+
"index": [
|
|
72
|
+
"dist/index.d.ts"
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
69
76
|
"module": "./dist/index.esm.js"
|
|
70
77
|
}
|