@backstage/frontend-defaults 0.3.0-next.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @backstage/frontend-defaults
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 76832a9: **BREAKING**: Removed the deprecated `CreateAppFeatureLoader` and support for it in other APIs. Switch existing usage to use the newer `createFrontendFeatureLoader` from `@backstage/frontend-plugin-api` instead.
8
+ - 5e12252: **BREAKING**: Restructured some of option fields of `createApp` and `createSpecializedApp`.
9
+
10
+ - For `createApp`, all option fields _except_ `features` and `bindRoutes` have been moved into a new `advanced` object field.
11
+ - For `createSpecializedApp`, all option fields _except_ `features`, `config`, and `bindRoutes` have been moved into a new `advanced` object field.
12
+
13
+ This helps highlight that some options are meant to rarely be needed or used, and simplifies the usage of those options that are almost always required.
14
+
15
+ As an example, if you used to supply a custom config loader, you would update your code as follows:
16
+
17
+ ```diff
18
+ createApp({
19
+ features: [...],
20
+ - configLoader: new MyCustomLoader(),
21
+ + advanced: {
22
+ + configLoader: new MyCustomLoader(),
23
+ + },
24
+ })
25
+ ```
26
+
27
+ ### Patch Changes
28
+
29
+ - 22de964: Deprecated `createPublicSignInApp`, which has been replaced by the new `appModulePublicSignIn` from `@backstage/plugin-app/alpha` instead.
30
+ - e4ddf22: Internal update to align with new blueprint parameter naming in the new frontend system.
31
+ - 8b1bf6e: Deprecated new frontend system config setting `app.experimental.packages` to just `app.packages`. The old config will continue working for the time being, but may be removed in a future release.
32
+ - 7adc846: Added support for passing through `allowUnknownExtensionConfig` as a flag
33
+ - e5a0a99: **BREAKING**: The `loadingComponent` option has been renamed to `loadingElement`, which is now found under `advanced.loadingElement`. The default loading element has also been switched to `<Progress />` from `@backstage/core-components`. This is of course an improvement over the previous `"Loading..."` text, but also helps prevent flicker when the app loading is fast.
34
+ - Updated dependencies
35
+ - @backstage/core-components@0.17.5
36
+ - @backstage/frontend-plugin-api@0.11.0
37
+ - @backstage/frontend-app-api@0.12.0
38
+ - @backstage/plugin-app@0.2.0
39
+
3
40
  ## 0.3.0-next.3
4
41
 
5
42
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"defaultConfigLoader.esm.js","sources":["../../../../../core-app-api/src/app/defaultConfigLoader.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 { AppConfig } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport { AppConfigLoader } from './types';\n\n/**\n * The default config loader, which expects that config is available at compile-time\n * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as\n * returned by the config loader.\n *\n * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string,\n * which can be rewritten at runtime to contain an additional JSON config object.\n * If runtime config is present, it will be placed first in the config array, overriding\n * other config values.\n *\n * @public\n */\nexport const defaultConfigLoader: AppConfigLoader = async () =>\n defaultConfigLoaderSync();\n\n/** @internal */\nexport function defaultConfigLoaderSync(\n // This string may be replaced at runtime to provide additional config.\n // It should be replaced by a JSON-serialized config object.\n // It's a param so we can test it, but at runtime this will always fall back to default.\n runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__',\n) {\n const configs = new Array<AppConfig>();\n\n const staticConfig = process.env.APP_CONFIG;\n if (staticConfig) {\n if (!Array.isArray(staticConfig)) {\n throw new Error('Static configuration has invalid format');\n }\n configs.push(...staticConfig);\n }\n\n // Check if we have any config script tags, otherwise fall back to injected config\n const configScripts = document.querySelectorAll(\n 'script[type=\"backstage.io/config\"]',\n );\n if (configScripts.length > 0) {\n for (const el of configScripts) {\n try {\n const content = el.textContent;\n if (!content) {\n throw new Error('tag is empty');\n }\n let data;\n try {\n data = JSON.parse(content);\n } catch (error) {\n throw new Error(`failed to parse config; ${error}`);\n }\n if (!Array.isArray(data)) {\n throw new Error('data is not an array');\n }\n configs.push(...data);\n } catch (error) {\n throw new Error(\n `Failed to load config from script tag, ${error.message}`,\n );\n }\n }\n } else if (\n runtimeConfigJson !==\n // Avoiding this string also being replaced at runtime\n '__app_injected_runtime_config__'.toLocaleUpperCase('en-US')\n ) {\n try {\n const data = JSON.parse(runtimeConfigJson) as JsonObject;\n if (Array.isArray(data)) {\n configs.push(...data);\n } else {\n configs.push({ data, context: 'env' });\n }\n } catch (error) {\n throw new Error(`Failed to load runtime configuration, ${error}`);\n }\n }\n\n const windowAppConfig = (window as any).__APP_CONFIG__;\n if (windowAppConfig) {\n configs.push({\n context: 'window',\n data: windowAppConfig,\n });\n }\n return configs;\n}\n"],"names":[],"mappings":"AAoCgB,SAAA,uBAAA,CAId,oBAA4B,iCAC5B,EAAA;AACA,EAAM,MAAA,OAAA,GAAU,IAAI,KAAiB,EAAA;AAErC,EAAM,MAAA,YAAA,GAAe,QAAQ,GAAI,CAAA,UAAA;AACjC,EAAA,IAAI,YAAc,EAAA;AAChB,IAAA,IAAI,CAAC,KAAA,CAAM,OAAQ,CAAA,YAAY,CAAG,EAAA;AAChC,MAAM,MAAA,IAAI,MAAM,yCAAyC,CAAA;AAAA;AAE3D,IAAQ,OAAA,CAAA,IAAA,CAAK,GAAG,YAAY,CAAA;AAAA;AAI9B,EAAA,MAAM,gBAAgB,QAAS,CAAA,gBAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAI,IAAA,aAAA,CAAc,SAAS,CAAG,EAAA;AAC5B,IAAA,KAAA,MAAW,MAAM,aAAe,EAAA;AAC9B,MAAI,IAAA;AACF,QAAA,MAAM,UAAU,EAAG,CAAA,WAAA;AACnB,QAAA,IAAI,CAAC,OAAS,EAAA;AACZ,UAAM,MAAA,IAAI,MAAM,cAAc,CAAA;AAAA;AAEhC,QAAI,IAAA,IAAA;AACJ,QAAI,IAAA;AACF,UAAO,IAAA,GAAA,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,iBAClB,KAAO,EAAA;AACd,UAAA,MAAM,IAAI,KAAA,CAAM,CAA2B,wBAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAEpD,QAAA,IAAI,CAAC,KAAA,CAAM,OAAQ,CAAA,IAAI,CAAG,EAAA;AACxB,UAAM,MAAA,IAAI,MAAM,sBAAsB,CAAA;AAAA;AAExC,QAAQ,OAAA,CAAA,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,eACb,KAAO,EAAA;AACd,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,uCAAA,EAA0C,MAAM,OAAO,CAAA;AAAA,SACzD;AAAA;AACF;AACF,GAEA,MAAA,IAAA,iBAAA;AAAA,EAEA,iCAAA,CAAkC,iBAAkB,CAAA,OAAO,CAC3D,EAAA;AACA,IAAI,IAAA;AACF,MAAM,MAAA,IAAA,GAAO,IAAK,CAAA,KAAA,CAAM,iBAAiB,CAAA;AACzC,MAAI,IAAA,KAAA,CAAM,OAAQ,CAAA,IAAI,CAAG,EAAA;AACvB,QAAQ,OAAA,CAAA,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,OACf,MAAA;AACL,QAAA,OAAA,CAAQ,IAAK,CAAA,EAAE,IAAM,EAAA,OAAA,EAAS,OAAO,CAAA;AAAA;AACvC,aACO,KAAO,EAAA;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAyC,sCAAA,EAAA,KAAK,CAAE,CAAA,CAAA;AAAA;AAClE;AAGF,EAAA,MAAM,kBAAmB,MAAe,CAAA,cAAA;AACxC,EAAA,IAAI,eAAiB,EAAA;AACnB,IAAA,OAAA,CAAQ,IAAK,CAAA;AAAA,MACX,OAAS,EAAA,QAAA;AAAA,MACT,IAAM,EAAA;AAAA,KACP,CAAA;AAAA;AAEH,EAAO,OAAA,OAAA;AACT;;;;"}
1
+ {"version":3,"file":"defaultConfigLoader.esm.js","sources":["../../../../../core-app-api/src/app/defaultConfigLoader.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 { AppConfig } from '@backstage/config';\nimport { JsonObject } from '@backstage/types';\nimport { AppConfigLoader } from './types';\n\n/**\n * The default config loader, which expects that config is available at compile-time\n * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as\n * returned by the config loader.\n *\n * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string,\n * which can be rewritten at runtime to contain an additional JSON config object.\n * If runtime config is present, it will be placed first in the config array, overriding\n * other config values.\n *\n * @public\n */\nexport const defaultConfigLoader: AppConfigLoader = async () =>\n defaultConfigLoaderSync();\n\n/** @internal */\nexport function defaultConfigLoaderSync(\n // This string may be replaced at runtime to provide additional config.\n // It should be replaced by a JSON-serialized config object.\n // It's a param so we can test it, but at runtime this will always fall back to default.\n runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__',\n) {\n const configs = new Array<AppConfig>();\n\n const staticConfig = process.env.APP_CONFIG;\n if (staticConfig) {\n if (!Array.isArray(staticConfig)) {\n throw new Error('Static configuration has invalid format');\n }\n configs.push(...staticConfig);\n }\n\n // Check if we have any config script tags, otherwise fall back to injected config\n const configScripts = document.querySelectorAll(\n 'script[type=\"backstage.io/config\"]',\n );\n if (configScripts.length > 0) {\n for (const el of configScripts) {\n try {\n const content = el.textContent;\n if (!content) {\n throw new Error('tag is empty');\n }\n let data;\n try {\n data = JSON.parse(content);\n } catch (error) {\n throw new Error(`failed to parse config; ${error}`);\n }\n if (!Array.isArray(data)) {\n throw new Error('data is not an array');\n }\n configs.push(...data);\n } catch (error) {\n throw new Error(\n `Failed to load config from script tag, ${error.message}`,\n );\n }\n }\n } else if (\n runtimeConfigJson !==\n // Avoiding this string also being replaced at runtime\n '__app_injected_runtime_config__'.toLocaleUpperCase('en-US')\n ) {\n try {\n const data = JSON.parse(runtimeConfigJson) as JsonObject;\n if (Array.isArray(data)) {\n configs.push(...data);\n } else {\n configs.push({ data, context: 'env' });\n }\n } catch (error) {\n throw new Error(`Failed to load runtime configuration, ${error}`);\n }\n }\n\n const windowAppConfig = (window as any).__APP_CONFIG__;\n if (windowAppConfig) {\n configs.push({\n context: 'window',\n data: windowAppConfig,\n });\n }\n return configs;\n}\n"],"names":[],"mappings":"AAoCO,SAAS,uBAAA,CAId,oBAA4B,iCAAA,EAC5B;AACA,EAAA,MAAM,OAAA,GAAU,IAAI,KAAA,EAAiB;AAErC,EAAA,MAAM,YAAA,GAAe,QAAQ,GAAA,CAAI,UAAA;AACjC,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,YAAY,CAAA,EAAG;AAChC,MAAA,MAAM,IAAI,MAAM,yCAAyC,CAAA;AAAA,IAC3D;AACA,IAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,YAAY,CAAA;AAAA,EAC9B;AAGA,EAAA,MAAM,gBAAgB,QAAA,CAAS,gBAAA;AAAA,IAC7B;AAAA,GACF;AACA,EAAA,IAAI,aAAA,CAAc,SAAS,CAAA,EAAG;AAC5B,IAAA,KAAA,MAAW,MAAM,aAAA,EAAe;AAC9B,MAAA,IAAI;AACF,QAAA,MAAM,UAAU,EAAA,CAAG,WAAA;AACnB,QAAA,IAAI,CAAC,OAAA,EAAS;AACZ,UAAA,MAAM,IAAI,MAAM,cAAc,CAAA;AAAA,QAChC;AACA,QAAA,IAAI,IAAA;AACJ,QAAA,IAAI;AACF,UAAA,IAAA,GAAO,IAAA,CAAK,MAAM,OAAO,CAAA;AAAA,QAC3B,SAAS,KAAA,EAAO;AACd,UAAA,MAAM,IAAI,KAAA,CAAM,CAAA,wBAAA,EAA2B,KAAK,CAAA,CAAE,CAAA;AAAA,QACpD;AACA,QAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACxB,UAAA,MAAM,IAAI,MAAM,sBAAsB,CAAA;AAAA,QACxC;AACA,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,MACtB,SAAS,KAAA,EAAO;AACd,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,uCAAA,EAA0C,MAAM,OAAO,CAAA;AAAA,SACzD;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAA,MAAA,IACE,iBAAA;AAAA,EAEA,iCAAA,CAAkC,iBAAA,CAAkB,OAAO,CAAA,EAC3D;AACA,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,iBAAiB,CAAA;AACzC,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACvB,QAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,IAAI,CAAA;AAAA,MACtB,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,IAAA,EAAM,OAAA,EAAS,OAAO,CAAA;AAAA,MACvC;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sCAAA,EAAyC,KAAK,CAAA,CAAE,CAAA;AAAA,IAClE;AAAA,EACF;AAEA,EAAA,MAAM,kBAAmB,MAAA,CAAe,cAAA;AACxC,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,OAAA,CAAQ,IAAA,CAAK;AAAA,MACX,OAAA,EAAS,QAAA;AAAA,MACT,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AACA,EAAA,OAAO,OAAA;AACT;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"overrideBaseUrlConfigs.esm.js","sources":["../../../../../core-app-api/src/app/overrideBaseUrlConfigs.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 { AppConfig, ConfigReader } from '@backstage/config';\n\n/**\n * Creates a base URL that uses to the current document origin.\n */\nfunction createLocalBaseUrl(fullUrl: string): string {\n const url = new URL(fullUrl);\n url.protocol = document.location.protocol;\n url.hostname = document.location.hostname;\n url.port = document.location.port;\n return url.toString().replace(/\\/$/, '');\n}\n\n/**\n * If we are able to override the app and backend base URLs to values that\n * match the origin of the current location, then this function returns a\n * new array of app configs that contain the overrides.\n *\n * @internal\n */\nexport function overrideBaseUrlConfigs(inputConfigs: AppConfig[]): AppConfig[] {\n const urlConfigReader = ConfigReader.fromConfigs(inputConfigs);\n\n // In tests we may not have `app.baseUrl` or `backend.baseUrl`, to keep them optional\n const appBaseUrl = urlConfigReader.getOptionalString('app.baseUrl');\n const backendBaseUrl = urlConfigReader.getOptionalString('backend.baseUrl');\n\n let configs = inputConfigs;\n\n let newBackendBaseUrl: string | undefined = undefined;\n let newAppBaseUrl: string | undefined = undefined;\n\n if (appBaseUrl && backendBaseUrl) {\n const appOrigin = new URL(appBaseUrl).origin;\n const backendOrigin = new URL(backendBaseUrl).origin;\n\n if (appOrigin === backendOrigin) {\n const maybeNewBackendBaseUrl = createLocalBaseUrl(backendBaseUrl);\n if (backendBaseUrl !== maybeNewBackendBaseUrl) {\n newBackendBaseUrl = maybeNewBackendBaseUrl;\n }\n }\n }\n\n if (appBaseUrl) {\n const maybeNewAppBaseUrl = createLocalBaseUrl(appBaseUrl);\n if (appBaseUrl !== maybeNewAppBaseUrl) {\n newAppBaseUrl = maybeNewAppBaseUrl;\n }\n }\n\n // Only add the relative config if there is actually data to add.\n if (newAppBaseUrl || newBackendBaseUrl) {\n configs = configs.concat({\n data: {\n app: newAppBaseUrl && {\n baseUrl: newAppBaseUrl,\n },\n backend: newBackendBaseUrl && {\n baseUrl: newBackendBaseUrl,\n },\n },\n context: 'relative-resolver',\n });\n }\n\n return configs;\n}\n"],"names":[],"mappings":";;AAqBA,SAAS,mBAAmB,OAAyB,EAAA;AACnD,EAAM,MAAA,GAAA,GAAM,IAAI,GAAA,CAAI,OAAO,CAAA;AAC3B,EAAI,GAAA,CAAA,QAAA,GAAW,SAAS,QAAS,CAAA,QAAA;AACjC,EAAI,GAAA,CAAA,QAAA,GAAW,SAAS,QAAS,CAAA,QAAA;AACjC,EAAI,GAAA,CAAA,IAAA,GAAO,SAAS,QAAS,CAAA,IAAA;AAC7B,EAAA,OAAO,GAAI,CAAA,QAAA,EAAW,CAAA,OAAA,CAAQ,OAAO,EAAE,CAAA;AACzC;AASO,SAAS,uBAAuB,YAAwC,EAAA;AAC7E,EAAM,MAAA,eAAA,GAAkB,YAAa,CAAA,WAAA,CAAY,YAAY,CAAA;AAG7D,EAAM,MAAA,UAAA,GAAa,eAAgB,CAAA,iBAAA,CAAkB,aAAa,CAAA;AAClE,EAAM,MAAA,cAAA,GAAiB,eAAgB,CAAA,iBAAA,CAAkB,iBAAiB,CAAA;AAE1E,EAAA,IAAI,OAAU,GAAA,YAAA;AAEd,EAAA,IAAI,iBAAwC,GAAA,KAAA,CAAA;AAC5C,EAAA,IAAI,aAAoC,GAAA,KAAA,CAAA;AAExC,EAAA,IAAI,cAAc,cAAgB,EAAA;AAChC,IAAA,MAAM,SAAY,GAAA,IAAI,GAAI,CAAA,UAAU,CAAE,CAAA,MAAA;AACtC,IAAA,MAAM,aAAgB,GAAA,IAAI,GAAI,CAAA,cAAc,CAAE,CAAA,MAAA;AAE9C,IAAA,IAAI,cAAc,aAAe,EAAA;AAC/B,MAAM,MAAA,sBAAA,GAAyB,mBAAmB,cAAc,CAAA;AAChE,MAAA,IAAI,mBAAmB,sBAAwB,EAAA;AAC7C,QAAoB,iBAAA,GAAA,sBAAA;AAAA;AACtB;AACF;AAGF,EAAA,IAAI,UAAY,EAAA;AACd,IAAM,MAAA,kBAAA,GAAqB,mBAAmB,UAAU,CAAA;AACxD,IAAA,IAAI,eAAe,kBAAoB,EAAA;AACrC,MAAgB,aAAA,GAAA,kBAAA;AAAA;AAClB;AAIF,EAAA,IAAI,iBAAiB,iBAAmB,EAAA;AACtC,IAAA,OAAA,GAAU,QAAQ,MAAO,CAAA;AAAA,MACvB,IAAM,EAAA;AAAA,QACJ,KAAK,aAAiB,IAAA;AAAA,UACpB,OAAS,EAAA;AAAA,SACX;AAAA,QACA,SAAS,iBAAqB,IAAA;AAAA,UAC5B,OAAS,EAAA;AAAA;AACX,OACF;AAAA,MACA,OAAS,EAAA;AAAA,KACV,CAAA;AAAA;AAGH,EAAO,OAAA,OAAA;AACT;;;;"}
1
+ {"version":3,"file":"overrideBaseUrlConfigs.esm.js","sources":["../../../../../core-app-api/src/app/overrideBaseUrlConfigs.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 { AppConfig, ConfigReader } from '@backstage/config';\n\n/**\n * Creates a base URL that uses to the current document origin.\n */\nfunction createLocalBaseUrl(fullUrl: string): string {\n const url = new URL(fullUrl);\n url.protocol = document.location.protocol;\n url.hostname = document.location.hostname;\n url.port = document.location.port;\n return url.toString().replace(/\\/$/, '');\n}\n\n/**\n * If we are able to override the app and backend base URLs to values that\n * match the origin of the current location, then this function returns a\n * new array of app configs that contain the overrides.\n *\n * @internal\n */\nexport function overrideBaseUrlConfigs(inputConfigs: AppConfig[]): AppConfig[] {\n const urlConfigReader = ConfigReader.fromConfigs(inputConfigs);\n\n // In tests we may not have `app.baseUrl` or `backend.baseUrl`, to keep them optional\n const appBaseUrl = urlConfigReader.getOptionalString('app.baseUrl');\n const backendBaseUrl = urlConfigReader.getOptionalString('backend.baseUrl');\n\n let configs = inputConfigs;\n\n let newBackendBaseUrl: string | undefined = undefined;\n let newAppBaseUrl: string | undefined = undefined;\n\n if (appBaseUrl && backendBaseUrl) {\n const appOrigin = new URL(appBaseUrl).origin;\n const backendOrigin = new URL(backendBaseUrl).origin;\n\n if (appOrigin === backendOrigin) {\n const maybeNewBackendBaseUrl = createLocalBaseUrl(backendBaseUrl);\n if (backendBaseUrl !== maybeNewBackendBaseUrl) {\n newBackendBaseUrl = maybeNewBackendBaseUrl;\n }\n }\n }\n\n if (appBaseUrl) {\n const maybeNewAppBaseUrl = createLocalBaseUrl(appBaseUrl);\n if (appBaseUrl !== maybeNewAppBaseUrl) {\n newAppBaseUrl = maybeNewAppBaseUrl;\n }\n }\n\n // Only add the relative config if there is actually data to add.\n if (newAppBaseUrl || newBackendBaseUrl) {\n configs = configs.concat({\n data: {\n app: newAppBaseUrl && {\n baseUrl: newAppBaseUrl,\n },\n backend: newBackendBaseUrl && {\n baseUrl: newBackendBaseUrl,\n },\n },\n context: 'relative-resolver',\n });\n }\n\n return configs;\n}\n"],"names":[],"mappings":";;AAqBA,SAAS,mBAAmB,OAAA,EAAyB;AACnD,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAO,CAAA;AAC3B,EAAA,GAAA,CAAI,QAAA,GAAW,SAAS,QAAA,CAAS,QAAA;AACjC,EAAA,GAAA,CAAI,QAAA,GAAW,SAAS,QAAA,CAAS,QAAA;AACjC,EAAA,GAAA,CAAI,IAAA,GAAO,SAAS,QAAA,CAAS,IAAA;AAC7B,EAAA,OAAO,GAAA,CAAI,QAAA,EAAS,CAAE,OAAA,CAAQ,OAAO,EAAE,CAAA;AACzC;AASO,SAAS,uBAAuB,YAAA,EAAwC;AAC7E,EAAA,MAAM,eAAA,GAAkB,YAAA,CAAa,WAAA,CAAY,YAAY,CAAA;AAG7D,EAAA,MAAM,UAAA,GAAa,eAAA,CAAgB,iBAAA,CAAkB,aAAa,CAAA;AAClE,EAAA,MAAM,cAAA,GAAiB,eAAA,CAAgB,iBAAA,CAAkB,iBAAiB,CAAA;AAE1E,EAAA,IAAI,OAAA,GAAU,YAAA;AAEd,EAAA,IAAI,iBAAA,GAAwC,MAAA;AAC5C,EAAA,IAAI,aAAA,GAAoC,MAAA;AAExC,EAAA,IAAI,cAAc,cAAA,EAAgB;AAChC,IAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,UAAU,CAAA,CAAE,MAAA;AACtC,IAAA,MAAM,aAAA,GAAgB,IAAI,GAAA,CAAI,cAAc,CAAA,CAAE,MAAA;AAE9C,IAAA,IAAI,cAAc,aAAA,EAAe;AAC/B,MAAA,MAAM,sBAAA,GAAyB,mBAAmB,cAAc,CAAA;AAChE,MAAA,IAAI,mBAAmB,sBAAA,EAAwB;AAC7C,QAAA,iBAAA,GAAoB,sBAAA;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,UAAA,EAAY;AACd,IAAA,MAAM,kBAAA,GAAqB,mBAAmB,UAAU,CAAA;AACxD,IAAA,IAAI,eAAe,kBAAA,EAAoB;AACrC,MAAA,aAAA,GAAgB,kBAAA;AAAA,IAClB;AAAA,EACF;AAGA,EAAA,IAAI,iBAAiB,iBAAA,EAAmB;AACtC,IAAA,OAAA,GAAU,QAAQ,MAAA,CAAO;AAAA,MACvB,IAAA,EAAM;AAAA,QACJ,KAAK,aAAA,IAAiB;AAAA,UACpB,OAAA,EAAS;AAAA,SACX;AAAA,QACA,SAAS,iBAAA,IAAqB;AAAA,UAC5B,OAAA,EAAS;AAAA;AACX,OACF;AAAA,MACA,OAAA,EAAS;AAAA,KACV,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,OAAA;AACT;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createApp.esm.js","sources":["../src/createApp.tsx"],"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 { JSX, lazy, ReactNode, Suspense } from 'react';\nimport {\n ConfigApi,\n coreExtensionData,\n ExtensionFactoryMiddleware,\n FrontendFeature,\n FrontendFeatureLoader,\n} from '@backstage/frontend-plugin-api';\nimport { Progress } from '@backstage/core-components';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { overrideBaseUrlConfigs } from '../../core-app-api/src/app/overrideBaseUrlConfigs';\nimport { ConfigReader } from '@backstage/config';\nimport {\n CreateAppRouteBinder,\n createSpecializedApp,\n FrontendPluginInfoResolver,\n} from '@backstage/frontend-app-api';\nimport appPlugin from '@backstage/plugin-app';\nimport { discoverAvailableFeatures } from './discovery';\nimport { resolveAsyncFeatures } from './resolution';\n\n/**\n * Options for {@link createApp}.\n *\n * @public\n */\nexport interface CreateAppOptions {\n /**\n * The list of features to load.\n */\n features?: (FrontendFeature | FrontendFeatureLoader)[];\n\n /**\n * Allows for the binding of plugins' external route refs within the app.\n */\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n\n /**\n * Advanced, more rarely used options.\n */\n advanced?: {\n /**\n * If set to true, the system will silently accept and move on if\n * encountering config for extensions that do not exist. The default is to\n * reject such config to help catch simple mistakes.\n *\n * This flag can be useful in some scenarios where you have a dynamic set of\n * extensions enabled at different times, but also increases the risk of\n * accidentally missing e.g. simple typos in your config.\n */\n allowUnknownExtensionConfig?: boolean;\n\n /**\n * Sets a custom config loader, replacing the builtin one.\n *\n * This can be used e.g. if you have the need to source config out of custom\n * storages.\n */\n configLoader?: () => Promise<{ config: ConfigApi }>;\n\n /**\n * Applies one or more middleware on every extension, as they are added to\n * the application.\n *\n * This is an advanced use case for modifying extension data on the fly as\n * it gets emitted by extensions being instantiated.\n */\n extensionFactoryMiddleware?:\n | ExtensionFactoryMiddleware\n | ExtensionFactoryMiddleware[];\n\n /**\n * The element to render while loading the app (waiting for config, features, etc).\n *\n * This is the `<Progress />` component from `@backstage/core-components` by default.\n * If set to `null` then no loading fallback element is rendered at all.\n */\n loadingElement?: ReactNode;\n\n /**\n * Allows for customizing how plugin info is retrieved.\n */\n pluginInfoResolver?: FrontendPluginInfoResolver;\n };\n}\n\n/**\n * Creates a new Backstage frontend app instance. See https://backstage.io/docs/frontend-system/building-apps/index\n *\n * @public\n */\nexport function createApp(options?: CreateAppOptions): {\n createRoot(): JSX.Element;\n} {\n let suspenseFallback = options?.advanced?.loadingElement;\n if (suspenseFallback === undefined) {\n suspenseFallback = <Progress />;\n }\n\n async function appLoader() {\n const config =\n (await options?.advanced?.configLoader?.().then(c => c.config)) ??\n ConfigReader.fromConfigs(\n overrideBaseUrlConfigs(defaultConfigLoaderSync()),\n );\n\n const { features: discoveredFeaturesAndLoaders } =\n discoverAvailableFeatures(config);\n const { features: loadedFeatures } = await resolveAsyncFeatures({\n config,\n features: [...discoveredFeaturesAndLoaders, ...(options?.features ?? [])],\n });\n\n const app = createSpecializedApp({\n features: [appPlugin, ...loadedFeatures],\n config,\n bindRoutes: options?.bindRoutes,\n advanced: options?.advanced,\n });\n\n const rootEl = app.tree.root.instance!.getData(\n coreExtensionData.reactElement,\n );\n\n return { default: () => rootEl };\n }\n\n const LazyApp = lazy(appLoader);\n\n return {\n createRoot() {\n return (\n <Suspense fallback={suspenseFallback}>\n <LazyApp />\n </Suspense>\n );\n },\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA6GO,SAAS,UAAU,OAExB,EAAA;AACA,EAAI,IAAA,gBAAA,GAAmB,SAAS,QAAU,EAAA,cAAA;AAC1C,EAAA,IAAI,qBAAqB,KAAW,CAAA,EAAA;AAClC,IAAA,gBAAA,uBAAoB,QAAS,EAAA,EAAA,CAAA;AAAA;AAG/B,EAAA,eAAe,SAAY,GAAA;AACzB,IAAM,MAAA,MAAA,GACH,MAAM,OAAA,EAAS,QAAU,EAAA,YAAA,IAAiB,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,CAAE,MAAM,CAAA,IAC7D,YAAa,CAAA,WAAA;AAAA,MACX,sBAAA,CAAuB,yBAAyB;AAAA,KAClD;AAEF,IAAA,MAAM,EAAE,QAAA,EAAU,4BAA6B,EAAA,GAC7C,0BAA0B,MAAM,CAAA;AAClC,IAAA,MAAM,EAAE,QAAA,EAAU,cAAe,EAAA,GAAI,MAAM,oBAAqB,CAAA;AAAA,MAC9D,MAAA;AAAA,MACA,QAAA,EAAU,CAAC,GAAG,4BAAA,EAA8B,GAAI,OAAS,EAAA,QAAA,IAAY,EAAG;AAAA,KACzE,CAAA;AAED,IAAA,MAAM,MAAM,oBAAqB,CAAA;AAAA,MAC/B,QAAU,EAAA,CAAC,SAAW,EAAA,GAAG,cAAc,CAAA;AAAA,MACvC,MAAA;AAAA,MACA,YAAY,OAAS,EAAA,UAAA;AAAA,MACrB,UAAU,OAAS,EAAA;AAAA,KACpB,CAAA;AAED,IAAA,MAAM,MAAS,GAAA,GAAA,CAAI,IAAK,CAAA,IAAA,CAAK,QAAU,CAAA,OAAA;AAAA,MACrC,iBAAkB,CAAA;AAAA,KACpB;AAEA,IAAO,OAAA,EAAE,OAAS,EAAA,MAAM,MAAO,EAAA;AAAA;AAGjC,EAAM,MAAA,OAAA,GAAU,KAAK,SAAS,CAAA;AAE9B,EAAO,OAAA;AAAA,IACL,UAAa,GAAA;AACX,MAAA,2BACG,QAAS,EAAA,EAAA,QAAA,EAAU,gBAClB,EAAA,QAAA,kBAAA,GAAA,CAAC,WAAQ,CACX,EAAA,CAAA;AAAA;AAEJ,GACF;AACF;;;;"}
1
+ {"version":3,"file":"createApp.esm.js","sources":["../src/createApp.tsx"],"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 { JSX, lazy, ReactNode, Suspense } from 'react';\nimport {\n ConfigApi,\n coreExtensionData,\n ExtensionFactoryMiddleware,\n FrontendFeature,\n FrontendFeatureLoader,\n} from '@backstage/frontend-plugin-api';\nimport { Progress } from '@backstage/core-components';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { defaultConfigLoaderSync } from '../../core-app-api/src/app/defaultConfigLoader';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { overrideBaseUrlConfigs } from '../../core-app-api/src/app/overrideBaseUrlConfigs';\nimport { ConfigReader } from '@backstage/config';\nimport {\n CreateAppRouteBinder,\n createSpecializedApp,\n FrontendPluginInfoResolver,\n} from '@backstage/frontend-app-api';\nimport appPlugin from '@backstage/plugin-app';\nimport { discoverAvailableFeatures } from './discovery';\nimport { resolveAsyncFeatures } from './resolution';\n\n/**\n * Options for {@link createApp}.\n *\n * @public\n */\nexport interface CreateAppOptions {\n /**\n * The list of features to load.\n */\n features?: (FrontendFeature | FrontendFeatureLoader)[];\n\n /**\n * Allows for the binding of plugins' external route refs within the app.\n */\n bindRoutes?(context: { bind: CreateAppRouteBinder }): void;\n\n /**\n * Advanced, more rarely used options.\n */\n advanced?: {\n /**\n * If set to true, the system will silently accept and move on if\n * encountering config for extensions that do not exist. The default is to\n * reject such config to help catch simple mistakes.\n *\n * This flag can be useful in some scenarios where you have a dynamic set of\n * extensions enabled at different times, but also increases the risk of\n * accidentally missing e.g. simple typos in your config.\n */\n allowUnknownExtensionConfig?: boolean;\n\n /**\n * Sets a custom config loader, replacing the builtin one.\n *\n * This can be used e.g. if you have the need to source config out of custom\n * storages.\n */\n configLoader?: () => Promise<{ config: ConfigApi }>;\n\n /**\n * Applies one or more middleware on every extension, as they are added to\n * the application.\n *\n * This is an advanced use case for modifying extension data on the fly as\n * it gets emitted by extensions being instantiated.\n */\n extensionFactoryMiddleware?:\n | ExtensionFactoryMiddleware\n | ExtensionFactoryMiddleware[];\n\n /**\n * The element to render while loading the app (waiting for config, features, etc).\n *\n * This is the `<Progress />` component from `@backstage/core-components` by default.\n * If set to `null` then no loading fallback element is rendered at all.\n */\n loadingElement?: ReactNode;\n\n /**\n * Allows for customizing how plugin info is retrieved.\n */\n pluginInfoResolver?: FrontendPluginInfoResolver;\n };\n}\n\n/**\n * Creates a new Backstage frontend app instance. See https://backstage.io/docs/frontend-system/building-apps/index\n *\n * @public\n */\nexport function createApp(options?: CreateAppOptions): {\n createRoot(): JSX.Element;\n} {\n let suspenseFallback = options?.advanced?.loadingElement;\n if (suspenseFallback === undefined) {\n suspenseFallback = <Progress />;\n }\n\n async function appLoader() {\n const config =\n (await options?.advanced?.configLoader?.().then(c => c.config)) ??\n ConfigReader.fromConfigs(\n overrideBaseUrlConfigs(defaultConfigLoaderSync()),\n );\n\n const { features: discoveredFeaturesAndLoaders } =\n discoverAvailableFeatures(config);\n const { features: loadedFeatures } = await resolveAsyncFeatures({\n config,\n features: [...discoveredFeaturesAndLoaders, ...(options?.features ?? [])],\n });\n\n const app = createSpecializedApp({\n features: [appPlugin, ...loadedFeatures],\n config,\n bindRoutes: options?.bindRoutes,\n advanced: options?.advanced,\n });\n\n const rootEl = app.tree.root.instance!.getData(\n coreExtensionData.reactElement,\n );\n\n return { default: () => rootEl };\n }\n\n const LazyApp = lazy(appLoader);\n\n return {\n createRoot() {\n return (\n <Suspense fallback={suspenseFallback}>\n <LazyApp />\n </Suspense>\n );\n },\n };\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AA6GO,SAAS,UAAU,OAAA,EAExB;AACA,EAAA,IAAI,gBAAA,GAAmB,SAAS,QAAA,EAAU,cAAA;AAC1C,EAAA,IAAI,qBAAqB,MAAA,EAAW;AAClC,IAAA,gBAAA,uBAAoB,QAAA,EAAA,EAAS,CAAA;AAAA,EAC/B;AAEA,EAAA,eAAe,SAAA,GAAY;AACzB,IAAA,MAAM,MAAA,GACH,MAAM,OAAA,EAAS,QAAA,EAAU,YAAA,IAAe,CAAE,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,MAAM,CAAA,IAC7D,YAAA,CAAa,WAAA;AAAA,MACX,sBAAA,CAAuB,yBAAyB;AAAA,KAClD;AAEF,IAAA,MAAM,EAAE,QAAA,EAAU,4BAAA,EAA6B,GAC7C,0BAA0B,MAAM,CAAA;AAClC,IAAA,MAAM,EAAE,QAAA,EAAU,cAAA,EAAe,GAAI,MAAM,oBAAA,CAAqB;AAAA,MAC9D,MAAA;AAAA,MACA,QAAA,EAAU,CAAC,GAAG,4BAAA,EAA8B,GAAI,OAAA,EAAS,QAAA,IAAY,EAAG;AAAA,KACzE,CAAA;AAED,IAAA,MAAM,MAAM,oBAAA,CAAqB;AAAA,MAC/B,QAAA,EAAU,CAAC,SAAA,EAAW,GAAG,cAAc,CAAA;AAAA,MACvC,MAAA;AAAA,MACA,YAAY,OAAA,EAAS,UAAA;AAAA,MACrB,UAAU,OAAA,EAAS;AAAA,KACpB,CAAA;AAED,IAAA,MAAM,MAAA,GAAS,GAAA,CAAI,IAAA,CAAK,IAAA,CAAK,QAAA,CAAU,OAAA;AAAA,MACrC,iBAAA,CAAkB;AAAA,KACpB;AAEA,IAAA,OAAO,EAAE,OAAA,EAAS,MAAM,MAAA,EAAO;AAAA,EACjC;AAEA,EAAA,MAAM,OAAA,GAAU,KAAK,SAAS,CAAA;AAE9B,EAAA,OAAO;AAAA,IACL,UAAA,GAAa;AACX,MAAA,2BACG,QAAA,EAAA,EAAS,QAAA,EAAU,gBAAA,EAClB,QAAA,kBAAA,GAAA,CAAC,WAAQ,CAAA,EACX,CAAA;AAAA,IAEJ;AAAA,GACF;AACF;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createPublicSignInApp.esm.js","sources":["../src/createPublicSignInApp.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { appModulePublicSignIn } from '@backstage/plugin-app/alpha';\nimport { CreateAppOptions, createApp } from './createApp';\n\n/**\n * @public\n * @deprecated Use {@link @backstage/plugin-app/alpha#appModulePublicSignIn} instead.\n */\nexport function createPublicSignInApp(options?: CreateAppOptions) {\n return createApp({\n ...options,\n features: [...(options?.features ?? []), appModulePublicSignIn],\n });\n}\n"],"names":[],"mappings":";;;AAuBO,SAAS,sBAAsB,OAA4B,EAAA;AAChE,EAAA,OAAO,SAAU,CAAA;AAAA,IACf,GAAG,OAAA;AAAA,IACH,UAAU,CAAC,GAAI,SAAS,QAAY,IAAA,IAAK,qBAAqB;AAAA,GAC/D,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"createPublicSignInApp.esm.js","sources":["../src/createPublicSignInApp.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { appModulePublicSignIn } from '@backstage/plugin-app/alpha';\nimport { CreateAppOptions, createApp } from './createApp';\n\n/**\n * @public\n * @deprecated Use {@link @backstage/plugin-app/alpha#appModulePublicSignIn} instead.\n */\nexport function createPublicSignInApp(options?: CreateAppOptions) {\n return createApp({\n ...options,\n features: [...(options?.features ?? []), appModulePublicSignIn],\n });\n}\n"],"names":[],"mappings":";;;AAuBO,SAAS,sBAAsB,OAAA,EAA4B;AAChE,EAAA,OAAO,SAAA,CAAU;AAAA,IACf,GAAG,OAAA;AAAA,IACH,UAAU,CAAC,GAAI,SAAS,QAAA,IAAY,IAAK,qBAAqB;AAAA,GAC/D,CAAA;AACH;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"discovery.esm.js","sources":["../src/discovery.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, ConfigReader } from '@backstage/config';\nimport {\n FrontendFeature,\n FrontendFeatureLoader,\n} from '@backstage/frontend-plugin-api';\nimport { isBackstageFeatureLoader } from './resolution';\n\ninterface DiscoveryGlobal {\n modules: Array<{ name: string; export?: string; default: unknown }>;\n}\n\nfunction readPackageDetectionConfig(config: Config) {\n // The experimental key is deprecated, but supported still for backwards compatibility\n const packages =\n config.getOptional('app.packages') ??\n config.getOptional('app.experimental.packages');\n if (packages === undefined || packages === null) {\n return undefined;\n }\n\n if (typeof packages === 'string') {\n if (packages !== 'all') {\n throw new Error(\n `Invalid app.packages mode, got '${packages}', expected 'all'`,\n );\n }\n return {};\n }\n\n if (typeof packages !== 'object' || Array.isArray(packages)) {\n throw new Error(\"Invalid config at 'app.packages', expected object\");\n }\n const packagesConfig = new ConfigReader(packages, 'app.packages');\n\n return {\n include: packagesConfig.getOptionalStringArray('include'),\n exclude: packagesConfig.getOptionalStringArray('exclude'),\n };\n}\n\n/**\n * @public\n */\nexport function discoverAvailableFeatures(config: Config): {\n features: (FrontendFeature | FrontendFeatureLoader)[];\n} {\n const discovered = (\n window as { '__@backstage/discovered__'?: DiscoveryGlobal }\n )['__@backstage/discovered__'];\n\n const detection = readPackageDetectionConfig(config);\n if (!detection) {\n return { features: [] };\n }\n\n return {\n features:\n discovered?.modules\n .filter(({ name }) => {\n if (detection.exclude?.includes(name)) {\n return false;\n }\n if (detection.include && !detection.include.includes(name)) {\n return false;\n }\n return true;\n })\n .map(m => m.default)\n .filter(isFeatureOrLoader) ?? [],\n };\n}\n\nfunction isBackstageFeature(obj: unknown): obj is FrontendFeature {\n if (obj !== null && typeof obj === 'object' && '$$type' in obj) {\n return (\n obj.$$type === '@backstage/FrontendPlugin' ||\n obj.$$type === '@backstage/FrontendModule'\n );\n }\n return false;\n}\n\nfunction isFeatureOrLoader(\n obj: unknown,\n): obj is FrontendFeature | FrontendFeatureLoader {\n return isBackstageFeature(obj) || isBackstageFeatureLoader(obj);\n}\n"],"names":[],"mappings":";;;AA2BA,SAAS,2BAA2B,MAAgB,EAAA;AAElD,EAAA,MAAM,WACJ,MAAO,CAAA,WAAA,CAAY,cAAc,CACjC,IAAA,MAAA,CAAO,YAAY,2BAA2B,CAAA;AAChD,EAAI,IAAA,QAAA,KAAa,KAAa,CAAA,IAAA,QAAA,KAAa,IAAM,EAAA;AAC/C,IAAO,OAAA,KAAA,CAAA;AAAA;AAGT,EAAI,IAAA,OAAO,aAAa,QAAU,EAAA;AAChC,IAAA,IAAI,aAAa,KAAO,EAAA;AACtB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,mCAAmC,QAAQ,CAAA,iBAAA;AAAA,OAC7C;AAAA;AAEF,IAAA,OAAO,EAAC;AAAA;AAGV,EAAA,IAAI,OAAO,QAAa,KAAA,QAAA,IAAY,KAAM,CAAA,OAAA,CAAQ,QAAQ,CAAG,EAAA;AAC3D,IAAM,MAAA,IAAI,MAAM,mDAAmD,CAAA;AAAA;AAErE,EAAA,MAAM,cAAiB,GAAA,IAAI,YAAa,CAAA,QAAA,EAAU,cAAc,CAAA;AAEhE,EAAO,OAAA;AAAA,IACL,OAAA,EAAS,cAAe,CAAA,sBAAA,CAAuB,SAAS,CAAA;AAAA,IACxD,OAAA,EAAS,cAAe,CAAA,sBAAA,CAAuB,SAAS;AAAA,GAC1D;AACF;AAKO,SAAS,0BAA0B,MAExC,EAAA;AACA,EAAM,MAAA,UAAA,GACJ,OACA,2BAA2B,CAAA;AAE7B,EAAM,MAAA,SAAA,GAAY,2BAA2B,MAAM,CAAA;AACnD,EAAA,IAAI,CAAC,SAAW,EAAA;AACd,IAAO,OAAA,EAAE,QAAU,EAAA,EAAG,EAAA;AAAA;AAGxB,EAAO,OAAA;AAAA,IACL,UACE,UAAY,EAAA,OAAA,CACT,OAAO,CAAC,EAAE,MAAW,KAAA;AACpB,MAAA,IAAI,SAAU,CAAA,OAAA,EAAS,QAAS,CAAA,IAAI,CAAG,EAAA;AACrC,QAAO,OAAA,KAAA;AAAA;AAET,MAAA,IAAI,UAAU,OAAW,IAAA,CAAC,UAAU,OAAQ,CAAA,QAAA,CAAS,IAAI,CAAG,EAAA;AAC1D,QAAO,OAAA,KAAA;AAAA;AAET,MAAO,OAAA,IAAA;AAAA,KACR,CACA,CAAA,GAAA,CAAI,CAAK,CAAA,KAAA,CAAA,CAAE,OAAO,CAClB,CAAA,MAAA,CAAO,iBAAiB,CAAA,IAAK;AAAC,GACrC;AACF;AAEA,SAAS,mBAAmB,GAAsC,EAAA;AAChE,EAAA,IAAI,QAAQ,IAAQ,IAAA,OAAO,GAAQ,KAAA,QAAA,IAAY,YAAY,GAAK,EAAA;AAC9D,IAAA,OACE,GAAI,CAAA,MAAA,KAAW,2BACf,IAAA,GAAA,CAAI,MAAW,KAAA,2BAAA;AAAA;AAGnB,EAAO,OAAA,KAAA;AACT;AAEA,SAAS,kBACP,GACgD,EAAA;AAChD,EAAA,OAAO,kBAAmB,CAAA,GAAG,CAAK,IAAA,wBAAA,CAAyB,GAAG,CAAA;AAChE;;;;"}
1
+ {"version":3,"file":"discovery.esm.js","sources":["../src/discovery.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, ConfigReader } from '@backstage/config';\nimport {\n FrontendFeature,\n FrontendFeatureLoader,\n} from '@backstage/frontend-plugin-api';\nimport { isBackstageFeatureLoader } from './resolution';\n\ninterface DiscoveryGlobal {\n modules: Array<{ name: string; export?: string; default: unknown }>;\n}\n\nfunction readPackageDetectionConfig(config: Config) {\n // The experimental key is deprecated, but supported still for backwards compatibility\n const packages =\n config.getOptional('app.packages') ??\n config.getOptional('app.experimental.packages');\n if (packages === undefined || packages === null) {\n return undefined;\n }\n\n if (typeof packages === 'string') {\n if (packages !== 'all') {\n throw new Error(\n `Invalid app.packages mode, got '${packages}', expected 'all'`,\n );\n }\n return {};\n }\n\n if (typeof packages !== 'object' || Array.isArray(packages)) {\n throw new Error(\"Invalid config at 'app.packages', expected object\");\n }\n const packagesConfig = new ConfigReader(packages, 'app.packages');\n\n return {\n include: packagesConfig.getOptionalStringArray('include'),\n exclude: packagesConfig.getOptionalStringArray('exclude'),\n };\n}\n\n/**\n * @public\n */\nexport function discoverAvailableFeatures(config: Config): {\n features: (FrontendFeature | FrontendFeatureLoader)[];\n} {\n const discovered = (\n window as { '__@backstage/discovered__'?: DiscoveryGlobal }\n )['__@backstage/discovered__'];\n\n const detection = readPackageDetectionConfig(config);\n if (!detection) {\n return { features: [] };\n }\n\n return {\n features:\n discovered?.modules\n .filter(({ name }) => {\n if (detection.exclude?.includes(name)) {\n return false;\n }\n if (detection.include && !detection.include.includes(name)) {\n return false;\n }\n return true;\n })\n .map(m => m.default)\n .filter(isFeatureOrLoader) ?? [],\n };\n}\n\nfunction isBackstageFeature(obj: unknown): obj is FrontendFeature {\n if (obj !== null && typeof obj === 'object' && '$$type' in obj) {\n return (\n obj.$$type === '@backstage/FrontendPlugin' ||\n obj.$$type === '@backstage/FrontendModule'\n );\n }\n return false;\n}\n\nfunction isFeatureOrLoader(\n obj: unknown,\n): obj is FrontendFeature | FrontendFeatureLoader {\n return isBackstageFeature(obj) || isBackstageFeatureLoader(obj);\n}\n"],"names":[],"mappings":";;;AA2BA,SAAS,2BAA2B,MAAA,EAAgB;AAElD,EAAA,MAAM,WACJ,MAAA,CAAO,WAAA,CAAY,cAAc,CAAA,IACjC,MAAA,CAAO,YAAY,2BAA2B,CAAA;AAChD,EAAA,IAAI,QAAA,KAAa,MAAA,IAAa,QAAA,KAAa,IAAA,EAAM;AAC/C,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,IAAI,aAAa,KAAA,EAAO;AACtB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,mCAAmC,QAAQ,CAAA,iBAAA;AAAA,OAC7C;AAAA,IACF;AACA,IAAA,OAAO,EAAC;AAAA,EACV;AAEA,EAAA,IAAI,OAAO,QAAA,KAAa,QAAA,IAAY,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,EAAG;AAC3D,IAAA,MAAM,IAAI,MAAM,mDAAmD,CAAA;AAAA,EACrE;AACA,EAAA,MAAM,cAAA,GAAiB,IAAI,YAAA,CAAa,QAAA,EAAU,cAAc,CAAA;AAEhE,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,cAAA,CAAe,sBAAA,CAAuB,SAAS,CAAA;AAAA,IACxD,OAAA,EAAS,cAAA,CAAe,sBAAA,CAAuB,SAAS;AAAA,GAC1D;AACF;AAKO,SAAS,0BAA0B,MAAA,EAExC;AACA,EAAA,MAAM,UAAA,GACJ,OACA,2BAA2B,CAAA;AAE7B,EAAA,MAAM,SAAA,GAAY,2BAA2B,MAAM,CAAA;AACnD,EAAA,IAAI,CAAC,SAAA,EAAW;AACd,IAAA,OAAO,EAAE,QAAA,EAAU,EAAC,EAAE;AAAA,EACxB;AAEA,EAAA,OAAO;AAAA,IACL,UACE,UAAA,EAAY,OAAA,CACT,OAAO,CAAC,EAAE,MAAK,KAAM;AACpB,MAAA,IAAI,SAAA,CAAU,OAAA,EAAS,QAAA,CAAS,IAAI,CAAA,EAAG;AACrC,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,IAAI,UAAU,OAAA,IAAW,CAAC,UAAU,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EAAG;AAC1D,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAA,CACA,GAAA,CAAI,CAAA,CAAA,KAAK,CAAA,CAAE,OAAO,CAAA,CAClB,MAAA,CAAO,iBAAiB,CAAA,IAAK;AAAC,GACrC;AACF;AAEA,SAAS,mBAAmB,GAAA,EAAsC;AAChE,EAAA,IAAI,QAAQ,IAAA,IAAQ,OAAO,GAAA,KAAQ,QAAA,IAAY,YAAY,GAAA,EAAK;AAC9D,IAAA,OACE,GAAA,CAAI,MAAA,KAAW,2BAAA,IACf,GAAA,CAAI,MAAA,KAAW,2BAAA;AAAA,EAEnB;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,kBACP,GAAA,EACgD;AAChD,EAAA,OAAO,kBAAA,CAAmB,GAAG,CAAA,IAAK,wBAAA,CAAyB,GAAG,CAAA;AAChE;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"createFrontendFeatureLoader.esm.js","sources":["../../../../../frontend-plugin-api/src/wiring/createFrontendFeatureLoader.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ConfigApi } from '../apis/definitions';\nimport { describeParentCallSite } from '../routing/describeParentCallSite';\nimport { FrontendFeature } from './types';\n\n/** @public */\nexport interface CreateFrontendFeatureLoaderOptions {\n loader(deps: {\n config: ConfigApi;\n }):\n | Iterable<\n | FrontendFeature\n | FrontendFeatureLoader\n | Promise<{ default: FrontendFeature | FrontendFeatureLoader }>\n >\n | Promise<\n Iterable<\n | FrontendFeature\n | FrontendFeatureLoader\n | Promise<{ default: FrontendFeature | FrontendFeatureLoader }>\n >\n >\n | AsyncIterable<\n | FrontendFeature\n | FrontendFeatureLoader\n | { default: FrontendFeature | FrontendFeatureLoader }\n >;\n}\n\n/** @public */\nexport interface FrontendFeatureLoader {\n readonly $$type: '@backstage/FrontendFeatureLoader';\n}\n\n/** @internal */\nexport interface InternalFrontendFeatureLoader extends FrontendFeatureLoader {\n readonly version: 'v1';\n readonly description: string;\n readonly loader: (deps: {\n config: ConfigApi;\n }) => Promise<(FrontendFeature | FrontendFeatureLoader)[]>;\n}\n\n/** @public */\nexport function createFrontendFeatureLoader(\n options: CreateFrontendFeatureLoaderOptions,\n): FrontendFeatureLoader {\n const description = `created at '${describeParentCallSite()}'`;\n return {\n $$type: '@backstage/FrontendFeatureLoader',\n version: 'v1',\n description,\n toString() {\n return `FeatureLoader{description=${description}}`;\n },\n async loader(deps: {\n config: ConfigApi;\n }): Promise<(FrontendFeature | FrontendFeatureLoader)[]> {\n const it = await options.loader(deps);\n const result = new Array<FrontendFeature | FrontendFeatureLoader>();\n for await (const item of it) {\n if (isFeatureOrLoader(item)) {\n result.push(item);\n } else if ('default' in item) {\n result.push(item.default);\n } else {\n throw new Error(`Invalid item \"${item}\"`);\n }\n }\n return result;\n },\n } as InternalFrontendFeatureLoader;\n}\n\n/** @internal */\nexport function isInternalFrontendFeatureLoader(opaque: {\n $$type: string;\n}): opaque is InternalFrontendFeatureLoader {\n if (opaque.$$type === '@backstage/FrontendFeatureLoader') {\n // Make sure we throw if invalid\n toInternalFrontendFeatureLoader(opaque as FrontendFeatureLoader);\n return true;\n }\n return false;\n}\n\n/** @internal */\nexport function toInternalFrontendFeatureLoader(\n plugin: FrontendFeatureLoader,\n): InternalFrontendFeatureLoader {\n const internal = plugin as InternalFrontendFeatureLoader;\n if (internal.$$type !== '@backstage/FrontendFeatureLoader') {\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\nfunction isFeatureOrLoader(\n obj: unknown,\n): obj is FrontendFeature | FrontendFeatureLoader {\n if (obj !== null && typeof obj === 'object' && '$$type' in obj) {\n return (\n obj.$$type === '@backstage/FrontendPlugin' ||\n obj.$$type === '@backstage/FrontendModule' ||\n obj.$$type === '@backstage/FrontendFeatureLoader'\n );\n }\n return false;\n}\n"],"names":[],"mappings":"AA0FO,SAAS,gCAAgC,MAEJ,EAAA;AAC1C,EAAI,IAAA,MAAA,CAAO,WAAW,kCAAoC,EAAA;AAExD,IAAA,+BAAA,CAAgC,MAA+B,CAAA;AAC/D,IAAO,OAAA,IAAA;AAAA;AAET,EAAO,OAAA,KAAA;AACT;AAGO,SAAS,gCACd,MAC+B,EAAA;AAC/B,EAAA,MAAM,QAAW,GAAA,MAAA;AACjB,EAAI,IAAA,QAAA,CAAS,WAAW,kCAAoC,EAAA;AAC1D,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
+ {"version":3,"file":"createFrontendFeatureLoader.esm.js","sources":["../../../../../frontend-plugin-api/src/wiring/createFrontendFeatureLoader.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ConfigApi } from '../apis/definitions';\nimport { describeParentCallSite } from '../routing/describeParentCallSite';\nimport { FrontendFeature } from './types';\n\n/** @public */\nexport interface CreateFrontendFeatureLoaderOptions {\n loader(deps: {\n config: ConfigApi;\n }):\n | Iterable<\n | FrontendFeature\n | FrontendFeatureLoader\n | Promise<{ default: FrontendFeature | FrontendFeatureLoader }>\n >\n | Promise<\n Iterable<\n | FrontendFeature\n | FrontendFeatureLoader\n | Promise<{ default: FrontendFeature | FrontendFeatureLoader }>\n >\n >\n | AsyncIterable<\n | FrontendFeature\n | FrontendFeatureLoader\n | { default: FrontendFeature | FrontendFeatureLoader }\n >;\n}\n\n/** @public */\nexport interface FrontendFeatureLoader {\n readonly $$type: '@backstage/FrontendFeatureLoader';\n}\n\n/** @internal */\nexport interface InternalFrontendFeatureLoader extends FrontendFeatureLoader {\n readonly version: 'v1';\n readonly description: string;\n readonly loader: (deps: {\n config: ConfigApi;\n }) => Promise<(FrontendFeature | FrontendFeatureLoader)[]>;\n}\n\n/** @public */\nexport function createFrontendFeatureLoader(\n options: CreateFrontendFeatureLoaderOptions,\n): FrontendFeatureLoader {\n const description = `created at '${describeParentCallSite()}'`;\n return {\n $$type: '@backstage/FrontendFeatureLoader',\n version: 'v1',\n description,\n toString() {\n return `FeatureLoader{description=${description}}`;\n },\n async loader(deps: {\n config: ConfigApi;\n }): Promise<(FrontendFeature | FrontendFeatureLoader)[]> {\n const it = await options.loader(deps);\n const result = new Array<FrontendFeature | FrontendFeatureLoader>();\n for await (const item of it) {\n if (isFeatureOrLoader(item)) {\n result.push(item);\n } else if ('default' in item) {\n result.push(item.default);\n } else {\n throw new Error(`Invalid item \"${item}\"`);\n }\n }\n return result;\n },\n } as InternalFrontendFeatureLoader;\n}\n\n/** @internal */\nexport function isInternalFrontendFeatureLoader(opaque: {\n $$type: string;\n}): opaque is InternalFrontendFeatureLoader {\n if (opaque.$$type === '@backstage/FrontendFeatureLoader') {\n // Make sure we throw if invalid\n toInternalFrontendFeatureLoader(opaque as FrontendFeatureLoader);\n return true;\n }\n return false;\n}\n\n/** @internal */\nexport function toInternalFrontendFeatureLoader(\n plugin: FrontendFeatureLoader,\n): InternalFrontendFeatureLoader {\n const internal = plugin as InternalFrontendFeatureLoader;\n if (internal.$$type !== '@backstage/FrontendFeatureLoader') {\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\nfunction isFeatureOrLoader(\n obj: unknown,\n): obj is FrontendFeature | FrontendFeatureLoader {\n if (obj !== null && typeof obj === 'object' && '$$type' in obj) {\n return (\n obj.$$type === '@backstage/FrontendPlugin' ||\n obj.$$type === '@backstage/FrontendModule' ||\n obj.$$type === '@backstage/FrontendFeatureLoader'\n );\n }\n return false;\n}\n"],"names":[],"mappings":"AA0FO,SAAS,gCAAgC,MAAA,EAEJ;AAC1C,EAAA,IAAI,MAAA,CAAO,WAAW,kCAAA,EAAoC;AAExD,IAAA,+BAAA,CAAgC,MAA+B,CAAA;AAC/D,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT;AAGO,SAAS,gCACd,MAAA,EAC+B;AAC/B,EAAA,MAAM,QAAA,GAAW,MAAA;AACjB,EAAA,IAAI,QAAA,CAAS,WAAW,kCAAA,EAAoC;AAC1D,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,mCAAA,EAAsC,QAAA,CAAS,MAAM,CAAA,CAAA,CAAG,CAAA;AAAA,EAC1E;AACA,EAAA,IAAI,QAAA,CAAS,YAAY,IAAA,EAAM;AAC7B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,sCAAA,EAAyC,SAAS,OAAO,CAAA,CAAA;AAAA,KAC3D;AAAA,EACF;AACA,EAAA,OAAO,QAAA;AACT;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"resolution.esm.js","sources":["../src/resolution.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { stringifyError } from '@backstage/errors';\nimport {\n FrontendFeature,\n FrontendFeatureLoader,\n} from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isInternalFrontendFeatureLoader } from '../../frontend-plugin-api/src/wiring/createFrontendFeatureLoader';\n\n/** @public */\nexport async function resolveAsyncFeatures(options: {\n config: Config;\n features?: (FrontendFeature | FrontendFeatureLoader)[];\n}): Promise<{ features: FrontendFeature[] }> {\n const loadedFeatures: FrontendFeature[] = [];\n const alreadyMetFeatureLoaders: FrontendFeatureLoader[] = [];\n const maxRecursionDepth = 5;\n\n async function applyFeatureLoaders(\n featuresOrLoaders: (FrontendFeature | FrontendFeatureLoader)[],\n recursionDepth: number,\n ) {\n if (featuresOrLoaders.length === 0) {\n return;\n }\n\n for (const featureOrLoader of featuresOrLoaders) {\n if (isBackstageFeatureLoader(featureOrLoader)) {\n if (alreadyMetFeatureLoaders.some(l => l === featureOrLoader)) {\n continue;\n }\n if (isInternalFrontendFeatureLoader(featureOrLoader)) {\n if (recursionDepth > maxRecursionDepth) {\n throw new Error(\n `Maximum feature loading recursion depth (${maxRecursionDepth}) reached for the feature loader ${featureOrLoader.description}`,\n );\n }\n alreadyMetFeatureLoaders.push(featureOrLoader);\n let result: (FrontendFeature | FrontendFeatureLoader)[];\n try {\n result = await featureOrLoader.loader({ config: options.config });\n } catch (e) {\n throw new Error(\n `Failed to read frontend features from loader ${\n featureOrLoader.description\n }: ${stringifyError(e)}`,\n );\n }\n await applyFeatureLoaders(result, recursionDepth + 1);\n }\n } else {\n loadedFeatures.push(featureOrLoader);\n }\n }\n }\n\n await applyFeatureLoaders(options.features ?? [], 1);\n\n return { features: loadedFeatures };\n}\n\nexport function isBackstageFeatureLoader(\n obj: unknown,\n): obj is FrontendFeatureLoader {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n '$$type' in obj &&\n obj.$$type === '@backstage/FrontendFeatureLoader'\n );\n}\n"],"names":[],"mappings":";;;AA0BA,eAAsB,qBAAqB,OAGE,EAAA;AAC3C,EAAA,MAAM,iBAAoC,EAAC;AAC3C,EAAA,MAAM,2BAAoD,EAAC;AAC3D,EAAA,MAAM,iBAAoB,GAAA,CAAA;AAE1B,EAAe,eAAA,mBAAA,CACb,mBACA,cACA,EAAA;AACA,IAAI,IAAA,iBAAA,CAAkB,WAAW,CAAG,EAAA;AAClC,MAAA;AAAA;AAGF,IAAA,KAAA,MAAW,mBAAmB,iBAAmB,EAAA;AAC/C,MAAI,IAAA,wBAAA,CAAyB,eAAe,CAAG,EAAA;AAC7C,QAAA,IAAI,wBAAyB,CAAA,IAAA,CAAK,CAAK,CAAA,KAAA,CAAA,KAAM,eAAe,CAAG,EAAA;AAC7D,UAAA;AAAA;AAEF,QAAI,IAAA,+BAAA,CAAgC,eAAe,CAAG,EAAA;AACpD,UAAA,IAAI,iBAAiB,iBAAmB,EAAA;AACtC,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,CAA4C,yCAAA,EAAA,iBAAiB,CAAoC,iCAAA,EAAA,eAAA,CAAgB,WAAW,CAAA;AAAA,aAC9H;AAAA;AAEF,UAAA,wBAAA,CAAyB,KAAK,eAAe,CAAA;AAC7C,UAAI,IAAA,MAAA;AACJ,UAAI,IAAA;AACF,YAAA,MAAA,GAAS,MAAM,eAAgB,CAAA,MAAA,CAAO,EAAE,MAAQ,EAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,mBACzD,CAAG,EAAA;AACV,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,gDACE,eAAgB,CAAA,WAClB,CAAK,EAAA,EAAA,cAAA,CAAe,CAAC,CAAC,CAAA;AAAA,aACxB;AAAA;AAEF,UAAM,MAAA,mBAAA,CAAoB,MAAQ,EAAA,cAAA,GAAiB,CAAC,CAAA;AAAA;AACtD,OACK,MAAA;AACL,QAAA,cAAA,CAAe,KAAK,eAAe,CAAA;AAAA;AACrC;AACF;AAGF,EAAA,MAAM,mBAAoB,CAAA,OAAA,CAAQ,QAAY,IAAA,IAAI,CAAC,CAAA;AAEnD,EAAO,OAAA,EAAE,UAAU,cAAe,EAAA;AACpC;AAEO,SAAS,yBACd,GAC8B,EAAA;AAC9B,EACE,OAAA,GAAA,KAAQ,QACR,OAAO,GAAA,KAAQ,YACf,QAAY,IAAA,GAAA,IACZ,IAAI,MAAW,KAAA,kCAAA;AAEnB;;;;"}
1
+ {"version":3,"file":"resolution.esm.js","sources":["../src/resolution.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Config } from '@backstage/config';\nimport { stringifyError } from '@backstage/errors';\nimport {\n FrontendFeature,\n FrontendFeatureLoader,\n} from '@backstage/frontend-plugin-api';\n// eslint-disable-next-line @backstage/no-relative-monorepo-imports\nimport { isInternalFrontendFeatureLoader } from '../../frontend-plugin-api/src/wiring/createFrontendFeatureLoader';\n\n/** @public */\nexport async function resolveAsyncFeatures(options: {\n config: Config;\n features?: (FrontendFeature | FrontendFeatureLoader)[];\n}): Promise<{ features: FrontendFeature[] }> {\n const loadedFeatures: FrontendFeature[] = [];\n const alreadyMetFeatureLoaders: FrontendFeatureLoader[] = [];\n const maxRecursionDepth = 5;\n\n async function applyFeatureLoaders(\n featuresOrLoaders: (FrontendFeature | FrontendFeatureLoader)[],\n recursionDepth: number,\n ) {\n if (featuresOrLoaders.length === 0) {\n return;\n }\n\n for (const featureOrLoader of featuresOrLoaders) {\n if (isBackstageFeatureLoader(featureOrLoader)) {\n if (alreadyMetFeatureLoaders.some(l => l === featureOrLoader)) {\n continue;\n }\n if (isInternalFrontendFeatureLoader(featureOrLoader)) {\n if (recursionDepth > maxRecursionDepth) {\n throw new Error(\n `Maximum feature loading recursion depth (${maxRecursionDepth}) reached for the feature loader ${featureOrLoader.description}`,\n );\n }\n alreadyMetFeatureLoaders.push(featureOrLoader);\n let result: (FrontendFeature | FrontendFeatureLoader)[];\n try {\n result = await featureOrLoader.loader({ config: options.config });\n } catch (e) {\n throw new Error(\n `Failed to read frontend features from loader ${\n featureOrLoader.description\n }: ${stringifyError(e)}`,\n );\n }\n await applyFeatureLoaders(result, recursionDepth + 1);\n }\n } else {\n loadedFeatures.push(featureOrLoader);\n }\n }\n }\n\n await applyFeatureLoaders(options.features ?? [], 1);\n\n return { features: loadedFeatures };\n}\n\nexport function isBackstageFeatureLoader(\n obj: unknown,\n): obj is FrontendFeatureLoader {\n return (\n obj !== null &&\n typeof obj === 'object' &&\n '$$type' in obj &&\n obj.$$type === '@backstage/FrontendFeatureLoader'\n );\n}\n"],"names":[],"mappings":";;;AA0BA,eAAsB,qBAAqB,OAAA,EAGE;AAC3C,EAAA,MAAM,iBAAoC,EAAC;AAC3C,EAAA,MAAM,2BAAoD,EAAC;AAC3D,EAAA,MAAM,iBAAA,GAAoB,CAAA;AAE1B,EAAA,eAAe,mBAAA,CACb,mBACA,cAAA,EACA;AACA,IAAA,IAAI,iBAAA,CAAkB,WAAW,CAAA,EAAG;AAClC,MAAA;AAAA,IACF;AAEA,IAAA,KAAA,MAAW,mBAAmB,iBAAA,EAAmB;AAC/C,MAAA,IAAI,wBAAA,CAAyB,eAAe,CAAA,EAAG;AAC7C,QAAA,IAAI,wBAAA,CAAyB,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,KAAM,eAAe,CAAA,EAAG;AAC7D,UAAA;AAAA,QACF;AACA,QAAA,IAAI,+BAAA,CAAgC,eAAe,CAAA,EAAG;AACpD,UAAA,IAAI,iBAAiB,iBAAA,EAAmB;AACtC,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,CAAA,yCAAA,EAA4C,iBAAiB,CAAA,iCAAA,EAAoC,eAAA,CAAgB,WAAW,CAAA;AAAA,aAC9H;AAAA,UACF;AACA,UAAA,wBAAA,CAAyB,KAAK,eAAe,CAAA;AAC7C,UAAA,IAAI,MAAA;AACJ,UAAA,IAAI;AACF,YAAA,MAAA,GAAS,MAAM,eAAA,CAAgB,MAAA,CAAO,EAAE,MAAA,EAAQ,OAAA,CAAQ,QAAQ,CAAA;AAAA,UAClE,SAAS,CAAA,EAAG;AACV,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,gDACE,eAAA,CAAgB,WAClB,CAAA,EAAA,EAAK,cAAA,CAAe,CAAC,CAAC,CAAA;AAAA,aACxB;AAAA,UACF;AACA,UAAA,MAAM,mBAAA,CAAoB,MAAA,EAAQ,cAAA,GAAiB,CAAC,CAAA;AAAA,QACtD;AAAA,MACF,CAAA,MAAO;AACL,QAAA,cAAA,CAAe,KAAK,eAAe,CAAA;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,mBAAA,CAAoB,OAAA,CAAQ,QAAA,IAAY,IAAI,CAAC,CAAA;AAEnD,EAAA,OAAO,EAAE,UAAU,cAAA,EAAe;AACpC;AAEO,SAAS,yBACd,GAAA,EAC8B;AAC9B,EAAA,OACE,GAAA,KAAQ,QACR,OAAO,GAAA,KAAQ,YACf,QAAA,IAAY,GAAA,IACZ,IAAI,MAAA,KAAW,kCAAA;AAEnB;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/frontend-defaults",
3
- "version": "0.3.0-next.3",
3
+ "version": "0.3.0",
4
4
  "backstage": {
5
5
  "role": "web-library"
6
6
  },
@@ -31,18 +31,18 @@
31
31
  "test": "backstage-cli package test"
32
32
  },
33
33
  "dependencies": {
34
- "@backstage/config": "1.3.3",
35
- "@backstage/core-components": "0.17.5-next.2",
36
- "@backstage/errors": "1.2.7",
37
- "@backstage/frontend-app-api": "0.12.0-next.3",
38
- "@backstage/frontend-plugin-api": "0.11.0-next.2",
39
- "@backstage/plugin-app": "0.2.0-next.2",
34
+ "@backstage/config": "^1.3.3",
35
+ "@backstage/core-components": "^0.17.5",
36
+ "@backstage/errors": "^1.2.7",
37
+ "@backstage/frontend-app-api": "^0.12.0",
38
+ "@backstage/frontend-plugin-api": "^0.11.0",
39
+ "@backstage/plugin-app": "^0.2.0",
40
40
  "@react-hookz/web": "^24.0.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@backstage/cli": "0.34.0-next.2",
44
- "@backstage/core-plugin-api": "1.10.9",
45
- "@backstage/test-utils": "1.7.11-next.0",
43
+ "@backstage/cli": "^0.34.0",
44
+ "@backstage/core-plugin-api": "^1.10.9",
45
+ "@backstage/test-utils": "^1.7.11",
46
46
  "@testing-library/jest-dom": "^6.0.0",
47
47
  "@testing-library/react": "^16.0.0",
48
48
  "@types/react": "^18.0.0",