From 170a2b318895b21c9d851cb1c547f2c67af4c99d Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Fri, 19 Dec 2025 13:16:54 -0700 Subject: [PATCH 001/211] chore: add newline to licenseInfo files Signed-off-by: aramissennyeydd --- .../templates/typescript-backstage-client/licenseInfo.mustache | 2 +- .../templates/typescript-backstage-server/licenseInfo.mustache | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/repo-tools/templates/typescript-backstage-client/licenseInfo.mustache b/packages/repo-tools/templates/typescript-backstage-client/licenseInfo.mustache index 7a1b10e780..f3855e8015 100644 --- a/packages/repo-tools/templates/typescript-backstage-client/licenseInfo.mustache +++ b/packages/repo-tools/templates/typescript-backstage-client/licenseInfo.mustache @@ -2,4 +2,4 @@ // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** \ No newline at end of file +// ****************************************************************** diff --git a/packages/repo-tools/templates/typescript-backstage-server/licenseInfo.mustache b/packages/repo-tools/templates/typescript-backstage-server/licenseInfo.mustache index 7a1b10e780..f3855e8015 100644 --- a/packages/repo-tools/templates/typescript-backstage-server/licenseInfo.mustache +++ b/packages/repo-tools/templates/typescript-backstage-server/licenseInfo.mustache @@ -2,4 +2,4 @@ // ****************************************************************** // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * -// ****************************************************************** \ No newline at end of file +// ****************************************************************** From cd75ed0ecca52b122d4398758c3f66759505be70 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Fri, 19 Dec 2025 13:17:32 -0700 Subject: [PATCH 002/211] add changeset Signed-off-by: aramissennyeydd --- .changeset/clean-suits-follow.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/clean-suits-follow.md diff --git a/.changeset/clean-suits-follow.md b/.changeset/clean-suits-follow.md new file mode 100644 index 0000000000..992b3e2947 --- /dev/null +++ b/.changeset/clean-suits-follow.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Add newline to OpenAPI license template files. From 97a726e0bb5b1615b4e1dffe899945398d5e5874 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sun, 9 Nov 2025 16:25:48 -0500 Subject: [PATCH 003/211] chore: disable old api reference generation Signed-off-by: aramissennyeydd --- .github/workflows/deploy_microsite.yml | 40 ++++++-------------------- microsite/package.json | 2 +- microsite/scripts/pre-build.js | 39 ------------------------- 3 files changed, 10 insertions(+), 71 deletions(-) delete mode 100644 microsite/scripts/pre-build.js diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 08f349c7ec..b403b0af9a 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -69,17 +69,6 @@ jobs: with: cache-prefix: ${{ runner.os }}-v22.x - - name: build API reference - run: yarn build:api-docs - - - name: upload API reference - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: stable-reference - path: docs/reference/ - if-no-files-found: error - retention-days: 1 - # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 @@ -89,13 +78,13 @@ jobs: restore-keys: | ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable- - - name: build API reference (beta) + - name: build API reference run: yarn backstage-repo-tools package-docs - - name: upload API reference (beta) + - name: upload API reference uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: stable-reference-beta + name: stable-reference path: type-docs/ if-no-files-found: error retention-days: 1 @@ -153,17 +142,6 @@ jobs: with: cache-prefix: ${{ runner.os }}-v22.x - - name: build API reference - run: yarn build:api-docs - - - name: upload API reference - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: next-reference - path: docs/reference/ - if-no-files-found: error - retention-days: 1 - # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 @@ -173,13 +151,13 @@ jobs: restore-keys: | ${{ runner.os }}-v${{ matrix.node-version }}-package-docs- - - name: build API reference (beta) + - name: build API reference run: yarn backstage-repo-tools package-docs - - name: upload API reference (beta) + - name: upload API reference uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: next-reference-beta + name: next-reference path: type-docs/ if-no-files-found: error retention-days: 1 @@ -317,12 +295,12 @@ jobs: - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: - name: stable-reference-beta - path: microsite/build/api/stable/ + name: stable-reference + path: microsite/build/api/ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: - name: next-reference-beta + name: next-reference path: microsite/build/api/next/ - name: Check the build output diff --git a/microsite/package.json b/microsite/package.json index edf07abb02..fd727ef1bb 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -4,7 +4,7 @@ "private": true, "license": "Apache-2.0", "scripts": { - "build": "node scripts/pre-build.js && docusaurus build", + "build": "docusaurus build", "deploy": "docusaurus deploy", "docusaurus": "docusaurus", "generate-openapi-docs": "yarn docusaurus clean-api-docs all && yarn docusaurus gen-api-docs all", diff --git a/microsite/scripts/pre-build.js b/microsite/scripts/pre-build.js deleted file mode 100644 index 3b5bd4fb0b..0000000000 --- a/microsite/scripts/pre-build.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -const { existsSync, writeFileSync, mkdirSync } = require('fs'); -const path = require('path'); - -const PLACEHOLDER = `--- -id: "index" -title: "Package Index" -description: "Index of all Backstage Packages" ---- -Run \`yarn build:api-docs\` to generate the API docs. -`; - -async function main() { - const referencesDir = '../docs/reference'; - if (!existsSync(referencesDir)) { - mkdirSync(referencesDir); - writeFileSync(path.join(referencesDir, 'index.md'), PLACEHOLDER); - } -} - -main().catch(error => { - console.error(error.stack); - process.exit(1); -}); From 14a8190e1696e9cf5120e636ae0199bab2e307aa Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sun, 9 Nov 2025 16:36:16 -0500 Subject: [PATCH 004/211] fix verify workflow too Signed-off-by: aramissennyeydd add back prebuild script Signed-off-by: aramissennyeydd fix script location Signed-off-by: aramissennyeydd skip techdocs Signed-off-by: aramissennyeydd make references/index tracked in git Signed-off-by: aramissennyeydd don't try to build with new reference docs Signed-off-by: aramissennyeydd fix redirect Signed-off-by: aramissennyeydd disable redirect Signed-off-by: aramissennyeydd --- .github/workflows/deploy_microsite.yml | 14 +----- .github/workflows/verify_microsite.yml | 65 ++++---------------------- docs/references/index.md | 7 +++ microsite/package.json | 2 +- 4 files changed, 17 insertions(+), 71 deletions(-) create mode 100644 docs/references/index.md diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index b403b0af9a..78ae43c637 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -238,12 +238,6 @@ jobs: run: yarn install --immutable working-directory: microsite - - name: download stable reference - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: stable-reference - path: docs/reference - - name: download stable OpenAPI API docs uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: @@ -272,12 +266,6 @@ jobs: run: yarn install --immutable working-directory: microsite - - name: download next reference - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: next-reference - path: docs/reference - - name: download next OpenAPI API docs uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: @@ -296,7 +284,7 @@ jobs: - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: stable-reference - path: microsite/build/api/ + path: microsite/build/api/stable/ - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 20a6e705cd..b3c8a45172 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -74,17 +74,6 @@ jobs: with: cache-prefix: ${{ runner.os }}-v22.x - - name: build API reference - run: yarn build:api-docs - - - name: upload API reference - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: stable-reference - path: docs/reference/ - if-no-files-found: error - retention-days: 1 - # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 @@ -94,13 +83,13 @@ jobs: restore-keys: | ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable- - - name: build API reference (beta) + - name: build API reference run: yarn backstage-repo-tools package-docs - - name: upload API reference (beta) + - name: upload API reference uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: stable-reference-beta + name: stable-reference path: type-docs/ # Always save success cache even if there were failures, that way it can be used in re-triggered builds @@ -155,17 +144,6 @@ jobs: with: cache-prefix: ${{ runner.os }}-v22.x - - name: build API reference - run: yarn build:api-docs - - - name: upload API reference - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 - with: - name: next-reference - path: docs/reference/ - if-no-files-found: error - retention-days: 1 - # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 @@ -175,13 +153,13 @@ jobs: restore-keys: | ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-next- - - name: build API reference (beta) + - name: build API reference run: yarn backstage-repo-tools package-docs - - name: upload API reference (beta) + - name: upload API reference uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: next-reference-beta + name: next-reference path: type-docs/ # Always save success cache even if there were failures, that way it can be used in re-triggered builds @@ -267,12 +245,6 @@ jobs: - name: verify yarn dependency duplicates run: node scripts/verify-lockfile-duplicates.js - - name: download stable reference - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: stable-reference - path: docs/reference - - name: download stable OpenAPI API docs uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: @@ -291,22 +263,13 @@ jobs: - name: clear generated docs run: git clean -fdx docs/ - - name: build API reference - run: yarn build:api-docs - - name: checkout master uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: clean: false - - name: Build MkDocs for TechDocs - run: mkdocs build --strict - - - name: download next reference - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: next-reference - path: docs/reference + # - name: Build MkDocs for TechDocs + # run: mkdocs build --strict - name: download next OpenAPI API docs uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 @@ -317,15 +280,3 @@ jobs: - name: build microsite run: yarn build working-directory: microsite - - - name: download stable reference (beta) - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: stable-reference-beta - path: api/stable/ - - - name: download next reference (beta) - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 - with: - name: next-reference-beta - path: api/next/ diff --git a/docs/references/index.md b/docs/references/index.md new file mode 100644 index 0000000000..abd5c99c0c --- /dev/null +++ b/docs/references/index.md @@ -0,0 +1,7 @@ +--- +id: 'index' +title: 'Package Index' +description: 'Index of all Backstage Packages' +--- + +Please use [the new API documentation](https://backstage.io/api/stable) for more information. diff --git a/microsite/package.json b/microsite/package.json index fd727ef1bb..8087d96b19 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -12,7 +12,7 @@ "prettier:fix": "prettier --write .", "publish-gh-pages": "docusaurus-publish", "rename-version": "docusaurus-rename-version", - "start": "node scripts/pre-build.js && docusaurus start", + "start": "docusaurus start", "swizzle": "docusaurus swizzle", "verify:sidebars": "node ./scripts/verify-sidebars", "version": "docusaurus-version", From 9fd02a4e12bf80e22f7903bde51d233da6ec1cb3 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Thu, 18 Dec 2025 08:48:15 -0700 Subject: [PATCH 005/211] fix sidebar conflict Signed-off-by: aramissennyeydd --- microsite/sidebars.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/sidebars.ts b/microsite/sidebars.ts index afe5616bbf..b30ffddc2a 100644 --- a/microsite/sidebars.ts +++ b/microsite/sidebars.ts @@ -751,7 +751,7 @@ export default { 'api/deprecations', 'references/glossary', 'api/utility-apis', - 'reference/index', + 'references/index', ], ), ], From 3c8a824bb6699bd7f33801f6add685dfa89d9f68 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 20 Dec 2025 20:36:39 -0700 Subject: [PATCH 006/211] add back strict build Signed-off-by: aramissennyeydd --- .github/workflows/verify_microsite.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index b3c8a45172..0145475121 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -268,8 +268,8 @@ jobs: with: clean: false - # - name: Build MkDocs for TechDocs - # run: mkdocs build --strict + - name: Build MkDocs for TechDocs + run: mkdocs build --strict - name: download next OpenAPI API docs uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 From cebfea769a3547a184e9c6c2f880734a972dabac Mon Sep 17 00:00:00 2001 From: James Brooks Date: Mon, 19 Jan 2026 23:29:30 +0000 Subject: [PATCH 007/211] Use unstyled link for LinkButton Signed-off-by: James Brooks --- .changeset/wet-cups-juggle.md | 5 ++++ .../src/components/Link/Link.tsx | 29 ++++++++++--------- .../components/LinkButton/LinkButton.test.tsx | 8 +++++ .../src/components/LinkButton/LinkButton.tsx | 4 +-- 4 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 .changeset/wet-cups-juggle.md diff --git a/.changeset/wet-cups-juggle.md b/.changeset/wet-cups-juggle.md new file mode 100644 index 0000000000..5e1185f7df --- /dev/null +++ b/.changeset/wet-cups-juggle.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Removed link styles from LinkButton to avoid styling inconsistencies related to import order. diff --git a/packages/core-components/src/components/Link/Link.tsx b/packages/core-components/src/components/Link/Link.tsx index f968d7b098..f1d0f116a9 100644 --- a/packages/core-components/src/components/Link/Link.tsx +++ b/packages/core-components/src/components/Link/Link.tsx @@ -180,11 +180,11 @@ const getNodeText = (node: ReactNode): string => { }; /** - * Thin wrapper on top of material-ui's Link component, which... - * - Makes the Link use react-router - * - Captures Link clicks as analytics events. + * Unstyled link primitive which... + * - Uses react-router for internal links. + * - Captures link clicks as analytics events. */ -export const Link = forwardRef( +export const UnstyledLink = forwardRef( ({ onClick, noTrack, externalLinkIcon, ...props }, ref) => { const classes = useStyles(); const analytics = useAnalytics(); @@ -213,7 +213,7 @@ export const Link = forwardRef( return external ? ( // External links - ( , Opens in a new window - + ) : ( // Interact with React Router for internal links - + ); }, -) as (props: LinkProps) => JSX.Element; +); + +/** + * Thin wrapper combining UnstyledLink with material-ui's Link component. + */ +export const Link = forwardRef((props, ref) => { + return ; +}) as (props: LinkProps) => JSX.Element; diff --git a/packages/core-components/src/components/LinkButton/LinkButton.test.tsx b/packages/core-components/src/components/LinkButton/LinkButton.test.tsx index 9334bffaf8..8df0922bd7 100644 --- a/packages/core-components/src/components/LinkButton/LinkButton.test.tsx +++ b/packages/core-components/src/components/LinkButton/LinkButton.test.tsx @@ -38,4 +38,12 @@ describe('', () => { }); expect(screen.getByText(testString)).toBeInTheDocument(); }); + + it('should not have MuiLink class names', async () => { + await renderInTestApp(Navigate!); + + const button = await screen.findByRole('button', { name: 'Navigate!' }); + expect(button).toHaveClass(/MuiButton/); + expect(button).not.toHaveClass(/MuiLink/); + }); }); diff --git a/packages/core-components/src/components/LinkButton/LinkButton.tsx b/packages/core-components/src/components/LinkButton/LinkButton.tsx index 820a438955..e10dc7c368 100644 --- a/packages/core-components/src/components/LinkButton/LinkButton.tsx +++ b/packages/core-components/src/components/LinkButton/LinkButton.tsx @@ -18,7 +18,7 @@ import MaterialButton, { ButtonProps as MaterialButtonProps, } from '@material-ui/core/Button'; import { forwardRef } from 'react'; -import { Link, LinkProps } from '../Link'; +import { UnstyledLink, LinkProps } from '../Link/Link'; /** * Properties for {@link LinkButton} @@ -35,7 +35,7 @@ export type LinkButtonProps = MaterialButtonProps & * This wrapper is here to reset the color of the Link and make typescript happy. */ const LinkWrapper = forwardRef((props, ref) => ( - + )); /** From 8d4c48b3f142dcfe290a2ecdcfd66fe2b64cde64 Mon Sep 17 00:00:00 2001 From: samarthsinh2660 Date: Tue, 20 Jan 2026 11:51:39 +0530 Subject: [PATCH 008/211] fix(catalog): add vertical spacing between tags in catalog table Fixes issue where tags in the component list had no vertical margin, causing borders to overlap when tags wrap to multiple lines. Changed marginBottom: '0px' to margin: '2px' for consistent spacing. Fixes #31229 Signed-off-by: samarthsinh2660 --- .changeset/fix-catalog-tags-spacing.md | 5 +++++ plugins/catalog/src/components/CatalogTable/columns.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/fix-catalog-tags-spacing.md diff --git a/.changeset/fix-catalog-tags-spacing.md b/.changeset/fix-catalog-tags-spacing.md new file mode 100644 index 0000000000..6e2540fa0b --- /dev/null +++ b/.changeset/fix-catalog-tags-spacing.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Fixed vertical spacing between tags in the catalog table. Tags now have consistent 2px margin to prevent border overlap when wrapped to multiple lines. diff --git a/plugins/catalog/src/components/CatalogTable/columns.tsx b/plugins/catalog/src/components/CatalogTable/columns.tsx index 65094bc568..d2216149cd 100644 --- a/plugins/catalog/src/components/CatalogTable/columns.tsx +++ b/plugins/catalog/src/components/CatalogTable/columns.tsx @@ -174,7 +174,7 @@ export const columnFactories = Object.freeze({ label={t} size="small" variant="outlined" - style={{ marginBottom: '0px' }} + style={{ margin: '2px' }} /> ))} From 7f1d92bd40e412d7024e3e4d7a0d20bc1c629b3c Mon Sep 17 00:00:00 2001 From: Hope Hadfield Date: Tue, 25 Nov 2025 16:20:05 -0500 Subject: [PATCH 009/211] Update backend-dynamic-feature-service README to reflect current state Signed-off-by: Hope Hadfield --- .../backend-dynamic-feature-service/README.md | 177 +++++++++++++++--- 1 file changed, 151 insertions(+), 26 deletions(-) diff --git a/packages/backend-dynamic-feature-service/README.md b/packages/backend-dynamic-feature-service/README.md index 6f5af13636..330845f22d 100644 --- a/packages/backend-dynamic-feature-service/README.md +++ b/packages/backend-dynamic-feature-service/README.md @@ -1,47 +1,172 @@ # @backstage/backend-dynamic-feature-service -This package adds experimental support for **dynamic backend features (plugins and modules)**, according to the content of the proposal in [RFC 18390](https://github.com/backstage/backstage/issues/18390) +This package provides support for **dynamic backend features (plugins and modules)** in Backstage, allowing you to load plugins at runtime from a separate directory without including them in your main application's package.json. -## Testing the backend dynamic plugins feature +## Purpose -In order to test the dynamic backend plugins feature provided by this package, example applications, as well as example dynamic plugins have been provided in provided in the [showcase repository](https://github.com/janus-idp/dynamic-backend-plugins-showcase), and instructions are provided in the [related Readme](https://github.com/janus-idp/dynamic-backend-plugins-showcase#readme). +This enables: + +- **Plugin distribution**: Distributing plugins as standalone packages +- **Runtime flexibility**: Adding or updating plugins without rebuilding the entire application +- **Isolation**: Keeping plugin-specific dependencies separate from core application dependencies +- **Modular deployments**: Different environments can load different sets of plugins + +**Important:** This service handles loading only - packaging and distribution are separate concerns with multiple approaches available (see Packaging Approaches section). + +## Related Packages + +- **`@backstage/frontend-dynamic-feature-loader`**: Companion package for loading dynamic frontend plugins. This is **only** supported in the New Frontend System. For more details, checkout the [README](../frontend-dynamic-feature-loader/README.md). ## How it works -The dynamic plugin manager is a service that scans a configured root directory (`dynamicPlugins.rootDirectory` in the app config) for dynamic plugin packages, and loads them dynamically. +### Core Architecture -In the `backend` application, it can be enabled by adding the `backend-dynamic-feature-service` as a dependency in the `package.json` and the following lines in the `src/index.ts` file: +The service consists of several key components: -```ts -const backend = createBackend(); -+ -+ backend.add(dynamicPluginsFeatureLoader) // provides features loaded by dynamic plugins -+ +1. **Plugin Scanner**: Scans a configured directory (`dynamicPlugins.rootDirectory`) for plugin packages +2. **Module Loader**: Loads plugin modules +3. **Feature Loader**: Integrates loaded plugins into the Backstage backend system +4. **Frontend Asset Server**: Serves frontend plugin assets (JavaScript, CSS, manifests) over HTTP for dynamic frontend plugins + +### Loading Process + +1. **Discovery**: The scanner identifies valid plugin packages in the dynamic plugins root directory +2. **Validation**: Each package is validated for required fields (`main`, `backstage.role`) +3. **Loading**: The module loader loads the plugin code +4. **Integration**: Plugins are registered as Backstage features and made available to the backend + +## Usage + +### Basic Setup + +Add the service to your backend application: + +1. **Install the package**: + +```bash +yarn add @backstage/backend-dynamic-feature-service ``` -### About the expected dynamic plugin structure +2. **Add one line to your backend**: -Due to some limitations of the current backstage codebase, the plugins need to be completed and repackaged to by used as dynamic plugins: +```ts +import { createBackend } from '@backstage/backend-defaults'; +import { dynamicPluginsFeatureLoader } from '@backstage/backend-dynamic-feature-service'; -1. they must provide a named entry point (`dynamicPluginInstaller`) of a specific type (`BackendDynamicPluginInstaller`), as can be found in the `src/dynamic` sub-folder of each dynamic plugin example provided in the [showcase repository](https://github.com/janus-idp/dynamic-backend-plugins-showcase). -2. they would have a modified `package.json` file in which dependencies are updated to share `@backstage` dependencies with the main application. -3. they may embed some dependency whose code is then merged with the plugin code. +const backend = createBackend(); +// Add this line to enable dynamic plugin support: +backend.add(dynamicPluginsFeatureLoader); +// ... your other plugins +backend.start(); +``` -Points 2 and 3 can be done by the `export-dynamic-plugin` CLI command used to perform the repackaging +### Configuration -### About the `export-dynamic-plugin` command +Configure the dynamic plugins directory in your `app-config.yaml`: -The `export-dynamic-plugin` CLI command, used the dynamic plugin examples provided in the [showcase repository](https://github.com/janus-idp/dynamic-backend-plugins-showcase), is part of the `@janus-idp/cli` package, and can be used to help packaging the dynamic plugins according to the constraints mentioned above, in order to allow straightforward testing of the dynamic plugins feature. +```yaml +dynamicPlugins: + rootDirectory: dynamic-plugins-root +``` -However the `backend-dynamic-feature-service` experimental package does **not** depend on the use of this additional CLI command, and in future steps of this backend dynamic plugin work, the use of such a dedicated command might not even be necessary. +## Dynamic Plugin Packaging -### About the support of the legacy backend system +### Package Structure Requirements -The backend dynamic plugins feature clearly targets the new backend system. -However some level of compatibility is provided with current backstage codebase, which still uses the legacy backend system, in order to allow testing and exploring dynamic backend plugin support on the widest range of contexts and installations. -However, this is temporary and will be removed once the next backend is ready to be used and has completely replaced the old one. -This is why the API related to the old backend is already marked as deprecated. +Before exploring specific packaging approaches, it's important to understand what constitutes a valid dynamic plugin package. Dynamic plugins follow the basic rules of Node.js packages, including: -### Future work +**Package contents:** -The current implementation of the dynamic plugin manager is a first step towards the final implementation of the dynamic features loading, which will be completed / simplified in future steps, as the status of the backstage codebase allows it. +- **package.json**: Package metadata and configuration +- **JavaScript files**: Either loadable by the Node.js CommonJS module loaders for backend plugins, or RSPack/webpack assets for frontend plugins +- **Configuration schema** (optional): JSON schema file (typically `dist/.config-schema.json`) for plugin configuration validation +- **Private dependencies** (optional): Embedded `node_modules` folder containing private dependencies for backend plugins + +**Required package.json fields:** + +- `name`: Package identifier +- `version`: Package version +- `main`: Entry point to the plugin's JavaScript code +- `backstage.role`: Must be set to `"backend-plugin"` or `"backend-plugin-module"` + +### Packaging Approaches + +Since this service only handles loading, you would choose a packaging approach based on your plugin's dependencies: + +### 1. Simple npm pack + +**When to use:** Plugin only uses dependencies that are already provided by the main Backstage application. + +**How to apply:** + +```bash +cd my-backstage-plugin +yarn pack +# Results in: package.tgz + +# Extract to dynamic plugins directory +mkdir -p /path/to/dynamic-plugins-root/my-backstage-plugin +tar -xzf package.tgz -C /path/to/dynamic-plugins-root/my-backstage-plugin --strip-components=1 +``` + +**Why this works:** The plugin can resolve all its dependencies from the main application's `node_modules`. + +**Reality:** Most plugins have private dependencies not available in the main application, so this approach has limited applicability. + +### 2. Manual packaging with dependency installation + +**When to use:** Plugin has private dependencies not available in the main Backstage application. + +**How to apply:** + +```bash +# Package the plugin +cd my-backstage-plugin +yarn pack + +# Extract and install dependencies +mkdir -p /path/to/dynamic-plugins-root/my-backstage-plugin +tar -xzf package.tgz -C /path/to/dynamic-plugin-root/my-backstage-plugin --strip-components=1 +cp yarn.lock /path/to/dynamic-plugin-root/my-backstage-plugin +cd /path/to/dynamic-plugins-root/my-backstage-plugin +yarn install # Installs all the plugin's dependencies +``` + +**Why this works:** Each plugin gets its own `node_modules` directory with all its dependencies. + +**Example scenario:** Plugin needs `axios@1.4.0` which isn't available in the main application. + +**Limitation:** `@backstage/*` dependencies will also be installed in the plugin's `node_modules` and will take priority over the main application's versions, leading to a big number of unnecessary duplicated dependencies, and potentially breaking consistency between core Backstage packages. + +### 3. Custom packaging CLI tool + +**When to use:** When you want to produce self-contained dynamic plugin packages that can be directly extracted without any post-action, and systematically use the core `@backstage` dependencies provided by the Backstage application. + +**What a packaging CLI needs to do:** + +1. **Analyze plugin dependencies** - Identify which are Backstage core vs private dependencies +2. **Create distribution package** - Generate a new directory with modified structure: + - Move `@backstage/*` packages from `dependencies` to `peerDependencies` in package.json + - Keep only private dependencies in the `dependencies` section + - Keep the built JavaScript code unchanged + - Include only the filtered private dependencies in `node_modules` +3. **Result** - A self-contained package that uses the main app's `@backstage/*` packages but includes its own private dependencies + +**Benefits:** + +- Systematic use of main application's `@backstage/*` packages (no version conflicts), enabling the future implementation of `@backstage` dependency version checking at start time +- Self-contained packages with only necessary private dependencies +- No post-installation steps required (extract and run) +- Consistent dependency structure across all dynamic plugins +- Production-ready distribution format + +**Example implementation:** The [`@red-hat-developer-hub/cli`](https://github.com/redhat-developer/rhdh-cli) tool implements this approach: + +```bash +cd my-backstage-plugin +npx @red-hat-developer-hub/cli@latest plugin export +# Creates a self-contained package with embedded dependencies in the `/dist-dynamic` sub-folder + +# Deploy the generated package +cp -r dist-dynamic /path/to/dynamic-plugins-root/my-backstage-plugin +``` From 9b4c4145d4e634629ef7fb535102f0a3476fcda6 Mon Sep 17 00:00:00 2001 From: Hope Hadfield Date: Tue, 16 Dec 2025 09:45:49 -0500 Subject: [PATCH 010/211] add changeset Signed-off-by: Hope Hadfield --- .changeset/yellow-years-run.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/yellow-years-run.md diff --git a/.changeset/yellow-years-run.md b/.changeset/yellow-years-run.md new file mode 100644 index 0000000000..fc57664911 --- /dev/null +++ b/.changeset/yellow-years-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-dynamic-feature-service': patch +--- + +Updated README for backend-dynamic-feature-service From bd54341c6485d86e73ce439da230bee3c802b99d Mon Sep 17 00:00:00 2001 From: Hope Hadfield Date: Tue, 20 Jan 2026 10:22:37 -0500 Subject: [PATCH 011/211] implement suggestions Signed-off-by: Hope Hadfield --- packages/backend-dynamic-feature-service/README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/backend-dynamic-feature-service/README.md b/packages/backend-dynamic-feature-service/README.md index 330845f22d..8122810f96 100644 --- a/packages/backend-dynamic-feature-service/README.md +++ b/packages/backend-dynamic-feature-service/README.md @@ -1,12 +1,12 @@ # @backstage/backend-dynamic-feature-service -This package provides support for **dynamic backend features (plugins and modules)** in Backstage, allowing you to load plugins at runtime from a separate directory without including them in your main application's package.json. +This package provides experimental support for **dynamic backend features (plugins and modules)** in Backstage, allowing you to load plugins at runtime from a separate directory without including them in your main application's package.json. ## Purpose This enables: -- **Plugin distribution**: Distributing plugins as standalone packages +- **Plugin distribution**: Distributing plugins as standalone artifacts - **Runtime flexibility**: Adding or updating plugins without rebuilding the entire application - **Isolation**: Keeping plugin-specific dependencies separate from core application dependencies - **Modular deployments**: Different environments can load different sets of plugins @@ -136,8 +136,6 @@ yarn install # Installs all the plugin's dependencies **Example scenario:** Plugin needs `axios@1.4.0` which isn't available in the main application. -**Limitation:** `@backstage/*` dependencies will also be installed in the plugin's `node_modules` and will take priority over the main application's versions, leading to a big number of unnecessary duplicated dependencies, and potentially breaking consistency between core Backstage packages. - ### 3. Custom packaging CLI tool **When to use:** When you want to produce self-contained dynamic plugin packages that can be directly extracted without any post-action, and systematically use the core `@backstage` dependencies provided by the Backstage application. From 0d3f4b5f9dc40d2ac6f743afd37e242f8daa5f44 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 12:18:06 +0000 Subject: [PATCH 012/211] chore(deps): bump diff from 5.2.0 to 5.2.2 in /microsite Bumps [diff](https://github.com/kpdecker/jsdiff) from 5.2.0 to 5.2.2. - [Changelog](https://github.com/kpdecker/jsdiff/blob/master/release-notes.md) - [Commits](https://github.com/kpdecker/jsdiff/compare/v5.2.0...v5.2.2) --- updated-dependencies: - dependency-name: diff dependency-version: 5.2.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 2af94aec1f..367b28a29c 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -5863,9 +5863,9 @@ __metadata: linkType: hard "diff@npm:^5.0.0": - version: 5.2.0 - resolution: "diff@npm:5.2.0" - checksum: 10/01b7b440f83a997350a988e9d2f558366c0f90f15be19f4aa7f1bb3109a4e153dfc3b9fbf78e14ea725717017407eeaa2271e3896374a0181e8f52445740846d + version: 5.2.2 + resolution: "diff@npm:5.2.2" + checksum: 10/8a885b38113d96138d87f6cb474ee959b7e9ab33c0c4cb1b07dcf019ec544945a2309d53d721532af020de4b3a58fb89f1026f64f42f9421aa9c3ae48a36998b languageName: node linkType: hard From 916688555273481c4e2f83fbca250bc64b784290 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Wed, 21 Jan 2026 13:27:07 +0100 Subject: [PATCH 013/211] workflows/sync_patch-release: stop running on PR branches Signed-off-by: Patrik Oldsberg --- .github/workflows/sync_patch-release.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/sync_patch-release.yml b/.github/workflows/sync_patch-release.yml index 4e3a622007..636d3ad14b 100644 --- a/.github/workflows/sync_patch-release.yml +++ b/.github/workflows/sync_patch-release.yml @@ -5,11 +5,6 @@ on: - master paths: - '.patches/**' - pull_request: - branches: - - master - paths: - - '.patches/**' concurrency: group: sync-patch-release From 026195494b00c602b617a496db4a08ab8ce6930d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 14:04:28 +0000 Subject: [PATCH 014/211] chore(deps): update github/codeql-action action to v3.31.10 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/verify_codeql.yml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 077eb2d856..9c3a749fcb 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -67,6 +67,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@f47c8e6a9bd05ef3ee422fc8d8663be7fe4bdc61 # v3.31.8 + uses: github/codeql-action/upload-sarif@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 632d03c35d..c70f9f4d82 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -58,6 +58,6 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} NODE_OPTIONS: --max-old-space-size=7168 - name: Upload Snyk report - uses: github/codeql-action/upload-sarif@f47c8e6a9bd05ef3ee422fc8d8663be7fe4bdc61 # v3.31.8 + uses: github/codeql-action/upload-sarif@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 with: sarif_file: snyk.sarif diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index 5d976f8583..cf8040fc43 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@f47c8e6a9bd05ef3ee422fc8d8663be7fe4bdc61 # v3.31.8 + uses: github/codeql-action/init@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@f47c8e6a9bd05ef3ee422fc8d8663be7fe4bdc61 # v3.31.8 + uses: github/codeql-action/autobuild@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +80,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f47c8e6a9bd05ef3ee422fc8d8663be7fe4bdc61 # v3.31.8 + uses: github/codeql-action/analyze@4bdb89f48054571735e3792627da6195c57459e2 # v3.31.10 From 85cd2c90b1dea13a49d01d3f763d8b1e224b6fcb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:08:46 +0000 Subject: [PATCH 015/211] fix(deps): update nextjs monorepo to v15.5.9 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs-ui/package.json | 4 ++-- docs-ui/yarn.lock | 41 ++++++++++++++++------------------------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/docs-ui/package.json b/docs-ui/package.json index ce9b8d070f..3fd8d192c4 100644 --- a/docs-ui/package.json +++ b/docs-ui/package.json @@ -24,7 +24,7 @@ "@lezer/highlight": "^1.2.1", "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", - "@next/mdx": "15.5.7", + "@next/mdx": "15.5.9", "@remixicon/react": "^4.6.0", "@uiw/codemirror-themes": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", @@ -47,7 +47,7 @@ "@types/react-dom": "19.1.7", "chokidar": "^3.6.0", "eslint": "^8", - "eslint-config-next": "15.5.7", + "eslint-config-next": "15.5.9", "lightningcss": "^1.28.2", "typescript": "^5", "unified": "^11.0.4" diff --git a/docs-ui/yarn.lock b/docs-ui/yarn.lock index 8b5a1d865e..9a67e4cd2e 100644 --- a/docs-ui/yarn.lock +++ b/docs-ui/yarn.lock @@ -679,18 +679,18 @@ __metadata: languageName: node linkType: hard -"@next/eslint-plugin-next@npm:15.5.7": - version: 15.5.7 - resolution: "@next/eslint-plugin-next@npm:15.5.7" +"@next/eslint-plugin-next@npm:15.5.9": + version: 15.5.9 + resolution: "@next/eslint-plugin-next@npm:15.5.9" dependencies: fast-glob: "npm:3.3.1" - checksum: 10/5b59860aeccc0d07a979c40e4405b410a9d7aa86a235d5dc0edbac8d2da35586f6f2fa3407cd3d80fd7e7d4f197c70291e9c6e71a64942d09b8fe82470e90ee0 + checksum: 10/63ab34b65e4ffc707fefba8a5fc3b46b37bac6ed2a2d2a682bec72448998ee0d6390c3007dac9714ea1020380ba4cf2328d11496ec0adf55df141eb8fe267133 languageName: node linkType: hard -"@next/mdx@npm:15.5.7": - version: 15.5.7 - resolution: "@next/mdx@npm:15.5.7" +"@next/mdx@npm:15.5.9": + version: 15.5.9 + resolution: "@next/mdx@npm:15.5.9" dependencies: source-map: "npm:^0.7.0" peerDependencies: @@ -701,7 +701,7 @@ __metadata: optional: true "@mdx-js/react": optional: true - checksum: 10/caf2ad1e3a8b02381ad33fe32f59e2ef4c15c96a04ba2a7d713991c142214d3b69d25b879ffc12da1c333ffd34d30aec44061d740f708c8e12529b183a159146 + checksum: 10/751a89e58485f95f7c0265aade76970934e2a32f77ae31da1be225c4aaabfbc6c81d072bf44c97283ea9c3adef20d56c7b344ffef480443ccc0954bcee2134b2 languageName: node linkType: hard @@ -2198,7 +2198,7 @@ __metadata: "@lezer/highlight": "npm:^1.2.1" "@mdx-js/loader": "npm:^3.1.0" "@mdx-js/react": "npm:^3.1.0" - "@next/mdx": "npm:15.5.7" + "@next/mdx": "npm:15.5.9" "@octokit/rest": "npm:^22.0.1" "@remixicon/react": "npm:^4.6.0" "@shikijs/transformers": "npm:^3.13.0" @@ -2211,7 +2211,7 @@ __metadata: chokidar: "npm:^3.6.0" clsx: "npm:^2.1.1" eslint: "npm:^8" - eslint-config-next: "npm:15.5.7" + eslint-config-next: "npm:15.5.9" html-react-parser: "npm:^5.2.5" lightningcss: "npm:^1.28.2" motion: "npm:^12.4.1" @@ -2523,11 +2523,11 @@ __metadata: languageName: node linkType: hard -"eslint-config-next@npm:15.5.7": - version: 15.5.7 - resolution: "eslint-config-next@npm:15.5.7" +"eslint-config-next@npm:15.5.9": + version: 15.5.9 + resolution: "eslint-config-next@npm:15.5.9" dependencies: - "@next/eslint-plugin-next": "npm:15.5.7" + "@next/eslint-plugin-next": "npm:15.5.9" "@rushstack/eslint-patch": "npm:^1.10.3" "@typescript-eslint/eslint-plugin": "npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" "@typescript-eslint/parser": "npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0" @@ -2543,7 +2543,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10/fd1813c7ef4e33ec297b86dd0de75879e10514b12cb04a103e7a5b8c5f27d2ea0a91dd15e6d530006c9fe3e7e18f815e900bd6ba117c3b8fbe311c38c340d899 + checksum: 10/c56015602d1d4c044e7486924a7bdfb72e04515aff22c97ec0f0c6ddeed8c3474001a9a4258d90c89d2627eae14d0d769382d3a47f65910c4360a2de0c9b9b8e languageName: node linkType: hard @@ -4766,16 +4766,7 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^3.0.1": - version: 3.0.2 - resolution: "minizlib@npm:3.0.2" - dependencies: - minipass: "npm:^7.1.2" - checksum: 10/c075bed1594f68dcc8c35122333520112daefd4d070e5d0a228bd4cf5580e9eed3981b96c0ae1d62488e204e80fd27b2b9d0068ca9a5ef3993e9565faf63ca41 - languageName: node - linkType: hard - -"minizlib@npm:^3.1.0": +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" dependencies: From 4cbd3f9d9836ee23d380c54df4b63b9bb4a5ba23 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 17:13:51 +0000 Subject: [PATCH 016/211] chore(deps): update actions/setup-node action to v6.2.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/api-breaking-changes.yml | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/deploy_docker-image.yml | 2 +- .github/workflows/deploy_microsite.yml | 6 +++--- .github/workflows/deploy_packages.yml | 2 +- .github/workflows/mui-migration-tracker.yml | 2 +- .github/workflows/sync_canon.yml | 2 +- .github/workflows/sync_code-formatting.yml | 2 +- .github/workflows/sync_patch-release.yml | 2 +- .github/workflows/sync_release-manifest.yml | 2 +- .github/workflows/sync_snyk-github-issues.yml | 2 +- .github/workflows/sync_version-packages.yml | 2 +- .github/workflows/verify_accessibility.yml | 2 +- .github/workflows/verify_chromatic.yml | 2 +- .github/workflows/verify_e2e-linux.yml | 2 +- .github/workflows/verify_e2e-techdocs.yml | 2 +- .github/workflows/verify_e2e-windows.yml | 2 +- .github/workflows/verify_microsite.yml | 6 +++--- .github/workflows/verify_microsite_accessibility.yml | 2 +- .github/workflows/verify_windows.yml | 2 +- 20 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index 7525ea3a07..0ca058e383 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -27,7 +27,7 @@ jobs: run: git fetch --depth 1 origin ${{ github.base_ref }} - name: setup-node - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55e01009c5..8aade120a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth @@ -71,7 +71,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth @@ -220,7 +220,7 @@ jobs: run: git fetch origin ${{ github.event.pull_request.base.ref }} - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index f7bb55c1b9..47432c69c6 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -31,7 +31,7 @@ jobs: ref: ${{ github.event.client_payload.version && env.RELEASE_VERSION || github.ref }} - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 52684d5529..d9f2680c57 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -59,7 +59,7 @@ jobs: ref: refs/tags/${{ steps.find-release.outputs.result }} - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth @@ -143,7 +143,7 @@ jobs: uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth @@ -245,7 +245,7 @@ jobs: egress-policy: audit - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 5135e0a1e2..cff0e43e81 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -71,7 +71,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/mui-migration-tracker.yml b/.github/workflows/mui-migration-tracker.yml index ffde14b5f0..b7f46f135b 100644 --- a/.github/workflows/mui-migration-tracker.yml +++ b/.github/workflows/mui-migration-tracker.yml @@ -26,7 +26,7 @@ jobs: uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - name: Setup Node.js - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/sync_canon.yml b/.github/workflows/sync_canon.yml index ea6d47bbf5..76841e017d 100644 --- a/.github/workflows/sync_canon.yml +++ b/.github/workflows/sync_canon.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index 7311d52ae4..95ec0893d2 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -20,7 +20,7 @@ jobs: fetch-depth: 0 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/sync_patch-release.yml b/.github/workflows/sync_patch-release.yml index 4e3a622007..4d1e300751 100644 --- a/.github/workflows/sync_patch-release.yml +++ b/.github/workflows/sync_patch-release.yml @@ -37,7 +37,7 @@ jobs: token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index 871b8b9712..60a4a5141a 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -19,7 +19,7 @@ jobs: ref: v${{ github.event.client_payload.version }} - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index 0d1233020c..c0edb98ec6 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 3ba9833645..4314fdba38 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -26,7 +26,7 @@ jobs: token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 8f1b611f1f..8dbf5e3800 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -26,7 +26,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x - name: yarn install diff --git a/.github/workflows/verify_chromatic.yml b/.github/workflows/verify_chromatic.yml index f08441bcc1..6facbd2049 100644 --- a/.github/workflows/verify_chromatic.yml +++ b/.github/workflows/verify_chromatic.yml @@ -34,7 +34,7 @@ jobs: fetch-depth: 10000 # Required to retrieve git history - name: Use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 55d92b30b3..b309e0d580 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -55,7 +55,7 @@ jobs: git config --global user.name 'GitHub e2e user' - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index fce6691e7f..64d4182f3a 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -42,7 +42,7 @@ jobs: python-version: '3.9' - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} - name: yarn install diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index ce037f8c01..1d9e707075 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -52,7 +52,7 @@ jobs: git config --global user.name 'GitHub e2e user' - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index b11ef3172c..b4faa8826d 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -64,7 +64,7 @@ jobs: ref: refs/tags/${{ steps.find-release.outputs.result }} - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth @@ -145,7 +145,7 @@ jobs: uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x registry-url: https://registry.npmjs.org/ # Needed for auth @@ -241,7 +241,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6 diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml index 0bfab3035b..d5505b3b76 100644 --- a/.github/workflows/verify_microsite_accessibility.yml +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Use Node.js 22.x - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index c15388d6b4..b85cb75af1 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -36,7 +36,7 @@ jobs: - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth From f767848e6d080cfcfad1d9623fad4488a45d2206 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 17:18:52 +0000 Subject: [PATCH 017/211] chore(deps): update codemirror Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs-ui/yarn.lock | 17 ++++------------- yarn.lock | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/docs-ui/yarn.lock b/docs-ui/yarn.lock index 8b5a1d865e..98448f8303 100644 --- a/docs-ui/yarn.lock +++ b/docs-ui/yarn.lock @@ -179,14 +179,14 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.34.4, @codemirror/view@npm:^6.35.0": - version: 6.38.8 - resolution: "@codemirror/view@npm:6.38.8" + version: 6.39.11 + resolution: "@codemirror/view@npm:6.39.11" dependencies: "@codemirror/state": "npm:^6.5.0" crelt: "npm:^1.0.6" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/81b1508015a378e4719d0239254173f0c5cd340c2abf96eb488fe5fb474bdb37ec1f010b9890ced774accd7aeb9443e7337cb6a89544b954273e5ddabece7cea + checksum: 10/691825e783a1ac720411f59fface596fe1ba0fbf0344ed61af84937c7dd4337298b1a91fbff93cee076dc1b1c5c6f7bd73b8660fa417fb484f157946ecac9986 languageName: node linkType: hard @@ -4766,16 +4766,7 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^3.0.1": - version: 3.0.2 - resolution: "minizlib@npm:3.0.2" - dependencies: - minipass: "npm:^7.1.2" - checksum: 10/c075bed1594f68dcc8c35122333520112daefd4d070e5d0a228bd4cf5580e9eed3981b96c0ae1d62488e204e80fd27b2b9d0068ca9a5ef3993e9565faf63ca41 - languageName: node - linkType: hard - -"minizlib@npm:^3.1.0": +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" dependencies: diff --git a/yarn.lock b/yarn.lock index af95e1f2fc..514b959712 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8351,16 +8351,16 @@ __metadata: linkType: hard "@codemirror/language@npm:^6.0.0": - version: 6.11.3 - resolution: "@codemirror/language@npm:6.11.3" + version: 6.12.1 + resolution: "@codemirror/language@npm:6.12.1" dependencies: "@codemirror/state": "npm:^6.0.0" "@codemirror/view": "npm:^6.23.0" - "@lezer/common": "npm:^1.1.0" + "@lezer/common": "npm:^1.5.0" "@lezer/highlight": "npm:^1.0.0" "@lezer/lr": "npm:^1.0.0" style-mod: "npm:^4.0.0" - checksum: 10/8538a2835c1de6ca2d520ff66449185f2ea3a93e7d69382d9db3b4db6460f4c46b44f19724458c50230abfa87cf2c225834d39c3fe3119c48370db6b3de0b772 + checksum: 10/a24c3512d38cbb2a20cc3128da0eea074b4a6102b6a5a041b3dfd5e67638fb61dcdf4743ed87708db882df5d72a84d9f891aac6fa68447830989c8e2d9ffa2ba languageName: node linkType: hard @@ -8417,14 +8417,14 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.23.0": - version: 6.38.8 - resolution: "@codemirror/view@npm:6.38.8" + version: 6.39.11 + resolution: "@codemirror/view@npm:6.39.11" dependencies: "@codemirror/state": "npm:^6.5.0" crelt: "npm:^1.0.6" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/81b1508015a378e4719d0239254173f0c5cd340c2abf96eb488fe5fb474bdb37ec1f010b9890ced774accd7aeb9443e7337cb6a89544b954273e5ddabece7cea + checksum: 10/691825e783a1ac720411f59fface596fe1ba0fbf0344ed61af84937c7dd4337298b1a91fbff93cee076dc1b1c5c6f7bd73b8660fa417fb484f157946ecac9986 languageName: node linkType: hard @@ -10992,10 +10992,10 @@ __metadata: languageName: node linkType: hard -"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.3.0": - version: 1.4.0 - resolution: "@lezer/common@npm:1.4.0" - checksum: 10/8d0626835b8567115923772619d887c212c1e965e0ae2a230e3ae8e6734e0ded3bf4ca3a1eb4aa0af6a7f0cbb4892fa04de399916192fd98a99db8bf0f18da63 +"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.3.0, @lezer/common@npm:^1.5.0": + version: 1.5.0 + resolution: "@lezer/common@npm:1.5.0" + checksum: 10/d99a45947c5033476f7c16f475b364e5b276e89a351641d8d785ceac88e8175f7b7b7d43dda80c3d9097f5e3379f018404bbe59a41d15992df23a03bbef3519b languageName: node linkType: hard From bde545ce601966e898c823fa2401823d5cb9f078 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:09:09 +0000 Subject: [PATCH 018/211] chore(deps): update dependency @faker-js/faker to v10.2.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index af95e1f2fc..d40d25c1b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9247,9 +9247,9 @@ __metadata: linkType: hard "@faker-js/faker@npm:^10.0.0": - version: 10.1.0 - resolution: "@faker-js/faker@npm:10.1.0" - checksum: 10/3332795debec5793dfaea79e740ed44bd7d94fb43cea8b1bd5d59af19f15c4a9533fa696cfebf024a9bba5d9d1a8a28cde40a92670c03cc11170a3025e551e65 + version: 10.2.0 + resolution: "@faker-js/faker@npm:10.2.0" + checksum: 10/5b1b9690ff3339405cf32e2a1ae6f98305ffd118fb84c2d16daae7f2759408cba1e4f264ba95a7e5fb473a07fd492e4077ec38f0280baddcb43f901fa5653a5c languageName: node linkType: hard From f5b1ea303ea1de04e179a42fd174c0e5916ff16c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 18:10:08 +0000 Subject: [PATCH 019/211] chore(deps): update dependency @google-cloud/cloud-sql-connector to v1.9.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index af95e1f2fc..81ee794c9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9436,14 +9436,14 @@ __metadata: linkType: hard "@google-cloud/cloud-sql-connector@npm:^1.4.0": - version: 1.8.5 - resolution: "@google-cloud/cloud-sql-connector@npm:1.8.5" + version: 1.9.0 + resolution: "@google-cloud/cloud-sql-connector@npm:1.9.0" dependencies: - "@googleapis/sqladmin": "npm:^31.1.0" + "@googleapis/sqladmin": "npm:^35.0.0" gaxios: "npm:^7.1.3" google-auth-library: "npm:^10.5.0" p-throttle: "npm:^7.0.0" - checksum: 10/38be5d2a32d68d2c3ef8b56c94c9eda299266a742fa2beecb9e907f27117bf1af25af5881195fa21da167b12dfdd22f55662086e90448b8c02824875eeb2cc85 + checksum: 10/30a1539a2d59ae78ef2ce275e047981dc548359d153f29715741183c5df0411da882372a020dd97be2ceb7ad6153026a14e9751b3314582f57ff7d18987c6812 languageName: node linkType: hard @@ -9545,12 +9545,12 @@ __metadata: languageName: node linkType: hard -"@googleapis/sqladmin@npm:^31.1.0": - version: 31.1.0 - resolution: "@googleapis/sqladmin@npm:31.1.0" +"@googleapis/sqladmin@npm:^35.0.0": + version: 35.0.0 + resolution: "@googleapis/sqladmin@npm:35.0.0" dependencies: - googleapis-common: "npm:^8.0.2-rc.0" - checksum: 10/720365e99bff8b7de161dc629ffb097624b98a3ff42febca47d9a9c73480b66faff39f3601614dce3178b3acf9d49c6e006633e1847cceaa7d21bc8437fa1936 + googleapis-common: "npm:^8.0.0" + checksum: 10/7ecae655921c3b1e71b0b9ff04271e6ba80a6c2d4a269770c0cff6b45397abdf6a3084daba70d2659a241bcb124d0701e66ecfab2f838933582b408000aedbb0 languageName: node linkType: hard @@ -33065,7 +33065,7 @@ __metadata: languageName: node linkType: hard -"google-auth-library@npm:^10.0.0-rc.1, google-auth-library@npm:^10.5.0": +"google-auth-library@npm:^10.1.0, google-auth-library@npm:^10.5.0": version: 10.5.0 resolution: "google-auth-library@npm:10.5.0" dependencies: @@ -33128,16 +33128,16 @@ __metadata: languageName: node linkType: hard -"googleapis-common@npm:^8.0.2-rc.0": - version: 8.0.2-rc.0 - resolution: "googleapis-common@npm:8.0.2-rc.0" +"googleapis-common@npm:^8.0.0": + version: 8.0.1 + resolution: "googleapis-common@npm:8.0.1" dependencies: extend: "npm:^3.0.2" gaxios: "npm:^7.0.0-rc.4" - google-auth-library: "npm:^10.0.0-rc.1" + google-auth-library: "npm:^10.1.0" qs: "npm:^6.7.0" url-template: "npm:^2.0.8" - checksum: 10/f630e8f37b2baa4980e3a66fb7736caa2a47f1f915bc6082ef844aa24be776ad2fbff39631346dcccc01c99dd5707fca02c786c6ba23ae459bcb23b125b4fb63 + checksum: 10/732cd95913cc44e0f543eb686a32588953e3ee1d84628e1616093f78604a0c7c5a5b0d4e085ece94ac0d33edfb9bcdb9ba3d8763e133bab4f3a610711e40305b languageName: node linkType: hard From f82acd06ece02364a19041760542fd9d51b46fa1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 19:17:55 +0000 Subject: [PATCH 020/211] chore(deps): update dependency @openapitools/openapi-generator-cli to v2.27.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 103 ++++++++++++++++++++++++------------------------------ 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/yarn.lock b/yarn.lock index 21f0bf446d..dfd2ee2b0d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8062,6 +8062,13 @@ __metadata: languageName: node linkType: hard +"@borewit/text-codec@npm:^0.2.1": + version: 0.2.1 + resolution: "@borewit/text-codec@npm:0.2.1" + checksum: 10/3d7e824ac4d3ea16e6e910a7f2bac79f262602c3dbc2f525fd9b86786269c5d7bbd673090a0277d7f92652e534f263e292d5ace080bc9bdf57dc6921c1973f70 + languageName: node + linkType: hard + "@bundled-es-modules/cookie@npm:^2.0.1": version: 2.0.1 resolution: "@bundled-es-modules/cookie@npm:2.0.1" @@ -11991,11 +11998,11 @@ __metadata: languageName: node linkType: hard -"@nestjs/common@npm:11.1.9": - version: 11.1.9 - resolution: "@nestjs/common@npm:11.1.9" +"@nestjs/common@npm:11.1.12": + version: 11.1.12 + resolution: "@nestjs/common@npm:11.1.12" dependencies: - file-type: "npm:21.1.0" + file-type: "npm:21.3.0" iterare: "npm:1.2.1" load-esm: "npm:1.0.3" tslib: "npm:2.8.1" @@ -12010,13 +12017,13 @@ __metadata: optional: true class-validator: optional: true - checksum: 10/996a13749f9ade78202b49421e64edf81be9df65b1b1bb4149d2adb8cbdbdf7033deafc2b89b8123ea0b647ea13d325a2aed4efa19b5b59a1e8d01dd4e554a76 + checksum: 10/0e8a0b73453f1e77ae1be6eabc68b8374e6a8a39cf46d5beafaa398fa72aed6b653df91b72e5b2aa2225251b1463ca79d8424d207a981f586ac216441f83ac59 languageName: node linkType: hard -"@nestjs/core@npm:11.1.9": - version: 11.1.9 - resolution: "@nestjs/core@npm:11.1.9" +"@nestjs/core@npm:11.1.12": + version: 11.1.12 + resolution: "@nestjs/core@npm:11.1.12" dependencies: "@nuxt/opencollective": "npm:0.4.1" fast-safe-stringify: "npm:2.1.1" @@ -12038,7 +12045,7 @@ __metadata: optional: true "@nestjs/websockets": optional: true - checksum: 10/2adc7440898574fc3715e0a5ea2a4faeae52bd13431a449645963d9aff0c06ac748cf2e439af29736406154634d033d0795b06069cc9cc9114ee29b167578b9d + checksum: 10/44f122101f411abb2b83cd6bab865006c4a74552d3903ca47a58b396608453b36b59cd6461f8689cd31c3d91b81960cea190d654448921ae0efedbd20ff52284 languageName: node linkType: hard @@ -13046,12 +13053,12 @@ __metadata: linkType: hard "@openapitools/openapi-generator-cli@npm:^2.4.26, @openapitools/openapi-generator-cli@npm:^2.7.0": - version: 2.25.2 - resolution: "@openapitools/openapi-generator-cli@npm:2.25.2" + version: 2.28.0 + resolution: "@openapitools/openapi-generator-cli@npm:2.28.0" dependencies: "@nestjs/axios": "npm:4.0.1" - "@nestjs/common": "npm:11.1.9" - "@nestjs/core": "npm:11.1.9" + "@nestjs/common": "npm:11.1.12" + "@nestjs/core": "npm:11.1.12" "@nuxtjs/opencollective": "npm:0.3.2" axios: "npm:1.13.2" chalk: "npm:4.1.2" @@ -13059,7 +13066,7 @@ __metadata: compare-versions: "npm:6.1.1" concurrently: "npm:9.2.1" console.table: "npm:0.10.0" - fs-extra: "npm:11.3.2" + fs-extra: "npm:11.3.3" glob: "npm:13.0.0" inquirer: "npm:8.2.7" proxy-agent: "npm:6.5.0" @@ -13068,7 +13075,7 @@ __metadata: tslib: "npm:2.8.1" bin: openapi-generator-cli: main.js - checksum: 10/8a6e2990f4816aadb8ac6f390aeb169fe2f842f8874417dd288ea4b177af9238a7af59c699953e644d17eeb6c5d712faf63ce3bf9419e429e71504e2033de7b6 + checksum: 10/42f6c887d8a16eca90420139e1d0d464bd78aad1d609a8a9589c3fbcda4ab3052f6b9310b7b54521583df50f2dfb4cb0c9de073e717e91eadbf4e230e70f3007 languageName: node linkType: hard @@ -20485,14 +20492,13 @@ __metadata: languageName: node linkType: hard -"@tokenizer/inflate@npm:^0.3.1": - version: 0.3.1 - resolution: "@tokenizer/inflate@npm:0.3.1" +"@tokenizer/inflate@npm:^0.4.1": + version: 0.4.1 + resolution: "@tokenizer/inflate@npm:0.4.1" dependencies: - debug: "npm:^4.4.1" - fflate: "npm:^0.8.2" - token-types: "npm:^6.0.0" - checksum: 10/a505d1b43b1a6c0ce0aacc681558daad4fc2f595a938dcd23c99cf3d6845189c7e5bd8f1785253675bd73ff45df8b47beeeab96197d30ff578d81029ff923103 + debug: "npm:^4.4.3" + token-types: "npm:^6.1.1" + checksum: 10/27d58757e1a6c004e86f8a5f1a40fe47cb48aa6891864d03de6eab27d42fafc1456f396bc8bc300e16913b0a85f42034d011db0213d17e544ed201a7fc24244e languageName: node linkType: hard @@ -28651,7 +28657,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.4.3, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.1, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:4.4.3, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -31684,13 +31690,6 @@ __metadata: languageName: node linkType: hard -"fflate@npm:^0.8.2": - version: 0.8.2 - resolution: "fflate@npm:0.8.2" - checksum: 10/2bd26ba6d235d428de793c6a0cd1aaa96a06269ebd4e21b46c8fd1bd136abc631acf27e188d47c3936db090bf3e1ede11d15ce9eae9bffdc4bfe1b9dc66ca9cb - languageName: node - linkType: hard - "figures@npm:^3.0.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -31709,15 +31708,15 @@ __metadata: languageName: node linkType: hard -"file-type@npm:21.1.0": - version: 21.1.0 - resolution: "file-type@npm:21.1.0" +"file-type@npm:21.3.0": + version: 21.3.0 + resolution: "file-type@npm:21.3.0" dependencies: - "@tokenizer/inflate": "npm:^0.3.1" - strtok3: "npm:^10.3.1" - token-types: "npm:^6.0.0" + "@tokenizer/inflate": "npm:^0.4.1" + strtok3: "npm:^10.3.4" + token-types: "npm:^6.1.1" uint8array-extras: "npm:^1.4.0" - checksum: 10/951d1e224c643e961c2b51fce236526b9a4789ea59fee53da3bdf5d29de8cc58ffa4b4601f83739fd0df22324108eb4b55f2b7cefbe93489e554a9e5de95ce3f + checksum: 10/8eb8707f34d0a0fd6c2d2b223edf1ed6235cb00a44a90216741be9e812ae08dc8497dbf4e2dd63e629728509cdf361070ed32ae2280724744463ab459da81a83 languageName: node linkType: hard @@ -32279,14 +32278,14 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:11.3.2": - version: 11.3.2 - resolution: "fs-extra@npm:11.3.2" +"fs-extra@npm:11.3.3, fs-extra@npm:^11.0.0, fs-extra@npm:^11.1.0, fs-extra@npm:^11.2.0, fs-extra@npm:~11.3.0": + version: 11.3.3 + resolution: "fs-extra@npm:11.3.3" dependencies: graceful-fs: "npm:^4.2.0" jsonfile: "npm:^6.0.1" universalify: "npm:^2.0.0" - checksum: 10/d559545c73fda69c75aa786f345c2f738b623b42aea850200b1582e006a35278f63787179e3194ba19413c26a280441758952b0c7e88dd96762d497e365a6c3e + checksum: 10/daeaefafbebe8fa6efd2fb96fc926f2c952be5877811f00a6794f0d64e0128e3d0d93368cd328f8f063b45deacf385c40e3d931aa46014245431cd2f4f89c67a languageName: node linkType: hard @@ -32313,17 +32312,6 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.0.0, fs-extra@npm:^11.1.0, fs-extra@npm:^11.2.0, fs-extra@npm:~11.3.0": - version: 11.3.3 - resolution: "fs-extra@npm:11.3.3" - dependencies: - graceful-fs: "npm:^4.2.0" - jsonfile: "npm:^6.0.1" - universalify: "npm:^2.0.0" - checksum: 10/daeaefafbebe8fa6efd2fb96fc926f2c952be5877811f00a6794f0d64e0128e3d0d93368cd328f8f063b45deacf385c40e3d931aa46014245431cd2f4f89c67a - languageName: node - linkType: hard - "fs-extra@npm:^7.0.1": version: 7.0.1 resolution: "fs-extra@npm:7.0.1" @@ -47787,7 +47775,7 @@ __metadata: languageName: node linkType: hard -"strtok3@npm:^10.3.1": +"strtok3@npm:^10.3.4": version: 10.3.4 resolution: "strtok3@npm:10.3.4" dependencies: @@ -48695,13 +48683,14 @@ __metadata: languageName: node linkType: hard -"token-types@npm:^6.0.0": - version: 6.0.0 - resolution: "token-types@npm:6.0.0" +"token-types@npm:^6.1.1": + version: 6.1.2 + resolution: "token-types@npm:6.1.2" dependencies: + "@borewit/text-codec": "npm:^0.2.1" "@tokenizer/token": "npm:^0.3.0" ieee754: "npm:^1.2.1" - checksum: 10/b541b605d602e8e6495745badb35f90ee8f997e43dc29bc51aee7e9a0bc3c6bc7372a305bd45f3e80d75223c2b6a5c7e65cb5159d8c4e49fa25cdbaae531fad4 + checksum: 10/0c7811a2da5a0ca474c795d883d871a184d1d54f67058d66084110f0b246fff66151885dbcb91d66533e776478bf57f3b4fac69ce03b805a0e1060def87947de languageName: node linkType: hard From e545097f4034bc470937f4ac82a9fcb9761be82c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 19:18:27 +0000 Subject: [PATCH 021/211] chore(deps): update dependency @remixicon/react to v4.8.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs-ui/yarn.lock | 17 ++++------------- yarn.lock | 6 +++--- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/docs-ui/yarn.lock b/docs-ui/yarn.lock index 8b5a1d865e..dc30d94da9 100644 --- a/docs-ui/yarn.lock +++ b/docs-ui/yarn.lock @@ -949,11 +949,11 @@ __metadata: linkType: hard "@remixicon/react@npm:^4.6.0": - version: 4.7.0 - resolution: "@remixicon/react@npm:4.7.0" + version: 4.8.0 + resolution: "@remixicon/react@npm:4.8.0" peerDependencies: react: ">=18.2.0" - checksum: 10/9ecee093dd4aec3744bcc7562eddb987bf9c7c059787dfecfbcc25a64c2f4833dc815dff715aa2a7f75011d65f877b3751a401d19cf2d725b5bcc685587d37f4 + checksum: 10/10241f2e07826ce7d595cf9687a35c96cc9cf9eb68a9431d7dfa1b9df479dbef302874d28c0502cee2a536cf34722de7c49ed12d10a2b071e4e42ba4a278c516 languageName: node linkType: hard @@ -4766,16 +4766,7 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^3.0.1": - version: 3.0.2 - resolution: "minizlib@npm:3.0.2" - dependencies: - minipass: "npm:^7.1.2" - checksum: 10/c075bed1594f68dcc8c35122333520112daefd4d070e5d0a228bd4cf5580e9eed3981b96c0ae1d62488e204e80fd27b2b9d0068ca9a5ef3993e9565faf63ca41 - languageName: node - linkType: hard - -"minizlib@npm:^3.1.0": +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" dependencies: diff --git a/yarn.lock b/yarn.lock index 21f0bf446d..cb21f9320e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16895,11 +16895,11 @@ __metadata: linkType: hard "@remixicon/react@npm:^4.6.0": - version: 4.7.0 - resolution: "@remixicon/react@npm:4.7.0" + version: 4.8.0 + resolution: "@remixicon/react@npm:4.8.0" peerDependencies: react: ">=18.2.0" - checksum: 10/9ecee093dd4aec3744bcc7562eddb987bf9c7c059787dfecfbcc25a64c2f4833dc815dff715aa2a7f75011d65f877b3751a401d19cf2d725b5bcc685587d37f4 + checksum: 10/10241f2e07826ce7d595cf9687a35c96cc9cf9eb68a9431d7dfa1b9df479dbef302874d28c0502cee2a536cf34722de7c49ed12d10a2b071e4e42ba4a278c516 languageName: node linkType: hard From 64c140db094e307232af7d44470a0cd9af4d407a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:07:00 +0000 Subject: [PATCH 022/211] chore(deps): update dependency @rspack/core to v1.7.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 164 +++++++++++++++++++++++++++--------------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/yarn.lock b/yarn.lock index dfd2ee2b0d..dd112cc03e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11494,10 +11494,10 @@ __metadata: languageName: node linkType: hard -"@module-federation/error-codes@npm:0.21.6": - version: 0.21.6 - resolution: "@module-federation/error-codes@npm:0.21.6" - checksum: 10/6ded1ecab780f1f9ec46a59adb200e75cdf11580d70aa79dd75d71fbbf276615690da277ea67aa1ceb5bc88386f5708cc1d2ba5526be5c9ff02397a6123e36bf +"@module-federation/error-codes@npm:0.22.0": + version: 0.22.0 + resolution: "@module-federation/error-codes@npm:0.22.0" + checksum: 10/4edb269e9f3039899f879788c84d2bfecff94ca8e87ffcd80dbf8589d8543ec32558b3fa05c8549a8abd3ac33e856ff2aacf458dea5c0d7bea608bf12bb13359 languageName: node linkType: hard @@ -11565,13 +11565,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-core@npm:0.21.6": - version: 0.21.6 - resolution: "@module-federation/runtime-core@npm:0.21.6" +"@module-federation/runtime-core@npm:0.22.0": + version: 0.22.0 + resolution: "@module-federation/runtime-core@npm:0.22.0" dependencies: - "@module-federation/error-codes": "npm:0.21.6" - "@module-federation/sdk": "npm:0.21.6" - checksum: 10/85efa2042d6f3a7cf0e4971b991472d4339d88f6f15684afb6d451f19ed934e225b2510c86b7bb4d2c5f64253ed7b0175f08c17f95bfc2b9929930a8a03fff1e + "@module-federation/error-codes": "npm:0.22.0" + "@module-federation/sdk": "npm:0.22.0" + checksum: 10/d21969198322b6f79e0513b702d0af5097613d47819724c849b6c677c163cd10fb8c89e3ff62b798bec498ee4d8e95dec71861071bc4ed74bd86a7e43193bc05 languageName: node linkType: hard @@ -11585,13 +11585,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-tools@npm:0.21.6": - version: 0.21.6 - resolution: "@module-federation/runtime-tools@npm:0.21.6" +"@module-federation/runtime-tools@npm:0.22.0": + version: 0.22.0 + resolution: "@module-federation/runtime-tools@npm:0.22.0" dependencies: - "@module-federation/runtime": "npm:0.21.6" - "@module-federation/webpack-bundler-runtime": "npm:0.21.6" - checksum: 10/36e7ccab948e11f310e87397a1a2185b56064e5691e553b34173686e2bc7372ec710e5ad48c026eb28c85b168765788b743aa2111513f3b57118b47636312dd1 + "@module-federation/runtime": "npm:0.22.0" + "@module-federation/webpack-bundler-runtime": "npm:0.22.0" + checksum: 10/0e7693c1ec02fc5bef770b478c8757cad9cfefb2310d1943151d0ad079b72472d9b2c8a087299e9124dfcd6b649c83290c7fdfa333865baab4ba193f39e7b6bd languageName: node linkType: hard @@ -11605,14 +11605,14 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime@npm:0.21.6": - version: 0.21.6 - resolution: "@module-federation/runtime@npm:0.21.6" +"@module-federation/runtime@npm:0.22.0": + version: 0.22.0 + resolution: "@module-federation/runtime@npm:0.22.0" dependencies: - "@module-federation/error-codes": "npm:0.21.6" - "@module-federation/runtime-core": "npm:0.21.6" - "@module-federation/sdk": "npm:0.21.6" - checksum: 10/93fd9bb284630933cab7e4bc070d648b56272f3636038c05eec7d1e3eeb189be3ccebe5f8ecc450197ee992d2616ed282d54e673ec0acd63adee4faddf80b144 + "@module-federation/error-codes": "npm:0.22.0" + "@module-federation/runtime-core": "npm:0.22.0" + "@module-federation/sdk": "npm:0.22.0" + checksum: 10/eca608be999d7d2e83abc1169643c2f795a5ed950f9e2bdf7000400a30b3e1e0ca4bdaa5daa09f55e44868383d444707e40236cec1aaa7b40432b0cce800b7f3 languageName: node linkType: hard @@ -11627,10 +11627,10 @@ __metadata: languageName: node linkType: hard -"@module-federation/sdk@npm:0.21.6": - version: 0.21.6 - resolution: "@module-federation/sdk@npm:0.21.6" - checksum: 10/effc4aa932e2f06742bda8f02aaec84e138f5512b50f18c38b051490020b20d3d8edf7ece853fccffc1f78a0b43dec78e69bf02150e7e2801d5ce03c3ee367b9 +"@module-federation/sdk@npm:0.22.0": + version: 0.22.0 + resolution: "@module-federation/sdk@npm:0.22.0" + checksum: 10/d7085d883730a33145052520787a7e59cf9c54b51b2946bebc7c63a6bb668bcc6cbdc27fa0b7354a62f5a7ee4e8829a66b84e644607498f2e37cfd5eb4ded0da languageName: node linkType: hard @@ -11652,13 +11652,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:0.21.6": - version: 0.21.6 - resolution: "@module-federation/webpack-bundler-runtime@npm:0.21.6" +"@module-federation/webpack-bundler-runtime@npm:0.22.0": + version: 0.22.0 + resolution: "@module-federation/webpack-bundler-runtime@npm:0.22.0" dependencies: - "@module-federation/runtime": "npm:0.21.6" - "@module-federation/sdk": "npm:0.21.6" - checksum: 10/a5ceb72ee3867acad5d7d3c654eb568068b1d5288f60ce9301bdc9f56effa5a4c26a732a2cec7176a81b87139566cd51dd8dfbc6112da05d47b870fa3ad3ba1f + "@module-federation/runtime": "npm:0.22.0" + "@module-federation/sdk": "npm:0.22.0" + checksum: 10/afd24406817dfc6474ebcf5be714ccf26690eb3f6f5172bda711c8f23dba149fe47293f7aa2d0733dfed0334c98d4d3d9e7c2da2be78750cae5a72d72f32ce93 languageName: node linkType: hard @@ -17211,92 +17211,92 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-darwin-arm64@npm:1.6.7" +"@rspack/binding-darwin-arm64@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-darwin-arm64@npm:1.7.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-darwin-x64@npm:1.6.7" +"@rspack/binding-darwin-x64@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-darwin-x64@npm:1.7.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.6.7" +"@rspack/binding-linux-arm64-gnu@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.7.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.6.7" +"@rspack/binding-linux-arm64-musl@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.7.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.6.7" +"@rspack/binding-linux-x64-gnu@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.7.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-linux-x64-musl@npm:1.6.7" +"@rspack/binding-linux-x64-musl@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-linux-x64-musl@npm:1.7.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-wasm32-wasi@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-wasm32-wasi@npm:1.6.7" +"@rspack/binding-wasm32-wasi@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-wasm32-wasi@npm:1.7.3" dependencies: "@napi-rs/wasm-runtime": "npm:1.0.7" conditions: cpu=wasm32 languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.6.7" +"@rspack/binding-win32-arm64-msvc@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.7.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.6.7" +"@rspack/binding-win32-ia32-msvc@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.7.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.6.7" +"@rspack/binding-win32-x64-msvc@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.7.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.6.7": - version: 1.6.7 - resolution: "@rspack/binding@npm:1.6.7" +"@rspack/binding@npm:1.7.3": + version: 1.7.3 + resolution: "@rspack/binding@npm:1.7.3" dependencies: - "@rspack/binding-darwin-arm64": "npm:1.6.7" - "@rspack/binding-darwin-x64": "npm:1.6.7" - "@rspack/binding-linux-arm64-gnu": "npm:1.6.7" - "@rspack/binding-linux-arm64-musl": "npm:1.6.7" - "@rspack/binding-linux-x64-gnu": "npm:1.6.7" - "@rspack/binding-linux-x64-musl": "npm:1.6.7" - "@rspack/binding-wasm32-wasi": "npm:1.6.7" - "@rspack/binding-win32-arm64-msvc": "npm:1.6.7" - "@rspack/binding-win32-ia32-msvc": "npm:1.6.7" - "@rspack/binding-win32-x64-msvc": "npm:1.6.7" + "@rspack/binding-darwin-arm64": "npm:1.7.3" + "@rspack/binding-darwin-x64": "npm:1.7.3" + "@rspack/binding-linux-arm64-gnu": "npm:1.7.3" + "@rspack/binding-linux-arm64-musl": "npm:1.7.3" + "@rspack/binding-linux-x64-gnu": "npm:1.7.3" + "@rspack/binding-linux-x64-musl": "npm:1.7.3" + "@rspack/binding-wasm32-wasi": "npm:1.7.3" + "@rspack/binding-win32-arm64-msvc": "npm:1.7.3" + "@rspack/binding-win32-ia32-msvc": "npm:1.7.3" + "@rspack/binding-win32-x64-msvc": "npm:1.7.3" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -17318,23 +17318,23 @@ __metadata: optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/80837f5cbdcac9eb6fb2213436baa3a5a841508340456c473e36bed2882197f456462db675b2d43cc1ed84d61109bae4bef0d474131a670113601ad80727769f + checksum: 10/295c59c22370f71e0f8543fca9819d7d13741e02ef340f1fdda84789d800e324a91d7e54c5119ec0e2a64d4074efff7d2a7f989522bef2d8cb12fb7abffbb5d5 languageName: node linkType: hard "@rspack/core@npm:^1.4.11": - version: 1.6.7 - resolution: "@rspack/core@npm:1.6.7" + version: 1.7.3 + resolution: "@rspack/core@npm:1.7.3" dependencies: - "@module-federation/runtime-tools": "npm:0.21.6" - "@rspack/binding": "npm:1.6.7" + "@module-federation/runtime-tools": "npm:0.22.0" + "@rspack/binding": "npm:1.7.3" "@rspack/lite-tapable": "npm:1.1.0" peerDependencies: "@swc/helpers": ">=0.5.1" peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/c374d954c86a24dfba59b6e3c6388ce365723c83ea7012a6e95175b6245f7080b13e8560c25d744558e850c04a10d95fbe7ee92b4eabbd40a1ef578caf60615b + checksum: 10/cd6a4d8dea6eb2fe8fd97b1266ef3ab0bdefd02970f70f0154b20c7ba3b4235cb92e712cf7f9f42bf64786663422ee46e85917b15fe06b563da22bc39ca5f35e languageName: node linkType: hard From b211deea341e0edfb4db082e63b32ff96a5a5a9b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 20:07:28 +0000 Subject: [PATCH 023/211] chore(deps): update dependency @rspack/plugin-react-refresh to v1.6.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index dfd2ee2b0d..ee91d55206 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17361,8 +17361,8 @@ __metadata: linkType: hard "@rspack/plugin-react-refresh@npm:^1.4.3": - version: 1.5.3 - resolution: "@rspack/plugin-react-refresh@npm:1.5.3" + version: 1.6.0 + resolution: "@rspack/plugin-react-refresh@npm:1.6.0" dependencies: error-stack-parser: "npm:^2.1.4" html-entities: "npm:^2.6.0" @@ -17372,7 +17372,7 @@ __metadata: peerDependenciesMeta: webpack-hot-middleware: optional: true - checksum: 10/3886a13dca3d42e9245480283ab839c16015a48eceda0d7352114de26968e319e8f110c5ec4dbe4c20d4a8457b368401840c736ce49f774edd450cf3ba193f62 + checksum: 10/b5c4209b146e51e3f89a19023076567ee08e57df1fdd0dd2cad9f2dbdc7ece3631def589512f457e5ac9fed4df8492c0cac7a203387e146bfd92721a8d572f77 languageName: node linkType: hard From 640d7ef13a24b62257f883bcee37264f811e611b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 21:04:27 +0000 Subject: [PATCH 024/211] chore(deps): update dependency better-sqlite3 to v12.6.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index dd112cc03e..923f54d53a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25788,13 +25788,13 @@ __metadata: linkType: hard "better-sqlite3@npm:^12.0.0": - version: 12.5.0 - resolution: "better-sqlite3@npm:12.5.0" + version: 12.6.2 + resolution: "better-sqlite3@npm:12.6.2" dependencies: bindings: "npm:^1.5.0" node-gyp: "npm:latest" prebuild-install: "npm:^7.1.1" - checksum: 10/8d81cde54231430d5b7fca8d7224ccdd13708ea769765e7a284af1744376568553016ed500f55e7ff992c57f12e29044da17a950adda2a4d361a5295c237d2ea + checksum: 10/28600c06c03c33b027717f48d7208561d4a51d1fab48b4efb9041cafb9ca79568bc3a702835ad3bdd2b17189942745df22ed827be220254dd2a3928e257c138f languageName: node linkType: hard From 0922aac9a12dc24ad2f46ca2c1e545a3c0b199a1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 22:02:45 +0000 Subject: [PATCH 025/211] chore(deps): update dependency motion to v12.27.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- docs-ui/yarn.lock | 49 +++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/docs-ui/yarn.lock b/docs-ui/yarn.lock index 8b5a1d865e..22b0ba99cf 100644 --- a/docs-ui/yarn.lock +++ b/docs-ui/yarn.lock @@ -3022,12 +3022,12 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:^12.23.26": - version: 12.23.26 - resolution: "framer-motion@npm:12.23.26" +"framer-motion@npm:^12.28.1": + version: 12.28.1 + resolution: "framer-motion@npm:12.28.1" dependencies: - motion-dom: "npm:^12.23.23" - motion-utils: "npm:^12.23.6" + motion-dom: "npm:^12.28.1" + motion-utils: "npm:^12.27.2" tslib: "npm:^2.4.0" peerDependencies: "@emotion/is-prop-valid": "*" @@ -3040,7 +3040,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 10/29d51ac57ce8c2599664bf706d7a83c2aba4fe1dee1c0c7d75d5c45c120b1c2ca5d257c3ff608992b26bf8e9cf258cacf231b0873d7b16f01c5eeb42b9831f78 + checksum: 10/f8101df55b9e2c9ca7eb362aaff250ba2f77ef01770cbf3d27bc9c2103504d247936b4e62f2a4059bc1a0a878f558d32407db9b182976fdd48b2549d0631be54 languageName: node linkType: hard @@ -4766,16 +4766,7 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^3.0.1": - version: 3.0.2 - resolution: "minizlib@npm:3.0.2" - dependencies: - minipass: "npm:^7.1.2" - checksum: 10/c075bed1594f68dcc8c35122333520112daefd4d070e5d0a228bd4cf5580e9eed3981b96c0ae1d62488e204e80fd27b2b9d0068ca9a5ef3993e9565faf63ca41 - languageName: node - linkType: hard - -"minizlib@npm:^3.1.0": +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": version: 3.1.0 resolution: "minizlib@npm:3.1.0" dependencies: @@ -4784,27 +4775,27 @@ __metadata: languageName: node linkType: hard -"motion-dom@npm:^12.23.23": - version: 12.23.23 - resolution: "motion-dom@npm:12.23.23" +"motion-dom@npm:^12.28.1": + version: 12.28.1 + resolution: "motion-dom@npm:12.28.1" dependencies: - motion-utils: "npm:^12.23.6" - checksum: 10/6bec34c28b478622bd23ce919c0d64e7f372cbc3f88e665f1e34fbf590e9bb53eebea086fb28f4e0c31941bd5274e086dc9b5360b68c4976891b94c4e92ca01a + motion-utils: "npm:^12.27.2" + checksum: 10/a0ba8bbd137d4b21df4fe517c2a9e02c891270974f212909929aadcc3043a3655bb28d2eceeb58152ee0ce25381fe0dd1a06c3b1e48ba15a5b13ec20938b42b0 languageName: node linkType: hard -"motion-utils@npm:^12.23.6": - version: 12.23.6 - resolution: "motion-utils@npm:12.23.6" - checksum: 10/fae741319e72389eca8f3e73e644ffe6501409d00c7febb2f9b836629ae86ef735e73d670d0591706ca5e93ce10228ddbcff0ecdeb09b4cd8df14df7007aedd9 +"motion-utils@npm:^12.27.2": + version: 12.27.2 + resolution: "motion-utils@npm:12.27.2" + checksum: 10/e4c4724b1bf11d5c6dca17e8c2fd42dc7270f03ca1fd1669908e3b8bb2cd528669c7aab8df134fedbc94806edb2f9e0e9f84c66cbe77d0cb923733417b5f9f79 languageName: node linkType: hard "motion@npm:^12.4.1": - version: 12.23.26 - resolution: "motion@npm:12.23.26" + version: 12.28.1 + resolution: "motion@npm:12.28.1" dependencies: - framer-motion: "npm:^12.23.26" + framer-motion: "npm:^12.28.1" tslib: "npm:^2.4.0" peerDependencies: "@emotion/is-prop-valid": "*" @@ -4817,7 +4808,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 10/c5e683d2c67e55816587cbbd451c5600dc0602aa04108ab0bf8d4472befd490e90b3177362dbc8b9f96316ec16383924cc574eb7c9e031540f1e88e64a2704ae + checksum: 10/4ad4e36ec237f22341db24a3beb87d3126facc52acde4d02d8b59cf3cbb98512e7e2aefe3256cc6e00e53a44fcaf01c998c427d79a113048b5a676524608cac5 languageName: node linkType: hard From 08586b48921b2e1b5d2977413769a62db1629d69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 22:03:23 +0000 Subject: [PATCH 026/211] chore(deps): update dependency sass to v1.97.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 2af94aec1f..f612c0caae 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -13055,8 +13055,8 @@ __metadata: linkType: hard "sass@npm:^1.57.1, sass@npm:^1.80.4": - version: 1.94.2 - resolution: "sass@npm:1.94.2" + version: 1.97.2 + resolution: "sass@npm:1.97.2" dependencies: "@parcel/watcher": "npm:^2.4.1" chokidar: "npm:^4.0.0" @@ -13067,7 +13067,7 @@ __metadata: optional: true bin: sass: sass.js - checksum: 10/e60c214ea93677740c9ddfad55c77fd433255bbfdd9faba137acf1215bed5ba6ad9d83efea81feb87a89283931d01f0435227e3fff37c65c263e0ee05f885328 + checksum: 10/a6b27e6a75bc8eedf0ce941a86e996f4a477640dfd214ecdcee5e8d7dca054bb03070974690b7f988a99ea729983cd8ddcdbb887bd068c73fde2d0010d835064 languageName: node linkType: hard From 05feb0862d97ee8a30863bbc45d1db6e758ffee4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:14:32 +0000 Subject: [PATCH 027/211] chore(deps): bump lodash-es from 4.17.21 to 4.17.23 Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23. - [Release notes](https://github.com/lodash/lodash/releases) - [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23) --- updated-dependencies: - dependency-name: lodash-es dependency-version: 4.17.23 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4e58ed6032..0b5e8da0f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37577,9 +37577,9 @@ __metadata: linkType: hard "lodash-es@npm:^4.17.21": - version: 4.17.21 - resolution: "lodash-es@npm:4.17.21" - checksum: 10/03f39878ea1e42b3199bd3f478150ab723f93cc8730ad86fec1f2804f4a07c6e30deaac73cad53a88e9c3db33348bb8ceeb274552390e7a75d7849021c02df43 + version: 4.17.23 + resolution: "lodash-es@npm:4.17.23" + checksum: 10/1feae200df22eb0bd93ca86d485e77784b8a9fb1d13e91b66e9baa7a7e5e04be088c12a7e20c2250fc0bd3db1bc0ef0affc7d9e3810b6af2455a3c6bf6dde59e languageName: node linkType: hard From f2dda8cc0bdfe420cf634521fc35b4c37d6d3285 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 00:12:04 +0000 Subject: [PATCH 028/211] chore(deps): update dependency lodash to v4.17.23 [security] Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 4e58ed6032..8b63cc4302 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37863,13 +37863,20 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.17.21, lodash@npm:^4.15.0, lodash@npm:^4.16.4, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:~4.17.15, lodash@npm:~4.17.21": +"lodash@npm:4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532 languageName: node linkType: hard +"lodash@npm:^4.15.0, lodash@npm:^4.16.4, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:~4.17.15, lodash@npm:~4.17.21": + version: 4.17.23 + resolution: "lodash@npm:4.17.23" + checksum: 10/82504c88250f58da7a5a4289f57a4f759c44946c005dd232821c7688b5fcfbf4a6268f6a6cdde4b792c91edd2f3b5398c1d2a0998274432cff76def48735e233 + languageName: node + linkType: hard + "log-node@npm:^8.0.3": version: 8.0.3 resolution: "log-node@npm:8.0.3" From a6c4a4db073aa9dd4900d5b0a96eeffb750399d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 00:13:26 +0000 Subject: [PATCH 029/211] chore(deps): update dependency isomorphic-git to v1.36.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4e58ed6032..a01b30aa00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35481,8 +35481,8 @@ __metadata: linkType: hard "isomorphic-git@npm:^1.23.0": - version: 1.35.1 - resolution: "isomorphic-git@npm:1.35.1" + version: 1.36.1 + resolution: "isomorphic-git@npm:1.36.1" dependencies: async-lock: "npm:^1.4.1" clean-git-ref: "npm:^2.0.1" @@ -35497,7 +35497,7 @@ __metadata: simple-get: "npm:^4.0.1" bin: isogit: cli.cjs - checksum: 10/8f9244ef09a02c2779aaf682b8db40b208860d57f1bf9deed3c35641a87221f56e490b3f94178579f95d46e6a4e1bb593590994d8138649b32d60d03f9e1f5f8 + checksum: 10/f68670b53c5216284afe785f5327dcedffb99dcbe947b338e1f3b20338382506561ef178b77391d2ce95f16912eb8035b2b4d3d52d832cf1cd86ceabc9b9174a languageName: node linkType: hard From 235a32e8149765157b9b410d475a5dc6edb9469c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 01:20:56 +0000 Subject: [PATCH 030/211] chore(deps): update dependency knip to v5.82.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 186 +++++++++++++++++++++++++++--------------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0ab2fbb6e8..44aa3b0e7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11976,14 +11976,14 @@ __metadata: languageName: node linkType: hard -"@napi-rs/wasm-runtime@npm:^1.1.0": - version: 1.1.0 - resolution: "@napi-rs/wasm-runtime@npm:1.1.0" +"@napi-rs/wasm-runtime@npm:^1.1.1": + version: 1.1.1 + resolution: "@napi-rs/wasm-runtime@npm:1.1.1" dependencies: "@emnapi/core": "npm:^1.7.1" "@emnapi/runtime": "npm:^1.7.1" "@tybys/wasm-util": "npm:^0.10.1" - checksum: 10/87c7ab4685527aa4820320020e2af5879b99d88e94b42cdc3690646722536f14656667392975a9576bf411a3804a464949fadbc343a646a2c2a8b2f10d921f6c + checksum: 10/080e7f2aefb84e09884d21c650a2cbafdf25bfd2634693791b27e36eec0ddaa3c1656a943f8c913ac75879a0b04e68f8a827897ee655ab54a93169accf05b194 languageName: node linkType: hard @@ -14156,144 +14156,144 @@ __metadata: languageName: node linkType: hard -"@oxc-resolver/binding-android-arm-eabi@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-android-arm-eabi@npm:11.14.2" +"@oxc-resolver/binding-android-arm-eabi@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-android-arm-eabi@npm:11.16.3" conditions: os=android & cpu=arm languageName: node linkType: hard -"@oxc-resolver/binding-android-arm64@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-android-arm64@npm:11.14.2" +"@oxc-resolver/binding-android-arm64@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-android-arm64@npm:11.16.3" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@oxc-resolver/binding-darwin-arm64@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-darwin-arm64@npm:11.14.2" +"@oxc-resolver/binding-darwin-arm64@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-darwin-arm64@npm:11.16.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@oxc-resolver/binding-darwin-x64@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-darwin-x64@npm:11.14.2" +"@oxc-resolver/binding-darwin-x64@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-darwin-x64@npm:11.16.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@oxc-resolver/binding-freebsd-x64@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-freebsd-x64@npm:11.14.2" +"@oxc-resolver/binding-freebsd-x64@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-freebsd-x64@npm:11.16.3" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.14.2" +"@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-arm-gnueabihf@npm:11.16.3" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@oxc-resolver/binding-linux-arm-musleabihf@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-arm-musleabihf@npm:11.14.2" +"@oxc-resolver/binding-linux-arm-musleabihf@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-arm-musleabihf@npm:11.16.3" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@oxc-resolver/binding-linux-arm64-gnu@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-arm64-gnu@npm:11.14.2" +"@oxc-resolver/binding-linux-arm64-gnu@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-arm64-gnu@npm:11.16.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@oxc-resolver/binding-linux-arm64-musl@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-arm64-musl@npm:11.14.2" +"@oxc-resolver/binding-linux-arm64-musl@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-arm64-musl@npm:11.16.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@oxc-resolver/binding-linux-ppc64-gnu@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-ppc64-gnu@npm:11.14.2" +"@oxc-resolver/binding-linux-ppc64-gnu@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-ppc64-gnu@npm:11.16.3" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@oxc-resolver/binding-linux-riscv64-gnu@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-riscv64-gnu@npm:11.14.2" +"@oxc-resolver/binding-linux-riscv64-gnu@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-riscv64-gnu@npm:11.16.3" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@oxc-resolver/binding-linux-riscv64-musl@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-riscv64-musl@npm:11.14.2" +"@oxc-resolver/binding-linux-riscv64-musl@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-riscv64-musl@npm:11.16.3" conditions: os=linux & cpu=riscv64 & libc=musl languageName: node linkType: hard -"@oxc-resolver/binding-linux-s390x-gnu@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-s390x-gnu@npm:11.14.2" +"@oxc-resolver/binding-linux-s390x-gnu@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-s390x-gnu@npm:11.16.3" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@oxc-resolver/binding-linux-x64-gnu@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-x64-gnu@npm:11.14.2" +"@oxc-resolver/binding-linux-x64-gnu@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-x64-gnu@npm:11.16.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@oxc-resolver/binding-linux-x64-musl@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-linux-x64-musl@npm:11.14.2" +"@oxc-resolver/binding-linux-x64-musl@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-linux-x64-musl@npm:11.16.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@oxc-resolver/binding-openharmony-arm64@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-openharmony-arm64@npm:11.14.2" +"@oxc-resolver/binding-openharmony-arm64@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-openharmony-arm64@npm:11.16.3" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@oxc-resolver/binding-wasm32-wasi@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-wasm32-wasi@npm:11.14.2" +"@oxc-resolver/binding-wasm32-wasi@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-wasm32-wasi@npm:11.16.3" dependencies: - "@napi-rs/wasm-runtime": "npm:^1.1.0" + "@napi-rs/wasm-runtime": "npm:^1.1.1" conditions: cpu=wasm32 languageName: node linkType: hard -"@oxc-resolver/binding-win32-arm64-msvc@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-win32-arm64-msvc@npm:11.14.2" +"@oxc-resolver/binding-win32-arm64-msvc@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-win32-arm64-msvc@npm:11.16.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@oxc-resolver/binding-win32-ia32-msvc@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-win32-ia32-msvc@npm:11.14.2" +"@oxc-resolver/binding-win32-ia32-msvc@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-win32-ia32-msvc@npm:11.16.3" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@oxc-resolver/binding-win32-x64-msvc@npm:11.14.2": - version: 11.14.2 - resolution: "@oxc-resolver/binding-win32-x64-msvc@npm:11.14.2" +"@oxc-resolver/binding-win32-x64-msvc@npm:11.16.3": + version: 11.16.3 + resolution: "@oxc-resolver/binding-win32-x64-msvc@npm:11.16.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -37050,8 +37050,8 @@ __metadata: linkType: hard "knip@npm:^5.42.0": - version: 5.71.0 - resolution: "knip@npm:5.71.0" + version: 5.82.1 + resolution: "knip@npm:5.82.1" dependencies: "@nodelib/fs.walk": "npm:^1.2.3" fast-glob: "npm:^3.3.3" @@ -37059,7 +37059,7 @@ __metadata: jiti: "npm:^2.6.0" js-yaml: "npm:^4.1.1" minimist: "npm:^1.2.8" - oxc-resolver: "npm:^11.13.2" + oxc-resolver: "npm:^11.15.0" picocolors: "npm:^1.1.1" picomatch: "npm:^4.0.1" smol-toml: "npm:^1.5.2" @@ -37071,7 +37071,7 @@ __metadata: bin: knip: bin/knip.js knip-bun: bin/knip-bun.js - checksum: 10/68c1508be9db70865559f5ebc106f8a6aced8e4799949655e92a59fbb3f530c042087e50565cfdd698b619f69f584ee214cc7b315f3241d483d09e4fd200c45b + checksum: 10/e41549af9dc9b8921b393cab881f848ce06c7fd915214cb1806ee01aec7155b4e045684bc8d1a396a588e8fe284693912a50edc5680312b1859a1f949dd4c37a languageName: node linkType: hard @@ -41286,30 +41286,30 @@ __metadata: languageName: node linkType: hard -"oxc-resolver@npm:^11.13.2": - version: 11.14.2 - resolution: "oxc-resolver@npm:11.14.2" +"oxc-resolver@npm:^11.15.0": + version: 11.16.3 + resolution: "oxc-resolver@npm:11.16.3" dependencies: - "@oxc-resolver/binding-android-arm-eabi": "npm:11.14.2" - "@oxc-resolver/binding-android-arm64": "npm:11.14.2" - "@oxc-resolver/binding-darwin-arm64": "npm:11.14.2" - "@oxc-resolver/binding-darwin-x64": "npm:11.14.2" - "@oxc-resolver/binding-freebsd-x64": "npm:11.14.2" - "@oxc-resolver/binding-linux-arm-gnueabihf": "npm:11.14.2" - "@oxc-resolver/binding-linux-arm-musleabihf": "npm:11.14.2" - "@oxc-resolver/binding-linux-arm64-gnu": "npm:11.14.2" - "@oxc-resolver/binding-linux-arm64-musl": "npm:11.14.2" - "@oxc-resolver/binding-linux-ppc64-gnu": "npm:11.14.2" - "@oxc-resolver/binding-linux-riscv64-gnu": "npm:11.14.2" - "@oxc-resolver/binding-linux-riscv64-musl": "npm:11.14.2" - "@oxc-resolver/binding-linux-s390x-gnu": "npm:11.14.2" - "@oxc-resolver/binding-linux-x64-gnu": "npm:11.14.2" - "@oxc-resolver/binding-linux-x64-musl": "npm:11.14.2" - "@oxc-resolver/binding-openharmony-arm64": "npm:11.14.2" - "@oxc-resolver/binding-wasm32-wasi": "npm:11.14.2" - "@oxc-resolver/binding-win32-arm64-msvc": "npm:11.14.2" - "@oxc-resolver/binding-win32-ia32-msvc": "npm:11.14.2" - "@oxc-resolver/binding-win32-x64-msvc": "npm:11.14.2" + "@oxc-resolver/binding-android-arm-eabi": "npm:11.16.3" + "@oxc-resolver/binding-android-arm64": "npm:11.16.3" + "@oxc-resolver/binding-darwin-arm64": "npm:11.16.3" + "@oxc-resolver/binding-darwin-x64": "npm:11.16.3" + "@oxc-resolver/binding-freebsd-x64": "npm:11.16.3" + "@oxc-resolver/binding-linux-arm-gnueabihf": "npm:11.16.3" + "@oxc-resolver/binding-linux-arm-musleabihf": "npm:11.16.3" + "@oxc-resolver/binding-linux-arm64-gnu": "npm:11.16.3" + "@oxc-resolver/binding-linux-arm64-musl": "npm:11.16.3" + "@oxc-resolver/binding-linux-ppc64-gnu": "npm:11.16.3" + "@oxc-resolver/binding-linux-riscv64-gnu": "npm:11.16.3" + "@oxc-resolver/binding-linux-riscv64-musl": "npm:11.16.3" + "@oxc-resolver/binding-linux-s390x-gnu": "npm:11.16.3" + "@oxc-resolver/binding-linux-x64-gnu": "npm:11.16.3" + "@oxc-resolver/binding-linux-x64-musl": "npm:11.16.3" + "@oxc-resolver/binding-openharmony-arm64": "npm:11.16.3" + "@oxc-resolver/binding-wasm32-wasi": "npm:11.16.3" + "@oxc-resolver/binding-win32-arm64-msvc": "npm:11.16.3" + "@oxc-resolver/binding-win32-ia32-msvc": "npm:11.16.3" + "@oxc-resolver/binding-win32-x64-msvc": "npm:11.16.3" dependenciesMeta: "@oxc-resolver/binding-android-arm-eabi": optional: true @@ -41351,7 +41351,7 @@ __metadata: optional: true "@oxc-resolver/binding-win32-x64-msvc": optional: true - checksum: 10/f64fa5cd61700b0ba370319f87b6c5ef45b9036f2c21719a563999285493f8152d9c7922d35d07208088d3893327de91c10e65cbc4131782aeeb9f9da365ae7c + checksum: 10/2d0da140e34a74347d03a233488ad0284bbb252a6d250b7ca78d5a431d04d72b8275d896b8fb0d03846f221bcbcb2395739187d2645bf84d1d1332b74b7720be languageName: node linkType: hard From c01a13e0a9bbee1706f3b2aa31c139c9e19b69a6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 01:21:22 +0000 Subject: [PATCH 031/211] chore(deps): update dependency ldapts to v8.1.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0ab2fbb6e8..536978590d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20697,15 +20697,6 @@ __metadata: languageName: node linkType: hard -"@types/asn1@npm:>=0.2.4": - version: 0.2.4 - resolution: "@types/asn1@npm:0.2.4" - dependencies: - "@types/node": "npm:*" - checksum: 10/01fee5a896a650f92c95556c83678d66677060b5aba7936b714b5c8ca2945cc8cabf57441e323f1800a783a55958b6230d109e988ca45e83b2d7a0e071c24069 - languageName: node - linkType: hard - "@types/aws-lambda@npm:^8.10.155, @types/aws-lambda@npm:^8.10.83": version: 8.10.159 resolution: "@types/aws-lambda@npm:8.10.159" @@ -25138,7 +25129,7 @@ __metadata: languageName: node linkType: hard -"asn1@npm:0.2.6, asn1@npm:^0.2.6": +"asn1@npm:^0.2.6": version: 0.2.6 resolution: "asn1@npm:0.2.6" dependencies: @@ -28657,7 +28648,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.4.3, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -37187,15 +37178,12 @@ __metadata: linkType: hard "ldapts@npm:^8.0.6": - version: 8.0.25 - resolution: "ldapts@npm:8.0.25" + version: 8.1.3 + resolution: "ldapts@npm:8.1.3" dependencies: - "@types/asn1": "npm:>=0.2.4" - asn1: "npm:0.2.6" - debug: "npm:4.4.3" strict-event-emitter-types: "npm:2.0.0" whatwg-url: "npm:15.1.0" - checksum: 10/75fb390d5b4154b2d8c5da11e9d8123b1ae1bf50842a62a99309b90e96529939e2816c36f250dc6fb8e6f248e5da8012fe61522c058fe767f2429645649e51f5 + checksum: 10/8a1071c02e93e6d8530b4d24a5e3e6022d4b84a5b153b86f2cc34be9593ac7b3de9122a681bfc5b700a866de8bc89da23136c9649ae6c99ea4ecfbbb342edc13 languageName: node linkType: hard From 3980ac050b84795d84d7f3f8540ffb8a135bf8c6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 02:04:11 +0000 Subject: [PATCH 032/211] chore(deps): update dependency mini-css-extract-plugin to v2.10.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c42d8199cd..a8c51d6d12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39264,14 +39264,14 @@ __metadata: linkType: hard "mini-css-extract-plugin@npm:^2.4.2, mini-css-extract-plugin@npm:^2.9.2": - version: 2.9.4 - resolution: "mini-css-extract-plugin@npm:2.9.4" + version: 2.10.0 + resolution: "mini-css-extract-plugin@npm:2.10.0" dependencies: schema-utils: "npm:^4.0.0" tapable: "npm:^2.2.1" peerDependencies: webpack: ^5.0.0 - checksum: 10/24a0418dc49baed58a10a8b81e4d2fe474e89ccdc9370a7c69bd0aeeb5a70d2b4ee12f33b98c95a68423c79c1de7cc2a25aaa2105600b712e7d594cb0d56c9d4 + checksum: 10/bae5350ab82171c6c9a22a4397df14aa69280f5ff0e1ff4d2429ea841bc096927b1e27ba7b75a9c3dd77bd44bab449d6197bd748381f1326cbc8befcb10d1a9e languageName: node linkType: hard From d4b2e52c408a0b72a759790d2a48db4a27d7558e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 02:04:40 +0000 Subject: [PATCH 033/211] chore(deps): update dependency mysql2 to v3.16.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index c42d8199cd..ccef9f0db2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38089,10 +38089,10 @@ __metadata: languageName: node linkType: hard -"lru.min@npm:^1.0.0": - version: 1.1.0 - resolution: "lru.min@npm:1.1.0" - checksum: 10/af2f74d6434634e229599116040fc72e4ea42b973de45ad87bf061786894b818f3c3cc3e70e6e0cc55e2df205a68bd060407281e79dfc437a0211320e840dd61 +"lru.min@npm:^1.0.0, lru.min@npm:^1.1.0": + version: 1.1.3 + resolution: "lru.min@npm:1.1.3" + checksum: 10/b741bf51e6f2620f35f66657c31a37bb4e6aaa3d7f4dfd8a657cd4e6a4d75be881057d71f54a907a4938d5993677d92cdfa71203054a73af6978514ba73767ee languageName: node linkType: hard @@ -39895,8 +39895,8 @@ __metadata: linkType: hard "mysql2@npm:^3.0.0": - version: 3.15.3 - resolution: "mysql2@npm:3.15.3" + version: 3.16.1 + resolution: "mysql2@npm:3.16.1" dependencies: aws-ssl-profiles: "npm:^1.1.1" denque: "npm:^2.1.0" @@ -39904,10 +39904,10 @@ __metadata: iconv-lite: "npm:^0.7.0" long: "npm:^5.2.1" lru.min: "npm:^1.0.0" - named-placeholders: "npm:^1.1.3" + named-placeholders: "npm:^1.1.6" seq-queue: "npm:^0.0.5" sqlstring: "npm:^2.3.2" - checksum: 10/96fbab423afb05a9ac397c272aa1097872797e224b7d1785ac05b7e46d0884c1e613ad9e2c3082ad7973f229357f9ab5824a7f90e8c582b2094ce52027fff8e3 + checksum: 10/c4e61df1e7855161966f2efd83f08322403e876194d9bc514606e1be2a91326e94d7d18b9d398c625df91a5df1e5ba73ff44c2a54c5238e1b04c00b876072215 languageName: node linkType: hard @@ -39922,12 +39922,12 @@ __metadata: languageName: node linkType: hard -"named-placeholders@npm:^1.1.3": - version: 1.1.3 - resolution: "named-placeholders@npm:1.1.3" +"named-placeholders@npm:^1.1.6": + version: 1.1.6 + resolution: "named-placeholders@npm:1.1.6" dependencies: - lru-cache: "npm:^7.14.1" - checksum: 10/7834adc91e92ae1b9c4413384e3ccd297de5168bb44017ff0536705ddc4db421723bd964607849265feb3f6ded390f84cf138e5925f22f7c13324f87a803dc73 + lru.min: "npm:^1.1.0" + checksum: 10/959d44f2a00e87baa2c2b2d43c2be76c6e841a0ed67af4bf4a8d91c54082e2b2db9ac8461b16db413dcb133c6f34669516d755f9a1a7956851ce81196a717f65 languageName: node linkType: hard From cd43a8d16c0b4527d64d19e10db20a0742596267 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 03:06:25 +0000 Subject: [PATCH 034/211] chore(deps): update dependency pg to v8.17.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5281d40360..8c1830d736 100644 --- a/yarn.lock +++ b/yarn.lock @@ -42148,10 +42148,10 @@ __metadata: languageName: node linkType: hard -"pg-cloudflare@npm:^1.2.7": - version: 1.2.7 - resolution: "pg-cloudflare@npm:1.2.7" - checksum: 10/3d171407cbce36436c461200666ba6bd884bfe98016972760a797cec850199b8024a40055d80322c5fc02909e1533a144e9b108a99f7d7e21d0c42612f9821fb +"pg-cloudflare@npm:^1.3.0": + version: 1.3.0 + resolution: "pg-cloudflare@npm:1.3.0" + checksum: 10/04007ebbd314bdc8e5983d5d0f71a942f1915d2312ed84894d55475010559665bcbb9941d55523d36be6386cd83490dffb5b1ea98ffbbc82a140ef5dfb68ab8d languageName: node linkType: hard @@ -42162,10 +42162,10 @@ __metadata: languageName: node linkType: hard -"pg-connection-string@npm:^2.3.0, pg-connection-string@npm:^2.5.0, pg-connection-string@npm:^2.9.1": - version: 2.9.1 - resolution: "pg-connection-string@npm:2.9.1" - checksum: 10/40e9e9cd752121e72bff18d83e6c7ecda9056426815a84294de018569a319293c924704c8b7f0604fdc588835c7927647dea4f3c87a014e715bcbb17d794e9f0 +"pg-connection-string@npm:^2.10.1, pg-connection-string@npm:^2.3.0, pg-connection-string@npm:^2.5.0": + version: 2.10.1 + resolution: "pg-connection-string@npm:2.10.1" + checksum: 10/8785cfac30f64a48f47031c24d7c5c53241fabb336d5be8f30ca06af0e16aea8d9647b645b4b44ed931717a47a0afb95ddb57e60b6af103767a4d8606dec22dc languageName: node linkType: hard @@ -42183,19 +42183,19 @@ __metadata: languageName: node linkType: hard -"pg-pool@npm:^3.10.1": - version: 3.10.1 - resolution: "pg-pool@npm:3.10.1" +"pg-pool@npm:^3.11.0": + version: 3.11.0 + resolution: "pg-pool@npm:3.11.0" peerDependencies: pg: ">=8.0" - checksum: 10/b389a714be59ebe53ec412cbff513191cc0b7a203faa5d26416b6a038cafdfe30fbf1a5936b77bb76109c49bd7c4a116870a5a46a45796b1b34c96f016d7fbe2 + checksum: 10/51c77d99f17cf791333467352df8326e0f70f9c517eada65a5e7819b2422f6e655e52319f5406eb578504442ae5f399b6e1d023e41d0c199aaf82879a890db6d languageName: node linkType: hard -"pg-protocol@npm:*, pg-protocol@npm:^1.10.3": - version: 1.10.3 - resolution: "pg-protocol@npm:1.10.3" - checksum: 10/31da85319084c03f403efee7accce9786964df82a7feb60e6bd77b71f1e622c74a2a644a2bc434389d0ab92e5abdeedea69ebdb53b1897d9f01d2a1f51a8a2fe +"pg-protocol@npm:*, pg-protocol@npm:^1.11.0": + version: 1.11.0 + resolution: "pg-protocol@npm:1.11.0" + checksum: 10/a70b1b4a3fc5b1be80dfdd65c829a149b8bd9df7488f9c47e0b51c9413aec5eb6da0a9ae9812891d74cd9f2ee90c0e391984a41b64603e7375fcbb9e07070b08 languageName: node linkType: hard @@ -42213,13 +42213,13 @@ __metadata: linkType: hard "pg@npm:^8.11.3, pg@npm:^8.9.0": - version: 8.16.3 - resolution: "pg@npm:8.16.3" + version: 8.17.2 + resolution: "pg@npm:8.17.2" dependencies: - pg-cloudflare: "npm:^1.2.7" - pg-connection-string: "npm:^2.9.1" - pg-pool: "npm:^3.10.1" - pg-protocol: "npm:^1.10.3" + pg-cloudflare: "npm:^1.3.0" + pg-connection-string: "npm:^2.10.1" + pg-pool: "npm:^3.11.0" + pg-protocol: "npm:^1.11.0" pg-types: "npm:2.2.0" pgpass: "npm:1.0.5" peerDependencies: @@ -42230,7 +42230,7 @@ __metadata: peerDependenciesMeta: pg-native: optional: true - checksum: 10/6a2885a3f581d6c6dddddf5a4bb2790ee84f402ed7d73ece8b6bc102c58c17e4c5f17894c241633aa2f1d4fedd8f2401a80a9a02ef18bb57d05cbbfd8a53ca4d + checksum: 10/0f0b70f1310791b263f970f8235ab054de26a94861e73727def8df1b976ab9dda7e9c34ecffb78260fe8569521798f8e811da6369b79601023a07ea0f5e7167a languageName: node linkType: hard From 201620c6f455ad774e91f1a75716bf4db433ae58 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 04:06:32 +0000 Subject: [PATCH 035/211] chore(deps): update dependency react-aria-components to v1.14.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 1366 ++++++++++++++++++++++++++--------------------------- 1 file changed, 683 insertions(+), 683 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8c1830d736..9799465de1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10199,12 +10199,12 @@ __metadata: languageName: unknown linkType: soft -"@internationalized/date@npm:^3.10.0": - version: 3.10.0 - resolution: "@internationalized/date@npm:3.10.0" +"@internationalized/date@npm:^3.10.1": + version: 3.10.1 + resolution: "@internationalized/date@npm:3.10.1" dependencies: "@swc/helpers": "npm:^0.5.0" - checksum: 10/5940667ea2b0be54b798088ef995f787508bf4305d3741babc8b1f48de008c872555a5440ae61fa7399461a7339d3257fb8e86cc2111e72e185fc37d99e06392 + checksum: 10/f259285157e7b9930d7679cea06e09215c4f6773efd093804bd711cff22cd6f3e5afb1208237c177e752da196b10302bb8adf539e76301af49bfad0baa70565c languageName: node linkType: hard @@ -15167,139 +15167,139 @@ __metadata: languageName: node linkType: hard -"@react-aria/autocomplete@npm:3.0.0-rc.3": - version: 3.0.0-rc.3 - resolution: "@react-aria/autocomplete@npm:3.0.0-rc.3" +"@react-aria/autocomplete@npm:3.0.0-rc.4": + version: 3.0.0-rc.4 + resolution: "@react-aria/autocomplete@npm:3.0.0-rc.4" dependencies: - "@react-aria/combobox": "npm:^3.14.0" - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/listbox": "npm:^3.15.0" - "@react-aria/searchfield": "npm:^3.8.9" - "@react-aria/textfield": "npm:^3.18.2" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/autocomplete": "npm:3.0.0-beta.3" - "@react-stately/combobox": "npm:^3.12.0" - "@react-types/autocomplete": "npm:3.0.0-alpha.35" + "@react-aria/combobox": "npm:^3.14.1" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/listbox": "npm:^3.15.1" + "@react-aria/searchfield": "npm:^3.8.10" + "@react-aria/textfield": "npm:^3.18.3" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/autocomplete": "npm:3.0.0-beta.4" + "@react-stately/combobox": "npm:^3.12.1" + "@react-types/autocomplete": "npm:3.0.0-alpha.36" "@react-types/button": "npm:^3.14.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7acddd01b0f07bdbd2cf09d01a8b0fb9551d27656a92adf995411620a1599f8ff6421a72de1fcd56611d5eced49073cae48aee8127f3620fc1ca38d4ce3dcb62 + checksum: 10/3901ea60c43ff68b381a04db64577de07e46bc63e95c7cb122ace6053a0c54f58ba206704a2cd19799499f4a31ce6413ba65e8af39e675b5cedd66e9a5870729 languageName: node linkType: hard -"@react-aria/breadcrumbs@npm:^3.5.29": - version: 3.5.29 - resolution: "@react-aria/breadcrumbs@npm:3.5.29" +"@react-aria/breadcrumbs@npm:^3.5.30": + version: 3.5.30 + resolution: "@react-aria/breadcrumbs@npm:3.5.30" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/link": "npm:^3.8.6" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/link": "npm:^3.8.7" + "@react-aria/utils": "npm:^3.32.0" "@react-types/breadcrumbs": "npm:^3.7.17" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/875c2280539362e33200a9869dc52e5cec8a7407e068faa70cc190eaf266135448410acf9dd37b59bd86e53f330ac32e7598ceec468a2339cc04f18f826c4cbc + checksum: 10/f1244b6bcacb8cc3351ec2df070e2c7cec671f8beeee7bb83fbc0d062f66688c6fb9e7a1db36e4cbabb94e9349de525979a98697541e466b0d89c9cbf891a1c8 languageName: node linkType: hard -"@react-aria/button@npm:^3.14.2": - version: 3.14.2 - resolution: "@react-aria/button@npm:3.14.2" +"@react-aria/button@npm:^3.14.3": + version: 3.14.3 + resolution: "@react-aria/button@npm:3.14.3" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/toolbar": "npm:3.0.0-beta.21" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/toggle": "npm:^3.9.2" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/toolbar": "npm:3.0.0-beta.22" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/toggle": "npm:^3.9.3" "@react-types/button": "npm:^3.14.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/d6891ab39a81b349a80ac2c381a5663e33caf209d29aa536b0a8ca2963898b011913593c71f4bceb3f48a54a95cd5bc842b0ffbb33498d38a120d1a6d28f932b + checksum: 10/383046c3b4ea5972f81f56251fc19b207d6d811d7ac06db866ccd809dbcc1b72358f4b1535fc65817929496c1acb15f7c2611e4777a371333bb1b150b954ccb8 languageName: node linkType: hard -"@react-aria/calendar@npm:^3.9.2": - version: 3.9.2 - resolution: "@react-aria/calendar@npm:3.9.2" +"@react-aria/calendar@npm:^3.9.3": + version: 3.9.3 + resolution: "@react-aria/calendar@npm:3.9.3" dependencies: - "@internationalized/date": "npm:^3.10.0" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" + "@internationalized/date": "npm:^3.10.1" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/calendar": "npm:^3.9.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/calendar": "npm:^3.9.1" "@react-types/button": "npm:^3.14.1" - "@react-types/calendar": "npm:^3.8.0" + "@react-types/calendar": "npm:^3.8.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/3697b14765895c864db1a9393b86e0370cda01fab44464e9c1966150d7c793bcc9fd1e7a50d34022b0730fcaf9fd1995d431ccb1b5518eb086ccfb4b35e47ff2 + checksum: 10/32f7b1cf1c447fb8343d15b468cc79efa0206dd20f7541b7858734f7abfe562a669e0be027f89b4e365a0479eb1cd4c704cde9e03128c68a41e9d16fbde36cad languageName: node linkType: hard -"@react-aria/checkbox@npm:^3.16.2": - version: 3.16.2 - resolution: "@react-aria/checkbox@npm:3.16.2" +"@react-aria/checkbox@npm:^3.16.3": + version: 3.16.3 + resolution: "@react-aria/checkbox@npm:3.16.3" dependencies: - "@react-aria/form": "npm:^3.1.2" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/toggle": "npm:^3.12.2" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/checkbox": "npm:^3.7.2" + "@react-aria/form": "npm:^3.1.3" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/toggle": "npm:^3.12.3" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/checkbox": "npm:^3.7.3" "@react-stately/form": "npm:^3.2.2" - "@react-stately/toggle": "npm:^3.9.2" + "@react-stately/toggle": "npm:^3.9.3" "@react-types/checkbox": "npm:^3.10.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/77058d41d78b8feea4801022ba56f225b69f9b6577edc42a747dea21e4b38ea8d343f3a38455e9ae47a95a41b77e37fa0929a7136507f7b446111919f5e34478 + checksum: 10/ef7f9f08a9d2953ae5788525932bd8da5fb0bcb1423a5a5551b7ca7ae74b306d9d47b303dfb374f5a7084fcdfc49dc8d779ccd55979f5811844ad5f20931d602 languageName: node linkType: hard -"@react-aria/collections@npm:^3.0.0": - version: 3.0.0 - resolution: "@react-aria/collections@npm:3.0.0" +"@react-aria/collections@npm:^3.0.1": + version: 3.0.1 + resolution: "@react-aria/collections@npm:3.0.1" dependencies: - "@react-aria/interactions": "npm:^3.25.6" + "@react-aria/interactions": "npm:^3.26.0" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" use-sync-external-store: "npm:^1.4.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/2704d35e0cc67e8e2f7fa61f593160768a633e2ff3ced45f01806928dc15738dcd005c49ff7130c0c92fbaabf0b0624833e54b7d24a10181c7910a469a9da52b + checksum: 10/e05897ccd0c994c2a257f6c9dbe167a4d7e5fbeae053833a746870072868ae3bc3f92c7e6f30e4c0d314b1f9450c8d1ffb67600ca89f6d5914577f5cc32ca363 languageName: node linkType: hard -"@react-aria/color@npm:^3.1.2": - version: 3.1.2 - resolution: "@react-aria/color@npm:3.1.2" +"@react-aria/color@npm:^3.1.3": + version: 3.1.3 + resolution: "@react-aria/color@npm:3.1.3" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/numberfield": "npm:^3.12.2" - "@react-aria/slider": "npm:^3.8.2" - "@react-aria/spinbutton": "npm:^3.6.19" - "@react-aria/textfield": "npm:^3.18.2" - "@react-aria/utils": "npm:^3.31.0" - "@react-aria/visually-hidden": "npm:^3.8.28" - "@react-stately/color": "npm:^3.9.2" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/numberfield": "npm:^3.12.3" + "@react-aria/slider": "npm:^3.8.3" + "@react-aria/spinbutton": "npm:^3.7.0" + "@react-aria/textfield": "npm:^3.18.3" + "@react-aria/utils": "npm:^3.32.0" + "@react-aria/visually-hidden": "npm:^3.8.29" + "@react-stately/color": "npm:^3.9.3" "@react-stately/form": "npm:^3.2.2" "@react-types/color": "npm:^3.1.2" "@react-types/shared": "npm:^3.32.1" @@ -15307,166 +15307,166 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/8b2c29097e86e2fa30bb5a53777dd09a7b2b24f6fb44ac1847275345aa895bb663735dcdc94bac90ba6fee074f11fe571f7effc5ba4ac4adb0bc87f67705479c + checksum: 10/4ec6de312403d8c98b9af3676a5778a0bc5fcaaf27055390d245ec79dee781cfb70a0747e2769526844041049f702451477debc9574817bc9e32897b821f4ba4 languageName: node linkType: hard -"@react-aria/combobox@npm:^3.14.0": - version: 3.14.0 - resolution: "@react-aria/combobox@npm:3.14.0" +"@react-aria/combobox@npm:^3.14.1": + version: 3.14.1 + resolution: "@react-aria/combobox@npm:3.14.1" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/listbox": "npm:^3.15.0" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/listbox": "npm:^3.15.1" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/menu": "npm:^3.19.3" - "@react-aria/overlays": "npm:^3.30.0" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/textfield": "npm:^3.18.2" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/menu": "npm:^3.19.4" + "@react-aria/overlays": "npm:^3.31.0" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/textfield": "npm:^3.18.3" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/collections": "npm:^3.12.8" - "@react-stately/combobox": "npm:^3.12.0" + "@react-stately/combobox": "npm:^3.12.1" "@react-stately/form": "npm:^3.2.2" "@react-types/button": "npm:^3.14.1" - "@react-types/combobox": "npm:^3.13.9" + "@react-types/combobox": "npm:^3.13.10" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/e583a99133b884e49b5848271a18d3dcbc6fdb4fb9aa9477d2164790b7386ada3316f6a4c19af3e8d989ed9c6207af12e2377c2f1644e8114afd916697a26c33 + checksum: 10/66348b7688b2cfe9c03eca3f0c72b7f1ce852859517b0661b094eb64bda255697df612a9feca550ac4d5b97d63ba291e57c37935604cfa1262cbfc9189046758 languageName: node linkType: hard -"@react-aria/datepicker@npm:^3.15.2": - version: 3.15.2 - resolution: "@react-aria/datepicker@npm:3.15.2" +"@react-aria/datepicker@npm:^3.15.3": + version: 3.15.3 + resolution: "@react-aria/datepicker@npm:3.15.3" dependencies: - "@internationalized/date": "npm:^3.10.0" + "@internationalized/date": "npm:^3.10.1" "@internationalized/number": "npm:^3.6.5" "@internationalized/string": "npm:^3.2.7" - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/form": "npm:^3.1.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/spinbutton": "npm:^3.6.19" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/datepicker": "npm:^3.15.2" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/form": "npm:^3.1.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/spinbutton": "npm:^3.7.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/datepicker": "npm:^3.15.3" "@react-stately/form": "npm:^3.2.2" "@react-types/button": "npm:^3.14.1" - "@react-types/calendar": "npm:^3.8.0" - "@react-types/datepicker": "npm:^3.13.2" + "@react-types/calendar": "npm:^3.8.1" + "@react-types/datepicker": "npm:^3.13.3" "@react-types/dialog": "npm:^3.5.22" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/05f9b7bda5e5aa304ea4eebfded8040ea040b12284f2aa67f6b2a9e5458526a1e8dfbe077a53989a8633debb8184123e0907186f5b511c5a4c85a48f4c755f4b + checksum: 10/86e4bd36111421f040ac1d5940d48cb4f3d8bdf5d1246da2adfaa6a6817620575040134d0d280cdeff11f46dc281ea1c5af55781a978414aef321ea770f70f1f languageName: node linkType: hard -"@react-aria/dialog@npm:^3.5.31": - version: 3.5.31 - resolution: "@react-aria/dialog@npm:3.5.31" +"@react-aria/dialog@npm:^3.5.32": + version: 3.5.32 + resolution: "@react-aria/dialog@npm:3.5.32" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/overlays": "npm:^3.30.0" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/overlays": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/dialog": "npm:^3.5.22" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/ea5d197b13d1441b45b5288382f9f25cf4ecec07ea6c8e989ce62b5f0ce2eaa7e6afe7e40fc808451b040eda2c41aa64f8e510d5205b26a5a07324b86468e53d + checksum: 10/015d5637ab99d1b751209d743557e638d81533032e4948cc46d5041503508e1f6cda284f6212395dc157d6b58af07d43e5fdc79490153d4fbab3aa15c418dd68 languageName: node linkType: hard -"@react-aria/disclosure@npm:^3.1.0": - version: 3.1.0 - resolution: "@react-aria/disclosure@npm:3.1.0" +"@react-aria/disclosure@npm:^3.1.1": + version: 3.1.1 + resolution: "@react-aria/disclosure@npm:3.1.1" dependencies: "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/disclosure": "npm:^3.0.8" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/disclosure": "npm:^3.0.9" "@react-types/button": "npm:^3.14.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/34f5a0b5c7130fc686025f8015cefe219b84ed153dbca93ee4b47b2f598ca833ed161426dc76d2f09ee480b88741df046fcbb600516eb77b919afcb80b1b61a3 + checksum: 10/8b9d60450e5815031ec32f58c7962588b7ab1ab80606cf5a169a47dd23c320e856dd3426600ff8b6717c194888d8ddb483488de033efa651b1fdfffe47cec524 languageName: node linkType: hard -"@react-aria/dnd@npm:^3.11.3": - version: 3.11.3 - resolution: "@react-aria/dnd@npm:3.11.3" +"@react-aria/dnd@npm:^3.11.4": + version: 3.11.4 + resolution: "@react-aria/dnd@npm:3.11.4" dependencies: "@internationalized/string": "npm:^3.2.7" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/overlays": "npm:^3.30.0" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/overlays": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/collections": "npm:^3.12.8" - "@react-stately/dnd": "npm:^3.7.1" + "@react-stately/dnd": "npm:^3.7.2" "@react-types/button": "npm:^3.14.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/48c58ab1fdb0e4dfce2e5933b580df3e46c0407535a28d2434fc6921d049bde45d0b3b94b7f3f4775e683e77c4a5b17ccfd9894c345230b7f49c7c0742663dce + checksum: 10/34edadce52664faaa7cc9a63eef1340e4ac7d62b3447818965793e2f0537a6630acaf530a4c8dd4026102e281f5157c43d29a0d8b7a14bc5ea0641bcddb8edb3 languageName: node linkType: hard -"@react-aria/focus@npm:^3.21.2": - version: 3.21.2 - resolution: "@react-aria/focus@npm:3.21.2" +"@react-aria/focus@npm:^3.21.3": + version: 3.21.3 + resolution: "@react-aria/focus@npm:3.21.3" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" clsx: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/4bce20d956c24ab08b707e84896afafd66b3496971efb6dd611dae1c4c1f47a5c99c786a96b2fd0eb083a7c9ba5368ac04ce3a937cc48fdcc8bb85f81e7a3098 + checksum: 10/a69c20b0c95364672f9f201829bf4201d9ef01ea628579b6cf6762b777e05fdbbbd234ab60fba03e313543d79bc13c2d812120b415d3f4680214e2dc2df5a5a6 languageName: node linkType: hard -"@react-aria/form@npm:^3.1.2": - version: 3.1.2 - resolution: "@react-aria/form@npm:3.1.2" +"@react-aria/form@npm:^3.1.3": + version: 3.1.3 + resolution: "@react-aria/form@npm:3.1.3" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/form": "npm:^3.2.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/140d8cb830fab6fc4d885e79fbdfc92f06af43134b6ab68ec93b58fec25220b823b58698bd9bbd574bc21d1c0d443813df89b44f5ea33b5bcd314128560bd4fd + checksum: 10/4dbbe0e78c81feef83bd720c2c441e220e51f99d038639fac5b00014739ac892b40b055649735993c2e41d5f731194c16d82112bda90a28a5abc406201e071b0 languageName: node linkType: hard -"@react-aria/grid@npm:^3.14.5": - version: 3.14.5 - resolution: "@react-aria/grid@npm:3.14.5" +"@react-aria/grid@npm:^3.14.6": + version: 3.14.6 + resolution: "@react-aria/grid@npm:3.14.6" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/collections": "npm:^3.12.8" - "@react-stately/grid": "npm:^3.11.6" - "@react-stately/selection": "npm:^3.20.6" + "@react-stately/grid": "npm:^3.11.7" + "@react-stately/selection": "npm:^3.20.7" "@react-types/checkbox": "npm:^3.10.2" "@react-types/grid": "npm:^3.3.6" "@react-types/shared": "npm:^3.32.1" @@ -15474,128 +15474,128 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7e8731b55366b05f619259fbeb149b63d7c17158e25a2ebaacbe3368f50e71f054e4cef3a943ca6076d3ebf0242cfcb155ff276ab984a7495dc46f6b5b13f1d2 + checksum: 10/a62695e47044de335628a3802afa05d72e75b9510c2d51d7f20f72875ece717ff411d75f0f0286fb286f222decc7870b885ef82522598a41b5b78775aaedccdf languageName: node linkType: hard -"@react-aria/gridlist@npm:^3.14.1": - version: 3.14.1 - resolution: "@react-aria/gridlist@npm:3.14.1" +"@react-aria/gridlist@npm:^3.14.2": + version: 3.14.2 + resolution: "@react-aria/gridlist@npm:3.14.2" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/grid": "npm:^3.14.5" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/list": "npm:^3.13.1" - "@react-stately/tree": "npm:^3.9.3" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/grid": "npm:^3.14.6" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/list": "npm:^3.13.2" + "@react-stately/tree": "npm:^3.9.4" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/1d7ff55a869a3157edd266b5d14a9e1380561bcb8d949fdd43e983e64932ec251aa7221e6156e93ea977b8942c5b9b8f9aae5f04ebc2ed27eef9be2b7e00b5ab + checksum: 10/d9ddc1744386f33dddfd66fa664054ffaaeb987a1af7ffe36bee971ac3f07b076e7bdee63d01510f7d5a69a2df0f22eae1578dfd428bc024e3a2a7d848716da4 languageName: node linkType: hard -"@react-aria/i18n@npm:^3.12.13": - version: 3.12.13 - resolution: "@react-aria/i18n@npm:3.12.13" +"@react-aria/i18n@npm:^3.12.14": + version: 3.12.14 + resolution: "@react-aria/i18n@npm:3.12.14" dependencies: - "@internationalized/date": "npm:^3.10.0" + "@internationalized/date": "npm:^3.10.1" "@internationalized/message": "npm:^3.1.8" "@internationalized/number": "npm:^3.6.5" "@internationalized/string": "npm:^3.2.7" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/a5464213e626c60d63e0dbab84907ab452c53022a27a5c482c8f86f23c96b53522e52005c5cfc829f388c807eaf246cded69d5bee24b9e0fc08f34f9b0359e82 + checksum: 10/87b10d152fc104892621ad344c536fd5308fb3be5954bd9ef7a4cd58fde3ada75feb4f9ed0cb08abd93eb4ec7b43485c7954640bf6cc06a899547e1fbebf594a languageName: node linkType: hard -"@react-aria/interactions@npm:^3.25.6": - version: 3.25.6 - resolution: "@react-aria/interactions@npm:3.25.6" +"@react-aria/interactions@npm:^3.26.0": + version: 3.26.0 + resolution: "@react-aria/interactions@npm:3.26.0" dependencies: "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/flags": "npm:^3.1.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/fd30e14107d919be25909ca8f69fb99d88aa1f1ead523717a390a40be6d774e40de47dd7f9fcc7e6d66e0f4abc12238001a10d2128cbd7af9b141641cacf6da4 + checksum: 10/181383a7b029ed32436c25d2212f800c89e85f10eea56f226b49338056d7197b6bc3db593d357e668aa22ee61f04a30e647265a259ff9a465003f79bca7a2abb languageName: node linkType: hard -"@react-aria/label@npm:^3.7.22": - version: 3.7.22 - resolution: "@react-aria/label@npm:3.7.22" +"@react-aria/label@npm:^3.7.23": + version: 3.7.23 + resolution: "@react-aria/label@npm:3.7.23" dependencies: - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/2bbfca230465057230597fd23f47ebe591bb5307ff7e37a082a4922e132f1310b69a06dcb4905c356b674b159bd09c2ca690e0526d9f303bed8f66eeb8a61b13 + checksum: 10/19b2a4649e7913ea3f9b6eb8e48e9b607c3659f07c630d94446e97157fabdc24d7973fcbd960af1fa0401c8d0163ca24b8b1af5f6f3a31ff8657cf03a61d9d77 languageName: node linkType: hard -"@react-aria/landmark@npm:^3.0.7": - version: 3.0.7 - resolution: "@react-aria/landmark@npm:3.0.7" +"@react-aria/landmark@npm:^3.0.8": + version: 3.0.8 + resolution: "@react-aria/landmark@npm:3.0.8" dependencies: - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" use-sync-external-store: "npm:^1.4.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/b86cbd8356fa08e01318644082b9ef948cb3d61695c3dfdb5bdc67a59ce3e3f774c91bf62795280c7ac620b87f9d7e66635c7e02a2c2eb2663b3e2b6a77a448a + checksum: 10/7711871d004bbce5d8fadec3d81350cb9ce1896b01e4cc5f247003fedb9b785d3ca24947ce3f688ca5a8145004dd2cbbf43f1ac44e884c8ed6a950fd40a9e9f2 languageName: node linkType: hard -"@react-aria/link@npm:^3.8.6": - version: 3.8.6 - resolution: "@react-aria/link@npm:3.8.6" +"@react-aria/link@npm:^3.8.7": + version: 3.8.7 + resolution: "@react-aria/link@npm:3.8.7" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/link": "npm:^3.6.5" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7f0fd02e7eb5c69ba5e280c57b0e7ec6568fbca231159f591201f9d753bd2f985613d0f75a54465f292a617684a55d8f5f10585538ce1fbc3f9f29fb3d339529 + checksum: 10/25e35bcf096bf96965cd29fbf8eabaeea14222ee0a1a94bf3d146164cb5f12197d0fb4bbabe8d77aaa61703669d549a77ac2c1b658f7bbc375552d25d9d17d54 languageName: node linkType: hard -"@react-aria/listbox@npm:^3.15.0": - version: 3.15.0 - resolution: "@react-aria/listbox@npm:3.15.0" +"@react-aria/listbox@npm:^3.15.1": + version: 3.15.1 + resolution: "@react-aria/listbox@npm:3.15.1" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/collections": "npm:^3.12.8" - "@react-stately/list": "npm:^3.13.1" + "@react-stately/list": "npm:^3.13.2" "@react-types/listbox": "npm:^3.7.4" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/e361b11ab4ae521a3e4d41ea811f15e2863b97aa9e65c19eef06c38b9f378191c34dfedf545d1ea4667aa0b89aa157712ed6f78950d71cfc712643666cc59d7a + checksum: 10/af7717b65d49d9751e0973a1a6ad27a500d3ae5241ff81ef9c94495a21e17e8c4693f3ed119ad4fc72411933b35c2bc377a7a61a43590df05212a02f3c6c23bf languageName: node linkType: hard @@ -15608,20 +15608,20 @@ __metadata: languageName: node linkType: hard -"@react-aria/menu@npm:^3.19.3": - version: 3.19.3 - resolution: "@react-aria/menu@npm:3.19.3" +"@react-aria/menu@npm:^3.19.4": + version: 3.19.4 + resolution: "@react-aria/menu@npm:3.19.4" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/overlays": "npm:^3.30.0" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/overlays": "npm:^3.31.0" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/collections": "npm:^3.12.8" - "@react-stately/menu": "npm:^3.9.8" - "@react-stately/selection": "npm:^3.20.6" - "@react-stately/tree": "npm:^3.9.3" + "@react-stately/menu": "npm:^3.9.9" + "@react-stately/selection": "npm:^3.20.7" + "@react-stately/tree": "npm:^3.9.4" "@react-types/button": "npm:^3.14.1" "@react-types/menu": "npm:^3.10.5" "@react-types/shared": "npm:^3.32.1" @@ -15629,58 +15629,58 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/04987d1fae12979335a755093b74e0a9c61d2bf1f207111bca2a8513ea43962b282315976d33e5f06d3e2e1fc1927e2f9030365c4e4a26024e156ebcb88a8d9c + checksum: 10/be6e508f7484582e179582c60374387e1dde1b1fc6154bffeff50ea7eddff819cd6a13c9938ae88bd98e5c503a19da793b93303255916c224028c1093b4452ce languageName: node linkType: hard -"@react-aria/meter@npm:^3.4.27": - version: 3.4.27 - resolution: "@react-aria/meter@npm:3.4.27" +"@react-aria/meter@npm:^3.4.28": + version: 3.4.28 + resolution: "@react-aria/meter@npm:3.4.28" dependencies: - "@react-aria/progress": "npm:^3.4.27" + "@react-aria/progress": "npm:^3.4.28" "@react-types/meter": "npm:^3.4.13" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/b79bc0f2a96b53ec427a25315cdec4a50dd1bc1cb1fbff9f84847c0bb2597851a514ecbbf4b0fbb18b165bc007142ad45bd432641301b8db990e09f281563d39 + checksum: 10/5cc4bba8199ec039213b3ac568180e2be58521ff48e7aa65be571ca656ab7dd27afdcaaf22b87f4260c288b44b3c65abff8c3b95f5b9f6264d6a65b51908f000 languageName: node linkType: hard -"@react-aria/numberfield@npm:^3.12.2": - version: 3.12.2 - resolution: "@react-aria/numberfield@npm:3.12.2" +"@react-aria/numberfield@npm:^3.12.3": + version: 3.12.3 + resolution: "@react-aria/numberfield@npm:3.12.3" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/spinbutton": "npm:^3.6.19" - "@react-aria/textfield": "npm:^3.18.2" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/spinbutton": "npm:^3.7.0" + "@react-aria/textfield": "npm:^3.18.3" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/form": "npm:^3.2.2" - "@react-stately/numberfield": "npm:^3.10.2" + "@react-stately/numberfield": "npm:^3.10.3" "@react-types/button": "npm:^3.14.1" - "@react-types/numberfield": "npm:^3.8.15" + "@react-types/numberfield": "npm:^3.8.16" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7ec498b0d3bf6a55e891e61d1693a6395a3e7b89a04d81af433a0cb3dd673603ce5dd7f861d39658a5fe6c0cd48356c8120c39b9a4495563d9b13af0f8c0b3ca + checksum: 10/6e4bf8ac54d6d62170988f66dd6d7be20b7c34c07275b5b5b357d79b52d397f5af3486135c3414f028a642a5b50ae87bba9a37c280dd5d42bf2daad75a348a98 languageName: node linkType: hard -"@react-aria/overlays@npm:^3.30.0": - version: 3.30.0 - resolution: "@react-aria/overlays@npm:3.30.0" +"@react-aria/overlays@npm:^3.31.0": + version: 3.31.0 + resolution: "@react-aria/overlays@npm:3.31.0" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/utils": "npm:^3.31.0" - "@react-aria/visually-hidden": "npm:^3.8.28" - "@react-stately/overlays": "npm:^3.6.20" + "@react-aria/utils": "npm:^3.32.0" + "@react-aria/visually-hidden": "npm:^3.8.29" + "@react-stately/overlays": "npm:^3.6.21" "@react-types/button": "npm:^3.14.1" "@react-types/overlays": "npm:^3.9.2" "@react-types/shared": "npm:^3.32.1" @@ -15688,56 +15688,56 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/32c25162b55078cf18ca6f514065d97982a9447b1ec9d75fda4db69fa909cd5dcc3d5b4c8917edecce7d8dd5b20bc51c3862945110c0badef021c86878e8fb4d + checksum: 10/85d345aea71e9fdc4491afbc1c1f3f052548eec2cbb7ee7cee76ae3df659225f7edf22c03db08c78165abcefc28eb19d31875754fa1662d07672393bdd81dca1 languageName: node linkType: hard -"@react-aria/progress@npm:^3.4.27": - version: 3.4.27 - resolution: "@react-aria/progress@npm:3.4.27" +"@react-aria/progress@npm:^3.4.28": + version: 3.4.28 + resolution: "@react-aria/progress@npm:3.4.28" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/utils": "npm:^3.32.0" "@react-types/progress": "npm:^3.5.16" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7b0ac3c2da6205096e94b4d72a2b2b0250f864cace05b562e49b86986dbebbab633e62fcc39ffa915974318de37d5f6256b1f584d8e33ebca70e564d32b3aca2 + checksum: 10/618e8281582a718ff92341716cda6415176725257d6a49f69de1986662344cdcda6de0bb2d5c91fc463bccba287a1ce9163e059d4fe57d93e5f7f8ad5aef5c2e languageName: node linkType: hard -"@react-aria/radio@npm:^3.12.2": - version: 3.12.2 - resolution: "@react-aria/radio@npm:3.12.2" +"@react-aria/radio@npm:^3.12.3": + version: 3.12.3 + resolution: "@react-aria/radio@npm:3.12.3" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/form": "npm:^3.1.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/radio": "npm:^3.11.2" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/form": "npm:^3.1.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/radio": "npm:^3.11.3" "@react-types/radio": "npm:^3.9.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/709bdd0515e797f2bf37dc17f55e46103aa918a4549880c15ecca7c03adc0716e5650376b1cc877437b12e5df4e3050138699707e4ec9fa611614a1e38b98eb0 + checksum: 10/6d16727fd8cc226a509c66e4a596691b4ca2a1d9012c56f982cb2c9b4c7db7f01f18473c6a7fc30d6d26fa94a0956a6ba6ef394640616312a496c4cf9d0d5f38 languageName: node linkType: hard -"@react-aria/searchfield@npm:^3.8.9": - version: 3.8.9 - resolution: "@react-aria/searchfield@npm:3.8.9" +"@react-aria/searchfield@npm:^3.8.10": + version: 3.8.10 + resolution: "@react-aria/searchfield@npm:3.8.10" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/textfield": "npm:^3.18.2" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/searchfield": "npm:^3.5.16" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/textfield": "npm:^3.18.3" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/searchfield": "npm:^3.5.17" "@react-types/button": "npm:^3.14.1" "@react-types/searchfield": "npm:^3.6.6" "@react-types/shared": "npm:^3.32.1" @@ -15745,100 +15745,100 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/a088f7b1c434aea1d4603acb34e591c8f11a1532a41407b68336933a73e1e5f55b07bb6323642af5ebeebe39756e1e60943487d77d69882409c491fd816e86f4 + checksum: 10/54d0fa2eddb4cdccb3587f3b16b9430bd0df8dfe6d8b2f432e8ef724728bdcc700ffebe49f6edb47747ec00fda8c1d74d7a6257f3095527b89959da707009b73 languageName: node linkType: hard -"@react-aria/select@npm:^3.17.0": - version: 3.17.0 - resolution: "@react-aria/select@npm:3.17.0" +"@react-aria/select@npm:^3.17.1": + version: 3.17.1 + resolution: "@react-aria/select@npm:3.17.1" dependencies: - "@react-aria/form": "npm:^3.1.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/listbox": "npm:^3.15.0" - "@react-aria/menu": "npm:^3.19.3" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" - "@react-aria/visually-hidden": "npm:^3.8.28" - "@react-stately/select": "npm:^3.8.0" + "@react-aria/form": "npm:^3.1.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/listbox": "npm:^3.15.1" + "@react-aria/menu": "npm:^3.19.4" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-aria/visually-hidden": "npm:^3.8.29" + "@react-stately/select": "npm:^3.9.0" "@react-types/button": "npm:^3.14.1" - "@react-types/select": "npm:^3.11.0" + "@react-types/select": "npm:^3.12.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/e986470eb5d8c675fbd5753e0051827c94dad00c5472015d4744e4e9233e7a4ff97f4634e94c5686ef9f65f2b6ffa894b24f3a07bde0437de6fe5802901969cd + checksum: 10/6e9ca9ab204605b75f8948f27055657965a8da3b0867aec1e2e9fd6f1c505c83409670c6a8a35daab0e519caefe7400fa7f1e411d7b9f968a92308c4d6929738 languageName: node linkType: hard -"@react-aria/selection@npm:^3.26.0": - version: 3.26.0 - resolution: "@react-aria/selection@npm:3.26.0" +"@react-aria/selection@npm:^3.27.0": + version: 3.27.0 + resolution: "@react-aria/selection@npm:3.27.0" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/selection": "npm:^3.20.6" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/selection": "npm:^3.20.7" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/6c3c59993c719cfdfd42ef9c753212e7bddf30c348117726e8abfe977876f583e0d0b6fa4ce73e46f1fa53e79e42f0866a333e257cef400c4a24bd5d1c81c379 + checksum: 10/48e9127e511861f2627cc3331fc78c6f68a2a88c4ce1ed6da2f1c0ed782da3da5b631feca96182b1a2d1fcafe5dd66f4e1ec0d7b9fc059834656704cacf657a2 languageName: node linkType: hard -"@react-aria/separator@npm:^3.4.13": - version: 3.4.13 - resolution: "@react-aria/separator@npm:3.4.13" +"@react-aria/separator@npm:^3.4.14": + version: 3.4.14 + resolution: "@react-aria/separator@npm:3.4.14" dependencies: - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/4326c36d002f3931c8c717b7aacbe30943d42ca190fe400a151d8f480dd65359a0bc9192da33213d4e8215bbfd47436de07fedfe957e83be4101a926d116862e + checksum: 10/4767b1fa34b7e114fc0d1cea0730a67f10d4bf04d9ae8d9a68e94e795df9c37987000c04e0d0aced808c4a44d98e14ea71040694a6ebdf30dfdda20f2a382be3 languageName: node linkType: hard -"@react-aria/slider@npm:^3.8.2": - version: 3.8.2 - resolution: "@react-aria/slider@npm:3.8.2" +"@react-aria/slider@npm:^3.8.3": + version: 3.8.3 + resolution: "@react-aria/slider@npm:3.8.3" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/slider": "npm:^3.7.2" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/slider": "npm:^3.7.3" "@react-types/shared": "npm:^3.32.1" "@react-types/slider": "npm:^3.8.2" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/3b9115eaf61f722929dcb739cd910b9e84f04644fdce8add21bc0f9305a3e7d0948c2e417afe424783b22738d52060505a32187aba1e7aca19dd572ebb6d10ed + checksum: 10/a2071cee32abd00653f77707f5d56cb163e770e66074504731df0c14183930673de39891b25b7b6f44f84b4941e12d9195bfd431a92bcfe8799424e6d3046600 languageName: node linkType: hard -"@react-aria/spinbutton@npm:^3.6.19": - version: 3.6.19 - resolution: "@react-aria/spinbutton@npm:3.6.19" +"@react-aria/spinbutton@npm:^3.7.0": + version: 3.7.0 + resolution: "@react-aria/spinbutton@npm:3.7.0" dependencies: - "@react-aria/i18n": "npm:^3.12.13" + "@react-aria/i18n": "npm:^3.12.14" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/button": "npm:^3.14.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/9b87a1c87a09ddde141bb23e8e3ce1b1137cb1931df848d6fd3bbcd0773b3579045413b725686629da02c22733ad5b5dfc57e06c5fefaf07f8cfb8c4e2334997 + checksum: 10/11dc1997f0e2fb345bf07b8669b7bfcd46cdcc739aa047dfa3d2fbc8a53e452f9e31c3fcd1d1b564c9c79a9629350d695a172fe0c9c3a13b21ed54f411ffb4e7 languageName: node linkType: hard @@ -15853,36 +15853,36 @@ __metadata: languageName: node linkType: hard -"@react-aria/switch@npm:^3.7.8": - version: 3.7.8 - resolution: "@react-aria/switch@npm:3.7.8" +"@react-aria/switch@npm:^3.7.9": + version: 3.7.9 + resolution: "@react-aria/switch@npm:3.7.9" dependencies: - "@react-aria/toggle": "npm:^3.12.2" - "@react-stately/toggle": "npm:^3.9.2" + "@react-aria/toggle": "npm:^3.12.3" + "@react-stately/toggle": "npm:^3.9.3" "@react-types/shared": "npm:^3.32.1" "@react-types/switch": "npm:^3.5.15" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/2a9cc90fb6f9593c8b41517d83e4a2e05f77ba544ee41d5beeb865329863ca70afa5beec94230512d3eab44d62f0c0c25110323ba89745ba64e05bee0f34c4ec + checksum: 10/d03fc7c082253fb84bd7949705a9672b87876710c5fd73df987b80753e749f42955822adf8f4ba2b586543848574743dd69ff89461bc994ff4156e03a2003a2f languageName: node linkType: hard -"@react-aria/table@npm:^3.17.8": - version: 3.17.8 - resolution: "@react-aria/table@npm:3.17.8" +"@react-aria/table@npm:^3.17.9": + version: 3.17.9 + resolution: "@react-aria/table@npm:3.17.9" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/grid": "npm:^3.14.5" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/grid": "npm:^3.14.6" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/utils": "npm:^3.31.0" - "@react-aria/visually-hidden": "npm:^3.8.28" + "@react-aria/utils": "npm:^3.32.0" + "@react-aria/visually-hidden": "npm:^3.8.29" "@react-stately/collections": "npm:^3.12.8" "@react-stately/flags": "npm:^3.1.2" - "@react-stately/table": "npm:^3.15.1" + "@react-stately/table": "npm:^3.15.2" "@react-types/checkbox": "npm:^3.10.2" "@react-types/grid": "npm:^3.3.6" "@react-types/shared": "npm:^3.32.1" @@ -15891,78 +15891,78 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/39281b279c2efea9342877e4e2cb1af38f776a7349bcbe6e6daabf281e01445c28b8c7cd38c4f3cfeaea42a126552fd332fc242abd1e5e009c9252e3f1b88512 + checksum: 10/daa03e2eb669034ac8c40f5cae1215a9b7ac059f249f5cd64d3fe541546ac10fc14d44bdb00b02f2bd2fb078a0218e1d08a74f1322713605c5c6fed416770722 languageName: node linkType: hard -"@react-aria/tabs@npm:^3.10.8": - version: 3.10.8 - resolution: "@react-aria/tabs@npm:3.10.8" +"@react-aria/tabs@npm:^3.10.9": + version: 3.10.9 + resolution: "@react-aria/tabs@npm:3.10.9" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/tabs": "npm:^3.8.6" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/tabs": "npm:^3.8.7" "@react-types/shared": "npm:^3.32.1" - "@react-types/tabs": "npm:^3.3.19" + "@react-types/tabs": "npm:^3.3.20" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/eae72fb6636ae1352ceec509605d35018d96f66aacab896748697da9f6b1e166c7bff88a0ffcfdee2869f653464df401d857906f44bdce5d5b9887b75f533287 + checksum: 10/c685ae601dd7bd1e3a69c0d066cf1dc06d8105d14f12056909b66d5bb36ef1697048fbb0f0c48d653e71904f512533510a13c590f23bba39bc3322e458a6c636 languageName: node linkType: hard -"@react-aria/tag@npm:^3.7.2": - version: 3.7.2 - resolution: "@react-aria/tag@npm:3.7.2" +"@react-aria/tag@npm:^3.7.3": + version: 3.7.3 + resolution: "@react-aria/tag@npm:3.7.3" dependencies: - "@react-aria/gridlist": "npm:^3.14.1" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/list": "npm:^3.13.1" + "@react-aria/gridlist": "npm:^3.14.2" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/list": "npm:^3.13.2" "@react-types/button": "npm:^3.14.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/757daed07380eee13a05b1ba0b72e062db689526dd67087cd34726a6003b4791cb450c9765be3ea32208ce1a430903db3885d07700cb8775cd8bcd97b8124cb4 + checksum: 10/e9be2003bf7ec6fbb4956be12098089be184b1eba05f2c3fd9fe100c7c7b93959090020f96b1967484264a9ec24731d8d7b03c160f3c17e57c9effe73a320a9f languageName: node linkType: hard -"@react-aria/textfield@npm:^3.18.2": - version: 3.18.2 - resolution: "@react-aria/textfield@npm:3.18.2" +"@react-aria/textfield@npm:^3.18.3": + version: 3.18.3 + resolution: "@react-aria/textfield@npm:3.18.3" dependencies: - "@react-aria/form": "npm:^3.1.2" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/form": "npm:^3.1.3" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/form": "npm:^3.2.2" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/shared": "npm:^3.32.1" "@react-types/textfield": "npm:^3.12.6" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/2713e3c42f502ecd1d8a505b01c404a6531d1450d8c422a12ac477d1d45dd5e11e9da48807e75c4915948f579969b4b894f1dda14d87ce3790ab67659b20a396 + checksum: 10/3983663883915e1aafd61caad890811be61de5ff0d6310dae01d9cb8c4abc2915a9b3604fc5817b2559f7ec561105f8afb544d07e38835138ed070217aeada98 languageName: node linkType: hard -"@react-aria/toast@npm:^3.0.8": - version: 3.0.8 - resolution: "@react-aria/toast@npm:3.0.8" +"@react-aria/toast@npm:^3.0.9": + version: 3.0.9 + resolution: "@react-aria/toast@npm:3.0.9" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/landmark": "npm:^3.0.7" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/landmark": "npm:^3.0.8" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/toast": "npm:^3.1.2" "@react-types/button": "npm:^3.14.1" "@react-types/shared": "npm:^3.32.1" @@ -15970,125 +15970,125 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/b8b21566723e9038f15517934c45c3e6406f28d476a682421aee270f01852e8a46f0170af13c3a5c8e80926169ebd7802d5def0115876e395dedad4fc9409f49 + checksum: 10/f63cf143eec3e763ddd2885172685c6b85aaac1fb4423ced81aeceba06121788664909bfde862815e2562a7acd03c54df9a83e3fd0ddc60885806f76df30d2bf languageName: node linkType: hard -"@react-aria/toggle@npm:^3.12.2": - version: 3.12.2 - resolution: "@react-aria/toggle@npm:3.12.2" +"@react-aria/toggle@npm:^3.12.3": + version: 3.12.3 + resolution: "@react-aria/toggle@npm:3.12.3" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/toggle": "npm:^3.9.2" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/toggle": "npm:^3.9.3" "@react-types/checkbox": "npm:^3.10.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/f6b9ce00d7a6bb0e7b6123826761cb7f40b9c7736ce8edc14b39f1865cd9165c086da8a3f6e27ddae8c8557f54f421bb0b4761c455dae81ed037a765b84b5cb7 + checksum: 10/78f48f6024c864b8ea3d7a23f8ecfaaa7af625a470b229b080338262d9934c849be99f323180730c931da76326241aa4ef94b362c74bdbcbfc6fc71848e67300 languageName: node linkType: hard -"@react-aria/toolbar@npm:3.0.0-beta.21": - version: 3.0.0-beta.21 - resolution: "@react-aria/toolbar@npm:3.0.0-beta.21" +"@react-aria/toolbar@npm:3.0.0-beta.22": + version: 3.0.0-beta.22 + resolution: "@react-aria/toolbar@npm:3.0.0-beta.22" dependencies: - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/ada5f50c4409997d46e4af0b739f1fada6d3c6ac0bdd3a7eef7ad2a440a53eaeebbd0685d19deb4b7c21832882dbe27a5a8162bf89135d2c930fbaf4bb5e0365 + checksum: 10/587ddaff3b11e58e4d0fb0cef59d8d7fc0481eb43f473c3cbaf6fe174a5e9ff52323636f767e2ada6b41e74ae73bab0a816846232ec1e33f35709cf91abcf49c languageName: node linkType: hard -"@react-aria/tooltip@npm:^3.8.8": - version: 3.8.8 - resolution: "@react-aria/tooltip@npm:3.8.8" +"@react-aria/tooltip@npm:^3.9.0": + version: 3.9.0 + resolution: "@react-aria/tooltip@npm:3.9.0" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/tooltip": "npm:^3.5.8" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/tooltip": "npm:^3.5.9" "@react-types/shared": "npm:^3.32.1" - "@react-types/tooltip": "npm:^3.4.21" + "@react-types/tooltip": "npm:^3.5.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/eddb4736b3813b26fdb21ce07cef170215a900a233d859605ad17a2c72c66ca8cc2fe105d3920f9b24fce0eb18e050551268dc25a9c5d4b987b7eca1e60b24e9 + checksum: 10/7c29f50c25ea4242efceaed6ec4c4183711c444874edbb69e4d96233482e62396ddc86e948cd46f2e8f3cd02f1a5fb9539b2c40e529a1fb41314ffdeb81ea2db languageName: node linkType: hard -"@react-aria/tree@npm:^3.1.4": - version: 3.1.4 - resolution: "@react-aria/tree@npm:3.1.4" +"@react-aria/tree@npm:^3.1.5": + version: 3.1.5 + resolution: "@react-aria/tree@npm:3.1.5" dependencies: - "@react-aria/gridlist": "npm:^3.14.1" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/utils": "npm:^3.31.0" - "@react-stately/tree": "npm:^3.9.3" + "@react-aria/gridlist": "npm:^3.14.2" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/utils": "npm:^3.32.0" + "@react-stately/tree": "npm:^3.9.4" "@react-types/button": "npm:^3.14.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/ed3e48d011b846026b4c3bf78a96fbe91ff892d995a94e94ff6f5218dcbd6736660507dee1e06aac9ce48f9f66fdc8898cf8486192cecad428242b5b4930fde8 + checksum: 10/7af49904f33d80bc7cfc5d60cded17737034518a961b39fbfe492da6c9683bf290de5f4c0f960ee4176631dc129928811f57e5510ce0fef5cb7cbdd3fc663ca4 languageName: node linkType: hard -"@react-aria/utils@npm:^3.31.0": - version: 3.31.0 - resolution: "@react-aria/utils@npm:3.31.0" +"@react-aria/utils@npm:^3.32.0": + version: 3.32.0 + resolution: "@react-aria/utils@npm:3.32.0" dependencies: "@react-aria/ssr": "npm:^3.9.10" "@react-stately/flags": "npm:^3.1.2" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" clsx: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/b021d2c3704ce934ec41cfc4d87fa6904fb3e007030e31b824cd8287053e866076cb7c7f072d6ed2fee82ca68f2e3f4677f3d58d7938e4b3315831f1fdea4d90 + checksum: 10/be60a6b175568880828f3d2d492386458c78b90574601d3f70689e1fabd3d0af26b6e6006b3ac919af5332e7b096e5982a107b8413cf6c10419049b3914465bf languageName: node linkType: hard -"@react-aria/virtualizer@npm:^4.1.10": - version: 4.1.10 - resolution: "@react-aria/virtualizer@npm:4.1.10" +"@react-aria/virtualizer@npm:^4.1.11": + version: 4.1.11 + resolution: "@react-aria/virtualizer@npm:4.1.11" dependencies: - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" "@react-stately/virtualizer": "npm:^4.4.4" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/a9d1083ee2e424a33cbe5520fc86fd5194a77555b3be21792f648a723770998320220bc32e49ae1551ce5e00b024520ad0bca6ca0d7fdee86bc6216eb1cf78dc + checksum: 10/c5cab6bc99affea02bc3f77618374e3c2e8e4ffa900ebee21a201acbeae1c3e24e9fe75ba810b8aa5432d36841cecddf3e9fde9886e18b9fd35c9db26f73ad24 languageName: node linkType: hard -"@react-aria/visually-hidden@npm:^3.8.28": - version: 3.8.28 - resolution: "@react-aria/visually-hidden@npm:3.8.28" +"@react-aria/visually-hidden@npm:^3.8.29": + version: 3.8.29 + resolution: "@react-aria/visually-hidden@npm:3.8.29" dependencies: - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/utils": "npm:^3.31.0" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/utils": "npm:^3.32.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/07e61b18d385127353014c2bd2bb9bba5035ac064988fa1bfc2f13d8050ccc9488891d4d3fbe6c79a808bfed7f06f1867b89ec1c975818712a36266040d76597 + checksum: 10/0009fab6147d069cd0b7cd02245d4a2c2e377596eb080d85725c0f9bc682e6e93713be633ca05877dbaa04e3e5563783b4cb42364005503747dab32ea8d2abc5 languageName: node linkType: hard @@ -16115,45 +16115,45 @@ __metadata: languageName: node linkType: hard -"@react-stately/autocomplete@npm:3.0.0-beta.3": - version: 3.0.0-beta.3 - resolution: "@react-stately/autocomplete@npm:3.0.0-beta.3" +"@react-stately/autocomplete@npm:3.0.0-beta.4": + version: 3.0.0-beta.4 + resolution: "@react-stately/autocomplete@npm:3.0.0-beta.4" dependencies: - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/3920ce479e577730870bc6845ee888a44a9a1f90188cf68efb60c74a8264b5886c9059b0cedc07b82fcb26afd770bff5fc971bef0d93ff6ba7d07024a3e628cd + checksum: 10/482a58cd5753df054edb27bf308d3922da2c7757c4e2227cefda3806ae2eb14ad771a7d3741eb57c00cee3e8c3cb4fc1c81006c2e920d64b18330f533043ba2a languageName: node linkType: hard -"@react-stately/calendar@npm:^3.9.0": - version: 3.9.0 - resolution: "@react-stately/calendar@npm:3.9.0" +"@react-stately/calendar@npm:^3.9.1": + version: 3.9.1 + resolution: "@react-stately/calendar@npm:3.9.1" dependencies: - "@internationalized/date": "npm:^3.10.0" - "@react-stately/utils": "npm:^3.10.8" - "@react-types/calendar": "npm:^3.8.0" + "@internationalized/date": "npm:^3.10.1" + "@react-stately/utils": "npm:^3.11.0" + "@react-types/calendar": "npm:^3.8.1" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/6021927974cfaee9b48e07c4a4744105d1076fbad841aafdbd8633deb105c56f724a2a2f51285b426755873c3c2c8ae677649c9c38431433e7cf9e8dea7b334c + checksum: 10/83b6c45ccaad5f888f6491124dfd27d6dfe3ef875a4c698c4d960fdad21749082d3812a4ff925ea63b2a122db31abe765c0c91855c0d7bcb17eae0ebc4c64dca languageName: node linkType: hard -"@react-stately/checkbox@npm:^3.7.2": - version: 3.7.2 - resolution: "@react-stately/checkbox@npm:3.7.2" +"@react-stately/checkbox@npm:^3.7.3": + version: 3.7.3 + resolution: "@react-stately/checkbox@npm:3.7.3" dependencies: "@react-stately/form": "npm:^3.2.2" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/checkbox": "npm:^3.10.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/e0dda0c536ee43b2b925b9bf2932fdf5d27370b55d069633bb1e4d6a9dfb0db783ed0119ab0893ec192aa4099da0341f39fd2065bfd2e6c89ca3997e92e83ca1 + checksum: 10/eb8861fd78a81dd8f27628b17074b011f4d2a26c17f5508452775a942bc4da39cd2b452b0a1de8411b80f62f568478a01a92c61f5b4cb1e2d49d2fdb6784a55b languageName: node linkType: hard @@ -16169,96 +16169,96 @@ __metadata: languageName: node linkType: hard -"@react-stately/color@npm:^3.9.2": - version: 3.9.2 - resolution: "@react-stately/color@npm:3.9.2" +"@react-stately/color@npm:^3.9.3": + version: 3.9.3 + resolution: "@react-stately/color@npm:3.9.3" dependencies: "@internationalized/number": "npm:^3.6.5" "@internationalized/string": "npm:^3.2.7" "@react-stately/form": "npm:^3.2.2" - "@react-stately/numberfield": "npm:^3.10.2" - "@react-stately/slider": "npm:^3.7.2" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/numberfield": "npm:^3.10.3" + "@react-stately/slider": "npm:^3.7.3" + "@react-stately/utils": "npm:^3.11.0" "@react-types/color": "npm:^3.1.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/1aaea8dc241911b634ef072352076a8b3eec25300bb6c88ff2ce6147f05d2504688465a0342272b74a8484cdf272d10d2d9abc6cbbb64a111228046369f92ce5 + checksum: 10/7649122020876679e1ea660c2c286e09bca0d1ce1752729e15705de68bec58c62255d3f1f3172528018e94c55e3dc19a36e76204588efb7a6d06eda09dc2c3fd languageName: node linkType: hard -"@react-stately/combobox@npm:^3.12.0": - version: 3.12.0 - resolution: "@react-stately/combobox@npm:3.12.0" +"@react-stately/combobox@npm:^3.12.1": + version: 3.12.1 + resolution: "@react-stately/combobox@npm:3.12.1" dependencies: "@react-stately/collections": "npm:^3.12.8" "@react-stately/form": "npm:^3.2.2" - "@react-stately/list": "npm:^3.13.1" - "@react-stately/overlays": "npm:^3.6.20" - "@react-stately/utils": "npm:^3.10.8" - "@react-types/combobox": "npm:^3.13.9" + "@react-stately/list": "npm:^3.13.2" + "@react-stately/overlays": "npm:^3.6.21" + "@react-stately/utils": "npm:^3.11.0" + "@react-types/combobox": "npm:^3.13.10" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/3471a9c2f60e215784f63085b4ebcb2445ec804b3f0f50261933261afd7c9f7ed6d0675f00b5581aa43763c37561bcf6c6967a0ab485f403b1f8cf33a6b65d40 + checksum: 10/c1198f1b897e50a7ac5aefeb8f28e3873d165d7355eda15279ef762e02c33245035da2672c208cf806290d16bfa2a0a7a4ff0c6cfa893ed39128605a1b265da5 languageName: node linkType: hard -"@react-stately/data@npm:^3.14.1": - version: 3.14.1 - resolution: "@react-stately/data@npm:3.14.1" +"@react-stately/data@npm:^3.15.0": + version: 3.15.0 + resolution: "@react-stately/data@npm:3.15.0" dependencies: "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/ac069e41036fd534a8ec4e77847234abc35aa02c1901b0abc3af3d5cede0baadb5ce0d5f648fed5d6b2e37f673669f2b21fbf74cfb760f0e99734f1d9e841d33 + checksum: 10/9aac5846536ad65b319507124825aa88043fbff72fbf0a4d4e1d52726eab6a0e876bd125f2ac03bb9e6ba4cd4ae72f2ae398662ef614e46ed34ab64775710776 languageName: node linkType: hard -"@react-stately/datepicker@npm:^3.15.2": - version: 3.15.2 - resolution: "@react-stately/datepicker@npm:3.15.2" +"@react-stately/datepicker@npm:^3.15.3": + version: 3.15.3 + resolution: "@react-stately/datepicker@npm:3.15.3" dependencies: - "@internationalized/date": "npm:^3.10.0" + "@internationalized/date": "npm:^3.10.1" "@internationalized/string": "npm:^3.2.7" "@react-stately/form": "npm:^3.2.2" - "@react-stately/overlays": "npm:^3.6.20" - "@react-stately/utils": "npm:^3.10.8" - "@react-types/datepicker": "npm:^3.13.2" + "@react-stately/overlays": "npm:^3.6.21" + "@react-stately/utils": "npm:^3.11.0" + "@react-types/datepicker": "npm:^3.13.3" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/59ef011e4d56a3a40923d0a2ce02f6150ca4e5c7b9dac638d4f2b512af9a213ea6cda2d8d85b114758f2db2b06a8840a93c5b9c8c30859366249d58d960ed149 + checksum: 10/6f9067a112541a944824bf807c820c2b5925c653112e6334e8914d0ee3d33c2caced4e2ba402495aa4867e7b208c9c724fe81b30c0e26adfa8ca041497db5518 languageName: node linkType: hard -"@react-stately/disclosure@npm:^3.0.8": - version: 3.0.8 - resolution: "@react-stately/disclosure@npm:3.0.8" +"@react-stately/disclosure@npm:^3.0.9": + version: 3.0.9 + resolution: "@react-stately/disclosure@npm:3.0.9" dependencies: - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/8a36946589a199a788c07ef6ab04d0a30868db0484c660e5066ba38dd8bb8cef6e00fca324e7645b561fc7f3b3ffa152add555f2971c6be532d9cd3b67237b31 + checksum: 10/b0458bc5255672af6b587a0b478b55077c481f223abb6094af04eeb8a922774a0da524dce6c540aec014f6efab4eecfa4d43a72eadd94e232dbdd959d49afd36 languageName: node linkType: hard -"@react-stately/dnd@npm:^3.7.1": - version: 3.7.1 - resolution: "@react-stately/dnd@npm:3.7.1" +"@react-stately/dnd@npm:^3.7.2": + version: 3.7.2 + resolution: "@react-stately/dnd@npm:3.7.2" dependencies: - "@react-stately/selection": "npm:^3.20.6" + "@react-stately/selection": "npm:^3.20.7" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/d5500055a5398914556eeb767b141e4192be446990dfecb385b0348a35dc0baf0b0a72567dde806369995e09f5dc27a13fe4261739da7bc8ba483875bd88e694 + checksum: 10/ee10f796d8bce81b20792506ab1ec0dc46ac7cc4c20e9d320db115dc4499a148b35d9fdb3e0d2c8991747b3bc7c70f4f7dca1f17a9b25c6bfce8bcb3f8617230 languageName: node linkType: hard @@ -16283,27 +16283,27 @@ __metadata: languageName: node linkType: hard -"@react-stately/grid@npm:^3.11.6": - version: 3.11.6 - resolution: "@react-stately/grid@npm:3.11.6" +"@react-stately/grid@npm:^3.11.7": + version: 3.11.7 + resolution: "@react-stately/grid@npm:3.11.7" dependencies: "@react-stately/collections": "npm:^3.12.8" - "@react-stately/selection": "npm:^3.20.6" + "@react-stately/selection": "npm:^3.20.7" "@react-types/grid": "npm:^3.3.6" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/b6bc2a24ff37bdf375caa0f73dd6812ec4368f314f7754472f8b5de47ed296c4d3aadab787768c50d17d5606732291f05448891d5b08c0facaf8a1c9f7a70d31 + checksum: 10/04cb52877bbc6464ddbb3a58d2ce817f7e6f4891a381c2b8117fffa599e6f39d0621e1a7caa423a801237276512dfc2fd4fca4b936991822275163cc10d94269 languageName: node linkType: hard -"@react-stately/layout@npm:^4.5.1": - version: 4.5.1 - resolution: "@react-stately/layout@npm:4.5.1" +"@react-stately/layout@npm:^4.5.2": + version: 4.5.2 + resolution: "@react-stately/layout@npm:4.5.2" dependencies: "@react-stately/collections": "npm:^3.12.8" - "@react-stately/table": "npm:^3.15.1" + "@react-stately/table": "npm:^3.15.2" "@react-stately/virtualizer": "npm:^4.4.4" "@react-types/grid": "npm:^3.3.6" "@react-types/shared": "npm:^3.32.1" @@ -16312,170 +16312,170 @@ __metadata: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/a4413d766efdea8decf4549cd773f5a94ce086690195223e605b78ccb9e1c0dd25011bd37a011b805c72a7a9f3a27dc1dd6c8684d9cc2a0b6efaa084fbd337a4 + checksum: 10/03bd63f2e2739420790c47340cc8db07e8a02d11364903d49c99a0f1a0b09fe047a667d9e656ecb2eb0b2c24aa702f05e4be7eeb7758c3fb6a525cc8139ddb9a languageName: node linkType: hard -"@react-stately/list@npm:^3.13.1": - version: 3.13.1 - resolution: "@react-stately/list@npm:3.13.1" +"@react-stately/list@npm:^3.13.2": + version: 3.13.2 + resolution: "@react-stately/list@npm:3.13.2" dependencies: "@react-stately/collections": "npm:^3.12.8" - "@react-stately/selection": "npm:^3.20.6" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/selection": "npm:^3.20.7" + "@react-stately/utils": "npm:^3.11.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/3da2ca14eaee5f0915c1c79df66268bf1e7cc5d60ce68170515180993941c4c8679033589a8bc38e08138b882a62a8ad69ca727a500c882bd0f4214ce9a69384 + checksum: 10/ae28932316ef07bd5939cb1c1cf930fb412dbb6d63ce0999d28076599c1ba29923dd5020f46c878f5d64b1a1cdd0ddc9210da0de1a36ade5f53c8624db5814df languageName: node linkType: hard -"@react-stately/menu@npm:^3.9.8": - version: 3.9.8 - resolution: "@react-stately/menu@npm:3.9.8" +"@react-stately/menu@npm:^3.9.9": + version: 3.9.9 + resolution: "@react-stately/menu@npm:3.9.9" dependencies: - "@react-stately/overlays": "npm:^3.6.20" + "@react-stately/overlays": "npm:^3.6.21" "@react-types/menu": "npm:^3.10.5" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7798aa91f0af2743d4d441be1a0bac54489413c187d0e926a336cf13482ad4d54c7215426cd6ea288b8bcf621406db6ebaca5209095be5bfb4dfe4464ece7875 + checksum: 10/e71ac18a795fd125813d856c5889aaaa0dfd70ce01960e24b1926d2b98e7129610e04e586033c7b063a43594abdc4e88a8cc08f314630076091dad21f670e933 languageName: node linkType: hard -"@react-stately/numberfield@npm:^3.10.2": - version: 3.10.2 - resolution: "@react-stately/numberfield@npm:3.10.2" +"@react-stately/numberfield@npm:^3.10.3": + version: 3.10.3 + resolution: "@react-stately/numberfield@npm:3.10.3" dependencies: "@internationalized/number": "npm:^3.6.5" "@react-stately/form": "npm:^3.2.2" - "@react-stately/utils": "npm:^3.10.8" - "@react-types/numberfield": "npm:^3.8.15" + "@react-stately/utils": "npm:^3.11.0" + "@react-types/numberfield": "npm:^3.8.16" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/5fd1d4cc1b90a7b03b98a82e47162044a78534291b9baa77847eddd4fb8040bddb91329dfcc7aa2a7cc38ace23817b6c9d52dd7b0f9bd7ce249f763006580797 + checksum: 10/8e008188c0328b49c725e5ab961854ec94cb345c611dc678782d844e8a637af3419556e34fa364d5cf08895b6c580d5e1a438c17e083f91e3c108ec1eb9f9125 languageName: node linkType: hard -"@react-stately/overlays@npm:^3.6.20": - version: 3.6.20 - resolution: "@react-stately/overlays@npm:3.6.20" +"@react-stately/overlays@npm:^3.6.21": + version: 3.6.21 + resolution: "@react-stately/overlays@npm:3.6.21" dependencies: - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/overlays": "npm:^3.9.2" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/e9a3023b3fc3d9383d36dbd0a1e6a0d3d84350dee4f5ce6578464d613e063ca32dd6685c4fdfaa774ded24497c9e5c56363b9650c551d5d1a4dbb6d7d1d04382 + checksum: 10/18a4b2edb043bdf8c26505fb1eb4ac11cd025c73fb8662944d6339dc7ce9b45cbbd04ed173ec0a5807365f1f95a3a517e6fcd142e49cfa1aa544fa4b412f3439 languageName: node linkType: hard -"@react-stately/radio@npm:^3.11.2": - version: 3.11.2 - resolution: "@react-stately/radio@npm:3.11.2" +"@react-stately/radio@npm:^3.11.3": + version: 3.11.3 + resolution: "@react-stately/radio@npm:3.11.3" dependencies: "@react-stately/form": "npm:^3.2.2" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/radio": "npm:^3.9.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/182946fb5e963fed62df59d8893ca92443e587bcf3d644b54b991b3acb2124b777af11884e6e31c953becc2dad2226a13238a219747fb596a7e08f0e31e97a22 + checksum: 10/87791f9cfb8973c74b0a1e2f517c952e7d74e6f56f46f13e4d618dfb8e796138a265f5bbd08161e3fa228fb571c1db1abfe7c3d177b50d5de67219d299a35e23 languageName: node linkType: hard -"@react-stately/searchfield@npm:^3.5.16": - version: 3.5.16 - resolution: "@react-stately/searchfield@npm:3.5.16" +"@react-stately/searchfield@npm:^3.5.17": + version: 3.5.17 + resolution: "@react-stately/searchfield@npm:3.5.17" dependencies: - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/searchfield": "npm:^3.6.6" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/879e736c6888e36e227efffa665420c7e4fd2b319c2fb11661d6689fbbdad549129adc1c218e531f79f5e52be5b4c075c3c8120363ca3238fec24195c1976527 + checksum: 10/ae639aeb1b03721e34045997d8aaa46eb7d3e03c162230be5fddcf6d775d0c0d42bb61c204bc2667fb75918b350d147fa0cf7b0e941053248b6a59617270273a languageName: node linkType: hard -"@react-stately/select@npm:^3.8.0": - version: 3.8.0 - resolution: "@react-stately/select@npm:3.8.0" +"@react-stately/select@npm:^3.9.0": + version: 3.9.0 + resolution: "@react-stately/select@npm:3.9.0" dependencies: "@react-stately/form": "npm:^3.2.2" - "@react-stately/list": "npm:^3.13.1" - "@react-stately/overlays": "npm:^3.6.20" - "@react-stately/utils": "npm:^3.10.8" - "@react-types/select": "npm:^3.11.0" + "@react-stately/list": "npm:^3.13.2" + "@react-stately/overlays": "npm:^3.6.21" + "@react-stately/utils": "npm:^3.11.0" + "@react-types/select": "npm:^3.12.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/d611bbd9d6b55a2dfb31455227efe54c9fc2684f37abd9ba5633b2d1b838bc8fc182e92e64e94ef17c8bbaf351a72daf6089c9d54ce8acad76d3f1be3bdb05aa + checksum: 10/32f51d5c7a122eef72f68a226dcbd6c30c79b9a9457b8a0b537fa95bb17c063f5f8421108e345ee6c78e8b31d940f5eb0a51c446924374db5a5e98773b4834f2 languageName: node linkType: hard -"@react-stately/selection@npm:^3.20.6": - version: 3.20.6 - resolution: "@react-stately/selection@npm:3.20.6" +"@react-stately/selection@npm:^3.20.7": + version: 3.20.7 + resolution: "@react-stately/selection@npm:3.20.7" dependencies: "@react-stately/collections": "npm:^3.12.8" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/b72941f9d23d285b826aa0a5be55e299d934b335d8ebe50c6b061f72b59f860a37549ac4fc0f41a7357eab0d40edb404207cca201fc78915b001b5532e45a9db + checksum: 10/98619e3283fb73c097577f6857d9b566c85bdeb9ddbcb65ea5c6845cfb9b6327378dc68c71588f5fc24b7ec85aa2c8cbd6accfb8f5ba0a2679d1be28c9f30072 languageName: node linkType: hard -"@react-stately/slider@npm:^3.7.2": - version: 3.7.2 - resolution: "@react-stately/slider@npm:3.7.2" +"@react-stately/slider@npm:^3.7.3": + version: 3.7.3 + resolution: "@react-stately/slider@npm:3.7.3" dependencies: - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/shared": "npm:^3.32.1" "@react-types/slider": "npm:^3.8.2" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/c11a5374904434bfad5e87767900ed40e12d4341abf74d835b59ab26efb69ad669c86016926f83eae5fb3b3e39c175f775c4457b77eec64d73785c92369a79da + checksum: 10/5a7f870da1a4f104c61295398fa3854c5c69834876e6a597aa1bb369e6ad219d24b7109caf8c56677a2744935f648d1fec22fd6f40e927f589b85158f4c62abd languageName: node linkType: hard -"@react-stately/table@npm:^3.15.1": - version: 3.15.1 - resolution: "@react-stately/table@npm:3.15.1" +"@react-stately/table@npm:^3.15.2": + version: 3.15.2 + resolution: "@react-stately/table@npm:3.15.2" dependencies: "@react-stately/collections": "npm:^3.12.8" "@react-stately/flags": "npm:^3.1.2" - "@react-stately/grid": "npm:^3.11.6" - "@react-stately/selection": "npm:^3.20.6" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/grid": "npm:^3.11.7" + "@react-stately/selection": "npm:^3.20.7" + "@react-stately/utils": "npm:^3.11.0" "@react-types/grid": "npm:^3.3.6" "@react-types/shared": "npm:^3.32.1" "@react-types/table": "npm:^3.13.4" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/005292a0803de55179a9d27aa7bb68f44865fff64921141fac7aab0fbd5d2adff27a9a70db904f9a57bd6bc8db84036e8ad58b7a1ea57c990829500eb3f3ee6d + checksum: 10/3b6cf1e561525dcde9c01b500e1d01e87efac2b9bcf75fac8870b1f0618b86e0fab15e1b9bbd83cd345f654f39c52253c5b26c1b22b903b0c3212ff33abbf3c9 languageName: node linkType: hard -"@react-stately/tabs@npm:^3.8.6": - version: 3.8.6 - resolution: "@react-stately/tabs@npm:3.8.6" +"@react-stately/tabs@npm:^3.8.7": + version: 3.8.7 + resolution: "@react-stately/tabs@npm:3.8.7" dependencies: - "@react-stately/list": "npm:^3.13.1" + "@react-stately/list": "npm:^3.13.2" "@react-types/shared": "npm:^3.32.1" - "@react-types/tabs": "npm:^3.3.19" + "@react-types/tabs": "npm:^3.3.20" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/ed71a8351b6b9db7c191cbb60acf72121751864b20c1d9a552438a1f4f6c96fdd41469396a17a86ef52287463ef3c8f217167f9498bc5f36e77a621415fac388 + checksum: 10/0a72d94ac45432aa4baae88d691824edac12efd8e2d1579ec0778310a8b136ae83ef95d7066446129a3f0a35e647954a03cb360000a0496c74cbe4512960a33b languageName: node linkType: hard @@ -16491,56 +16491,56 @@ __metadata: languageName: node linkType: hard -"@react-stately/toggle@npm:^3.9.2": - version: 3.9.2 - resolution: "@react-stately/toggle@npm:3.9.2" +"@react-stately/toggle@npm:^3.9.3": + version: 3.9.3 + resolution: "@react-stately/toggle@npm:3.9.3" dependencies: - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/utils": "npm:^3.11.0" "@react-types/checkbox": "npm:^3.10.2" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/06f0e11cbbda0b479113df6c82e650db9ac772162566fd749da19a3ca274d25831849dc6c67383f4a440be82f61b5dff80e97513763f66bf3994a55febed5827 + checksum: 10/68aaeb48a54fef76a4da19b186fc42ba65f400605b25ab2ac95b58a35b565f2704397ac4cfaf6d1a09f64cb5ac4946ea078433032c2a0ca79854082681888a47 languageName: node linkType: hard -"@react-stately/tooltip@npm:^3.5.8": - version: 3.5.8 - resolution: "@react-stately/tooltip@npm:3.5.8" +"@react-stately/tooltip@npm:^3.5.9": + version: 3.5.9 + resolution: "@react-stately/tooltip@npm:3.5.9" dependencies: - "@react-stately/overlays": "npm:^3.6.20" - "@react-types/tooltip": "npm:^3.4.21" + "@react-stately/overlays": "npm:^3.6.21" + "@react-types/tooltip": "npm:^3.5.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/fbddb1f0efc6a6275f8f558bf80b984aea0c49f6c0d17fc42fc27e48a02a3c36bc9ca3eed6de9347a3b12289d21e22dc3de159b8b4b8ab21fe18218ab8a0e4ec + checksum: 10/67c26c7e1777b61ee74411f12a0c971ad8550b5675eddc746761890b1854488d8774dcc0e91b30e46e5935c9c6751e7c2a38e189eba9a1efb4c97385a4b7a5e9 languageName: node linkType: hard -"@react-stately/tree@npm:^3.9.3": - version: 3.9.3 - resolution: "@react-stately/tree@npm:3.9.3" +"@react-stately/tree@npm:^3.9.4": + version: 3.9.4 + resolution: "@react-stately/tree@npm:3.9.4" dependencies: "@react-stately/collections": "npm:^3.12.8" - "@react-stately/selection": "npm:^3.20.6" - "@react-stately/utils": "npm:^3.10.8" + "@react-stately/selection": "npm:^3.20.7" + "@react-stately/utils": "npm:^3.11.0" "@react-types/shared": "npm:^3.32.1" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/294cc70ffa08280a187a85f72c2402547d71295ddb95b2752fcfa395016947e7fe27116e970df597bab87319e18b3bf1f6b837124fb4bbda8686f7f0018b46ab + checksum: 10/7a3c785ca54f3139f442731e1a106302f4c0bc5731a95d5fdcb96980e7fce3e430062bfef6bf692fc5a7aa6b8ffe08e3e621c7a671249fba81768554ea21a397 languageName: node linkType: hard -"@react-stately/utils@npm:^3.10.8": - version: 3.10.8 - resolution: "@react-stately/utils@npm:3.10.8" +"@react-stately/utils@npm:^3.11.0": + version: 3.11.0 + resolution: "@react-stately/utils@npm:3.11.0" dependencies: "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7878ec47b132075566708bae630cb86d8237dde976eb3793bba43695abbb29fcaea9d00ea3f4f7244fcda253368f5b2b85263c37665c24e390500cdcc978c6fe + checksum: 10/ae3414559cde99230cfd05c19a6498fffa94d268d139825fb4777f5beba4ec5b39f19f1199c8e625d5977ae7f568d1c0dd447b87ec0ab4ed12721303409b9f16 languageName: node linkType: hard @@ -16557,16 +16557,16 @@ __metadata: languageName: node linkType: hard -"@react-types/autocomplete@npm:3.0.0-alpha.35": - version: 3.0.0-alpha.35 - resolution: "@react-types/autocomplete@npm:3.0.0-alpha.35" +"@react-types/autocomplete@npm:3.0.0-alpha.36": + version: 3.0.0-alpha.36 + resolution: "@react-types/autocomplete@npm:3.0.0-alpha.36" dependencies: - "@react-types/combobox": "npm:^3.13.9" + "@react-types/combobox": "npm:^3.13.10" "@react-types/searchfield": "npm:^3.6.6" "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/2a51111a94f5df1ce0b1b137a69ca783fa2fa399b4c305ebc7f137b0aa6a420cfe2a3925d40fda9a7d0d3b832d390823015959eee41b6c6b31f7a6b30739a596 + checksum: 10/054b3d186d50f02fbd48e5370a447a3cfaafecf3aaddc04cdb0411cf9e170d11ceec5ca6f8fb654fbd32a65a74ae52c7a900af6ebe3683ad94f36860d4e8ee8f languageName: node linkType: hard @@ -16593,15 +16593,15 @@ __metadata: languageName: node linkType: hard -"@react-types/calendar@npm:^3.8.0": - version: 3.8.0 - resolution: "@react-types/calendar@npm:3.8.0" +"@react-types/calendar@npm:^3.8.1": + version: 3.8.1 + resolution: "@react-types/calendar@npm:3.8.1" dependencies: - "@internationalized/date": "npm:^3.10.0" + "@internationalized/date": "npm:^3.10.1" "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/06fe32c8e5d78eedcb511fa3d18f66582b5664ab85c572538e24b953e8204a7c5f98407df0d028625a1dfdd94223153e8e04d9d7feefd96f7533b90fb82be907 + checksum: 10/a43039061afd7eac4197dc85f9a4112d41ae8869da79efddd0b206a38cef0154947e29e39c92571d22c29b17bfba3ef71fc684b519904dc701580a5aabae7bd1 languageName: node linkType: hard @@ -16628,28 +16628,28 @@ __metadata: languageName: node linkType: hard -"@react-types/combobox@npm:^3.13.9": - version: 3.13.9 - resolution: "@react-types/combobox@npm:3.13.9" +"@react-types/combobox@npm:^3.13.10": + version: 3.13.10 + resolution: "@react-types/combobox@npm:3.13.10" dependencies: "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/0583d24f38b965e5c2cd2b436a7ad374635e183dc7c12145ce9ed0a166d7b6e46ed4685a68c9fb24aed065d1fca644ca3b0606cb360ae5fac3a9881e88ee2f2b + checksum: 10/d640e2dc622a67242987a1a304f86e30d0ba13e853c48dabf35ad278e4af250abc2bbb959ce8ac91797f725db297c5cb20b023219dcbbab48f675cf11c3e0147 languageName: node linkType: hard -"@react-types/datepicker@npm:^3.13.2": - version: 3.13.2 - resolution: "@react-types/datepicker@npm:3.13.2" +"@react-types/datepicker@npm:^3.13.3": + version: 3.13.3 + resolution: "@react-types/datepicker@npm:3.13.3" dependencies: - "@internationalized/date": "npm:^3.10.0" - "@react-types/calendar": "npm:^3.8.0" + "@internationalized/date": "npm:^3.10.1" + "@react-types/calendar": "npm:^3.8.1" "@react-types/overlays": "npm:^3.9.2" "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/1f1686360f64051f5eff5e170ab78e890b20e07680b6031c96c77e47085ca022c55997018911095cd61076bdf63470f61c99ac25ef9b493420f69cde6e4763bd + checksum: 10/f8e72e8ba6737861ac9eb0553a0ad65708d34c3d989aad394918436fa150d0533be8efcd48cb892739b0b33e31af5eca4ddfde9bc2ac7ff7c3f863cdf18f991d languageName: node linkType: hard @@ -16732,14 +16732,14 @@ __metadata: languageName: node linkType: hard -"@react-types/numberfield@npm:^3.8.15": - version: 3.8.15 - resolution: "@react-types/numberfield@npm:3.8.15" +"@react-types/numberfield@npm:^3.8.16": + version: 3.8.16 + resolution: "@react-types/numberfield@npm:3.8.16" dependencies: "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/242584ab3ece90a5e26ebee9788c25fd44b85d6be405c5f31e08bda9d58ad8d7fcf199744a2fcd816275da13ae546ca0239175403e68788a43563f2a070c0574 + checksum: 10/aac7b1fa219e9d971579c4632af31701c898d7a0a6bf168095d5b04822cd9c93059fbdb21f1d2fe92c62ed50922c6a8c9fc63d5be755ab94436a5da9c028fede languageName: node linkType: hard @@ -16788,14 +16788,14 @@ __metadata: languageName: node linkType: hard -"@react-types/select@npm:^3.11.0": - version: 3.11.0 - resolution: "@react-types/select@npm:3.11.0" +"@react-types/select@npm:^3.12.0": + version: 3.12.0 + resolution: "@react-types/select@npm:3.12.0" dependencies: "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/6c431daa847a034ce06625fe75f220f301e8bdb50961c74e915f36919641f7907aa0d16d15127b9889853d197d66d964fd3726bd6c446b8aecf0e756e30e3925 + checksum: 10/66ac890f40b517bd98e974e3bc8083ab0579b3ad1daa5a5ae308256034c75ae95841f40c8a3b76de5704d57f2b07cdce432c17f522216f9b636ecd0b5abf6aa6 languageName: node linkType: hard @@ -16842,14 +16842,14 @@ __metadata: languageName: node linkType: hard -"@react-types/tabs@npm:^3.3.19": - version: 3.3.19 - resolution: "@react-types/tabs@npm:3.3.19" +"@react-types/tabs@npm:^3.3.20": + version: 3.3.20 + resolution: "@react-types/tabs@npm:3.3.20" dependencies: "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/cdc3217251502c6f89621ab366a4d23e32a334dc3ca2f5366cd980fb828bb413be1262a76f4c4794a0908bfd02c3237a3608e283b02ef472707fb7c22b80b44b + checksum: 10/1526cc79cf73692681ac1c2d8c87a2ffb9f120231da0351b09c82fd4c38bf7f140aa845ad3ef7e0763c9270dfb246498bc01cda476aeff8150421e61287b4f2a languageName: node linkType: hard @@ -16864,15 +16864,15 @@ __metadata: languageName: node linkType: hard -"@react-types/tooltip@npm:^3.4.21": - version: 3.4.21 - resolution: "@react-types/tooltip@npm:3.4.21" +"@react-types/tooltip@npm:^3.5.0": + version: 3.5.0 + resolution: "@react-types/tooltip@npm:3.5.0" dependencies: "@react-types/overlays": "npm:^3.9.2" "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/50d1beea407a7e58ef8e3638c2517d8c90f6b15777d396b4adf7cffbd80d38dfb7d68d6a38127ed130bfe7b0c3116c5d499de3f7b6353f54d022a22739e0141d + checksum: 10/66c20eb0e0d66628e739510e5fba8eb56534beec1887f7efcf1867fb5e43b8da3502c3f865059d08ce672e3c6e02876470497ab77dd3378f9ba6fbb8f1ca1de2 languageName: node linkType: hard @@ -43823,28 +43823,28 @@ __metadata: linkType: hard "react-aria-components@npm:^1.13.0": - version: 1.13.0 - resolution: "react-aria-components@npm:1.13.0" + version: 1.14.0 + resolution: "react-aria-components@npm:1.14.0" dependencies: - "@internationalized/date": "npm:^3.10.0" + "@internationalized/date": "npm:^3.10.1" "@internationalized/string": "npm:^3.2.7" - "@react-aria/autocomplete": "npm:3.0.0-rc.3" - "@react-aria/collections": "npm:^3.0.0" - "@react-aria/dnd": "npm:^3.11.3" - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/interactions": "npm:^3.25.6" + "@react-aria/autocomplete": "npm:3.0.0-rc.4" + "@react-aria/collections": "npm:^3.0.1" + "@react-aria/dnd": "npm:^3.11.4" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/interactions": "npm:^3.26.0" "@react-aria/live-announcer": "npm:^3.4.4" - "@react-aria/overlays": "npm:^3.30.0" + "@react-aria/overlays": "npm:^3.31.0" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/textfield": "npm:^3.18.2" - "@react-aria/toolbar": "npm:3.0.0-beta.21" - "@react-aria/utils": "npm:^3.31.0" - "@react-aria/virtualizer": "npm:^4.1.10" - "@react-stately/autocomplete": "npm:3.0.0-beta.3" - "@react-stately/layout": "npm:^4.5.1" - "@react-stately/selection": "npm:^3.20.6" - "@react-stately/table": "npm:^3.15.1" - "@react-stately/utils": "npm:^3.10.8" + "@react-aria/textfield": "npm:^3.18.3" + "@react-aria/toolbar": "npm:3.0.0-beta.22" + "@react-aria/utils": "npm:^3.32.0" + "@react-aria/virtualizer": "npm:^4.1.11" + "@react-stately/autocomplete": "npm:3.0.0-beta.4" + "@react-stately/layout": "npm:^4.5.2" + "@react-stately/selection": "npm:^3.20.7" + "@react-stately/table": "npm:^3.15.2" + "@react-stately/utils": "npm:^3.11.0" "@react-stately/virtualizer": "npm:^4.4.4" "@react-types/form": "npm:^3.7.16" "@react-types/grid": "npm:^3.3.6" @@ -43852,66 +43852,66 @@ __metadata: "@react-types/table": "npm:^3.13.4" "@swc/helpers": "npm:^0.5.0" client-only: "npm:^0.0.1" - react-aria: "npm:^3.44.0" - react-stately: "npm:^3.42.0" + react-aria: "npm:^3.45.0" + react-stately: "npm:^3.43.0" use-sync-external-store: "npm:^1.4.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/8bff1890ef342b0c32d6a50329ce4657bce2fcc91f2a2f903351f375354b186b47f265e7718caaff0ed9a5f630037f17649d85d00bc20a4928a6417204d75ff2 + checksum: 10/cc842f03a6cb10a11ac6c95fbdcc97329a2bce7cf9203bd3fc9f4f4ea248bd308bc31b39f6df3c6b427be5adc370813e04b7a4cf41e8a0193195dde048462d9b languageName: node linkType: hard -"react-aria@npm:^3.44.0": - version: 3.44.0 - resolution: "react-aria@npm:3.44.0" +"react-aria@npm:^3.45.0": + version: 3.45.0 + resolution: "react-aria@npm:3.45.0" dependencies: "@internationalized/string": "npm:^3.2.7" - "@react-aria/breadcrumbs": "npm:^3.5.29" - "@react-aria/button": "npm:^3.14.2" - "@react-aria/calendar": "npm:^3.9.2" - "@react-aria/checkbox": "npm:^3.16.2" - "@react-aria/color": "npm:^3.1.2" - "@react-aria/combobox": "npm:^3.14.0" - "@react-aria/datepicker": "npm:^3.15.2" - "@react-aria/dialog": "npm:^3.5.31" - "@react-aria/disclosure": "npm:^3.1.0" - "@react-aria/dnd": "npm:^3.11.3" - "@react-aria/focus": "npm:^3.21.2" - "@react-aria/gridlist": "npm:^3.14.1" - "@react-aria/i18n": "npm:^3.12.13" - "@react-aria/interactions": "npm:^3.25.6" - "@react-aria/label": "npm:^3.7.22" - "@react-aria/landmark": "npm:^3.0.7" - "@react-aria/link": "npm:^3.8.6" - "@react-aria/listbox": "npm:^3.15.0" - "@react-aria/menu": "npm:^3.19.3" - "@react-aria/meter": "npm:^3.4.27" - "@react-aria/numberfield": "npm:^3.12.2" - "@react-aria/overlays": "npm:^3.30.0" - "@react-aria/progress": "npm:^3.4.27" - "@react-aria/radio": "npm:^3.12.2" - "@react-aria/searchfield": "npm:^3.8.9" - "@react-aria/select": "npm:^3.17.0" - "@react-aria/selection": "npm:^3.26.0" - "@react-aria/separator": "npm:^3.4.13" - "@react-aria/slider": "npm:^3.8.2" + "@react-aria/breadcrumbs": "npm:^3.5.30" + "@react-aria/button": "npm:^3.14.3" + "@react-aria/calendar": "npm:^3.9.3" + "@react-aria/checkbox": "npm:^3.16.3" + "@react-aria/color": "npm:^3.1.3" + "@react-aria/combobox": "npm:^3.14.1" + "@react-aria/datepicker": "npm:^3.15.3" + "@react-aria/dialog": "npm:^3.5.32" + "@react-aria/disclosure": "npm:^3.1.1" + "@react-aria/dnd": "npm:^3.11.4" + "@react-aria/focus": "npm:^3.21.3" + "@react-aria/gridlist": "npm:^3.14.2" + "@react-aria/i18n": "npm:^3.12.14" + "@react-aria/interactions": "npm:^3.26.0" + "@react-aria/label": "npm:^3.7.23" + "@react-aria/landmark": "npm:^3.0.8" + "@react-aria/link": "npm:^3.8.7" + "@react-aria/listbox": "npm:^3.15.1" + "@react-aria/menu": "npm:^3.19.4" + "@react-aria/meter": "npm:^3.4.28" + "@react-aria/numberfield": "npm:^3.12.3" + "@react-aria/overlays": "npm:^3.31.0" + "@react-aria/progress": "npm:^3.4.28" + "@react-aria/radio": "npm:^3.12.3" + "@react-aria/searchfield": "npm:^3.8.10" + "@react-aria/select": "npm:^3.17.1" + "@react-aria/selection": "npm:^3.27.0" + "@react-aria/separator": "npm:^3.4.14" + "@react-aria/slider": "npm:^3.8.3" "@react-aria/ssr": "npm:^3.9.10" - "@react-aria/switch": "npm:^3.7.8" - "@react-aria/table": "npm:^3.17.8" - "@react-aria/tabs": "npm:^3.10.8" - "@react-aria/tag": "npm:^3.7.2" - "@react-aria/textfield": "npm:^3.18.2" - "@react-aria/toast": "npm:^3.0.8" - "@react-aria/tooltip": "npm:^3.8.8" - "@react-aria/tree": "npm:^3.1.4" - "@react-aria/utils": "npm:^3.31.0" - "@react-aria/visually-hidden": "npm:^3.8.28" + "@react-aria/switch": "npm:^3.7.9" + "@react-aria/table": "npm:^3.17.9" + "@react-aria/tabs": "npm:^3.10.9" + "@react-aria/tag": "npm:^3.7.3" + "@react-aria/textfield": "npm:^3.18.3" + "@react-aria/toast": "npm:^3.0.9" + "@react-aria/tooltip": "npm:^3.9.0" + "@react-aria/tree": "npm:^3.1.5" + "@react-aria/utils": "npm:^3.32.0" + "@react-aria/visually-hidden": "npm:^3.8.29" "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/46b428af3f68703c03d7836a79f288dae1e945cb34164220a61f57d77df0a5adfc6c4a4c993282f9dbf57f159c9d249e1d644b57b61caca3dec8af1285404ec1 + checksum: 10/9c674ccc8c07ab66333276edb64471e94cd80fefe8bc41b48be79034f1bba3d180037a8bd89847a60c90076168ab12870ca381bc61b1ca1f76167e54a985559c languageName: node linkType: hard @@ -44521,39 +44521,39 @@ __metadata: languageName: node linkType: hard -"react-stately@npm:^3.42.0": - version: 3.42.0 - resolution: "react-stately@npm:3.42.0" +"react-stately@npm:^3.43.0": + version: 3.43.0 + resolution: "react-stately@npm:3.43.0" dependencies: - "@react-stately/calendar": "npm:^3.9.0" - "@react-stately/checkbox": "npm:^3.7.2" + "@react-stately/calendar": "npm:^3.9.1" + "@react-stately/checkbox": "npm:^3.7.3" "@react-stately/collections": "npm:^3.12.8" - "@react-stately/color": "npm:^3.9.2" - "@react-stately/combobox": "npm:^3.12.0" - "@react-stately/data": "npm:^3.14.1" - "@react-stately/datepicker": "npm:^3.15.2" - "@react-stately/disclosure": "npm:^3.0.8" - "@react-stately/dnd": "npm:^3.7.1" + "@react-stately/color": "npm:^3.9.3" + "@react-stately/combobox": "npm:^3.12.1" + "@react-stately/data": "npm:^3.15.0" + "@react-stately/datepicker": "npm:^3.15.3" + "@react-stately/disclosure": "npm:^3.0.9" + "@react-stately/dnd": "npm:^3.7.2" "@react-stately/form": "npm:^3.2.2" - "@react-stately/list": "npm:^3.13.1" - "@react-stately/menu": "npm:^3.9.8" - "@react-stately/numberfield": "npm:^3.10.2" - "@react-stately/overlays": "npm:^3.6.20" - "@react-stately/radio": "npm:^3.11.2" - "@react-stately/searchfield": "npm:^3.5.16" - "@react-stately/select": "npm:^3.8.0" - "@react-stately/selection": "npm:^3.20.6" - "@react-stately/slider": "npm:^3.7.2" - "@react-stately/table": "npm:^3.15.1" - "@react-stately/tabs": "npm:^3.8.6" + "@react-stately/list": "npm:^3.13.2" + "@react-stately/menu": "npm:^3.9.9" + "@react-stately/numberfield": "npm:^3.10.3" + "@react-stately/overlays": "npm:^3.6.21" + "@react-stately/radio": "npm:^3.11.3" + "@react-stately/searchfield": "npm:^3.5.17" + "@react-stately/select": "npm:^3.9.0" + "@react-stately/selection": "npm:^3.20.7" + "@react-stately/slider": "npm:^3.7.3" + "@react-stately/table": "npm:^3.15.2" + "@react-stately/tabs": "npm:^3.8.7" "@react-stately/toast": "npm:^3.1.2" - "@react-stately/toggle": "npm:^3.9.2" - "@react-stately/tooltip": "npm:^3.5.8" - "@react-stately/tree": "npm:^3.9.3" + "@react-stately/toggle": "npm:^3.9.3" + "@react-stately/tooltip": "npm:^3.5.9" + "@react-stately/tree": "npm:^3.9.4" "@react-types/shared": "npm:^3.32.1" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/4913f8ae9beb909d07669b97c0960dadad72c3c9e572051158a64acac3eadc73ff495b3c145b3673c15494751506e5b8a666a59f65364900fa66917412971d32 + checksum: 10/a82d9ba6fdb9a922cb0820de2678be548fa08fc011a9b0be35f3a0022049219810ce1696b7b9104db907b3103233143801649b584d8a06fec582dfb78c81dcf9 languageName: node linkType: hard From 44f5d049ca9ecd58a1cd7b138bc2aa72a550506f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 22 Jan 2026 11:19:21 +0100 Subject: [PATCH 036/211] collect all pg config interpretation in one function (#32465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik AdelÃļw --- .changeset/lazy-groups-hunt.md | 5 + .../database/connectors/postgres.ts | 363 ++++++++---------- 2 files changed, 174 insertions(+), 194 deletions(-) create mode 100644 .changeset/lazy-groups-hunt.md diff --git a/.changeset/lazy-groups-hunt.md b/.changeset/lazy-groups-hunt.md new file mode 100644 index 0000000000..56e0b19035 --- /dev/null +++ b/.changeset/lazy-groups-hunt.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Minor internal restructure of the postgres config loading code diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts index 4a6893e9c1..1522222b6f 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts @@ -15,7 +15,7 @@ */ import { LifecycleService, LoggerService } from '@backstage/backend-plugin-api'; -import { Config, ConfigReader } from '@backstage/config'; +import { Config } from '@backstage/config'; import { ForwardedError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; import knexFactory, { Knex } from 'knex'; @@ -23,8 +23,6 @@ import { merge, omit } from 'lodash'; import limiterFactory from 'p-limit'; import { Client } from 'pg'; import { Connector } from '../types'; -import defaultNameOverride from './defaultNameOverride'; -import defaultSchemaOverride from './defaultSchemaOverride'; import { mergeDatabaseConfig } from './mergeDatabaseConfig'; import format from 'pg-format'; @@ -293,6 +291,159 @@ function normalizeConnection( : connection; } +/** + * The computed configuration for a plugin's postgres database connection. + */ +export interface PgPluginDatabaseConfig { + /** The database client type (e.g. 'pg') */ + client: string; + /** Whether the client type was overridden at the plugin level */ + clientOverridden: boolean; + /** The optional role to set on connections */ + role: string | undefined; + /** Additional knex configuration merged from base and plugin config */ + additionalKnexConfig: JsonObject | undefined; + /** Whether to ensure the database exists */ + ensureExists: boolean; + /** Whether to ensure the schema exists */ + ensureSchemaExists: boolean; + /** The plugin division mode ('database' or 'schema') */ + pluginDivisionMode: string; + /** The connection configuration */ + connection: Knex.StaticConnectionConfig; + /** The database name, if any */ + databaseName: string | undefined; + /** Database client overrides including schema overrides if applicable */ + databaseClientOverrides: Knex.Config; + /** The full knex config for the plugin */ + knexConfig: Knex.Config; +} + +/** + * Computes all postgres database configuration for a plugin from the provided config. + * + * @param config - The database config object + * @param pluginId - The plugin ID to compute config for + * @param prefix - The database name prefix (e.g. 'backstage_plugin_') + * @returns All computed configuration values for the plugin + */ +export function computePgPluginConfig( + config: Config, + pluginId: string, + prefix: string, +): PgPluginDatabaseConfig { + // Client type + const pluginClient = config.getOptionalString( + `${pluginPath(pluginId)}.client`, + ); + const baseClient = config.getString('client'); + const client = pluginClient ?? baseClient; + const clientOverridden = client !== baseClient; + + // Role + const role = + config.getOptionalString(`${pluginPath(pluginId)}.role`) ?? + config.getOptionalString('role'); + + // Additional knex config + const pluginKnexConfig = config + .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`) + ?.get(); + const baseKnexConfig = config + .getOptionalConfig('knexConfig') + ?.get(); + const additionalKnexConfig = merge(baseKnexConfig, pluginKnexConfig); + + // Ensure exists flags + const baseEnsureExists = config.getOptionalBoolean('ensureExists') ?? true; + const ensureExists = + config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? + baseEnsureExists; + + const baseEnsureSchemaExists = + config.getOptionalBoolean('ensureSchemaExists') ?? false; + const ensureSchemaExists = + config.getOptionalBoolean( + `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`, + ) ?? baseEnsureSchemaExists; + + // Plugin division mode + const pluginDivisionMode = + config.getOptionalString('pluginDivisionMode') ?? 'database'; + + // Connection config + let baseConnection = normalizeConnection(config.get('connection')); + + // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. + // The `database` property from the base connection is omitted unless + // `pluginDivisionMode` is set to `schema`. + if (pluginDivisionMode !== 'schema') { + baseConnection = omit(baseConnection, 'database'); + } + + // Get and normalize optional plugin specific database connection + const pluginConnection = normalizeConnection( + config.getOptional(`${pluginPath(pluginId)}.connection`), + ); + + ( + baseConnection as Knex.PgConnectionConfig + ).application_name ||= `backstage_plugin_${pluginId}`; + + const connection = { + // Include base connection if client type has not been overridden + ...(clientOverridden ? {} : baseConnection), + ...pluginConnection, + } as Knex.StaticConnectionConfig; + + // Database name + const connectionDatabaseName = (connection as Knex.ConnectionConfig) + ?.database; + let databaseName: string | undefined; + + if (pluginDivisionMode === 'schema') { + // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied + // or fallback to default knex database + databaseName = connectionDatabaseName; + } else { + // All other supported databases should fallback to an auto-prefixed name + databaseName = connectionDatabaseName ?? `${prefix}${pluginId}`; + } + + // Database client overrides + let databaseClientOverrides: Knex.Config = {}; + if (databaseName) { + databaseClientOverrides = { connection: { database: databaseName } }; + } + if (pluginDivisionMode === 'schema') { + databaseClientOverrides = mergeDatabaseConfig({}, databaseClientOverrides, { + searchPath: [pluginId], + }); + } + + // Full knex config for plugin + const knexConfig: Knex.Config = { + ...additionalKnexConfig, + client, + connection, + ...(role && { role }), + }; + + return { + client, + clientOverridden, + role, + additionalKnexConfig, + ensureExists, + ensureSchemaExists, + pluginDivisionMode, + connection, + databaseName, + databaseClientOverrides, + knexConfig, + }; +} + export class PgConnector implements Connector { private readonly config: Config; private readonly prefix: string; @@ -309,30 +460,26 @@ export class PgConnector implements Connector { lifecycle: LifecycleService; }, ): Promise { - const pluginConfig = new ConfigReader( - this.getConfigForPlugin(pluginId) as JsonObject, + const pluginDbConfig = computePgPluginConfig( + this.config, + pluginId, + this.prefix, ); - const databaseName = this.getDatabaseName(pluginId); - if (databaseName && this.getEnsureExistsConfig(pluginId)) { + if (pluginDbConfig.databaseName && pluginDbConfig.ensureExists) { try { - await ensurePgDatabaseExists(pluginConfig, databaseName); + await ensurePgDatabaseExists(this.config, pluginDbConfig.databaseName); } catch (error) { throw new Error( - `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, + `Failed to connect to the database to make sure that '${pluginDbConfig.databaseName}' exists, ${error}`, ); } } - let schemaOverrides; - if (this.getPluginDivisionModeConfig() === 'schema') { - schemaOverrides = defaultSchemaOverride(pluginId); - if ( - this.getEnsureSchemaExistsConfig(pluginId) || - this.getEnsureExistsConfig(pluginId) - ) { + if (pluginDbConfig.pluginDivisionMode === 'schema') { + if (pluginDbConfig.ensureSchemaExists || pluginDbConfig.ensureExists) { try { - await ensurePgSchemaExists(pluginConfig, pluginId); + await ensurePgSchemaExists(this.config, pluginId); } catch (error) { throw new Error( `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`, @@ -341,186 +488,14 @@ export class PgConnector implements Connector { } } - const databaseClientOverrides = mergeDatabaseConfig( - {}, - this.getDatabaseOverrides(pluginId), - schemaOverrides, - ); - const client = createPgDatabaseClient( - pluginConfig, - databaseClientOverrides, + this.config, + mergeDatabaseConfig( + pluginDbConfig.knexConfig, + pluginDbConfig.databaseClientOverrides, + ), ); return client; } - - /** - * Provides the canonical database name for a given plugin. - * - * This method provides the effective database name which is determined using global - * and plugin specific database config. If no explicit database name is configured - * and `pluginDivisionMode` is not `schema`, this method will provide a generated name - * which is the pluginId prefixed with 'backstage_plugin_'. If `pluginDivisionMode` is - * `schema`, it will fallback to using the default database for the knex instance. - * - * @param pluginId - Lookup the database name for given plugin - * @returns String representing the plugin's database name - */ - private getDatabaseName(pluginId: string): string | undefined { - const connection = this.getConnectionConfig(pluginId); - - const databaseName = (connection as Knex.ConnectionConfig)?.database; - - // `pluginDivisionMode` as `schema` should use overridden databaseName if supplied or fallback to default knex database - if (this.getPluginDivisionModeConfig() === 'schema') { - return databaseName; - } - - // all other supported databases should fallback to an auto-prefixed name - return databaseName ?? `${this.prefix}${pluginId}`; - } - - /** - * Provides the client type which should be used for a given plugin. - * - * The client type is determined by plugin specific config if present. - * Otherwise the base client is used as the fallback. - * - * @param pluginId - Plugin to get the client type for - * @returns Object with client type returned as `client` and boolean - * representing whether or not the client was overridden as - * `overridden` - */ - private getClientType(pluginId: string): { - client: string; - overridden: boolean; - } { - const pluginClient = this.config.getOptionalString( - `${pluginPath(pluginId)}.client`, - ); - - const baseClient = this.config.getString('client'); - const client = pluginClient ?? baseClient; - return { - client, - overridden: client !== baseClient, - }; - } - - private getRoleConfig(pluginId: string): string | undefined { - return ( - this.config.getOptionalString(`${pluginPath(pluginId)}.role`) ?? - this.config.getOptionalString('role') - ); - } - - /** - * Provides the knexConfig which should be used for a given plugin. - * - * @param pluginId - Plugin to get the knexConfig for - * @returns The merged knexConfig value or undefined if it isn't specified - */ - private getAdditionalKnexConfig(pluginId: string): JsonObject | undefined { - const pluginConfig = this.config - .getOptionalConfig(`${pluginPath(pluginId)}.knexConfig`) - ?.get(); - - const baseConfig = this.config - .getOptionalConfig('knexConfig') - ?.get(); - - return merge(baseConfig, pluginConfig); - } - - private getEnsureExistsConfig(pluginId: string): boolean { - const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true; - return ( - this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? - baseConfig - ); - } - - private getEnsureSchemaExistsConfig(pluginId: string): boolean { - const baseConfig = - this.config.getOptionalBoolean('ensureSchemaExists') ?? false; - return ( - this.config.getOptionalBoolean( - `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`, - ) ?? baseConfig - ); - } - - private getPluginDivisionModeConfig(): string { - return this.config.getOptionalString('pluginDivisionMode') ?? 'database'; - } - - /** - * Provides a Knex connection plugin config by combining base and plugin - * config. - * - * This method provides a baseConfig for a plugin database connector. If the - * client type has not been overridden, the global connection config will be - * included with plugin specific config as the base. Values from the plugin - * connection take precedence over the base. Base database name is omitted - * unless `pluginDivisionMode` is set to `schema`. - */ - private getConnectionConfig(pluginId: string): Knex.StaticConnectionConfig { - const { overridden } = this.getClientType(pluginId); - - let baseConnection = normalizeConnection(this.config.get('connection')); - - // Databases cannot be shared unless the `pluginDivisionMode` is set to `schema`. The - // `database` property from the base connection is omitted unless `pluginDivisionMode` - // is set to `schema`. - if (this.getPluginDivisionModeConfig() !== 'schema') { - baseConnection = omit(baseConnection, 'database'); - } - - // get and normalize optional plugin specific database connection - const connection = normalizeConnection( - this.config.getOptional(`${pluginPath(pluginId)}.connection`), - ); - - ( - baseConnection as Knex.PgConnectionConfig - ).application_name ||= `backstage_plugin_${pluginId}`; - - return { - // include base connection if client type has not been overridden - ...(overridden ? {} : baseConnection), - ...connection, - } as Knex.StaticConnectionConfig; - } - - /** - * Provides a Knex database config for a given plugin. - * - * This method provides a Knex configuration object along with the plugin's - * client type. - * - * @param pluginId - The plugin that the database config should correspond with - */ - private getConfigForPlugin(pluginId: string): Knex.Config { - const { client } = this.getClientType(pluginId); - const role = this.getRoleConfig(pluginId); - - return { - ...this.getAdditionalKnexConfig(pluginId), - client, - connection: this.getConnectionConfig(pluginId), - ...(role && { role }), - }; - } - - /** - * Provides a partial `Knex.Config`â€ĸ database name override for a given plugin. - * - * @param pluginId - Target plugin to get database name override - * @returns Partial `Knex.Config` with database name override - */ - private getDatabaseOverrides(pluginId: string): Knex.Config { - const databaseName = this.getDatabaseName(pluginId); - return databaseName ? defaultNameOverride(databaseName) : {}; - } } From 5c9ea217464c39782200601a9a4366fa62c92ad2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:46:44 +0000 Subject: [PATCH 037/211] chore(deps): update dependency react-hook-form to v7.71.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5feda28012..5471856a81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44175,11 +44175,11 @@ __metadata: linkType: hard "react-hook-form@npm:^7.12.2": - version: 7.67.0 - resolution: "react-hook-form@npm:7.67.0" + version: 7.71.1 + resolution: "react-hook-form@npm:7.71.1" peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - checksum: 10/0e74a48b2da0f79166d5c6701f78a08fd5165e901e46ce5a578f0a2f25005e861023ffb35c1a96373c23701a84c3fce2111c4aced824d2ca08c9b2420a645551 + checksum: 10/54b56aa52a6ac17fb5aa68ce072b08f50b75c2705bf878069f817c346e464c1ebfbf67be372d94cf8e620fa07ca8e64ffca71bb69071dd0290a1ef8f43eefb9d languageName: node linkType: hard From a582db6807deeb3f798cbd69c71048bf0deb62ac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 11:05:41 +0000 Subject: [PATCH 038/211] chore(deps): update dependency @testing-library/react to v16.3.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index f98354e5b2..b574417451 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20425,8 +20425,8 @@ __metadata: linkType: hard "@testing-library/react@npm:^16.0.0": - version: 16.3.1 - resolution: "@testing-library/react@npm:16.3.1" + version: 16.3.2 + resolution: "@testing-library/react@npm:16.3.2" dependencies: "@babel/runtime": "npm:^7.12.5" peerDependencies: @@ -20440,7 +20440,7 @@ __metadata: optional: true "@types/react-dom": optional: true - checksum: 10/2d1221861c41711f2767c57c5b5158593baf6f7ca4bf15fad9e79bc9aeaadb56c2e327943b8733d2788f1826499fe01daef7289a7a0149b48dd7f51b33b12e58 + checksum: 10/0ca88c6f672d00c2afd1bdedeff9b5382dd8157038efeb9762dc016731030075624be7106b92d2b5e5c52812faea85263e69272c14b6f8700eb48a4a8af6feef languageName: node linkType: hard From 4fc7bf037af081046a3d929485bb8e9f1080c24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 22 Jan 2026 12:18:07 +0100 Subject: [PATCH 039/211] upgrade to tar v7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik AdelÃļw --- .changeset/green-flowers-brush.md | 5 +++ .changeset/odd-eagles-guess.md | 8 ++++ packages/backend-defaults/package.json | 2 +- .../lib/tree/ReadableArrayResponse.ts | 2 +- .../lib/tree/TarArchiveResponse.test.ts | 2 +- .../urlReader/lib/tree/TarArchiveResponse.ts | 10 ++--- packages/cli/package.json | 2 +- .../cli/src/modules/build/lib/buildBackend.ts | 4 +- .../build/lib/packager/createDistWorkspace.ts | 4 +- packages/repo-tools/package.json | 2 +- .../commands/generate-patch/generate-patch.ts | 2 +- plugins/scaffolder-backend/package.json | 1 - plugins/scaffolder-node/package.json | 2 +- .../scaffolder-node/src/tasks/serializer.ts | 2 +- yarn.lock | 44 +++++++------------ 15 files changed, 45 insertions(+), 47 deletions(-) create mode 100644 .changeset/green-flowers-brush.md create mode 100644 .changeset/odd-eagles-guess.md diff --git a/.changeset/green-flowers-brush.md b/.changeset/green-flowers-brush.md new file mode 100644 index 0000000000..bf57f18dcb --- /dev/null +++ b/.changeset/green-flowers-brush.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Removed unused dependency diff --git a/.changeset/odd-eagles-guess.md b/.changeset/odd-eagles-guess.md new file mode 100644 index 0000000000..d40ab80640 --- /dev/null +++ b/.changeset/odd-eagles-guess.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-defaults': patch +'@backstage/plugin-scaffolder-node': patch +'@backstage/repo-tools': patch +'@backstage/cli': patch +--- + +Bump to tar v7 diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index cb75e9ce5f..709095e603 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -187,7 +187,7 @@ "rate-limit-redis": "^4.2.0", "raw-body": "^2.4.1", "selfsigned": "^2.0.0", - "tar": "^6.1.12", + "tar": "^7.5.6", "triple-beam": "^1.4.1", "uuid": "^11.0.0", "winston": "^3.2.1", diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts index 6740771ad7..12f7e2d99b 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts @@ -25,7 +25,7 @@ import platformPath, { dirname } from 'path'; import getRawBody from 'raw-body'; import fs from 'fs-extra'; import { promisify } from 'util'; -import tar from 'tar'; +import * as tar from 'tar'; import { pipeline as pipelineCb, Readable } from 'stream'; import { FromReadableArrayOptions } from '../types'; diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.test.ts index 36b3d129e9..4ceb526e9a 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.test.ts @@ -18,7 +18,7 @@ import fs from 'fs-extra'; import { resolve as resolvePath, join as joinPath } from 'path'; import { TarArchiveResponse } from './TarArchiveResponse'; import { createMockDirectory } from '@backstage/backend-test-utils'; -import tar from 'tar'; +import * as tar from 'tar'; const archiveData = fs.readFileSync( resolvePath(__filename, '../../__fixtures__/mock-main.tar.gz'), diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts index ab3a593c18..067975eee0 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts @@ -24,13 +24,11 @@ import concatStream from 'concat-stream'; import fs from 'fs-extra'; import platformPath from 'path'; import { pipeline as pipelineCb, Readable } from 'stream'; -import tar, { FileStat, Parse, ParseStream, ReadEntry } from 'tar'; +import * as tar from 'tar'; +import type { ReadEntry } from 'tar'; import { promisify } from 'util'; import { stripFirstDirectoryFromPath } from './util'; -// Tar types for `Parse` is not a proper constructor, but it should be -const TarParseStream = Parse as unknown as { new (): ParseStream }; - const pipeline = promisify(pipelineCb); /** @@ -85,7 +83,7 @@ export class TarArchiveResponse implements UrlReaderServiceReadTreeResponse { this.onlyOnce(); const files = Array(); - const parser = new TarParseStream(); + const parser = new tar.Parser(); parser.on('entry', (entry: ReadEntry & Readable) => { if (entry.type === 'Directory') { @@ -184,7 +182,7 @@ export class TarArchiveResponse implements UrlReaderServiceReadTreeResponse { } // Block symlinks/hardlinks that escape the extraction directory - const entry = stat as FileStat & { type?: string; linkpath?: string }; + const entry = stat as ReadEntry; if ( (entry.type === 'SymbolicLink' || entry.type === 'Link') && entry.linkpath diff --git a/packages/cli/package.json b/packages/cli/package.json index 0669dadc5e..8cc681747c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -133,7 +133,7 @@ "style-loader": "^3.3.1", "sucrase": "^3.20.2", "swc-loader": "^0.2.3", - "tar": "^6.1.12", + "tar": "^7.5.6", "ts-checker-rspack-plugin": "^1.1.5", "ts-morph": "^24.0.0", "undici": "^7.2.3", diff --git a/packages/cli/src/modules/build/lib/buildBackend.ts b/packages/cli/src/modules/build/lib/buildBackend.ts index e109402da1..7f18d7e326 100644 --- a/packages/cli/src/modules/build/lib/buildBackend.ts +++ b/packages/cli/src/modules/build/lib/buildBackend.ts @@ -17,7 +17,7 @@ import os from 'os'; import fs from 'fs-extra'; import { resolve as resolvePath } from 'path'; -import tar, { CreateOptions } from 'tar'; +import * as tar from 'tar'; import { createDistWorkspace } from './packager'; import { getEnvironmentParallelism } from '../../../lib/parallel'; import { buildPackage, Output } from './builder'; @@ -78,7 +78,7 @@ export async function buildBackend(options: BuildBackendOptions) { portable: true, noMtime: true, gzip: true, - } as CreateOptions & { noMtime: boolean }, + }, [''], ); } finally { diff --git a/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts b/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts index 90635f1a73..ee5b1a1789 100644 --- a/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts +++ b/packages/cli/src/modules/build/lib/packager/createDistWorkspace.ts @@ -22,7 +22,7 @@ import { relative as relativePath, } from 'path'; import { tmpdir } from 'os'; -import tar, { CreateOptions, FileOptions } from 'tar'; +import * as tar from 'tar'; import partition from 'lodash/partition'; import { paths } from '../../../../lib/paths'; import { run } from '@backstage/cli-common'; @@ -268,7 +268,7 @@ export async function createDistWorkspace( portable: true, noMtime: true, gzip: options.skeleton.endsWith('.gz'), - } as CreateOptions & FileOptions & { noMtime: boolean }, + }, skeletonFiles, ); } diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 60cb036b50..e98735f62f 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -80,7 +80,7 @@ "minimatch": "^9.0.0", "p-limit": "^3.0.2", "portfinder": "^1.0.32", - "tar": "^6.1.12", + "tar": "^7.5.6", "ts-morph": "^24.0.0", "yaml-diff-patch": "^2.0.0", "zod": "^3.22.4" diff --git a/packages/repo-tools/src/commands/generate-patch/generate-patch.ts b/packages/repo-tools/src/commands/generate-patch/generate-patch.ts index 90cbe7f322..c1521b37a1 100644 --- a/packages/repo-tools/src/commands/generate-patch/generate-patch.ts +++ b/packages/repo-tools/src/commands/generate-patch/generate-patch.ts @@ -28,7 +28,7 @@ import { ForwardedError } from '@backstage/errors'; import { Readable } from 'stream'; import { finished } from 'stream/promises'; import { ReadableStream } from 'stream/web'; -import tar from 'tar'; +import * as tar from 'tar'; // TODO: add option for this const DEFAULT_REGISTRY_URL = 'https://registry.npmjs.org'; diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 90ed4dafb7..82f2258b40 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -104,7 +104,6 @@ "p-limit": "^3.1.0", "p-queue": "^6.6.2", "prom-client": "^15.0.0", - "tar": "^6.1.12", "triple-beam": "^1.4.1", "uuid": "^11.0.0", "winston": "^3.2.1", diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 192e92466a..fba744c975 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -69,7 +69,7 @@ "jsonschema": "^1.5.0", "lodash": "^4.17.21", "p-limit": "^3.1.0", - "tar": "^6.1.12", + "tar": "^7.5.6", "winston": "^3.2.1", "winston-transport": "^4.7.0", "zod": "^3.22.4", diff --git a/plugins/scaffolder-node/src/tasks/serializer.ts b/plugins/scaffolder-node/src/tasks/serializer.ts index baf1b42157..ee3a91f2d9 100644 --- a/plugins/scaffolder-node/src/tasks/serializer.ts +++ b/plugins/scaffolder-node/src/tasks/serializer.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import tar from 'tar'; +import * as tar from 'tar'; import concatStream from 'concat-stream'; import { promisify } from 'util'; import { pipeline as pipelineCb, Readable } from 'stream'; diff --git a/yarn.lock b/yarn.lock index f98354e5b2..e21b83d527 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3044,7 +3044,7 @@ __metadata: raw-body: "npm:^2.4.1" selfsigned: "npm:^2.0.0" supertest: "npm:^7.0.0" - tar: "npm:^6.1.12" + tar: "npm:^7.5.6" triple-beam: "npm:^1.4.1" uuid: "npm:^11.0.0" wait-for-expect: "npm:^3.0.2" @@ -3402,7 +3402,7 @@ __metadata: style-loader: "npm:^3.3.1" sucrase: "npm:^3.20.2" swc-loader: "npm:^0.2.3" - tar: "npm:^6.1.12" + tar: "npm:^7.5.6" terser-webpack-plugin: "npm:^5.1.3" ts-checker-rspack-plugin: "npm:^1.1.5" ts-morph: "npm:^24.0.0" @@ -6924,7 +6924,6 @@ __metadata: prom-client: "npm:^15.0.0" strip-ansi: "npm:^7.1.0" supertest: "npm:^7.0.0" - tar: "npm:^6.1.12" triple-beam: "npm:^1.4.1" uuid: "npm:^11.0.0" wait-for-expect: "npm:^3.0.2" @@ -7007,7 +7006,7 @@ __metadata: jsonschema: "npm:^1.5.0" lodash: "npm:^4.17.21" p-limit: "npm:^3.1.0" - tar: "npm:^6.1.12" + tar: "npm:^7.5.6" winston: "npm:^3.2.1" winston-transport: "npm:^4.7.0" zod: "npm:^3.22.4" @@ -7896,7 +7895,7 @@ __metadata: minimatch: "npm:^9.0.0" p-limit: "npm:^3.0.2" portfinder: "npm:^1.0.32" - tar: "npm:^6.1.12" + tar: "npm:^7.5.6" ts-morph: "npm:^24.0.0" typedoc: "npm:^0.28.0" yaml-diff-patch: "npm:^2.0.0" @@ -39532,13 +39531,12 @@ __metadata: languageName: node linkType: hard -"minizlib@npm:^3.0.1": - version: 3.0.1 - resolution: "minizlib@npm:3.0.1" +"minizlib@npm:^3.0.1, minizlib@npm:^3.1.0": + version: 3.1.0 + resolution: "minizlib@npm:3.1.0" dependencies: - minipass: "npm:^7.0.4" - rimraf: "npm:^5.0.5" - checksum: 10/622cb85f51e5c206a080a62d20db0d7b4066f308cb6ce82a9644da112367c3416ae7062017e631eb7ac8588191cfa4a9a279b8651c399265202b298e98c4acef + minipass: "npm:^7.1.2" + checksum: 10/f47365cc2cb7f078cbe7e046eb52655e2e7e97f8c0a9a674f4da60d94fb0624edfcec9b5db32e8ba5a99a5f036f595680ae6fe02a262beaa73026e505cc52f99 languageName: node linkType: hard @@ -39580,15 +39578,6 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^3.0.1": - version: 3.0.1 - resolution: "mkdirp@npm:3.0.1" - bin: - mkdirp: dist/cjs/src/bin.js - checksum: 10/16fd79c28645759505914561e249b9a1f5fe3362279ad95487a4501e4467abeb714fd35b95307326b8fd03f3c7719065ef11a6f97b7285d7888306d1bd2232ba - languageName: node - linkType: hard - "mock-socket@npm:^9.3.0": version: 9.3.1 resolution: "mock-socket@npm:9.3.1" @@ -45521,7 +45510,7 @@ __metadata: languageName: node linkType: hard -"rimraf@npm:^5.0.1, rimraf@npm:^5.0.5": +"rimraf@npm:^5.0.1": version: 5.0.10 resolution: "rimraf@npm:5.0.10" dependencies: @@ -48187,7 +48176,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^6.0.2, tar@npm:^6.0.5, tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.12, tar@npm:^6.1.2, tar@npm:^6.2.1": +"tar@npm:^6.0.2, tar@npm:^6.0.5, tar@npm:^6.1.0, tar@npm:^6.1.11, tar@npm:^6.1.2, tar@npm:^6.2.1": version: 6.2.1 resolution: "tar@npm:6.2.1" dependencies: @@ -48201,17 +48190,16 @@ __metadata: languageName: node linkType: hard -"tar@npm:^7.4.3": - version: 7.4.3 - resolution: "tar@npm:7.4.3" +"tar@npm:^7.4.3, tar@npm:^7.5.6": + version: 7.5.6 + resolution: "tar@npm:7.5.6" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" - minizlib: "npm:^3.0.1" - mkdirp: "npm:^3.0.1" + minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10/12a2a4fc6dee23e07cc47f1aeb3a14a1afd3f16397e1350036a8f4cdfee8dcac7ef5978337a4e7b2ac2c27a9a6d46388fc2088ea7c80cb6878c814b1425f8ecf + checksum: 10/cf4a84d79b9327fcf765f2ea16de4702b9b8dd7dc6b1840b16e0f999628d96b81b2c7efbf83d4eb42b0164856f1db887a5a61ffef97d36cdb77cac742219f9ee languageName: node linkType: hard From 8b2ce74d6f51ed823ce20b6e5a19ad812c06a01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 22 Jan 2026 13:06:47 +0100 Subject: [PATCH 040/211] fix links in release notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik AdelÃļw --- docs/releases/v1.47.0.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/releases/v1.47.0.md b/docs/releases/v1.47.0.md index 6831b96b46..55dfd17693 100644 --- a/docs/releases/v1.47.0.md +++ b/docs/releases/v1.47.0.md @@ -94,13 +94,13 @@ Contributed by [@imod](https://github.com/imod) in [#31410](https://github.com/b Widget configuration changes are now only saved when the Save button is explicitly clicked. A new Cancel button has been added to enable users to discard unsaved changes -Contributed by [@kmikko](https://github.com/kmikko) in [#31198](https://github.com/pull/31198) +Contributed by [@kmikko](https://github.com/kmikko) in [#31198](https://github.com/backstage/backstage/pull/31198) ### Improved performance for `GithubOrgEntityProvider` `GithubOrgEntityProvider` membership event handling and edit team has been improved. The provider now fetches only the specific user's teams instead of all organization users when processing membership events, and uses `addEntitiesOperation` instead of `replaceEntitiesOperation` to avoid unnecessary entity deletions. -Contributed by [@angeliski](https://github.com/angeliski) in [#32184](https://github.com/pull/32184) +Contributed by [@angeliski](https://github.com/angeliski) in [#32184](https://github.com/backstage/backstage/pull/32184) ### `Link` component now uses React Router’s navigation system From b5a8bb0f7fd55d322fef8734b9878152abc5e7e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 12:07:18 +0000 Subject: [PATCH 041/211] chore(deps): update dependency react-resizable to v3.1.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index eae4c507cb..e37f236f6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -44084,16 +44084,16 @@ __metadata: languageName: node linkType: hard -"react-draggable@npm:^4.0.0, react-draggable@npm:^4.0.3": - version: 4.4.6 - resolution: "react-draggable@npm:4.4.6" +"react-draggable@npm:^4.0.0, react-draggable@npm:^4.5.0": + version: 4.5.0 + resolution: "react-draggable@npm:4.5.0" dependencies: - clsx: "npm:^1.1.1" + clsx: "npm:^2.1.1" prop-types: "npm:^15.8.1" peerDependencies: react: ">= 16.3.0" react-dom: ">= 16.3.0" - checksum: 10/51b9ac7f913797fc1cebc30ae383f346883033c45eb91e9b0b92e9ebd224bb1545b4ae2391825b649b798cc711a38351a5f41be24d949c64c6703ebc24eba661 + checksum: 10/fd4080a082fad199103c6978c8a78c80154a0205dbd14ca2c7bb75634132a6afd83a3e4669f2d8fe54efe68569ef24a992439183289d83e71030b0a1791d3476 languageName: node linkType: hard @@ -44415,14 +44415,15 @@ __metadata: linkType: hard "react-resizable@npm:^3.0.4, react-resizable@npm:^3.0.5": - version: 3.0.5 - resolution: "react-resizable@npm:3.0.5" + version: 3.1.3 + resolution: "react-resizable@npm:3.1.3" dependencies: prop-types: "npm:15.x" - react-draggable: "npm:^4.0.3" + react-draggable: "npm:^4.5.0" peerDependencies: react: ">= 16.3" - checksum: 10/745fad6ac827857b3a80d1d648b8d6723aa72fc17d5410a01707073f3d37b4adf6e0354dfe3cc33dee34d6e546a3fbd5603ef73e385dfc5218a425a39bf96275 + react-dom: ">= 16.3" + checksum: 10/bc7126fabbd2f0ba422fa0da77fcfcebf2b4f28141d3faf02067632d258e345bc5f63891f9d5eca0e9ea31dc461b74ec69073a18f905b25bdd392ca6283577f9 languageName: node linkType: hard From eb38220d1230b4c7c15a15e6dc16ecea250649a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 21 Jan 2026 17:22:42 +0100 Subject: [PATCH 042/211] collect all pg config interpretation in one function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik AdelÃļw --- .../database/connectors/postgres.test.ts | 492 ++++++++++++++++++ .../database/connectors/postgres.ts | 4 +- 2 files changed, 494 insertions(+), 2 deletions(-) diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.test.ts b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.test.ts index 254bdf6c9d..debbaf79d0 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.test.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.test.ts @@ -17,10 +17,12 @@ import { Config, ConfigReader } from '@backstage/config'; import { buildPgDatabaseConfig, + computePgPluginConfig, createPgDatabaseClient, getPgConnectionConfig, parsePgConnectionString, } from './postgres'; +import { type Knex } from 'knex'; jest.mock('@google-cloud/cloud-sql-connector'); @@ -394,3 +396,493 @@ describe('postgres', () => { }); }); }); + +describe('computePgPluginConfig', () => { + const prefix = 'backstage_plugin_'; + + describe('client', () => { + it('uses base client when no plugin client specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.client).toBe('pg'); + expect(result.clientOverridden).toBe(false); + }); + + it('uses plugin client when specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + plugin: { + catalog: { + client: 'better-sqlite3', + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.client).toBe('better-sqlite3'); + expect(result.clientOverridden).toBe(true); + }); + }); + + describe('role', () => { + it('returns undefined when no role specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.role).toBeUndefined(); + }); + + it('uses base role when no plugin role specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + role: 'base_role', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.role).toBe('base_role'); + }); + + it('uses plugin role when specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + role: 'base_role', + plugin: { + catalog: { + role: 'plugin_role', + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.role).toBe('plugin_role'); + }); + }); + + describe('additionalKnexConfig', () => { + it('returns empty object when no knexConfig specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.additionalKnexConfig).toEqual({}); + }); + + it('uses base knexConfig when no plugin config', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + knexConfig: { debug: true }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.additionalKnexConfig).toEqual({ debug: true }); + }); + + it('merges base and plugin knexConfig', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + knexConfig: { debug: true, pool: { min: 0 } }, + plugin: { + catalog: { + knexConfig: { pool: { max: 10 } }, + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.additionalKnexConfig).toEqual({ + debug: true, + pool: { min: 0, max: 10 }, + }); + }); + }); + + describe('ensureExists', () => { + it('defaults to true when not specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.ensureExists).toBe(true); + }); + + it('uses base ensureExists when no plugin setting', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + ensureExists: false, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.ensureExists).toBe(false); + }); + + it('uses plugin ensureExists when specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + ensureExists: true, + plugin: { + catalog: { + ensureExists: false, + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.ensureExists).toBe(false); + }); + }); + + describe('ensureSchemaExists', () => { + it('defaults to false when not specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.ensureSchemaExists).toBe(false); + }); + + it('uses base ensureSchemaExists when no plugin setting', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + ensureSchemaExists: true, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.ensureSchemaExists).toBe(true); + }); + }); + + describe('pluginDivisionMode', () => { + it('defaults to database when not specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.pluginDivisionMode).toBe('database'); + }); + + it('uses specified pluginDivisionMode', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + pluginDivisionMode: 'schema', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.pluginDivisionMode).toBe('schema'); + }); + }); + + describe('connection', () => { + it('sets application_name to plugin id', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection).toMatchObject({ + application_name: 'backstage_plugin_catalog', + }); + }); + + it('preserves existing application_name', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost', application_name: 'custom_name' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection).toMatchObject({ + application_name: 'custom_name', + }); + }); + + it('omits database from base connection when pluginDivisionMode is database', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost', database: 'shared_db' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection.database).toBeUndefined(); + }); + + it('keeps database from base connection when pluginDivisionMode is schema', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost', database: 'shared_db' }, + pluginDivisionMode: 'schema', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection.database).toBe('shared_db'); + }); + + it('merges plugin connection with base connection', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost', user: 'base_user' }, + plugin: { + catalog: { + connection: { password: 'plugin_pass' }, + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection).toMatchObject({ + host: 'localhost', + user: 'base_user', + password: 'plugin_pass', + }); + }); + + it('excludes base connection when client is overridden', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost', user: 'base_user' }, + plugin: { + catalog: { + client: 'better-sqlite3', + connection: { filename: ':memory:' }, + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection.host).toBeUndefined(); + expect(result.connection.user).toBeUndefined(); + expect( + (result.connection as Knex.BetterSqlite3ConnectionConfig).filename, + ).toBe(':memory:'); + }); + + it('parses connection string in base config', () => { + const config = new ConfigReader({ + client: 'pg', + connection: 'postgresql://user:pass@localhost:5432/mydb', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection).toMatchObject({ + host: 'localhost', + user: 'user', + password: 'pass', + port: '5432', + }); + }); + + it('parses connection string in plugin config', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'base-host' }, + plugin: { + catalog: { + connection: 'postgresql://plugin:pass@plugin-host:5432/plugindb', + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.connection).toMatchObject({ + host: 'plugin-host', + user: 'plugin', + password: 'pass', + port: '5432', + }); + }); + }); + + describe('databaseName', () => { + it('auto-generates database name with prefix in database mode', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.databaseName).toBe('backstage_plugin_catalog'); + }); + + it('uses connection database when specified in database mode', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + plugin: { + catalog: { + connection: { database: 'custom_db' }, + }, + }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.databaseName).toBe('custom_db'); + }); + + it('uses connection database in schema mode', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost', database: 'shared_db' }, + pluginDivisionMode: 'schema', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.databaseName).toBe('shared_db'); + }); + + it('returns undefined when no database in schema mode', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + pluginDivisionMode: 'schema', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.databaseName).toBeUndefined(); + }); + }); + + describe('databaseClientOverrides', () => { + it('sets connection.database when databaseName exists', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.databaseClientOverrides).toEqual({ + connection: { database: 'backstage_plugin_catalog' }, + }); + }); + + it('adds searchPath when in schema mode', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost', database: 'shared_db' }, + pluginDivisionMode: 'schema', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.databaseClientOverrides).toEqual({ + connection: { database: 'shared_db' }, + searchPath: ['catalog'], + }); + }); + + it('returns empty object when no databaseName in schema mode', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + pluginDivisionMode: 'schema', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.databaseClientOverrides).toEqual({ + searchPath: ['catalog'], + }); + }); + }); + + describe('knexConfig', () => { + it('includes client and connection', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.knexConfig.client).toBe('pg'); + expect(result.knexConfig.connection).toBeDefined(); + }); + + it('includes role when specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + role: 'my_role', + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect((result.knexConfig as any).role).toBe('my_role'); + }); + + it('does not include role when not specified', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect((result.knexConfig as any).role).toBeUndefined(); + }); + + it('includes additionalKnexConfig properties', () => { + const config = new ConfigReader({ + client: 'pg', + connection: { host: 'localhost' }, + knexConfig: { debug: true, pool: { min: 2 } }, + }); + + const result = computePgPluginConfig(config, 'catalog', prefix); + + expect(result.knexConfig.debug).toBe(true); + expect(result.knexConfig.pool).toEqual({ min: 2 }); + }); + }); +}); diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts index 1522222b6f..380ff2bd0c 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts @@ -310,7 +310,7 @@ export interface PgPluginDatabaseConfig { /** The plugin division mode ('database' or 'schema') */ pluginDivisionMode: string; /** The connection configuration */ - connection: Knex.StaticConnectionConfig; + connection: Knex.PgConnectionConfig; /** The database name, if any */ databaseName: string | undefined; /** Database client overrides including schema overrides if applicable */ @@ -394,7 +394,7 @@ export function computePgPluginConfig( // Include base connection if client type has not been overridden ...(clientOverridden ? {} : baseConnection), ...pluginConnection, - } as Knex.StaticConnectionConfig; + } as Knex.PgConnectionConfig; // Database name const connectionDatabaseName = (connection as Knex.ConnectionConfig) From 12b28a935763db6519f45a5988dcc24705aaecdd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 13:10:04 +0000 Subject: [PATCH 043/211] chore(deps): update dependency supertest to v7.2.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index e37f236f6a..418bb9daf2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27748,7 +27748,7 @@ __metadata: languageName: node linkType: hard -"cookie-signature@npm:^1.2.1": +"cookie-signature@npm:^1.2.1, cookie-signature@npm:^1.2.2": version: 1.2.2 resolution: "cookie-signature@npm:1.2.2" checksum: 10/be44a3c9a56f3771aea3a8bd8ad8f0a8e2679bcb967478267f41a510b4eb5ec55085386ba79c706c4ac21605ca76f4251973444b90283e0eb3eeafe8a92c7708 @@ -32121,16 +32121,16 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0, form-data@npm:^4.0.4": - version: 4.0.4 - resolution: "form-data@npm:4.0.4" +"form-data@npm:^4.0.0, form-data@npm:^4.0.4, form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" es-set-tostringtag: "npm:^2.1.0" hasown: "npm:^2.0.2" mime-types: "npm:^2.1.12" - checksum: 10/a4b62e21932f48702bc468cc26fb276d186e6b07b557e3dd7cc455872bdbb82db7db066844a64ad3cf40eaf3a753c830538183570462d3649fdfd705601cbcfb + checksum: 10/52ecd6e927c8c4e215e68a7ad5e0f7c1031397439672fd9741654b4a94722c4182e74cc815b225dcb5be3f4180f36428f67c6dd39eaa98af0dcfdd26c00c19cd languageName: node linkType: hard @@ -47916,30 +47916,31 @@ __metadata: languageName: node linkType: hard -"superagent@npm:^10.2.3": - version: 10.2.3 - resolution: "superagent@npm:10.2.3" +"superagent@npm:^10.3.0": + version: 10.3.0 + resolution: "superagent@npm:10.3.0" dependencies: component-emitter: "npm:^1.3.1" cookiejar: "npm:^2.1.4" debug: "npm:^4.3.7" fast-safe-stringify: "npm:^2.1.1" - form-data: "npm:^4.0.4" + form-data: "npm:^4.0.5" formidable: "npm:^3.5.4" methods: "npm:^1.1.2" mime: "npm:2.6.0" - qs: "npm:^6.11.2" - checksum: 10/377bf938e68927dd772169c5285be27872bf6e84fac01c52bcd9396bc5b348c9ded8f8be54649510ec09a67bc5096055847b37cb01b3bca0eb06ff1856170e35 + qs: "npm:^6.14.1" + checksum: 10/e62b13281403ef9a1f716465cf5d83c12dbb418afa8197d8ca3cc35c8dd1825a81bd5c627512ae289684f6b7c48fc9d76ce321c28de6142f69bbe905e866427f languageName: node linkType: hard "supertest@npm:^7.0.0, supertest@npm:^7.1.0": - version: 7.1.4 - resolution: "supertest@npm:7.1.4" + version: 7.2.2 + resolution: "supertest@npm:7.2.2" dependencies: + cookie-signature: "npm:^1.2.2" methods: "npm:^1.1.2" - superagent: "npm:^10.2.3" - checksum: 10/ecb5d41f2b62b257dbdcabac245c32b8e8fb264fe2636dd85c2c883569d23dc14adc0a471abb84187cbdb49bc36ad870ad355b4a0b85973f510fd57fc229e6cc + superagent: "npm:^10.3.0" + checksum: 10/41b29e005b7f0fec8e062df1e43037d5a29b40c1172331d1b448e8b39ed3cc020d2aac342aad6d6e75c1e07b2f08c32bff6472cd726ab4fc9b3f10969937979d languageName: node linkType: hard From d095bab16657e1d4884ca8f42dbbb7b0cdff793f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 13:10:32 +0000 Subject: [PATCH 044/211] chore(deps): update dependency swagger-ui-react to v5.31.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e37f236f6a..c9bc59a1e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48030,8 +48030,8 @@ __metadata: linkType: hard "swagger-ui-react@npm:^5.27.1": - version: 5.30.3 - resolution: "swagger-ui-react@npm:5.30.3" + version: 5.31.0 + resolution: "swagger-ui-react@npm:5.31.0" dependencies: "@babel/runtime-corejs3": "npm:^7.27.1" "@scarf/scarf": "npm:=1.4.0" @@ -48070,7 +48070,7 @@ __metadata: peerDependencies: react: ">=16.8.0 <20" react-dom: ">=16.8.0 <20" - checksum: 10/6f466414072806e60a3e36d58029b0679cd80cd9488e79a2142bebcd0829efa587f1b3249c2feab3ab692b3a70f5b90038f3fd14430f8ae41c6ec904f8041f79 + checksum: 10/328a531cb12639b1a72eedc5ab9532b9cbbf1ae2944b12476475c3263d1b9fee142c9ad5f44a610608689b05a91cc3456d129caa46235cef2fe16a0fb94869b4 languageName: node linkType: hard From 0098d15aa8775ba6a8276acff6923afa597bc30e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 13:57:33 +0000 Subject: [PATCH 045/211] chore(deps): bump undici from 5.29.0 to 7.19.0 Bumps [undici](https://github.com/nodejs/undici) from 5.29.0 to 7.19.0. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v5.29.0...v7.19.0) --- updated-dependencies: - dependency-name: undici dependency-version: 7.19.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 304f819d75..2e56850968 100644 --- a/yarn.lock +++ b/yarn.lock @@ -49552,9 +49552,9 @@ __metadata: linkType: hard "undici@npm:^7.16.0, undici@npm:^7.2.3": - version: 7.18.2 - resolution: "undici@npm:7.18.2" - checksum: 10/7d8b921717f5a11fe7e3631dd46ea6dbd80173bdbd454c0115d2c52595352b4a620a3e48c02c40e8f32a416b8bd0028b027cbc8ddebc2d9cdc7a283d50399e3c + version: 7.19.0 + resolution: "undici@npm:7.19.0" + checksum: 10/5a60b11ed23b2eea74e71123595ffe2b91005b80295051e1ba89119ee9b547ea09c395fd992d7609e0507452177853a8c0405d1a9e7938b2e17292b7aeaba6b0 languageName: node linkType: hard From a761789575b5866a535d9dde121017018ba0908d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 22 Jan 2026 15:32:00 +0100 Subject: [PATCH 046/211] SECURITY.md: update Discord notification channel Signed-off-by: Patrik Oldsberg --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index 5c6c8106d5..46f36f0a67 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,7 +8,7 @@ See our [Release & Versioning Policy](https://backstage.io/docs/overview/version Please report sensitive security issues via Spotify's [bug-bounty program](https://hackerone.com/spotify) rather than GitHub. -If you have questions about a potential vulnerability, please reach out on Discord by asking for a maintainer in the `#support` channel, or via direct message to a maintainer. +If you have questions about a potential vulnerability, please reach out on Discord by asking for a maintainer in the `#security` channel, or via direct message to a maintainer. ## Remediation and Notification Process From 6edd37cb21785eef140ddaf8638812cb2f9b703d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 14:53:17 +0000 Subject: [PATCH 047/211] chore(deps): update actions/setup-python digest to a309ff8 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_microsite.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 472c710984..c62152a6e6 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -222,7 +222,7 @@ jobs: uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: node-version: 22.x - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: '3.9' From 063272149a23c690f986f696a8713d4397258e7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:44:15 +0000 Subject: [PATCH 048/211] chore(deps): update dependency winston to v3.19.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index f6377f3d6e..eb2266f248 100644 --- a/yarn.lock +++ b/yarn.lock @@ -51026,8 +51026,8 @@ __metadata: linkType: hard "winston@npm:^3.13.0, winston@npm:^3.2.1": - version: 3.18.3 - resolution: "winston@npm:3.18.3" + version: 3.19.0 + resolution: "winston@npm:3.19.0" dependencies: "@colors/colors": "npm:^1.6.0" "@dabh/diagnostics": "npm:^2.0.8" @@ -51040,7 +51040,7 @@ __metadata: stack-trace: "npm:0.0.x" triple-beam: "npm:^1.3.0" winston-transport: "npm:^4.9.0" - checksum: 10/0d94690e051c625ff5a2731b3057ddaa709445cc791c707c56f99ad7b5bf3eebe634656f707c01c9409dc21dbc118903464f80e29c0cb438b1cc2739c0135e51 + checksum: 10/8279e221d8017da601a725939d31d65de71504d8328051312a85b1b4d7ddc68634329f8d611fb1ff91cb797643409635f3e97ef5b4a650c587639e080af76b7b languageName: node linkType: hard From 1e669ccd644a3504db7db5411c0135511b09e4bc Mon Sep 17 00:00:00 2001 From: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Date: Thu, 22 Jan 2026 08:45:06 -0700 Subject: [PATCH 049/211] chore: migrate audit events docs to docsite (#32143) * chore: migrate audit events docs to docsite Signed-off-by: aramissennyeydd * change headers Signed-off-by: aramissennyeydd * fix rebase conflict in sidebars.ts Signed-off-by: aramissennyeydd * correct sidebar Signed-off-by: aramissennyeydd --------- Signed-off-by: aramissennyeydd --- .changeset/rotten-paths-pump.md | 5 ++ .changeset/yellow-ties-dream.md | 5 ++ .../features/software-catalog/audit-events.md | 49 +++++++++++++++++++ .../software-templates/audit-events.md | 32 ++++++++++++ microsite/sidebars.ts | 2 + plugins/catalog-backend/README.md | 46 ----------------- plugins/scaffolder-backend/README.md | 29 ----------- 7 files changed, 93 insertions(+), 75 deletions(-) create mode 100644 .changeset/rotten-paths-pump.md create mode 100644 .changeset/yellow-ties-dream.md create mode 100644 docs/features/software-catalog/audit-events.md create mode 100644 docs/features/software-templates/audit-events.md diff --git a/.changeset/rotten-paths-pump.md b/.changeset/rotten-paths-pump.md new file mode 100644 index 0000000000..3d0031edfe --- /dev/null +++ b/.changeset/rotten-paths-pump.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Migrate audit events reference docs to http://backstage.io/docs. diff --git a/.changeset/yellow-ties-dream.md b/.changeset/yellow-ties-dream.md new file mode 100644 index 0000000000..8326b28319 --- /dev/null +++ b/.changeset/yellow-ties-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Migrate audit events reference docs to http://backstage.io/docs. diff --git a/docs/features/software-catalog/audit-events.md b/docs/features/software-catalog/audit-events.md new file mode 100644 index 0000000000..9fc003f595 --- /dev/null +++ b/docs/features/software-catalog/audit-events.md @@ -0,0 +1,49 @@ +--- +id: audit-events +title: Audit Events +description: Tracking access to your Software Catalog. +--- + +The Catalog backend emits audit events for various operations. Events are grouped logically by `eventId`, with `subEventId` providing further distinction within an operation group. + +## Entity Events + +- **`entity-fetch`**: Retrieves entities. +- **Note:** By default, "low" severity audit events like `entity-fetch` aren't logged because they map to the "debug" level, while Backstage defaults to "info" level logging. To see `entity-fetch` events, update your `app-config.yaml` by setting `backend.auditor.severityLogLevelMappings.low: info`. See the [Auditor Service documentation](https://backstage.io/docs/backend-system/core-services/auditor/#severity-levels-and-default-mappings) for details on severity mappings. + + Filter on `queryType`. + + - **`all`**: Fetching all entities. (GET `/entities`) + - **`by-id`**: Fetching a single entity using its UID. (GET `/entities/by-uid/:uid`) + - **`by-name`**: Fetching a single entity using its kind, namespace, and name. (GET `/entities/by-name/:kind/:namespace/:name`) + - **`by-query`**: Fetching multiple entities using a filter query. (GET `/entities/by-query`) + - **`by-refs`**: Fetching a batch of entities by their entity refs. (POST `/entities/by-refs`) + - **`ancestry`**: Fetching the ancestry of an entity. (GET `/entities/by-name/:kind/:namespace/:name/ancestry`) + +- **`entity-mutate`**: Modifies entities. + + Filter on `actionType`. + + - **`delete`**: Deleting a single entity. Note: this will not be a permanent deletion and the entity will be restored if the parent location is still present in the catalog. (DELETE `/entities/by-uid/:uid`) + - **`refresh`**: Scheduling an entity refresh. (POST `/entities/refresh`) + +- **`entity-validate`**: Validates an entity. (POST `/entities/validate`) + +- **`entity-facets`**: Retrieves entity facets. (GET `/entity-facets`) + +## Location Events + +- **`location-fetch`**: Retrieves locations. + + Filter on `actionType`. + + - **`all`**: Fetching all locations. (GET `/locations`) + - **`by-id`**: Fetching a single location by ID. (GET `/locations/:id`) + - **`by-entity`**: Fetching locations associated with an entity ref. (GET `/locations/by-entity`) + +- **`location-mutate`**: Modifies locations. + + - **`create`**: Creating a new location. (POST `/locations`) + - **`delete`**: Deleting a location and its associated entities. (DELETE `/locations/:id`) + +- **`location-analyze`**: Analyzes a location. (POST `/locations/analyze`) diff --git a/docs/features/software-templates/audit-events.md b/docs/features/software-templates/audit-events.md new file mode 100644 index 0000000000..cf50d98317 --- /dev/null +++ b/docs/features/software-templates/audit-events.md @@ -0,0 +1,32 @@ +--- +id: audit-events +title: Audit Events +description: Tracking access to your Scaffolder. +--- + +The Scaffolder backend emits audit events for various operations. Events are grouped logically by `eventId`, with `subEventId` providing further distinction when needed. + +## Template Events + +- **`template-parameter-schema`**: Retrieves template parameter schemas. (GET `/v2/templates/:namespace/:kind/:name/parameter-schema`) + +## Action Events + +- **`action-fetch`**: Retrieves installed actions. (GET `/v2/actions`) + +## Task Events + +- **`task`**: Operations related to Scaffolder tasks. + + Filter on `actionType`. + + - **`create`**: Creates a new task. (POST `/v2/tasks`) + - **`list`**: Fetches details of all tasks. (GET `/v2/tasks`) + - **`get`**: Fetches details of a specific task. (GET `/v2/tasks/:taskId`) + - **`cancel`**: Cancels a running task. (POST `/v2/tasks/:taskId/cancel`) + - **`retry`**: Retries a failed task. (POST `/v2/tasks/:taskId/retry`) + - **`stream`**: Retrieves a stream of task logs. (GET `/v2/tasks/:taskId/eventstream`) + - **`events`**: Retrieves a snapshot of task logs. (GET `/v2/tasks/:taskId/events`) + - **`dry-run`**: Creates a dry-run task. (POST `/v2/dry-run`) All audit logs for events associated with dry runs have the `meta.isDryLog` flag set to `true`. + - **`stale-cancel`**: Automated cancellation of stale tasks. + - **`execute`**: Tracks the initiation and completion of a real scaffolder task execution. This event will not occur during dry runs. diff --git a/microsite/sidebars.ts b/microsite/sidebars.ts index 33bee593f8..5432edd6db 100644 --- a/microsite/sidebars.ts +++ b/microsite/sidebars.ts @@ -247,6 +247,7 @@ export default { 'features/software-catalog/extending-the-model', 'features/software-catalog/external-integrations', 'features/software-catalog/catalog-customization', + 'features/software-catalog/audit-events', { type: 'category', label: 'API', @@ -289,6 +290,7 @@ export default { 'features/software-templates/dry-run-testing', 'features/software-templates/experimental', 'features/software-templates/templating-extensions', + 'features/software-templates/audit-events', { type: 'category', label: 'API', diff --git a/plugins/catalog-backend/README.md b/plugins/catalog-backend/README.md index 00b88cf060..3d42d90252 100644 --- a/plugins/catalog-backend/README.md +++ b/plugins/catalog-backend/README.md @@ -87,52 +87,6 @@ yarn start This will launch both frontend and backend in the same window, populated with some example entities. -## Audit Events - -The Catalog backend emits audit events for various operations. Events are grouped logically by `eventId`, with `subEventId` providing further distinction within an operation group. - -**Entity Events:** - -- **`entity-fetch`**: Retrieves entities. -- **Note:** By default, "low" severity audit events like `entity-fetch` aren't logged because they map to the "debug" level, while Backstage defaults to "info" level logging. To see `entity-fetch` events, update your `app-config.yaml` by setting `backend.auditor.severityLogLevelMappings.low: info`. See the [Auditor Service documentation](https://backstage.io/docs/backend-system/core-services/auditor/#severity-levels-and-default-mappings) for details on severity mappings. - - Filter on `queryType`. - - - **`all`**: Fetching all entities. (GET `/entities`) - - **`by-id`**: Fetching a single entity using its UID. (GET `/entities/by-uid/:uid`) - - **`by-name`**: Fetching a single entity using its kind, namespace, and name. (GET `/entities/by-name/:kind/:namespace/:name`) - - **`by-query`**: Fetching multiple entities using a filter query. (GET `/entities/by-query`) - - **`by-refs`**: Fetching a batch of entities by their entity refs. (POST `/entities/by-refs`) - - **`ancestry`**: Fetching the ancestry of an entity. (GET `/entities/by-name/:kind/:namespace/:name/ancestry`) - -- **`entity-mutate`**: Modifies entities. - - Filter on `actionType`. - - - **`delete`**: Deleting a single entity. Note: this will not be a permanent deletion and the entity will be restored if the parent location is still present in the catalog. (DELETE `/entities/by-uid/:uid`) - - **`refresh`**: Scheduling an entity refresh. (POST `/entities/refresh`) - -- **`entity-validate`**: Validates an entity. (POST `/entities/validate`) - -- **`entity-facets`**: Retrieves entity facets. (GET `/entity-facets`) - -**Location Events:** - -- **`location-fetch`**: Retrieves locations. - - Filter on `actionType`. - - - **`all`**: Fetching all locations. (GET `/locations`) - - **`by-id`**: Fetching a single location by ID. (GET `/locations/:id`) - - **`by-entity`**: Fetching locations associated with an entity ref. (GET `/locations/by-entity`) - -- **`location-mutate`**: Modifies locations. - - - **`create`**: Creating a new location. (POST `/locations`) - - **`delete`**: Deleting a location and its associated entities. (DELETE `/locations/:id`) - -- **`location-analyze`**: Analyzes a location. (POST `/locations/analyze`) - ## Links - [catalog](https://github.com/backstage/backstage/tree/master/plugins/catalog) diff --git a/plugins/scaffolder-backend/README.md b/plugins/scaffolder-backend/README.md index 3c4a2cb283..1e7dc91384 100644 --- a/plugins/scaffolder-backend/README.md +++ b/plugins/scaffolder-backend/README.md @@ -124,32 +124,3 @@ Default secrets are resolved from environment variables and accessible via `${{ ``` **Security Note:** Secrets are automatically masked in logs and are only available to backend actions, never exposed to the frontend. - -## Audit Events - -The Scaffolder backend emits audit events for various operations. Events are grouped logically by `eventId`, with `subEventId` providing further distinction when needed. - -**Template Events:** - -- **`template-parameter-schema`**: Retrieves template parameter schemas. (GET `/v2/templates/:namespace/:kind/:name/parameter-schema`) - -**Action Events:** - -- **`action-fetch`**: Retrieves installed actions. (GET `/v2/actions`) - -**Task Events:** - -- **`task`**: Operations related to Scaffolder tasks. - - Filter on `actionType`. - - - **`create`**: Creates a new task. (POST `/v2/tasks`) - - **`list`**: Fetches details of all tasks. (GET `/v2/tasks`) - - **`get`**: Fetches details of a specific task. (GET `/v2/tasks/:taskId`) - - **`cancel`**: Cancels a running task. (POST `/v2/tasks/:taskId/cancel`) - - **`retry`**: Retries a failed task. (POST `/v2/tasks/:taskId/retry`) - - **`stream`**: Retrieves a stream of task logs. (GET `/v2/tasks/:taskId/eventstream`) - - **`events`**: Retrieves a snapshot of task logs. (GET `/v2/tasks/:taskId/events`) - - **`dry-run`**: Creates a dry-run task. (POST `/v2/dry-run`) All audit logs for events associated with dry runs have the `meta.isDryLog` flag set to `true`. - - **`stale-cancel`**: Automated cancellation of stale tasks. - - **`execute`**: Tracks the initiation and completion of a real scaffolder task execution. This event will not occur during dry runs. From 12efa16428d2f4d4921b2291205e7fd50371b6cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 16:29:54 +0000 Subject: [PATCH 050/211] chore(deps): update dependency ws to v8.19.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index f6377f3d6e..cd38d67632 100644 --- a/yarn.lock +++ b/yarn.lock @@ -51142,8 +51142,8 @@ __metadata: linkType: hard "ws@npm:*, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.18.2, ws@npm:^8.18.3, ws@npm:^8.8.0": - version: 8.18.3 - resolution: "ws@npm:8.18.3" + version: 8.19.0 + resolution: "ws@npm:8.19.0" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -51152,7 +51152,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10/725964438d752f0ab0de582cd48d6eeada58d1511c3f613485b5598a83680bedac6187c765b0fe082e2d8cc4341fc57707c813ae780feee82d0c5efe6a4c61b6 + checksum: 10/26e4901e93abaf73af9f26a93707c95b4845e91a7a347ec8c569e6e9be7f9df066f6c2b817b2d685544e208207898a750b78461e6e8d810c11a370771450c31b languageName: node linkType: hard From 57d75c90df053a7343184da2d8e837c8d37ff0da Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:19:35 +0000 Subject: [PATCH 051/211] chore(deps): update actions/checkout action to v6.0.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/api-breaking-changes.yml | 2 +- .github/workflows/automate_changeset_feedback.yml | 2 +- .github/workflows/automate_merge_message.yml | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/cleanup_patch-files.yml | 4 ++-- .github/workflows/deploy_docker-image.yml | 2 +- .github/workflows/deploy_microsite.yml | 8 ++++---- .github/workflows/deploy_packages.yml | 2 +- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_canon.yml | 4 ++-- .github/workflows/sync_code-formatting.yml | 2 +- .github/workflows/sync_dependabot-changesets.yml | 2 +- .github/workflows/sync_patch-release.yml | 2 +- .github/workflows/sync_release-manifest.yml | 4 ++-- .github/workflows/sync_renovate-changesets.yml | 2 +- .github/workflows/sync_snyk-github-issues.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/sync_version-packages.yml | 2 +- .github/workflows/verify_accessibility.yml | 2 +- .github/workflows/verify_chromatic.yml | 2 +- .github/workflows/verify_codeql.yml | 2 +- .github/workflows/verify_docs-quality.yml | 2 +- .github/workflows/verify_e2e-linux.yml | 2 +- .github/workflows/verify_e2e-techdocs.yml | 2 +- .github/workflows/verify_e2e-windows.yml | 2 +- .github/workflows/verify_fossa.yml | 2 +- .github/workflows/verify_microsite.yml | 8 ++++---- .github/workflows/verify_microsite_accessibility.yml | 2 +- .github/workflows/verify_windows.yml | 2 +- 29 files changed, 40 insertions(+), 40 deletions(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index 0ca058e383..defc6f6a21 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -18,7 +18,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Fetch the commit that's merged into the base rather than the target ref # This will let us diff only the contents of the PR, without fetching more history diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index 257db3929a..1403bff13b 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -27,7 +27,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Fetch the commit that's merged into the base rather than the target ref # This will let us diff only the contents of the PR, without fetching more history diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml index 83a1e32fcf..d644feb580 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: '${{ github.event.pull_request.merge_commit_sha }}' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8aade120a4..8f86e45dc8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 @@ -68,7 +68,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 @@ -210,7 +210,7 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: fetch master branch run: git fetch origin master diff --git a/.github/workflows/cleanup_patch-files.yml b/.github/workflows/cleanup_patch-files.yml index 3dc0e27837..4d95459ab6 100644 --- a/.github/workflows/cleanup_patch-files.yml +++ b/.github/workflows/cleanup_patch-files.yml @@ -22,7 +22,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} @@ -89,7 +89,7 @@ jobs: - name: Checkout master if: steps.extract-pr-numbers.outputs.has_pr_numbers == 'true' - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: master token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 47432c69c6..484d102b7a 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -25,7 +25,7 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: path: backstage ref: ${{ github.event.client_payload.version && env.RELEASE_VERSION || github.ref }} diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 2c9ebdf36a..de394b8385 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -54,7 +54,7 @@ jobs: result-encoding: string - name: checkout latest release - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: refs/tags/${{ steps.find-release.outputs.result }} @@ -129,7 +129,7 @@ jobs: egress-policy: audit - name: checkout master - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js 22.x uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 @@ -230,7 +230,7 @@ jobs: # Stable docs - name: checkout latest release - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: refs/tags/${{ needs.stable.outputs.release }} @@ -258,7 +258,7 @@ jobs: # Next docs - name: checkout master - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: clean: false diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index cff0e43e81..b795f658ad 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -68,7 +68,7 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 9c3a749fcb..08f6198757 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -34,7 +34,7 @@ jobs: egress-policy: audit - name: 'Checkout code' - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/sync_canon.yml b/.github/workflows/sync_canon.yml index 76841e017d..851618cd3b 100644 --- a/.github/workflows/sync_canon.yml +++ b/.github/workflows/sync_canon.yml @@ -13,7 +13,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js 22.x uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 @@ -27,7 +27,7 @@ jobs: cache-prefix: ${{ runner.os }}-v22.x - name: Checkout backstage/docs-ui - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: backstage/docs-ui path: bui-external-docs diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index 95ec0893d2..ada0388908 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -14,7 +14,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Fetch changes to previous commit - required for 'only_changed' in Prettier action fetch-depth: 0 diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml index 9e894631cc..ebe1635aef 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_patch-release.yml b/.github/workflows/sync_patch-release.yml index 4d1e300751..8fafac8568 100644 --- a/.github/workflows/sync_patch-release.yml +++ b/.github/workflows/sync_patch-release.yml @@ -30,7 +30,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 20000 fetch-tags: true diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index 60a4a5141a..5e7d804bc4 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -13,7 +13,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # 'v' prefix is added here for the tag, we keep it out of the manifest logic ref: v${{ github.event.client_payload.version }} @@ -35,7 +35,7 @@ jobs: # Checkout backstage/versions into /backstage/versions, which is where store the output - name: Checkout versions - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: backstage/versions path: versions diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index 07f2e24697..339cfa509a 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index c0edb98ec6..bbd902aefb 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js 22.x uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index c70f9f4d82..207ad64b49 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -29,7 +29,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Monitor and Synchronize Snyk Policies uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master with: diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 4314fdba38..20374b24cf 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 20000 fetch-tags: true diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 8dbf5e3800..95cfaa44c2 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -24,7 +24,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js 22.x uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 with: diff --git a/.github/workflows/verify_chromatic.yml b/.github/workflows/verify_chromatic.yml index 6facbd2049..8c4dcbf4b5 100644 --- a/.github/workflows/verify_chromatic.yml +++ b/.github/workflows/verify_chromatic.yml @@ -29,7 +29,7 @@ jobs: egress-policy: audit - name: Checkout code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 10000 # Required to retrieve git history diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index cf8040fc43..8999824dd6 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -47,7 +47,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 8810f05a16..c4979a6582 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # Vale does not support file excludes, so we use the script to generate a list of files instead # The action also does not allow args or a local config file to be passed in, so the files array diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index b309e0d580..41ae7c06d0 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -47,7 +47,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Configure Git run: | diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 64d4182f3a..6a7a4b2af7 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -36,7 +36,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 1d9e707075..bd9b190a54 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -44,7 +44,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Configure Git run: | diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index 681ac2c07a..4966435eac 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -19,7 +19,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Fossa run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash" diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 472c710984..5e6ea86182 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -59,7 +59,7 @@ jobs: result-encoding: string - name: checkout latest release - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: ref: refs/tags/${{ steps.find-release.outputs.result }} @@ -131,7 +131,7 @@ jobs: egress-policy: audit - name: checkout master - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js 22.x uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 @@ -216,7 +216,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js 22.x uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 @@ -264,7 +264,7 @@ jobs: run: git clean -fdx docs/ - name: checkout master - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: clean: false diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml index d5505b3b76..4ff24ac73c 100644 --- a/.github/workflows/verify_microsite_accessibility.yml +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Use Node.js 22.x uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index b85cb75af1..345824e67a 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -33,7 +33,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 From ac91d6fccb69a79d6098d91e3810daa1355b7b55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 20:21:18 +0000 Subject: [PATCH 052/211] chore(deps): update actions/setup-python action to v6.2.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/verify_e2e-techdocs.yml | 2 +- .github/workflows/verify_e2e-windows.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 6a7a4b2af7..d01b4f9c3e 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -37,7 +37,7 @@ jobs: egress-policy: audit - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index bd9b190a54..561963cc05 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -58,7 +58,7 @@ jobs: registry-url: https://registry.npmjs.org/ # Needed for auth - name: setup python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: '3.10' From c4cae01af8c36ea722116b959305ca1052eb8cef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Jan 2026 20:25:36 +0000 Subject: [PATCH 053/211] chore(deps): update docker/setup-buildx-action action to v3.12.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/deploy_docker-image.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 484d102b7a..75e0a759df 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -59,7 +59,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - name: Build and push uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 From 2ee354a36c3925598eead43bf00f0f51e824ca77 Mon Sep 17 00:00:00 2001 From: Phil Kuang Date: Thu, 22 Jan 2026 11:28:38 -0500 Subject: [PATCH 054/211] refactor(pg-search): return numberOfResults with queries Signed-off-by: Phil Kuang --- .changeset/lucky-masks-doubt.md | 5 +++++ plugins/search-backend-module-pg/report.api.md | 1 + .../src/PgSearchEngine/PgSearchEngine.test.ts | 6 ++++++ .../src/PgSearchEngine/PgSearchEngine.ts | 7 ++++++- .../src/database/DatabaseDocumentStore.test.ts | 12 ++++++++++++ .../src/database/DatabaseDocumentStore.ts | 1 + .../search-backend-module-pg/src/database/types.ts | 4 ++++ 7 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 .changeset/lucky-masks-doubt.md diff --git a/.changeset/lucky-masks-doubt.md b/.changeset/lucky-masks-doubt.md new file mode 100644 index 0000000000..b20633308f --- /dev/null +++ b/.changeset/lucky-masks-doubt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-pg': patch +--- + +Return `numberOfResults` count with search query responses diff --git a/plugins/search-backend-module-pg/report.api.md b/plugins/search-backend-module-pg/report.api.md index 67e60b7822..317547cbdc 100644 --- a/plugins/search-backend-module-pg/report.api.md +++ b/plugins/search-backend-module-pg/report.api.md @@ -89,6 +89,7 @@ export interface DocumentResultRow { document: IndexableDocument; // (undocumented) highlight: IndexableDocument; + total_count: string; // (undocumented) type: string; } diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts index bc3df76692..9c77a04e44 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.test.ts @@ -193,6 +193,7 @@ describe('PgSearchEngine', () => { database.transaction.mockImplementation(fn => fn(tx)); database.query.mockResolvedValue([ { + total_count: '1', document: { title: 'Hello World', text: 'Lorem Ipsum', @@ -212,6 +213,7 @@ describe('PgSearchEngine', () => { }); expect(results).toEqual({ + numberOfResults: 1, results: [ { document: { @@ -251,6 +253,7 @@ describe('PgSearchEngine', () => { Array(30) .fill(0) .map((_, i) => ({ + total_count: '30', document: { title: 'Hello World', text: 'Lorem Ipsum', @@ -270,6 +273,7 @@ describe('PgSearchEngine', () => { }); expect(results).toEqual({ + numberOfResults: 30, results: Array(25) .fill(0) .map((_, i) => ({ @@ -309,6 +313,7 @@ describe('PgSearchEngine', () => { Array(30) .fill(0) .map((_, i) => ({ + total_count: '30', document: { title: 'Hello World', text: 'Lorem Ipsum', @@ -330,6 +335,7 @@ describe('PgSearchEngine', () => { }); expect(results).toEqual({ + numberOfResults: 30, results: Array(30) .fill(0) .map((_, i) => ({ diff --git a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts index 1a024be33c..ea75b1a739 100644 --- a/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts +++ b/plugins/search-backend-module-pg/src/PgSearchEngine/PgSearchEngine.ts @@ -269,7 +269,12 @@ export class PgSearchEngine implements SearchEngine { }), ); - return { results, nextPageCursor, previousPageCursor }; + return { + results, + numberOfResults: rows.length > 0 ? parseInt(rows[0].total_count, 10) : 0, + nextPageCursor, + previousPageCursor, + }; } } diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts index ef249fa33d..7ef48d8398 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.test.ts @@ -274,6 +274,7 @@ describe('DatabaseDocumentStore', () => { expect(rows).toEqual([ { + total_count: '2', document: { location: 'LOCATION-1', text: 'Hello World', @@ -320,6 +321,7 @@ describe('DatabaseDocumentStore', () => { expect(rows).toEqual([ { + total_count: '2', document: { location: 'LOCATION-1', text: 'Around the world', @@ -329,6 +331,7 @@ describe('DatabaseDocumentStore', () => { type: 'test', }, { + total_count: '2', document: { location: 'LOCATION-1', text: 'Hello World', @@ -382,6 +385,7 @@ describe('DatabaseDocumentStore', () => { expect(rows).toEqual([ { + total_count: '1', document: { location: 'LOCATION-1', text: 'Hello World', @@ -436,6 +440,7 @@ describe('DatabaseDocumentStore', () => { expect(rows).toEqual([ { + total_count: '1', document: { location: 'LOCATION-1', text: 'Hello World', @@ -503,6 +508,7 @@ describe('DatabaseDocumentStore', () => { expect(rows).toEqual([ { + total_count: '4', document: { location: 'LOCATION-1', text: 'Hello World', @@ -513,6 +519,7 @@ describe('DatabaseDocumentStore', () => { type: 'my-type', }, { + total_count: '4', document: { location: 'LOCATION-1', text: 'Hello World', @@ -523,6 +530,7 @@ describe('DatabaseDocumentStore', () => { type: 'my-type', }, { + total_count: '4', document: { location: 'LOCATION-1', text: 'Hello World', @@ -533,6 +541,7 @@ describe('DatabaseDocumentStore', () => { type: 'my-type', }, { + total_count: '4', document: { location: 'LOCATION-1', text: 'Hello World', @@ -585,6 +594,7 @@ describe('DatabaseDocumentStore', () => { expect(rows).toEqual([ { + total_count: '1', document: { location: 'LOCATION-1', text: 'Hello World', @@ -635,6 +645,7 @@ describe('DatabaseDocumentStore', () => { expect(rows).toEqual([ { + total_count: '2', document: { title: 'Lorem Ipsum', text: 'Hello World', @@ -645,6 +656,7 @@ describe('DatabaseDocumentStore', () => { type: 'my-type', }, { + total_count: '2', document: { title: 'Dolor sit amet', text: 'Hello World', diff --git a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts index 21512361a9..bd63756b06 100644 --- a/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts +++ b/plugins/search-backend-module-pg/src/database/DatabaseDocumentStore.ts @@ -215,6 +215,7 @@ export class DatabaseDocumentStore implements DatabaseStore { } query.select('type', 'document'); + query.select(tx.raw('COUNT(*) OVER() AS total_count')); if (pgTerm && options.useHighlight) { const headlineOptions = `MaxWords=${options.maxWords}, MinWords=${options.minWords}, ShortWord=${options.shortWord}, HighlightAll=${options.highlightAll}, MaxFragments=${options.maxFragments}, FragmentDelimiter=${options.fragmentDelimiter}, StartSel=${options.preTag}, StopSel=${options.postTag}`; diff --git a/plugins/search-backend-module-pg/src/database/types.ts b/plugins/search-backend-module-pg/src/database/types.ts index f8529fbebe..5012e1d792 100644 --- a/plugins/search-backend-module-pg/src/database/types.ts +++ b/plugins/search-backend-module-pg/src/database/types.ts @@ -58,6 +58,10 @@ export interface RawDocumentRow { /** @public */ export interface DocumentResultRow { + /** + * Total number of documents matching the query, regardless of pagination. + */ + total_count: string; document: IndexableDocument; type: string; highlight: IndexableDocument; From f2612c20e5b8b994621d11e771a386d9bee17a4e Mon Sep 17 00:00:00 2001 From: Yash Oswal Date: Fri, 23 Jan 2026 13:00:32 +0530 Subject: [PATCH 055/211] fix(devtools): improve permission feedback in ScheduledTasksContent Updated the `ScheduledTasksContent` component to provide better visual feedback when a user lacks permission to run a task. A new `CreateNotAllowed` component displays a disabled block icon with a tooltip instead of the default error behavior. Signed-off-by: Yash Oswal --- .changeset/small-jars-lick.md | 5 +++++ .../ScheduledTasksContent/ScheduledTasksContent.tsx | 13 ++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 .changeset/small-jars-lick.md diff --git a/.changeset/small-jars-lick.md b/.changeset/small-jars-lick.md new file mode 100644 index 0000000000..b2160eb7f7 --- /dev/null +++ b/.changeset/small-jars-lick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-devtools': patch +--- + +Fixes an issue where a user lacking permission to schedule tasks can now easily see the issue through a custom icon + tooltip. diff --git a/plugins/devtools/src/components/Content/ScheduledTasksContent/ScheduledTasksContent.tsx b/plugins/devtools/src/components/Content/ScheduledTasksContent/ScheduledTasksContent.tsx index 2d4abd56ef..7517cf8ea5 100644 --- a/plugins/devtools/src/components/Content/ScheduledTasksContent/ScheduledTasksContent.tsx +++ b/plugins/devtools/src/components/Content/ScheduledTasksContent/ScheduledTasksContent.tsx @@ -35,6 +35,7 @@ import { alertApiRef, configApiRef, useApi } from '@backstage/core-plugin-api'; import RefreshIcon from '@material-ui/icons/Refresh'; import NightsStay from '@material-ui/icons/NightsStay'; import ErrorIcon from '@material-ui/icons/Error'; +import BlockIcon from '@material-ui/icons/Block'; import CircularProgress from '@material-ui/core/CircularProgress'; import { ScheduledTaskDetailPanel } from './ScheduledTaskDetailedPanel'; import { RequirePermission } from '@backstage/plugin-permission-react'; @@ -89,6 +90,12 @@ const StatusDisplay = ({ ); +const CreateNotAllowed = () => ( + + + +); + /** @public */ export const ScheduledTasksContent = () => { const classes = useStyles(); @@ -198,7 +205,10 @@ export const ScheduledTasksContent = () => { { title: 'Actions', render: (rowData: TaskApiTasksResponse) => ( - + } + > { ), sorting: false, width: '10%', + align: 'center', }, ]; From 4d50e1f051157b06b4483d650462129ecc285ed7 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 23 Jan 2026 11:26:59 +0100 Subject: [PATCH 056/211] app-visualizer: improve rendering performance of details page Signed-off-by: Patrik Oldsberg --- .changeset/legal-impalas-shine.md | 5 + .../AppVisualizerPage/DetailedVisualizer.tsx | 358 ++++++++++++------ 2 files changed, 255 insertions(+), 108 deletions(-) create mode 100644 .changeset/legal-impalas-shine.md diff --git a/.changeset/legal-impalas-shine.md b/.changeset/legal-impalas-shine.md new file mode 100644 index 0000000000..f9d54690dc --- /dev/null +++ b/.changeset/legal-impalas-shine.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app-visualizer': patch +--- + +Improved rendering performance of the details page. diff --git a/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx b/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx index 1d38ec8267..8881904819 100644 --- a/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx +++ b/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx @@ -31,6 +31,7 @@ import { RiCloseCircleLine as DisabledIcon, } from '@remixicon/react'; import { Focusable } from 'react-aria-components'; +import { memo, useMemo, useState, useEffect, useRef } from 'react'; function getContrastColor(bgColor: string): string { const hex = bgColor.replace('#', ''); @@ -105,131 +106,233 @@ function getFullPath(node?: AppNode): string { return getFullPath(parent) + part; } -function Output(props: { dataRef: ExtensionDataRef; node?: AppNode }) { - const { dataRef, node } = props; - const { id } = dataRef; - const instance = node?.instance; - - const routeResolutionApi = useApi(routeResolutionApiRef); - - const { backgroundColor, color } = getOutputColor(id); - - const chipStyle: React.CSSProperties = { - height: 20, - padding: '0 10px', - borderRadius: '10px', - color, - backgroundColor, - display: 'flex', - alignItems: 'center', - fontWeight: - 'var(--bui-font-weight-regular)' as React.CSSProperties['fontWeight'], - }; - - if (id === coreExtensionData.routeRef.id && node) { - try { - const routeRef = props.node?.instance?.getData( - coreExtensionData.routeRef, - ); - const link = routeRef && routeResolutionApi.resolve(routeRef)?.(); - if (link) { - return ( - - - link - - {id} - - ); - } - } catch { - /* ignore */ +function collectAllNodes(node: AppNode): AppNode[] { + const nodes: AppNode[] = [node]; + for (const children of node.edges.attachments.values()) { + for (const child of children) { + nodes.push(...collectAllNodes(child)); } } - - let tooltip = id; - let text: string | undefined = undefined; - if (id === coreExtensionData.routePath.id) { - text = String(instance?.getData(dataRef) ?? ''); - tooltip = getFullPath(node); - } - - return ( - - - {text} - - {tooltip} - - ); + return nodes; } -function Attachments(props: { +function useProgressiveRender(rootNode: AppNode) { + const [renderedNodes, setRenderedNodes] = useState>(new Set()); + const [isComplete, setIsComplete] = useState(false); + const processingRef = useRef(false); + const timeoutRef = useRef | null>(null); + + useEffect(() => { + if (processingRef.current) { + return undefined; + } + + processingRef.current = true; + const allNodes = collectAllNodes(rootNode); + + const batchSize = 10; + let currentIndex = 0; + const rendered = new Set(); + + const processBatch = () => { + const endIndex = Math.min(currentIndex + batchSize, allNodes.length); + for (let i = currentIndex; i < endIndex; i++) { + rendered.add(allNodes[i].spec.id); + } + currentIndex = endIndex; + + setRenderedNodes(new Set(rendered)); + + if (currentIndex < allNodes.length) { + timeoutRef.current = setTimeout(processBatch, 0); + } else { + setIsComplete(true); + processingRef.current = false; + timeoutRef.current = null; + } + }; + + rendered.add(rootNode.spec.id); + setRenderedNodes(new Set(rendered)); + currentIndex = 1; + + if (allNodes.length > 1) { + timeoutRef.current = setTimeout(processBatch, 0); + } else { + setIsComplete(true); + processingRef.current = false; + } + + return () => { + processingRef.current = false; + if (timeoutRef.current !== null) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + }; + }, [rootNode]); + + return { renderedNodes, isComplete }; +} + +const Output = memo( + function Output(props: { + dataRef: ExtensionDataRef; + node?: AppNode; + }) { + const { dataRef, node } = props; + const { id } = dataRef; + const instance = node?.instance; + + const routeResolutionApi = useApi(routeResolutionApiRef); + + const { backgroundColor, color } = getOutputColor(id); + + const chipStyle: React.CSSProperties = { + height: 20, + padding: '0 10px', + borderRadius: '10px', + color, + backgroundColor, + display: 'flex', + alignItems: 'center', + fontWeight: + 'var(--bui-font-weight-regular)' as React.CSSProperties['fontWeight'], + }; + + if (id === coreExtensionData.routeRef.id && node) { + try { + const routeRef = props.node?.instance?.getData( + coreExtensionData.routeRef, + ); + const link = routeRef && routeResolutionApi.resolve(routeRef)?.(); + if (link) { + return ( + + + link + + {id} + + ); + } + } catch { + /* ignore */ + } + } + + let tooltip = id; + let text: string | undefined = undefined; + if (id === coreExtensionData.routePath.id) { + text = String(instance?.getData(dataRef) ?? ''); + tooltip = getFullPath(node); + } + + return ( + + + {text} + + {tooltip} + + ); + }, + (prevProps, nextProps) => { + return ( + prevProps.dataRef.id === nextProps.dataRef.id && + prevProps.node?.spec.id === nextProps.node?.spec.id + ); + }, +); + +function AttachmentsComponent(props: { node: AppNode; enabled: boolean; depth: number; + renderedNodes: Set; }) { - const { node, depth } = props; + const { node, depth, renderedNodes } = props; const { attachments } = node.edges; + const sortedAttachments = useMemo(() => { + return [...attachments.entries()].sort(([a], [b]) => a.localeCompare(b)); + }, [attachments]); + if (attachments.size === 0) { return null; } return ( - {[...attachments.entries()] - .sort(([a], [b]) => a.localeCompare(b)) - .map(([key, children], idx) => { - return ( - - - -
{key}
-
- - {children.map(childNode => ( - - ))} - -
- ); - })} + {sortedAttachments.map(([key, children], idx) => { + return ( + + + +
{key}
+
+ + {children.map(childNode => ( + + ))} + +
+ ); + })}
); } -function Extension(props: { node: AppNode; depth: number }) { - const { node, depth } = props; +function ExtensionComponent(props: { + node: AppNode; + depth: number; + renderedNodes: Set; +}) { + const { node, depth, renderedNodes } = props; const enabled = Boolean(node.instance); - const dataRefs = node.instance && [...node.instance.getDataRefs()]; - // Build tooltip text - const tooltipParts = []; - let currentNode = node; - tooltipParts.push(currentNode.spec.id); - while (currentNode.edges.attachedTo) { - const input = currentNode.edges.attachedTo.input; - currentNode = currentNode.edges.attachedTo.node; - tooltipParts.push(`${currentNode.spec.id} [${input}]`); + const tooltipText = useMemo(() => { + const tooltipParts = []; + let currentNode = node; + tooltipParts.push(currentNode.spec.id); + while (currentNode.edges.attachedTo) { + const input = currentNode.edges.attachedTo.input; + currentNode = currentNode.edges.attachedTo.node; + tooltipParts.push(`${currentNode.spec.id} [${input}]`); + } + tooltipParts.reverse(); + return tooltipParts.join('\n'); + }, [node]); + + const sortedDataRefs = useMemo(() => { + if (!node.instance) { + return []; + } + const dataRefs = [...node.instance.getDataRefs()]; + return dataRefs.sort((a, b) => a.id.localeCompare(b.id)); + }, [node.instance]); + + const shouldRender = renderedNodes.has(node.spec.id); + + if (!shouldRender) { + return null; } - tooltipParts.reverse(); - const tooltipText = tooltipParts.join('\n'); return ( - {dataRefs && - dataRefs.length > 0 && - dataRefs - .sort((a, b) => a.id.localeCompare(b.id)) - .map(ref => )} + {sortedDataRefs.length > 0 && + sortedDataRefs.map(ref => ( + + ))} {!enabled && } - + ); } +const Extension = memo(ExtensionComponent, (prevProps, nextProps) => { + if ( + prevProps.node.spec.id !== nextProps.node.spec.id || + prevProps.depth !== nextProps.depth + ) { + return false; + } + + const nodeId = prevProps.node.spec.id; + const wasRendered = prevProps.renderedNodes.has(nodeId); + const isRendered = nextProps.renderedNodes.has(nodeId); + + if (wasRendered !== isRendered) { + return false; + } + + if (prevProps.renderedNodes.size !== nextProps.renderedNodes.size) { + return false; + } + + for (const children of prevProps.node.edges.attachments.values()) { + for (const child of children) { + const wasChildRendered = prevProps.renderedNodes.has(child.spec.id); + const isChildRendered = nextProps.renderedNodes.has(child.spec.id); + if (wasChildRendered !== isChildRendered) { + return false; + } + } + } + + return true; +}); + const legendMap = { 'React Element': coreExtensionData.reactElement, 'Utility API': ApiBlueprint.dataRefs.factory, @@ -305,10 +445,12 @@ function Legend() { } export function DetailedVisualizer({ tree }: { tree: AppTree }) { + const { renderedNodes } = useProgressiveRender(tree.root); + return ( - + Date: Thu, 22 Jan 2026 14:22:56 +0100 Subject: [PATCH 057/211] frontend-plugin-api: add new `internal` option for extension inputs Signed-off-by: Patrik Oldsberg --- .changeset/fuzzy-shrimps-refuse.md | 5 + .changeset/many-bags-brake.md | 5 + .changeset/quiet-humans-hammer.md | 13 ++ packages/frontend-app-api/report.api.md | 8 ++ .../src/tree/instantiateAppNodeTree.test.ts | 114 +++++++++++++----- .../src/tree/instantiateAppNodeTree.ts | 25 +++- .../src/wiring/createErrorCollector.ts | 8 ++ .../src/maybeCreateErrorPage.tsx | 1 + packages/frontend-plugin-api/report.api.md | 6 +- .../src/wiring/createExtensionInput.test.ts | 42 ++++--- .../src/wiring/createExtensionInput.ts | 66 +++++++++- .../src/wiring/resolveInputOverrides.ts | 14 ++- plugins/app/report.api.md | 17 +++ plugins/app/src/extensions/AppNav.tsx | 9 +- plugins/app/src/extensions/AppRoot.tsx | 35 ++---- plugins/app/src/extensions/AppThemeApi.tsx | 14 +-- plugins/app/src/extensions/IconsApi.ts | 14 +-- .../src/extensions/SwappableComponentsApi.ts | 28 ++--- .../app/src/extensions/TranslationsApi.tsx | 15 +-- plugins/catalog/report-alpha.api.md | 7 ++ plugins/devtools/report-alpha.api.md | 1 + plugins/home/report-alpha.api.md | 1 + plugins/scaffolder-react/report-alpha.api.md | 1 + plugins/scaffolder/report-alpha.api.md | 4 + plugins/search/report-alpha.api.md | 6 + plugins/techdocs/report-alpha.api.md | 4 + plugins/user-settings/report-alpha.api.md | 1 + 27 files changed, 315 insertions(+), 149 deletions(-) create mode 100644 .changeset/fuzzy-shrimps-refuse.md create mode 100644 .changeset/many-bags-brake.md create mode 100644 .changeset/quiet-humans-hammer.md diff --git a/.changeset/fuzzy-shrimps-refuse.md b/.changeset/fuzzy-shrimps-refuse.md new file mode 100644 index 0000000000..c2814da631 --- /dev/null +++ b/.changeset/fuzzy-shrimps-refuse.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Implemented support for the `internal` extension input option. diff --git a/.changeset/many-bags-brake.md b/.changeset/many-bags-brake.md new file mode 100644 index 0000000000..91b48d63f4 --- /dev/null +++ b/.changeset/many-bags-brake.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Added a new `internal` option to `createExtensionInput` that marks the input as only allowing attachments from the same plugin. diff --git a/.changeset/quiet-humans-hammer.md b/.changeset/quiet-humans-hammer.md new file mode 100644 index 0000000000..1878473ff0 --- /dev/null +++ b/.changeset/quiet-humans-hammer.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-app': patch +--- + +The deprecation of the following blueprints listed below is not complete. These blueprints must now be provided via an override or a module for the `app` plugin. Extensions from other plugins will now trigger a warning in the app and be ignored. + +- `IconBundleBlueprint` +- `NavContentBlueprint` +- `RouterBlueprint` +- `SignInPageBlueprint` +- `SwappableComponentBlueprint` +- `ThemeBlueprint` +- `TranslationBlueprint` diff --git a/packages/frontend-app-api/report.api.md b/packages/frontend-app-api/report.api.md index 06b5e55278..e8fc019007 100644 --- a/packages/frontend-app-api/report.api.md +++ b/packages/frontend-app-api/report.api.md @@ -59,6 +59,14 @@ export type AppErrorTypes = { inputName: string; }; }; + EXTENSION_INPUT_INTERNAL_IGNORED: { + context: { + node: AppNode; + inputName: string; + extensionId: string; + plugin: FrontendPlugin; + }; + }; EXTENSION_ATTACHMENT_CONFLICT: { context: { node: AppNode; diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index a07fdf352d..8156ae7a12 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -142,6 +142,36 @@ function createV1Extension(opts: { return ext; } +function mirrorInputs(ctx: { + inputs: { + [name in string]: + | undefined + | ResolvedExtensionInput + | Array>; + }; +}) { + return [ + inputMirrorDataRef( + Object.fromEntries( + Object.entries(ctx.inputs).map(([k, v]) => [ + k, + Array.isArray(v) + ? v.map(vi => ({ + node: vi.node, + test: vi.get(testDataRef), + other: vi.get(otherDataRef), + })) + : { + node: v?.node, + test: v?.get(testDataRef), + other: v?.get(otherDataRef), + }, + ]), + ), + ), + ]; +} + describe('instantiateAppNodeTree', () => { describe('v1', () => { const simpleExtension = createV1Extension({ @@ -237,6 +267,60 @@ describe('instantiateAppNodeTree', () => { expect(childNode?.instance).toBeDefined(); }); + it('should ignore non-matching plugin attachments for internal inputs', () => { + const otherPlugin = createFrontendPlugin({ pluginId: 'other' }); + const tree = resolveAppTree( + 'root-node', + [ + makeSpec( + resolveExtensionDefinition( + createExtension({ + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + test: createExtensionInput([testDataRef], { + singleton: true, + internal: true, + }), + }, + output: [inputMirrorDataRef], + factory: mirrorInputs, + }), + { namespace: 'root-node' }, + ), + ), + makeSpec(simpleExtension, { + id: 'child-node-app', + attachTo: { id: 'root-node', input: 'test' }, + }), + makeSpec(simpleExtension, { + id: 'child-node-other', + attachTo: { id: 'root-node', input: 'test' }, + plugin: otherPlugin, + }), + ], + collector, + ); + + instantiateAppNodeTree(tree.root, testApis, collector); + + expect(tree.root.instance?.getData(inputMirrorDataRef)).toMatchObject({ + test: { node: { spec: { id: 'child-node-app' } }, test: 'test' }, + }); + expect(collector.collectErrors()).toEqual([ + { + code: 'EXTENSION_INPUT_INTERNAL_IGNORED', + message: + "extension 'child-node-other' from plugin 'other' attached to input 'test' on 'root-node' was ignored, the input is marked as internal and attached extensions must therefore be provided via an override or a module for the 'app' plugin, not the 'other' plugin", + context: { + node: tree.root, + inputName: 'test', + extensionId: 'child-node-other', + plugin: otherPlugin, + }, + }, + ]); + }); + it('should not instantiate disabled attachments', () => { const tree = resolveAppTree( 'root-node', @@ -783,36 +867,6 @@ describe('instantiateAppNodeTree', () => { { namespace: 'app' }, ); - function mirrorInputs(ctx: { - inputs: { - [name in string]: - | undefined - | ResolvedExtensionInput - | Array>; - }; - }) { - return [ - inputMirrorDataRef( - Object.fromEntries( - Object.entries(ctx.inputs).map(([k, v]) => [ - k, - Array.isArray(v) - ? v.map(vi => ({ - node: vi.node, - test: vi.get(testDataRef), - other: vi.get(otherDataRef), - })) - : { - node: v?.node, - test: v?.get(testDataRef), - other: v?.get(otherDataRef), - }, - ]), - ), - ), - ]; - } - it('should instantiate a single node', () => { const tree = resolveAppTree( 'root-node', diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 04082f8520..85638f316d 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -246,10 +246,32 @@ function resolveV2Inputs( inputMap: { [inputName in string]: ExtensionInput }, attachments: ReadonlyMap, parentCollector: ErrorCollector<{ node: AppNode }>, + node: AppNode, ): ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }> { return mapValues(inputMap, (input, inputName) => { - const attachedNodes = attachments.get(inputName) ?? []; + const allAttachedNodes = attachments.get(inputName) ?? []; const collector = parentCollector.child({ inputName }); + const inputPluginId = node.spec.plugin.id; + + const attachedNodes = input.config.internal + ? allAttachedNodes.filter(attachment => { + const attachmentPluginId = attachment.spec.plugin.id; + if (attachmentPluginId !== inputPluginId) { + collector.report({ + code: 'EXTENSION_INPUT_INTERNAL_IGNORED', + message: + `extension '${attachment.spec.id}' from plugin '${attachmentPluginId}' attached to input '${inputName}' on '${node.spec.id}' was ignored, ` + + `the input is marked as internal and attached extensions must therefore be provided via an override or a module for the '${inputPluginId}' plugin, not the '${attachmentPluginId}' plugin`, + context: { + extensionId: attachment.spec.id, + plugin: attachment.spec.plugin, + }, + }); + return false; + } + return true; + }) + : allAttachedNodes; if (input.config.singleton) { if (attachedNodes.length > 1) { @@ -354,6 +376,7 @@ export function createAppNodeInstance(options: { internalExtension.inputs, attachments, collector, + node, ), }; const outputDataValues = options.extensionFactoryMiddleware diff --git a/packages/frontend-app-api/src/wiring/createErrorCollector.ts b/packages/frontend-app-api/src/wiring/createErrorCollector.ts index dab69ba19e..5f081bc092 100644 --- a/packages/frontend-app-api/src/wiring/createErrorCollector.ts +++ b/packages/frontend-app-api/src/wiring/createErrorCollector.ts @@ -38,6 +38,14 @@ export type AppErrorTypes = { EXTENSION_INPUT_DATA_MISSING: { context: { node: AppNode; inputName: string }; }; + EXTENSION_INPUT_INTERNAL_IGNORED: { + context: { + node: AppNode; + inputName: string; + extensionId: string; + plugin: FrontendPlugin; + }; + }; EXTENSION_ATTACHMENT_CONFLICT: { context: { node: AppNode; inputName: string }; }; diff --git a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx index 6bad97f666..c0fb807deb 100644 --- a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx +++ b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx @@ -22,6 +22,7 @@ const DEFAULT_WARNING_CODES: Array = [ 'EXTENSION_IGNORED', 'INVALID_EXTENSION_CONFIG_KEY', 'EXTENSION_INPUT_DATA_IGNORED', + 'EXTENSION_INPUT_INTERNAL_IGNORED', 'EXTENSION_OUTPUT_IGNORED', ]; diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 1a7f2e3c7a..3d5efd5765 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -604,7 +604,7 @@ export function createExtensionDataRef(): { }): ConfigurableExtensionDataRef; }; -// @public (undocumented) +// @public export function createExtensionInput< UExtensionData extends ExtensionDataRef< unknown, @@ -616,6 +616,7 @@ export function createExtensionInput< TConfig extends { singleton?: boolean; optional?: boolean; + internal?: boolean; }, >( extensionData: Array, @@ -630,6 +631,7 @@ export function createExtensionInput< { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; + internal: TConfig['internal'] extends true ? true : false; } >; @@ -1288,9 +1290,11 @@ export interface ExtensionInput< TConfig extends { singleton: boolean; optional: boolean; + internal?: boolean; } = { singleton: boolean; optional: boolean; + internal?: boolean; }, > { // (undocumented) diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts index 279878542a..9b32ca947f 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts @@ -29,28 +29,28 @@ describe('createExtensionInput', () => { expect(input).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: false, optional: false }, + config: { singleton: false, optional: false, internal: false }, withContext: expect.any(Function), }); const x1: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x2: ExtensionInput< typeof stringDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x3: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: true; optional: false } + { singleton: true; optional: false; internal: false } > = input; // @ts-expect-error const x4: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: true } + { singleton: false; optional: true; internal: false } > = input; unused(x1, x2, x3, x4); @@ -64,7 +64,7 @@ describe('createExtensionInput', () => { expect(inputWithContext).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: false, optional: false }, + config: { singleton: false, optional: false, internal: false }, withContext: expect.any(Function), context, }); @@ -77,28 +77,28 @@ describe('createExtensionInput', () => { expect(input).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: true, optional: false }, + config: { singleton: true, optional: false, internal: false }, withContext: expect.any(Function), }); const x1: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: true; optional: false } + { singleton: true; optional: false; internal: false } > = input; // @ts-expect-error const x2: ExtensionInput< typeof stringDataRef, - { singleton: true; optional: false } + { singleton: true; optional: false; internal: false } > = input; // @ts-expect-error const x3: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x4: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: true } + { singleton: false; optional: true; internal: false } > = input; unused(x1, x2, x3, x4); @@ -112,28 +112,28 @@ describe('createExtensionInput', () => { expect(input).toEqual({ $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], - config: { singleton: true, optional: true }, + config: { singleton: true, optional: true, internal: false }, withContext: expect.any(Function), }); const x1: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: true; optional: true } + { singleton: true; optional: true; internal: false } > = input; // @ts-expect-error const x2: ExtensionInput< typeof stringDataRef, - { singleton: true; optional: true } + { singleton: true; optional: true; internal: false } > = input; // @ts-expect-error const x3: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: false } + { singleton: false; optional: false; internal: false } > = input; // @ts-expect-error const x4: ExtensionInput< typeof stringDataRef | typeof numberDataRef, - { singleton: false; optional: true } + { singleton: false; optional: true; internal: false } > = input; unused(x1, x2, x3, x4); @@ -144,4 +144,14 @@ describe('createExtensionInput', () => { createExtensionInput([stringDataRef, stringDataRef], { singleton: true }), ).toThrow("ExtensionInput may not have duplicate data refs: 'str'"); }); + + it('should create an internal input', () => { + const input = createExtensionInput([stringDataRef], { internal: true }); + expect(input).toEqual({ + $$type: '@backstage/ExtensionInput', + extensionData: [stringDataRef], + config: { singleton: false, optional: false, internal: true }, + withContext: expect.any(Function), + }); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts index 57040c8adf..b4fe9983b9 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts @@ -27,9 +27,14 @@ export interface ExtensionInput< string, { optional?: true } > = ExtensionDataRef, - TConfig extends { singleton: boolean; optional: boolean } = { + TConfig extends { singleton: boolean; optional: boolean; + internal?: boolean; + } = { + singleton: boolean; + optional: boolean; + internal?: boolean; }, > { readonly $$type: '@backstage/ExtensionInput'; @@ -38,10 +43,60 @@ export interface ExtensionInput< readonly replaces?: Array<{ id: string; input: string }>; } -/** @public */ +/** + * Creates a new extension input to be passed to the input map of an extension. + * + * @remarks + * + * Extension inputs created with this function can be passed to any `inputs` map + * as part of creating or overriding an extension. + * + * The array of extension data references defines the data this input expects. + * If the required data is not provided by the attached extension, the + * attachment will fail. + * + * The `config` object can be used to restrict the behavior and shape of the + * input. By default an input will accept zero or more extensions from any + * plugin. The following options are available: + * + * - `singleton`: If set to `true`, only one extension can be attached to the + * input at a time. Additional extension will trigger an app error and be + * ignored. + * - `optional`: If set to `true`, the input is optional and can be omitted, + * this only has an effect if the `singleton` is set to `true`. + * - `internal`: If set to `true`, only extensions from the same plugins will be + * allowed to attach to this input. Other extensions will trigger an app error + * and be ignored. + * + * @param extensionData - The array of extension data references that this input + * expects. + * @param config - The configuration object for the input. + * @returns An extension input declaration. + * @example + * ```ts + * const extension = createExtension({ + * attachTo: { id: 'example-parent', input: 'example-input' }, + * inputs: { + * content: createExtensionInput([coreExtensionData.reactElement], { + * singleton: true, + * }), + * }, + * output: [coreExtensionData.reactElement], + * *factory({ inputs }) { + * const content = inputs.content?.get(coreExtensionData.reactElement); + * yield coreExtensionData.reactElement({content}); + * }, + * }); + * ``` + * @public + */ export function createExtensionInput< UExtensionData extends ExtensionDataRef, - TConfig extends { singleton?: boolean; optional?: boolean }, + TConfig extends { + singleton?: boolean; + optional?: boolean; + internal?: boolean; + }, >( extensionData: Array, config?: TConfig & { replaces?: Array<{ id: string; input: string }> }, @@ -50,6 +105,7 @@ export function createExtensionInput< { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; + internal: TConfig['internal'] extends true ? true : false; } > { if (process.env.NODE_ENV !== 'production') { @@ -81,6 +137,9 @@ export function createExtensionInput< optional: Boolean(config?.optional) as TConfig['optional'] extends true ? true : false, + internal: Boolean(config?.internal) as TConfig['internal'] extends true + ? true + : false, }, replaces: config?.replaces, }; @@ -90,6 +149,7 @@ export function createExtensionInput< { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; + internal: TConfig['internal'] extends true ? true : false; } > { return OpaqueExtensionInput.createInstance(undefined, { diff --git a/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts index b46bb426fa..b9f239842d 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts @@ -37,6 +37,7 @@ export type ResolvedInputValueOverrides< { optional: infer IOptional extends boolean; singleton: boolean; + internal?: boolean; } > ? IOptional extends true @@ -44,7 +45,11 @@ export type ResolvedInputValueOverrides< : KName : never]: TInputs[KName] extends ExtensionInput< infer IDataRefs, - { optional: boolean; singleton: infer ISingleton extends boolean } + { + optional: boolean; + singleton: infer ISingleton extends boolean; + internal?: boolean; + } > ? ISingleton extends true ? Iterable> @@ -56,6 +61,7 @@ export type ResolvedInputValueOverrides< { optional: infer IOptional extends boolean; singleton: boolean; + internal?: boolean; } > ? IOptional extends true @@ -63,7 +69,11 @@ export type ResolvedInputValueOverrides< : never : never]?: TInputs[KName] extends ExtensionInput< infer IDataRefs, - { optional: boolean; singleton: infer ISingleton extends boolean } + { + optional: boolean; + singleton: infer ISingleton extends boolean; + internal?: boolean; + } > ? ISingleton extends true ? Iterable> diff --git a/plugins/app/report.api.md b/plugins/app/report.api.md index c5ee77ef01..a966ddeff1 100644 --- a/plugins/app/report.api.md +++ b/plugins/app/report.api.md @@ -40,6 +40,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; }; @@ -57,6 +58,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; content: ExtensionInput< @@ -64,6 +66,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; }; @@ -89,6 +92,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; content: ExtensionInput< @@ -100,6 +104,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: true; } >; }; @@ -121,6 +126,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: true; } >; signInPage: ExtensionInput< @@ -132,6 +138,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: true; } >; children: ExtensionInput< @@ -139,6 +146,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: true; optional: false; + internal: false; } >; elements: ExtensionInput< @@ -146,6 +154,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; wrappers: ExtensionInput< @@ -157,6 +166,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -182,6 +192,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -218,6 +229,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -262,6 +274,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -471,6 +484,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -592,6 +606,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -669,6 +684,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; @@ -703,6 +719,7 @@ const appPlugin: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: true; } >; }; diff --git a/plugins/app/src/extensions/AppNav.tsx b/plugins/app/src/extensions/AppNav.tsx index 8e76a16781..bba27d1414 100644 --- a/plugins/app/src/extensions/AppNav.tsx +++ b/plugins/app/src/extensions/AppNav.tsx @@ -90,18 +90,11 @@ export const AppNav = createExtension({ content: createExtensionInput([NavContentBlueprint.dataRefs.component], { singleton: true, optional: true, + internal: true, }), }, output: [coreExtensionData.reactElement], *factory({ inputs }) { - if (inputs.content && inputs.content.node.spec.plugin?.id !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: NavContent should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.content.node.spec.id}`, - ); - } - const Content = inputs.content?.get(NavContentBlueprint.dataRefs.component) ?? DefaultNavContent; diff --git a/plugins/app/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx index c9f3035e5f..43e10a2288 100644 --- a/plugins/app/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -59,37 +59,26 @@ export const AppRoot = createExtension({ router: createExtensionInput([RouterBlueprint.dataRefs.component], { singleton: true, optional: true, + internal: true, }), signInPage: createExtensionInput([SignInPageBlueprint.dataRefs.component], { singleton: true, optional: true, + internal: true, }), children: createExtensionInput([coreExtensionData.reactElement], { singleton: true, }), elements: createExtensionInput([coreExtensionData.reactElement]), - wrappers: createExtensionInput([ - AppRootWrapperBlueprint.dataRefs.component, - ]), + wrappers: createExtensionInput( + [AppRootWrapperBlueprint.dataRefs.component], + { + internal: true, + }, + ), }, output: [coreExtensionData.reactElement], factory({ inputs, apis }) { - if (inputs.router && inputs.router.node.spec.plugin?.id !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: Router should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.router.node.spec.id}`, - ); - } - - if (inputs.signInPage && inputs.signInPage.node.spec.plugin?.id !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: SignInPage should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${inputs.signInPage.node.spec.id}`, - ); - } - if (isProtectedApp()) { const identityApi = apis.get(identityApiRef); if (!identityApi) { @@ -117,16 +106,8 @@ export const AppRoot = createExtension({ for (const wrapper of inputs.wrappers) { const Component = wrapper.get(AppRootWrapperBlueprint.dataRefs.component); - const pluginId = wrapper.node.spec.plugin.id; if (Component) { content = {content}; - if (pluginId !== 'app') { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: AppRootWrappers should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${wrapper.node.spec.id}`, - ); - } } } diff --git a/plugins/app/src/extensions/AppThemeApi.tsx b/plugins/app/src/extensions/AppThemeApi.tsx index 2b265ddb53..cf46cab09e 100644 --- a/plugins/app/src/extensions/AppThemeApi.tsx +++ b/plugins/app/src/extensions/AppThemeApi.tsx @@ -37,6 +37,7 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({ inputs: { themes: createExtensionInput([ThemeBlueprint.dataRefs.theme], { replaces: [{ id: 'app', input: 'themes' }], + internal: true, }), }, factory: (originalFactory, { inputs }) => { @@ -45,19 +46,6 @@ export const AppThemeApi = ApiBlueprint.makeWithOverrides({ api: appThemeApiRef, deps: {}, factory: () => { - const nonAppExtensions = inputs.themes.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - const list = nonAppExtensions.map(i => i.node.spec.id).join(', '); - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: Theme should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`, - ); - } - return AppThemeSelector.createWithStorage( inputs.themes.map(i => i.get(ThemeBlueprint.dataRefs.theme)), ); diff --git a/plugins/app/src/extensions/IconsApi.ts b/plugins/app/src/extensions/IconsApi.ts index f10e927161..9be4a3e79a 100644 --- a/plugins/app/src/extensions/IconsApi.ts +++ b/plugins/app/src/extensions/IconsApi.ts @@ -33,6 +33,7 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({ inputs: { icons: createExtensionInput([IconBundleBlueprint.dataRefs.icons], { replaces: [{ id: 'app', input: 'icons' }], + internal: true, }), }, factory: (originalFactory, { inputs }) => { @@ -41,19 +42,6 @@ export const IconsApi = ApiBlueprint.makeWithOverrides({ api: iconsApiRef, deps: {}, factory: () => { - const nonAppExtensions = inputs.icons.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - const list = nonAppExtensions.map(i => i.node.spec.id).join(', '); - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: IconBundle should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`, - ); - } - return new DefaultIconsApi( inputs.icons .map(i => i.get(IconBundleBlueprint.dataRefs.icons)) diff --git a/plugins/app/src/extensions/SwappableComponentsApi.ts b/plugins/app/src/extensions/SwappableComponentsApi.ts index f261d999c0..d72b52a332 100644 --- a/plugins/app/src/extensions/SwappableComponentsApi.ts +++ b/plugins/app/src/extensions/SwappableComponentsApi.ts @@ -28,9 +28,12 @@ import { DefaultSwappableComponentsApi } from '../apis/SwappableComponentsApi'; export const SwappableComponentsApi = ApiBlueprint.makeWithOverrides({ name: 'swappable-components', inputs: { - components: createExtensionInput([ - SwappableComponentBlueprint.dataRefs.component, - ]), + components: createExtensionInput( + [SwappableComponentBlueprint.dataRefs.component], + { + internal: true, + }, + ), }, factory: (originalFactory, { inputs }) => { return originalFactory(defineParams => @@ -38,25 +41,8 @@ export const SwappableComponentsApi = ApiBlueprint.makeWithOverrides({ api: swappableComponentsApiRef, deps: {}, factory: () => { - const nonAppExtensions = inputs.components.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - // eslint-disable-next-line no-console - console.warn( - `SwappableComponents should only be installed as an extension in the app plugin. You can either use appPlugin.override(), or provide a module for the app-plugin with the extension there instead. Invalid extensions: ${nonAppExtensions - .map(i => i.node.spec.id) - .join(', ')}`, - ); - } - - const appExtensions = inputs.components.filter( - i => i.node.spec.plugin?.id === 'app', - ); - return DefaultSwappableComponentsApi.fromComponents( - appExtensions.map(i => + inputs.components.map(i => i.get(SwappableComponentBlueprint.dataRefs.component), ), ); diff --git a/plugins/app/src/extensions/TranslationsApi.tsx b/plugins/app/src/extensions/TranslationsApi.tsx index 2a6017af4b..8421dba9d9 100644 --- a/plugins/app/src/extensions/TranslationsApi.tsx +++ b/plugins/app/src/extensions/TranslationsApi.tsx @@ -34,7 +34,7 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({ inputs: { translations: createExtensionInput( [TranslationBlueprint.dataRefs.translation], - { replaces: [{ id: 'app', input: 'translations' }] }, + { replaces: [{ id: 'app', input: 'translations' }], internal: true }, ), }, factory: (originalFactory, { inputs }) => { @@ -43,19 +43,6 @@ export const TranslationsApi = ApiBlueprint.makeWithOverrides({ api: translationApiRef, deps: { languageApi: appLanguageApiRef }, factory: ({ languageApi }) => { - const nonAppExtensions = inputs.translations.filter( - i => i.node.spec.plugin?.id !== 'app', - ); - - if (nonAppExtensions.length > 0) { - const list = nonAppExtensions.map(i => i.node.spec.id).join(', '); - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: Translations should only be installed as an extension in the app plugin. ` + - `You can either use appPlugin.override(), or a module for the app plugin. The following extension will be ignored in the future: ${list}`, - ); - } - return I18nextTranslationApi.create({ languageApi, resources: inputs.translations.map(i => diff --git a/plugins/catalog/report-alpha.api.md b/plugins/catalog/report-alpha.api.md index f70572e371..e83089b611 100644 --- a/plugins/catalog/report-alpha.api.md +++ b/plugins/catalog/report-alpha.api.md @@ -345,6 +345,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -794,6 +795,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; cards: ExtensionInput< @@ -822,6 +824,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -1000,6 +1003,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -1064,6 +1068,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; contents: ExtensionInput< @@ -1105,6 +1110,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; contextMenuItems: ExtensionInput< @@ -1119,6 +1125,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/devtools/report-alpha.api.md b/plugins/devtools/report-alpha.api.md index 3f78a90587..8f3e24f7f3 100644 --- a/plugins/devtools/report-alpha.api.md +++ b/plugins/devtools/report-alpha.api.md @@ -92,6 +92,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: true; + internal: false; } >; }; diff --git a/plugins/home/report-alpha.api.md b/plugins/home/report-alpha.api.md index 6344805372..99e9b07463 100644 --- a/plugins/home/report-alpha.api.md +++ b/plugins/home/report-alpha.api.md @@ -85,6 +85,7 @@ const _default: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: false; } >; }; diff --git a/plugins/scaffolder-react/report-alpha.api.md b/plugins/scaffolder-react/report-alpha.api.md index 2839851521..1befe9ca46 100644 --- a/plugins/scaffolder-react/report-alpha.api.md +++ b/plugins/scaffolder-react/report-alpha.api.md @@ -219,6 +219,7 @@ export const formFieldsApi: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/scaffolder/report-alpha.api.md b/plugins/scaffolder/report-alpha.api.md index 7ffe367327..b9aa82782f 100644 --- a/plugins/scaffolder/report-alpha.api.md +++ b/plugins/scaffolder/report-alpha.api.md @@ -91,6 +91,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -118,6 +119,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -218,6 +220,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -411,6 +414,7 @@ export const formDecoratorsApi: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/search/report-alpha.api.md b/plugins/search/report-alpha.api.md index b129d4ab6c..2e82c77416 100644 --- a/plugins/search/report-alpha.api.md +++ b/plugins/search/report-alpha.api.md @@ -97,6 +97,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; resultTypes: ExtensionInput< @@ -112,6 +113,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; searchFilters: ExtensionInput< @@ -125,6 +127,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; @@ -215,6 +218,7 @@ export const searchPage: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; resultTypes: ExtensionInput< @@ -230,6 +234,7 @@ export const searchPage: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; searchFilters: ExtensionInput< @@ -243,6 +248,7 @@ export const searchPage: OverridableExtensionDefinition<{ { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/techdocs/report-alpha.api.md b/plugins/techdocs/report-alpha.api.md index 96c778b09b..cbdb912938 100644 --- a/plugins/techdocs/report-alpha.api.md +++ b/plugins/techdocs/report-alpha.api.md @@ -84,6 +84,7 @@ const _default: OverridableFrontendPlugin< { singleton: boolean; optional: boolean; + internal?: boolean; } >; }; @@ -146,6 +147,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; emptyState: ExtensionInput< @@ -159,6 +161,7 @@ const _default: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: false; } >; }; @@ -290,6 +293,7 @@ const _default: OverridableFrontendPlugin< { singleton: false; optional: false; + internal: false; } >; }; diff --git a/plugins/user-settings/report-alpha.api.md b/plugins/user-settings/report-alpha.api.md index 73ef5cfa3d..7ff63e2403 100644 --- a/plugins/user-settings/report-alpha.api.md +++ b/plugins/user-settings/report-alpha.api.md @@ -66,6 +66,7 @@ const _default: OverridableFrontendPlugin< { singleton: true; optional: true; + internal: false; } >; }; From c38b74df5fe28f2638b8ab46191f0c47f3148a32 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 23 Jan 2026 01:31:30 +0100 Subject: [PATCH 058/211] frontend-plugin-api: complete deprecation of app blueprints Signed-off-by: Patrik Oldsberg --- .changeset/green-llamas-wink.md | 6 + .changeset/lemon-eyes-grin.md | 5 + .changeset/polite-glasses-throw.md | 13 + .changeset/quiet-humans-hammer.md | 4 +- .changeset/slimy-dots-cross.md | 5 + packages/app-next/package.json | 1 + .../examples/notFoundErrorPageExtension.tsx | 6 +- .../app-next/src/modules/appModuleNav.tsx | 6 +- packages/core-compat-api/package.json | 1 + .../src/convertLegacyAppOptions.tsx | 12 +- packages/create-app/src/lib/versions.ts | 2 + .../next-app/packages/app/package.json.hbs | 1 + .../packages/app/src/modules/nav/Sidebar.tsx | 2 +- packages/frontend-defaults/package.json | 1 + .../frontend-defaults/src/createApp.test.tsx | 2 +- .../src/createPublicSignInApp.test.tsx | 6 +- packages/frontend-plugin-api/report.api.md | 256 ------------------ .../src/blueprints/index.ts | 15 - plugins/app-react/report.api.md | 45 +-- .../AppRootWrapperBlueprint.test.tsx | 4 +- .../blueprints/AppRootWrapperBlueprint.tsx | 12 +- .../src/blueprints/IconBundleBlueprint.ts | 12 +- .../blueprints/NavContentBlueprint.test.tsx | 0 .../src/blueprints/NavContentBlueprint.ts | 12 +- .../src/blueprints/RouterBlueprint.test.tsx | 2 +- .../src/blueprints/RouterBlueprint.tsx | 10 +- .../blueprints/SignInPageBlueprint.test.tsx | 0 .../src/blueprints/SignInPageBlueprint.tsx | 14 +- .../SwappableComponentBlueprint.test.tsx | 6 +- .../blueprints/SwappableComponentBlueprint.ts | 10 +- .../src/blueprints/ThemeBlueprint.test.ts | 2 +- .../src/blueprints/ThemeBlueprint.ts | 12 +- .../blueprints/TranslationBlueprint.test.ts | 2 +- .../src/blueprints/TranslationBlueprint.ts | 13 +- plugins/app-react/src/blueprints/index.ts | 104 +------ .../AppVisualizerPage/DetailedVisualizer.tsx | 3 - plugins/app/report.api.md | 4 +- .../src/alpha/appModulePublicSignIn.test.tsx | 6 +- .../DefaultSwappableComponentsApi.test.tsx | 2 +- .../DefaultSwappableComponentsApi.tsx | 2 +- plugins/app/src/extensions/AppNav.tsx | 8 +- plugins/app/src/extensions/AppRoot.tsx | 8 +- plugins/app/src/extensions/AppThemeApi.tsx | 2 +- .../app/src/extensions/DefaultSignInPage.tsx | 2 +- plugins/app/src/extensions/IconsApi.ts | 2 +- .../src/extensions/SwappableComponentsApi.ts | 2 +- .../app/src/extensions/TranslationsApi.tsx | 2 +- plugins/app/src/extensions/components.tsx | 3 +- yarn.lock | 3 + 49 files changed, 178 insertions(+), 475 deletions(-) create mode 100644 .changeset/green-llamas-wink.md create mode 100644 .changeset/lemon-eyes-grin.md create mode 100644 .changeset/polite-glasses-throw.md create mode 100644 .changeset/slimy-dots-cross.md rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/AppRootWrapperBlueprint.test.tsx (97%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/AppRootWrapperBlueprint.tsx (79%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/IconBundleBlueprint.ts (75%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/NavContentBlueprint.test.tsx (100%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/NavContentBlueprint.ts (87%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/RouterBlueprint.test.tsx (98%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/RouterBlueprint.tsx (83%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/SignInPageBlueprint.test.tsx (100%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/SignInPageBlueprint.tsx (83%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/SwappableComponentBlueprint.test.tsx (97%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/SwappableComponentBlueprint.ts (89%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/ThemeBlueprint.test.ts (96%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/ThemeBlueprint.ts (75%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/TranslationBlueprint.test.ts (98%) rename {packages/frontend-plugin-api => plugins/app-react}/src/blueprints/TranslationBlueprint.ts (76%) diff --git a/.changeset/green-llamas-wink.md b/.changeset/green-llamas-wink.md new file mode 100644 index 0000000000..0590b59ef3 --- /dev/null +++ b/.changeset/green-llamas-wink.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-compat-api': patch +'@backstage/plugin-app-visualizer': patch +--- + +Internal updates for blueprint moves to `@backstage/plugin-app-react`. diff --git a/.changeset/lemon-eyes-grin.md b/.changeset/lemon-eyes-grin.md new file mode 100644 index 0000000000..890a143c47 --- /dev/null +++ b/.changeset/lemon-eyes-grin.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Switched `next-app` template to use blueprint from `@backstage/plugin-app-react. diff --git a/.changeset/polite-glasses-throw.md b/.changeset/polite-glasses-throw.md new file mode 100644 index 0000000000..67f554591a --- /dev/null +++ b/.changeset/polite-glasses-throw.md @@ -0,0 +1,13 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +**BREAKING**: The following blueprints have been removed and are now only available from `@backstage/plugin-app-react`: + +- `IconBundleBlueprint` +- `NavContentBlueprint` +- `RouterBlueprint` +- `SignInPageBlueprint` +- `SwappableComponentBlueprint` +- `ThemeBlueprint` +- `TranslationBlueprint` diff --git a/.changeset/quiet-humans-hammer.md b/.changeset/quiet-humans-hammer.md index 1878473ff0..d47fd647f1 100644 --- a/.changeset/quiet-humans-hammer.md +++ b/.changeset/quiet-humans-hammer.md @@ -1,8 +1,8 @@ --- -'@backstage/plugin-app': patch +'@backstage/plugin-app': minor --- -The deprecation of the following blueprints listed below is not complete. These blueprints must now be provided via an override or a module for the `app` plugin. Extensions from other plugins will now trigger a warning in the app and be ignored. +**BREAKING**: Extensions created with the following blueprints must now be provided via an override or a module for the `app` plugin. Extensions from other plugins will now trigger a warning in the app and be ignored. - `IconBundleBlueprint` - `NavContentBlueprint` diff --git a/.changeset/slimy-dots-cross.md b/.changeset/slimy-dots-cross.md new file mode 100644 index 0000000000..fa1cf18aa2 --- /dev/null +++ b/.changeset/slimy-dots-cross.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-defaults': patch +--- + +Dependency update for tests. diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 46fcabbf5a..866979f4a1 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -48,6 +48,7 @@ "@backstage/integration-react": "workspace:^", "@backstage/plugin-api-docs": "workspace:^", "@backstage/plugin-app": "workspace:^", + "@backstage/plugin-app-react": "workspace:^", "@backstage/plugin-app-visualizer": "workspace:^", "@backstage/plugin-auth": "workspace:^", "@backstage/plugin-auth-react": "workspace:^", diff --git a/packages/app-next/src/examples/notFoundErrorPageExtension.tsx b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx index 7f690f90cb..965fe92eb8 100644 --- a/packages/app-next/src/examples/notFoundErrorPageExtension.tsx +++ b/packages/app-next/src/examples/notFoundErrorPageExtension.tsx @@ -14,10 +14,8 @@ * limitations under the License. */ -import { - SwappableComponentBlueprint, - NotFoundErrorPage, -} from '@backstage/frontend-plugin-api'; +import { NotFoundErrorPage } from '@backstage/frontend-plugin-api'; +import { SwappableComponentBlueprint } from '@backstage/plugin-app-react'; import Box from '@material-ui/core/Box'; import Typography from '@material-ui/core/Typography'; import { Button } from '@backstage/core-components'; diff --git a/packages/app-next/src/modules/appModuleNav.tsx b/packages/app-next/src/modules/appModuleNav.tsx index ff89b2d1de..9da6358dd3 100644 --- a/packages/app-next/src/modules/appModuleNav.tsx +++ b/packages/app-next/src/modules/appModuleNav.tsx @@ -28,10 +28,8 @@ import { import SearchIcon from '@material-ui/icons/Search'; import MenuIcon from '@material-ui/icons/Menu'; import BuildIcon from '@material-ui/icons/Build'; -import { - createFrontendModule, - NavContentBlueprint, -} from '@backstage/frontend-plugin-api'; +import { createFrontendModule } from '@backstage/frontend-plugin-api'; +import { NavContentBlueprint } from '@backstage/plugin-app-react'; import { SidebarSearchModal } from '@backstage/plugin-search'; import { NotificationsSidebarItem } from '@backstage/plugin-notifications'; import { diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 7262eb20bd..eb34da3d34 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -33,6 +33,7 @@ "dependencies": { "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", + "@backstage/plugin-app-react": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", diff --git a/packages/core-compat-api/src/convertLegacyAppOptions.tsx b/packages/core-compat-api/src/convertLegacyAppOptions.tsx index 650ea9cd58..09b8329112 100644 --- a/packages/core-compat-api/src/convertLegacyAppOptions.tsx +++ b/packages/core-compat-api/src/convertLegacyAppOptions.tsx @@ -16,21 +16,23 @@ import { ComponentType } from 'react'; import { - SwappableComponentBlueprint, ApiBlueprint, ErrorDisplayProps, createExtension, createFrontendModule, ExtensionDefinition, FrontendModule, - IconBundleBlueprint, - RouterBlueprint, - SignInPageBlueprint, - ThemeBlueprint, ErrorDisplay as SwappableErrorDisplay, NotFoundErrorPage as SwappableNotFoundErrorPage, Progress as SwappableProgress, } from '@backstage/frontend-plugin-api'; +import { + IconBundleBlueprint, + RouterBlueprint, + SignInPageBlueprint, + SwappableComponentBlueprint, + ThemeBlueprint, +} from '@backstage/plugin-app-react'; import { AnyApiFactory, AppComponents, diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 51fa8dff84..db865a2db5 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -55,6 +55,7 @@ import { version as ui } from '../../../ui/package.json'; import { version as pluginApiDocs } from '../../../../plugins/api-docs/package.json'; import { version as pluginAppVisualizer } from '../../../../plugins/app-visualizer/package.json'; import { version as pluginAppBackend } from '../../../../plugins/app-backend/package.json'; +import { version as pluginAppReact } from '../../../../plugins/app-react/package.json'; import { version as pluginAuthBackend } from '../../../../plugins/auth-backend/package.json'; import { version as pluginAuthBackendModuleGithubProvider } from '../../../../plugins/auth-backend-module-github-provider/package.json'; import { version as pluginAuthBackendModuleGuestProvider } from '../../../../plugins/auth-backend-module-guest-provider/package.json'; @@ -118,6 +119,7 @@ export const packageVersions = { '@backstage/repo-tools': repoTools, '@backstage/plugin-api-docs': pluginApiDocs, '@backstage/plugin-app-backend': pluginAppBackend, + '@backstage/plugin-app-react': pluginAppReact, '@backstage/plugin-app-visualizer': pluginAppVisualizer, '@backstage/plugin-auth-backend': pluginAuthBackend, '@backstage/plugin-auth-backend-module-github-provider': diff --git a/packages/create-app/templates/next-app/packages/app/package.json.hbs b/packages/create-app/templates/next-app/packages/app/package.json.hbs index 726983fd69..bda3b32007 100644 --- a/packages/create-app/templates/next-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/next-app/packages/app/package.json.hbs @@ -21,6 +21,7 @@ "@backstage/frontend-defaults": "^{{ version '@backstage/frontend-defaults'}}", "@backstage/frontend-plugin-api": "^{{ version '@backstage/frontend-plugin-api'}}", "@backstage/integration-react": "^{{ version '@backstage/integration-react'}}", + "@backstage/plugin-app-react": "^{{ version '@backstage/plugin-app-react'}}", "@backstage/plugin-app-visualizer": "^{{ version '@backstage/plugin-app-visualizer'}}", "@backstage/plugin-catalog": "^{{ version '@backstage/plugin-catalog'}}", "@backstage/plugin-notifications": "^{{ version '@backstage/plugin-notifications'}}", diff --git a/packages/create-app/templates/next-app/packages/app/src/modules/nav/Sidebar.tsx b/packages/create-app/templates/next-app/packages/app/src/modules/nav/Sidebar.tsx index ddfdde64d0..4d229714fa 100644 --- a/packages/create-app/templates/next-app/packages/app/src/modules/nav/Sidebar.tsx +++ b/packages/create-app/templates/next-app/packages/app/src/modules/nav/Sidebar.tsx @@ -7,7 +7,7 @@ import { } from '@backstage/core-components'; import { compatWrapper } from '@backstage/core-compat-api'; import { Sidebar } from '@backstage/core-components'; -import { NavContentBlueprint } from '@backstage/frontend-plugin-api'; +import { NavContentBlueprint } from '@backstage/plugin-app-react'; import { SidebarLogo } from './SidebarLogo'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import HomeIcon from '@material-ui/icons/Home'; diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index c47987083d..2a69357878 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -42,6 +42,7 @@ "devDependencies": { "@backstage/cli": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/plugin-app-react": "workspace:^", "@backstage/test-utils": "workspace:^", "@testing-library/jest-dom": "^6.0.0", "@testing-library/react": "^16.0.0", diff --git a/packages/frontend-defaults/src/createApp.test.tsx b/packages/frontend-defaults/src/createApp.test.tsx index c0928e7815..47f6b7895d 100644 --- a/packages/frontend-defaults/src/createApp.test.tsx +++ b/packages/frontend-defaults/src/createApp.test.tsx @@ -22,11 +22,11 @@ import { PageBlueprint, createFrontendPlugin, createFrontendFeatureLoader, - ThemeBlueprint, createFrontendModule, useAppNode, FrontendPluginInfo, } from '@backstage/frontend-plugin-api'; +import { ThemeBlueprint } from '@backstage/plugin-app-react'; import { screen, waitFor } from '@testing-library/react'; import { createApp } from './createApp'; import { mockApis, renderWithEffects } from '@backstage/test-utils'; diff --git a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx index e80d058140..398dbcbf4a 100644 --- a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx +++ b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx @@ -14,10 +14,8 @@ * limitations under the License. */ -import { - SignInPageBlueprint, - createFrontendModule, -} from '@backstage/frontend-plugin-api'; +import { createFrontendModule } from '@backstage/frontend-plugin-api'; +import { SignInPageBlueprint } from '@backstage/plugin-app-react'; import { render, screen, waitFor } from '@testing-library/react'; import { useEffect } from 'react'; import { createPublicSignInApp } from './createPublicSignInApp'; diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 3d5efd5765..76ca364952 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -13,7 +13,6 @@ import { ExpandRecursive } from '@backstage/types'; import { ExtensionBlueprint as ExtensionBlueprint_2 } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprintParams as ExtensionBlueprintParams_2 } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef as ExtensionDataRef_2 } from '@backstage/frontend-plugin-api'; -import { IconComponent as IconComponent_2 } from '@backstage/frontend-plugin-api'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { JSX as JSX_2 } from 'react/jsx-runtime'; @@ -21,7 +20,6 @@ import { JSX as JSX_3 } from 'react'; import { Observable } from '@backstage/types'; import { PropsWithChildren } from 'react'; import { ReactNode } from 'react'; -import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api'; import { SwappableComponentRef as SwappableComponentRef_2 } from '@backstage/frontend-plugin-api'; import type { z } from 'zod'; @@ -271,30 +269,6 @@ export const AppRootElementBlueprint: ExtensionBlueprint_2<{ dataRefs: never; }>; -// @public @deprecated -export const AppRootWrapperBlueprint: ExtensionBlueprint_2<{ - kind: 'app-root-wrapper'; - params: { - Component?: [error: 'Use the `component` parameter instead']; - component: (props: { children: ReactNode }) => JSX.Element | null; - }; - output: ExtensionDataRef_2< - (props: { children: ReactNode }) => JSX.Element | null, - 'app.root.wrapper', - {} - >; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - component: ConfigurableExtensionDataRef_2< - (props: { children: ReactNode }) => JSX.Element | null, - 'app.root.wrapper', - {} - >; - }; -}>; - // @public export type AppTheme = { id: string; @@ -1461,33 +1435,6 @@ export const googleAuthApiRef: ApiRef< SessionApi >; -// @public @deprecated (undocumented) -export const IconBundleBlueprint: ExtensionBlueprint_2<{ - kind: 'icon-bundle'; - params: { - icons: { [key in string]: IconComponent }; - }; - output: ExtensionDataRef_2< - { - [x: string]: IconComponent; - }, - 'core.icons', - {} - >; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - icons: ConfigurableExtensionDataRef_2< - { - [x: string]: IconComponent; - }, - 'core.icons', - {} - >; - }; -}>; - // @public export type IconComponent = ComponentType<{ fontSize?: 'medium' | 'large' | 'small' | 'inherit'; @@ -1526,45 +1473,6 @@ export const microsoftAuthApiRef: ApiRef< SessionApi >; -// @public @deprecated -export const NavContentBlueprint: ExtensionBlueprint_2<{ - kind: 'nav-content'; - params: { - component: NavContentComponent; - }; - output: ExtensionDataRef_2< - NavContentComponent, - 'core.nav-content.component', - {} - >; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - component: ConfigurableExtensionDataRef_2< - NavContentComponent, - 'core.nav-content.component', - {} - >; - }; -}>; - -// @public -export type NavContentComponent = ( - props: NavContentComponentProps, -) => JSX.Element | null; - -// @public -export interface NavContentComponentProps { - items: Array<{ - icon: IconComponent_2; - title: string; - routeRef: RouteRef_2; - to: string; - text: string; - }>; -} - // @public export const NavItemBlueprint: ExtensionBlueprint_2<{ kind: 'nav-item'; @@ -1927,30 +1835,6 @@ export type RouteFunc = ( : readonly [params: TParams] ) => string; -// @public @deprecated (undocumented) -export const RouterBlueprint: ExtensionBlueprint_2<{ - kind: 'app-router-component'; - params: { - Component?: [error: 'Use the `component` parameter instead']; - component: (props: { children: ReactNode }) => JSX.Element | null; - }; - output: ExtensionDataRef_2< - (props: { children: ReactNode }) => JSX.Element | null, - 'app.router.wrapper', - {} - >; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - component: ConfigurableExtensionDataRef_2< - (props: { children: ReactNode }) => JSX.Element | null, - 'app.router.wrapper', - {} - >; - }; -}>; - // @public export interface RouteRef< TParams extends AnyRouteRefParams = AnyRouteRefParams, @@ -2002,35 +1886,6 @@ export namespace SessionState { export type SignedOut = typeof SessionState.SignedOut; } -// @public @deprecated -export const SignInPageBlueprint: ExtensionBlueprint_2<{ - kind: 'sign-in-page'; - params: { - loader: () => Promise>; - }; - output: ExtensionDataRef_2< - ComponentType, - 'core.sign-in-page.component', - {} - >; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - component: ConfigurableExtensionDataRef_2< - ComponentType, - 'core.sign-in-page.component', - {} - >; - }; -}>; - -// @public -export type SignInPageProps = { - onSignInSuccess(identityApi: IdentityApi): void; - children?: ReactNode; -}; - // @public export interface StorageApi { forBucket(name: string): StorageApi; @@ -2070,65 +1925,6 @@ export interface SubRouteRef< readonly T: TParams; } -// @public @deprecated -export const SwappableComponentBlueprint: ExtensionBlueprint_2<{ - kind: 'component'; - params: >(params: { - component: Ref extends SwappableComponentRef< - any, - infer IExternalComponentProps - > - ? { - ref: Ref; - } & ((props: IExternalComponentProps) => JSX.Element | null) - : never; - loader: Ref extends SwappableComponentRef - ? - | (() => (props: IInnerComponentProps) => JSX.Element | null) - | (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>) - : never; - }) => ExtensionBlueprintParams_2<{ - component: Ref extends SwappableComponentRef< - any, - infer IExternalComponentProps - > - ? { - ref: Ref; - } & ((props: IExternalComponentProps) => JSX.Element | null) - : never; - loader: Ref extends SwappableComponentRef - ? - | (() => (props: IInnerComponentProps) => JSX.Element | null) - | (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>) - : never; - }>; - output: ExtensionDataRef_2< - { - ref: SwappableComponentRef; - loader: - | (() => (props: {}) => JSX.Element | null) - | (() => Promise<(props: {}) => JSX.Element | null>); - }, - 'core.swappableComponent', - {} - >; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - component: ConfigurableExtensionDataRef_2< - { - ref: SwappableComponentRef; - loader: - | (() => (props: {}) => JSX.Element | null) - | (() => Promise<(props: {}) => JSX.Element | null>); - }, - 'core.swappableComponent', - {} - >; - }; -}>; - // @public (undocumented) export type SwappableComponentRef< TInnerComponentProps extends {} = {}, @@ -2154,21 +1950,6 @@ export interface SwappableComponentsApi { // @public export const swappableComponentsApiRef: ApiRef_2; -// @public @deprecated -export const ThemeBlueprint: ExtensionBlueprint_2<{ - kind: 'theme'; - params: { - theme: AppTheme; - }; - output: ExtensionDataRef_2; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - theme: ConfigurableExtensionDataRef_2; - }; -}>; - // @public (undocumented) export type TranslationApi = { getTranslation< @@ -2190,43 +1971,6 @@ export type TranslationApi = { // @public (undocumented) export const translationApiRef: ApiRef; -// @public @deprecated -export const TranslationBlueprint: ExtensionBlueprint_2<{ - kind: 'translation'; - params: { - resource: TranslationResource | TranslationMessages; - }; - output: ExtensionDataRef_2< - | TranslationResource - | TranslationMessages< - string, - { - [x: string]: string; - }, - boolean - >, - 'core.translation.translation', - {} - >; - inputs: {}; - config: {}; - configInput: {}; - dataRefs: { - translation: ConfigurableExtensionDataRef_2< - | TranslationResource - | TranslationMessages< - string, - { - [x: string]: string; - }, - boolean - >, - 'core.translation.translation', - {} - >; - }; -}>; - // @public (undocumented) export type TranslationFunction< TMessages extends { diff --git a/packages/frontend-plugin-api/src/blueprints/index.ts b/packages/frontend-plugin-api/src/blueprints/index.ts index 379c8d237b..344b44dbff 100644 --- a/packages/frontend-plugin-api/src/blueprints/index.ts +++ b/packages/frontend-plugin-api/src/blueprints/index.ts @@ -20,20 +20,5 @@ export { } from './AnalyticsImplementationBlueprint'; export { ApiBlueprint } from './ApiBlueprint'; export { AppRootElementBlueprint } from './AppRootElementBlueprint'; -export { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint'; -export { IconBundleBlueprint } from './IconBundleBlueprint'; -export { - NavContentBlueprint, - type NavContentComponent, - type NavContentComponentProps, -} from './NavContentBlueprint'; export { NavItemBlueprint } from './NavItemBlueprint'; export { PageBlueprint } from './PageBlueprint'; -export { RouterBlueprint } from './RouterBlueprint'; -export { - type SignInPageProps, - SignInPageBlueprint, -} from './SignInPageBlueprint'; -export { ThemeBlueprint } from './ThemeBlueprint'; -export { TranslationBlueprint } from './TranslationBlueprint'; -export { SwappableComponentBlueprint } from './SwappableComponentBlueprint'; diff --git a/plugins/app-react/report.api.md b/plugins/app-react/report.api.md index 251152a9e6..78f4bd3bfb 100644 --- a/plugins/app-react/report.api.md +++ b/plugins/app-react/report.api.md @@ -10,10 +10,9 @@ import { ExtensionBlueprint } from '@backstage/frontend-plugin-api'; import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api'; import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/frontend-plugin-api'; -import { NavContentComponent } from '@backstage/frontend-plugin-api'; -import { NavContentComponentProps } from '@backstage/frontend-plugin-api'; +import { IdentityApi } from '@backstage/frontend-plugin-api'; import { ReactNode } from 'react'; -import { SignInPageProps } from '@backstage/frontend-plugin-api'; +import { RouteRef } from '@backstage/frontend-plugin-api'; import { SwappableComponentRef } from '@backstage/frontend-plugin-api'; import { TranslationMessages } from '@backstage/frontend-plugin-api'; import { TranslationResource } from '@backstage/frontend-plugin-api'; @@ -92,9 +91,21 @@ export const NavContentBlueprint: ExtensionBlueprint<{ }; }>; -export { NavContentComponent }; +// @public +export type NavContentComponent = ( + props: NavContentComponentProps, +) => JSX.Element | null; -export { NavContentComponentProps }; +// @public +export interface NavContentComponentProps { + items: Array<{ + icon: IconComponent; + title: string; + routeRef: RouteRef; + to: string; + text: string; + }>; +} // @public export const RouterBlueprint: ExtensionBlueprint<{ @@ -143,7 +154,11 @@ export const SignInPageBlueprint: ExtensionBlueprint<{ }; }>; -export { SignInPageProps }; +// @public +export type SignInPageProps = { + onSignInSuccess(identityApi: IdentityApi): void; + children?: ReactNode; +}; // @public export const SwappableComponentBlueprint: ExtensionBlueprint<{ @@ -160,14 +175,7 @@ export const SwappableComponentBlueprint: ExtensionBlueprint<{ loader: Ref extends SwappableComponentRef ? | (() => (props: IInnerComponentProps) => JSX.Element | null) - | (() => Promise< - (props: IInnerComponentProps) => JSX.Element - /** - * Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin. - * - * @public - */ | null - >) + | (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>) : never; }) => ExtensionBlueprintParams<{ component: Ref extends SwappableComponentRef< @@ -181,14 +189,7 @@ export const SwappableComponentBlueprint: ExtensionBlueprint<{ loader: Ref extends SwappableComponentRef ? | (() => (props: IInnerComponentProps) => JSX.Element | null) - | (() => Promise< - (props: IInnerComponentProps) => JSX.Element - /** - * Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin. - * - * @public - */ | null - >) + | (() => Promise<(props: IInnerComponentProps) => JSX.Element | null>) : never; }>; output: ExtensionDataRef< diff --git a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx b/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.test.tsx similarity index 97% rename from packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx rename to plugins/app-react/src/blueprints/AppRootWrapperBlueprint.test.tsx index 0f26af3243..cd459b371f 100644 --- a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx +++ b/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.test.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,7 @@ import { coreExtensionData, createExtension, createExtensionInput, -} from '../wiring'; +} from '@backstage/frontend-plugin-api'; import { renderTestApp } from '@backstage/frontend-test-utils'; describe('AppRootWrapperBlueprint', () => { diff --git a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.tsx b/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.tsx similarity index 79% rename from packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.tsx rename to plugins/app-react/src/blueprints/AppRootWrapperBlueprint.tsx index 184a8c3327..bfeef61e1d 100644 --- a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.tsx +++ b/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,10 @@ */ import { ReactNode } from 'react'; -import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; +import { + createExtensionBlueprint, + createExtensionDataRef, +} from '@backstage/frontend-plugin-api'; const componentDataRef = createExtensionDataRef< (props: { children: ReactNode }) => JSX.Element | null @@ -24,12 +27,9 @@ const componentDataRef = createExtensionDataRef< /** * Creates a extensions that render a React wrapper at the app root, enclosing * the app layout. This is useful for example for adding global React contexts - * and similar. + * and similar. This blueprint is limited to use by the app plugin. * * @public - * @deprecated Use {@link @backstage/plugin-app-react#AppRootWrapperBlueprint} instead. - * If you were using this blueprint to provide a context for your plugin, - * use `PluginWrapperBlueprint` from `@backstage/frontend-plugin-api/alpha` instead. */ export const AppRootWrapperBlueprint = createExtensionBlueprint({ kind: 'app-root-wrapper', diff --git a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts b/plugins/app-react/src/blueprints/IconBundleBlueprint.ts similarity index 75% rename from packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts rename to plugins/app-react/src/blueprints/IconBundleBlueprint.ts index 2f52f7590a..ce340c2aeb 100644 --- a/packages/frontend-plugin-api/src/blueprints/IconBundleBlueprint.ts +++ b/plugins/app-react/src/blueprints/IconBundleBlueprint.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,20 @@ * limitations under the License. */ -import { IconComponent } from '../icons'; -import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; +import { IconComponent } from '@backstage/frontend-plugin-api'; +import { + createExtensionBlueprint, + createExtensionDataRef, +} from '@backstage/frontend-plugin-api'; const iconsDataRef = createExtensionDataRef<{ [key in string]: IconComponent; }>().with({ id: 'core.icons' }); /** + * Creates an extension that adds icon bundles to your app. This blueprint is limited to use by the app plugin. + * * @public - * @deprecated Use {@link @backstage/plugin-app-react#IconBundleBlueprint} instead. */ export const IconBundleBlueprint = createExtensionBlueprint({ kind: 'icon-bundle', diff --git a/packages/frontend-plugin-api/src/blueprints/NavContentBlueprint.test.tsx b/plugins/app-react/src/blueprints/NavContentBlueprint.test.tsx similarity index 100% rename from packages/frontend-plugin-api/src/blueprints/NavContentBlueprint.test.tsx rename to plugins/app-react/src/blueprints/NavContentBlueprint.test.tsx diff --git a/packages/frontend-plugin-api/src/blueprints/NavContentBlueprint.ts b/plugins/app-react/src/blueprints/NavContentBlueprint.ts similarity index 87% rename from packages/frontend-plugin-api/src/blueprints/NavContentBlueprint.ts rename to plugins/app-react/src/blueprints/NavContentBlueprint.ts index f56de2032b..0f21ed832a 100644 --- a/packages/frontend-plugin-api/src/blueprints/NavContentBlueprint.ts +++ b/plugins/app-react/src/blueprints/NavContentBlueprint.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,10 @@ */ import { IconComponent, RouteRef } from '@backstage/frontend-plugin-api'; -import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; +import { + createExtensionBlueprint, + createExtensionDataRef, +} from '@backstage/frontend-plugin-api'; /** * The props for the {@link NavContentComponent}. @@ -25,7 +28,7 @@ import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; export interface NavContentComponentProps { /** * The nav items available to the component. These are all the items created - * with the {@link NavItemBlueprint} in the app. + * with the {@link @backstage/frontend-plugin-api#NavItemBlueprint} in the app. * * In addition to the original properties from the nav items, these also * include a resolved route path as `to`, and duplicated `title` as `text` to @@ -57,10 +60,9 @@ const componentDataRef = createExtensionDataRef().with({ }); /** - * Creates an extension that replaces the entire nav bar with your own component. + * Creates an extension that replaces the entire nav bar with your own component. This blueprint is limited to use by the app plugin. * * @public - * @deprecated Use {@link @backstage/plugin-app-react#NavContentBlueprint} instead. */ export const NavContentBlueprint = createExtensionBlueprint({ kind: 'nav-content', diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx b/plugins/app-react/src/blueprints/RouterBlueprint.test.tsx similarity index 98% rename from packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx rename to plugins/app-react/src/blueprints/RouterBlueprint.test.tsx index f2e5199124..0371794fff 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.test.tsx +++ b/plugins/app-react/src/blueprints/RouterBlueprint.test.tsx @@ -20,7 +20,7 @@ import { coreExtensionData, createExtension, createExtensionInput, -} from '../wiring'; +} from '@backstage/frontend-plugin-api'; import { createExtensionTester } from '@backstage/frontend-test-utils'; describe('RouterBlueprint', () => { diff --git a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.tsx b/plugins/app-react/src/blueprints/RouterBlueprint.tsx similarity index 83% rename from packages/frontend-plugin-api/src/blueprints/RouterBlueprint.tsx rename to plugins/app-react/src/blueprints/RouterBlueprint.tsx index 88096cf16a..dde2bc83e8 100644 --- a/packages/frontend-plugin-api/src/blueprints/RouterBlueprint.tsx +++ b/plugins/app-react/src/blueprints/RouterBlueprint.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,19 @@ */ import { ReactNode } from 'react'; -import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; +import { + createExtensionBlueprint, + createExtensionDataRef, +} from '@backstage/frontend-plugin-api'; const componentDataRef = createExtensionDataRef< (props: { children: ReactNode }) => JSX.Element | null >().with({ id: 'app.router.wrapper' }); /** + * Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin. + * * @public - * @deprecated Use {@link @backstage/plugin-app-react#RouterBlueprint} instead. */ export const RouterBlueprint = createExtensionBlueprint({ kind: 'app-router-component', diff --git a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx b/plugins/app-react/src/blueprints/SignInPageBlueprint.test.tsx similarity index 100% rename from packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.test.tsx rename to plugins/app-react/src/blueprints/SignInPageBlueprint.test.tsx diff --git a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.tsx b/plugins/app-react/src/blueprints/SignInPageBlueprint.tsx similarity index 83% rename from packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.tsx rename to plugins/app-react/src/blueprints/SignInPageBlueprint.tsx index 9f51e8f666..fd60947b0a 100644 --- a/packages/frontend-plugin-api/src/blueprints/SignInPageBlueprint.tsx +++ b/plugins/app-react/src/blueprints/SignInPageBlueprint.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,12 @@ */ import { ComponentType, lazy, ReactNode } from 'react'; -import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; -import { ExtensionBoundary } from '../components'; -import { IdentityApi } from '../apis'; +import { + createExtensionBlueprint, + createExtensionDataRef, + ExtensionBoundary, + IdentityApi, +} from '@backstage/frontend-plugin-api'; /** * Props for the `SignInPage` component. @@ -41,10 +44,9 @@ const componentDataRef = createExtensionDataRef< >().with({ id: 'core.sign-in-page.component' }); /** - * Creates an extension that replaces the sign in page. + * Creates an extension that replaces the sign in page. This blueprint is limited to use by the app plugin. * * @public - * @deprecated Use {@link @backstage/plugin-app-react#SignInPageBlueprint} instead. */ export const SignInPageBlueprint = createExtensionBlueprint({ kind: 'sign-in-page', diff --git a/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.test.tsx b/plugins/app-react/src/blueprints/SwappableComponentBlueprint.test.tsx similarity index 97% rename from packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.test.tsx rename to plugins/app-react/src/blueprints/SwappableComponentBlueprint.test.tsx index 63436308ed..8d9f886c58 100644 --- a/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.test.tsx +++ b/plugins/app-react/src/blueprints/SwappableComponentBlueprint.test.tsx @@ -14,9 +14,11 @@ * limitations under the License. */ import { renderTestApp } from '@backstage/frontend-test-utils'; -import { createSwappableComponent } from '../components'; +import { + createSwappableComponent, + PageBlueprint, +} from '@backstage/frontend-plugin-api'; import { SwappableComponentBlueprint } from './SwappableComponentBlueprint'; -import { PageBlueprint } from './PageBlueprint'; import { screen } from '@testing-library/react'; describe('SwappableComponentBlueprint', () => { diff --git a/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.ts b/plugins/app-react/src/blueprints/SwappableComponentBlueprint.ts similarity index 89% rename from packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.ts rename to plugins/app-react/src/blueprints/SwappableComponentBlueprint.ts index 38e333826a..801e258367 100644 --- a/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.ts +++ b/plugins/app-react/src/blueprints/SwappableComponentBlueprint.ts @@ -1,5 +1,5 @@ /* - * Copyright 2025 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SwappableComponentRef } from '../components'; + +import { SwappableComponentRef } from '@backstage/frontend-plugin-api'; import { createExtensionBlueprint, createExtensionBlueprintParams, createExtensionDataRef, -} from '../wiring'; +} from '@backstage/frontend-plugin-api'; export const componentDataRef = createExtensionDataRef<{ ref: SwappableComponentRef; @@ -28,10 +29,9 @@ export const componentDataRef = createExtensionDataRef<{ }>().with({ id: 'core.swappableComponent' }); /** - * Blueprint for creating swappable components from a SwappableComponentRef and a loader + * Blueprint for creating swappable components from a SwappableComponentRef and a loader. This blueprint is limited to use by the app plugin. * * @public - * @deprecated Use {@link @backstage/plugin-app-react#SwappableComponentBlueprint} instead. */ export const SwappableComponentBlueprint = createExtensionBlueprint({ kind: 'component', diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts b/plugins/app-react/src/blueprints/ThemeBlueprint.test.ts similarity index 96% rename from packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts rename to plugins/app-react/src/blueprints/ThemeBlueprint.test.ts index 7a53f28fa3..5ee66ece67 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.test.ts +++ b/plugins/app-react/src/blueprints/ThemeBlueprint.test.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AppTheme } from '../apis/definitions/AppThemeApi'; +import { AppTheme } from '@backstage/frontend-plugin-api'; import { ThemeBlueprint } from './ThemeBlueprint'; import { createExtensionTester } from '@backstage/frontend-test-utils'; diff --git a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts b/plugins/app-react/src/blueprints/ThemeBlueprint.ts similarity index 75% rename from packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts rename to plugins/app-react/src/blueprints/ThemeBlueprint.ts index f93075181e..559b442f4c 100644 --- a/packages/frontend-plugin-api/src/blueprints/ThemeBlueprint.ts +++ b/plugins/app-react/src/blueprints/ThemeBlueprint.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,20 @@ * limitations under the License. */ -import { AppTheme } from '../apis/definitions/AppThemeApi'; -import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; +import { AppTheme } from '@backstage/frontend-plugin-api'; +import { + createExtensionBlueprint, + createExtensionDataRef, +} from '@backstage/frontend-plugin-api'; const themeDataRef = createExtensionDataRef().with({ id: 'core.theme.theme', }); /** - * Creates an extension that adds/replaces an app theme. + * Creates an extension that adds/replaces an app theme. This blueprint is limited to use by the app plugin. * * @public - * @deprecated Use {@link @backstage/plugin-app-react#ThemeBlueprint} instead. */ export const ThemeBlueprint = createExtensionBlueprint({ kind: 'theme', diff --git a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts b/plugins/app-react/src/blueprints/TranslationBlueprint.test.ts similarity index 98% rename from packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts rename to plugins/app-react/src/blueprints/TranslationBlueprint.test.ts index 07e02c1562..bf291526ca 100644 --- a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.test.ts +++ b/plugins/app-react/src/blueprints/TranslationBlueprint.test.ts @@ -17,7 +17,7 @@ import { createExtensionTester } from '@backstage/frontend-test-utils'; import { createTranslationMessages, createTranslationRef, -} from '../translation'; +} from '@backstage/frontend-plugin-api'; import { TranslationBlueprint } from './TranslationBlueprint'; describe('TranslationBlueprint', () => { diff --git a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts b/plugins/app-react/src/blueprints/TranslationBlueprint.ts similarity index 76% rename from packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts rename to plugins/app-react/src/blueprints/TranslationBlueprint.ts index 491053d763..a45fb26b49 100644 --- a/packages/frontend-plugin-api/src/blueprints/TranslationBlueprint.ts +++ b/plugins/app-react/src/blueprints/TranslationBlueprint.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2026 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,21 @@ * limitations under the License. */ -import { createExtensionBlueprint, createExtensionDataRef } from '../wiring'; -import { TranslationMessages, TranslationResource } from '../translation'; +import { + createExtensionBlueprint, + createExtensionDataRef, + TranslationMessages, + TranslationResource, +} from '@backstage/frontend-plugin-api'; const translationDataRef = createExtensionDataRef< TranslationResource | TranslationMessages >().with({ id: 'core.translation.translation' }); /** - * Creates an extension that adds translations to your app. + * Creates an extension that adds translations to your app. This blueprint is limited to use by the app plugin. * * @public - * @deprecated Use {@link @backstage/plugin-app-react#TranslationBlueprint} instead. */ export const TranslationBlueprint = createExtensionBlueprint({ kind: 'translation', diff --git a/plugins/app-react/src/blueprints/index.ts b/plugins/app-react/src/blueprints/index.ts index f5e4d7ac4a..15a0fdcd47 100644 --- a/plugins/app-react/src/blueprints/index.ts +++ b/plugins/app-react/src/blueprints/index.ts @@ -14,94 +14,16 @@ * limitations under the License. */ -import { - AppRootWrapperBlueprint as _AppRootWrapperBlueprint, - IconBundleBlueprint as _IconBundleBlueprint, - NavContentBlueprint as _NavContentBlueprint, - type NavContentComponent, - type NavContentComponentProps, - RouterBlueprint as _RouterBlueprint, - SignInPageBlueprint as _SignInPageBlueprint, - type SignInPageProps, - SwappableComponentBlueprint as _SwappableComponentBlueprint, - ThemeBlueprint as _ThemeBlueprint, - TranslationBlueprint as _TranslationBlueprint, -} from '@backstage/frontend-plugin-api'; - -/** - * Creates an extension that renders a React wrapper at the app root, enclosing - * the app layout. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const AppRootWrapperBlueprint = _AppRootWrapperBlueprint; - -/** - * Creates an extension that adds/replaces an app theme. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const ThemeBlueprint = _ThemeBlueprint; - -/** - * Blueprint for creating swappable components from a SwappableComponentRef and a loader. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const SwappableComponentBlueprint = _SwappableComponentBlueprint; - -/** - * Creates an extension that replaces the sign in page. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const SignInPageBlueprint = _SignInPageBlueprint; - -/** - * Creates an extension that replaces the router component. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const RouterBlueprint = _RouterBlueprint; - -/** - * Creates an extension that replaces the entire nav bar with your own component. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const NavContentBlueprint = _NavContentBlueprint; - -/** - * Creates an extension that adds icon bundles to your app. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const IconBundleBlueprint = _IconBundleBlueprint; - -/** - * Props for the `SignInPage` component. - * - * @public - */ -export type { SignInPageProps }; - -/** - * The props for the {@link NavContentComponent}. - * - * @public - */ -export type { NavContentComponentProps }; - -/** - * A component that renders the nav bar content, to be passed to the {@link NavContentBlueprint}. - * - * @public - */ -export type { NavContentComponent }; - -/** - * Creates an extension that adds translations to your app. This blueprint is limited to use by the app plugin. - * - * @public - */ -export const TranslationBlueprint = _TranslationBlueprint; +export { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint'; +export { IconBundleBlueprint } from './IconBundleBlueprint'; +export { NavContentBlueprint } from './NavContentBlueprint'; +export type { + NavContentComponent, + NavContentComponentProps, +} from './NavContentBlueprint'; +export { RouterBlueprint } from './RouterBlueprint'; +export { SignInPageBlueprint } from './SignInPageBlueprint'; +export type { SignInPageProps } from './SignInPageBlueprint'; +export { SwappableComponentBlueprint } from './SwappableComponentBlueprint'; +export { ThemeBlueprint } from './ThemeBlueprint'; +export { TranslationBlueprint } from './TranslationBlueprint'; diff --git a/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx b/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx index 1d38ec8267..d6423b48f0 100644 --- a/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx +++ b/plugins/app-visualizer/src/components/AppVisualizerPage/DetailedVisualizer.tsx @@ -21,7 +21,6 @@ import { coreExtensionData, ApiBlueprint, NavItemBlueprint, - ThemeBlueprint, useApi, routeResolutionApiRef, } from '@backstage/frontend-plugin-api'; @@ -78,7 +77,6 @@ const getOutputColor = createOutputColorGenerator( [coreExtensionData.routePath.id]: '#ffeb3b', [coreExtensionData.routeRef.id]: '#9c27b0', [ApiBlueprint.dataRefs.factory.id]: '#2196f3', - [ThemeBlueprint.dataRefs.theme.id]: '#cddc39', [NavItemBlueprint.dataRefs.target.id]: '#ff9800', }, @@ -280,7 +278,6 @@ const legendMap = { 'Route Path': coreExtensionData.routePath, 'Route Ref': coreExtensionData.routeRef, 'Nav Target': NavItemBlueprint.dataRefs.target, - Theme: ThemeBlueprint.dataRefs.theme, }; function Legend() { diff --git a/plugins/app/report.api.md b/plugins/app/report.api.md index a966ddeff1..256517b86f 100644 --- a/plugins/app/report.api.md +++ b/plugins/app/report.api.md @@ -15,12 +15,12 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { IconComponent } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; -import { NavContentComponent } from '@backstage/frontend-plugin-api'; +import { NavContentComponent } from '@backstage/plugin-app-react'; import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api'; import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; -import { SignInPageProps } from '@backstage/frontend-plugin-api'; +import { SignInPageProps } from '@backstage/plugin-app-react'; import { SwappableComponentRef } from '@backstage/frontend-plugin-api'; import { TranslationMessages } from '@backstage/frontend-plugin-api'; import { TranslationResource } from '@backstage/frontend-plugin-api'; diff --git a/plugins/app/src/alpha/appModulePublicSignIn.test.tsx b/plugins/app/src/alpha/appModulePublicSignIn.test.tsx index 787b8949e5..c7f1e5a68d 100644 --- a/plugins/app/src/alpha/appModulePublicSignIn.test.tsx +++ b/plugins/app/src/alpha/appModulePublicSignIn.test.tsx @@ -14,10 +14,8 @@ * limitations under the License. */ -import { - SignInPageBlueprint, - createFrontendModule, -} from '@backstage/frontend-plugin-api'; +import { createFrontendModule } from '@backstage/frontend-plugin-api'; +import { SignInPageBlueprint } from '@backstage/plugin-app-react'; import { render, screen, waitFor } from '@testing-library/react'; import { useEffect } from 'react'; import { appModulePublicSignIn } from './appModulePublicSignIn'; diff --git a/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx b/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx index bdd81f338c..9d3483319f 100644 --- a/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx +++ b/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.test.tsx @@ -20,9 +20,9 @@ import { createExtensionInput, createFrontendModule, createSwappableComponent, - SwappableComponentBlueprint, swappableComponentsApiRef, } from '@backstage/frontend-plugin-api'; +import { SwappableComponentBlueprint } from '@backstage/plugin-app-react'; import { DefaultSwappableComponentsApi } from './DefaultSwappableComponentsApi'; import { render, screen } from '@testing-library/react'; import { renderInTestApp, renderTestApp } from '@backstage/frontend-test-utils'; diff --git a/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.tsx b/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.tsx index 593fff24ff..b7c496b3d1 100644 --- a/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.tsx +++ b/plugins/app/src/apis/SwappableComponentsApi/DefaultSwappableComponentsApi.tsx @@ -17,8 +17,8 @@ import { SwappableComponentRef, SwappableComponentsApi, - SwappableComponentBlueprint, } from '@backstage/frontend-plugin-api'; +import { SwappableComponentBlueprint } from '@backstage/plugin-app-react'; import { OpaqueSwappableComponentRef } from '@internal/frontend'; import { lazy } from 'react'; diff --git a/plugins/app/src/extensions/AppNav.tsx b/plugins/app/src/extensions/AppNav.tsx index bba27d1414..12e0c1a5c8 100644 --- a/plugins/app/src/extensions/AppNav.tsx +++ b/plugins/app/src/extensions/AppNav.tsx @@ -19,14 +19,16 @@ import { coreExtensionData, createExtensionInput, NavItemBlueprint, - NavContentBlueprint, - NavContentComponentProps, routeResolutionApiRef, IconComponent, RouteRef, useApi, - NavContentComponent, } from '@backstage/frontend-plugin-api'; +import { + NavContentBlueprint, + NavContentComponent, + NavContentComponentProps, +} from '@backstage/plugin-app-react'; import { Sidebar, SidebarItem } from '@backstage/core-components'; import { useMemo } from 'react'; diff --git a/plugins/app/src/extensions/AppRoot.tsx b/plugins/app/src/extensions/AppRoot.tsx index 43e10a2288..6237643056 100644 --- a/plugins/app/src/extensions/AppRoot.tsx +++ b/plugins/app/src/extensions/AppRoot.tsx @@ -22,9 +22,6 @@ import { JSX, } from 'react'; import { - AppRootWrapperBlueprint, - RouterBlueprint, - SignInPageBlueprint, coreExtensionData, discoveryApiRef, fetchApiRef, @@ -33,6 +30,11 @@ import { createExtensionInput, routeResolutionApiRef, } from '@backstage/frontend-plugin-api'; +import { + AppRootWrapperBlueprint, + RouterBlueprint, + SignInPageBlueprint, +} from '@backstage/plugin-app-react'; import { DiscoveryApi, ErrorApi, diff --git a/plugins/app/src/extensions/AppThemeApi.tsx b/plugins/app/src/extensions/AppThemeApi.tsx index cf46cab09e..e9fa23b574 100644 --- a/plugins/app/src/extensions/AppThemeApi.tsx +++ b/plugins/app/src/extensions/AppThemeApi.tsx @@ -22,10 +22,10 @@ import DarkIcon from '@material-ui/icons/Brightness2'; import LightIcon from '@material-ui/icons/WbSunny'; import { createExtensionInput, - ThemeBlueprint, ApiBlueprint, appThemeApiRef, } from '@backstage/frontend-plugin-api'; +import { ThemeBlueprint } from '@backstage/plugin-app-react'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { AppThemeSelector } from '../../../../packages/core-app-api/src/apis/implementations'; diff --git a/plugins/app/src/extensions/DefaultSignInPage.tsx b/plugins/app/src/extensions/DefaultSignInPage.tsx index 10ad638b00..48d34c09c2 100644 --- a/plugins/app/src/extensions/DefaultSignInPage.tsx +++ b/plugins/app/src/extensions/DefaultSignInPage.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { SignInPageBlueprint } from '@backstage/frontend-plugin-api'; +import { SignInPageBlueprint } from '@backstage/plugin-app-react'; import { SignInPage } from '@backstage/core-components'; export const DefaultSignInPage = SignInPageBlueprint.make({ diff --git a/plugins/app/src/extensions/IconsApi.ts b/plugins/app/src/extensions/IconsApi.ts index 9be4a3e79a..d8a97eb7c7 100644 --- a/plugins/app/src/extensions/IconsApi.ts +++ b/plugins/app/src/extensions/IconsApi.ts @@ -16,10 +16,10 @@ import { createExtensionInput, - IconBundleBlueprint, ApiBlueprint, iconsApiRef, } from '@backstage/frontend-plugin-api'; +import { IconBundleBlueprint } from '@backstage/plugin-app-react'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { DefaultIconsApi } from '../../../../packages/frontend-app-api/src/apis/implementations/IconsApi'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports diff --git a/plugins/app/src/extensions/SwappableComponentsApi.ts b/plugins/app/src/extensions/SwappableComponentsApi.ts index d72b52a332..838b80e942 100644 --- a/plugins/app/src/extensions/SwappableComponentsApi.ts +++ b/plugins/app/src/extensions/SwappableComponentsApi.ts @@ -15,11 +15,11 @@ */ import { - SwappableComponentBlueprint, createExtensionInput, ApiBlueprint, swappableComponentsApiRef, } from '@backstage/frontend-plugin-api'; +import { SwappableComponentBlueprint } from '@backstage/plugin-app-react'; import { DefaultSwappableComponentsApi } from '../apis/SwappableComponentsApi'; /** diff --git a/plugins/app/src/extensions/TranslationsApi.tsx b/plugins/app/src/extensions/TranslationsApi.tsx index 8421dba9d9..a327aba299 100644 --- a/plugins/app/src/extensions/TranslationsApi.tsx +++ b/plugins/app/src/extensions/TranslationsApi.tsx @@ -15,9 +15,9 @@ */ import { ApiBlueprint, - TranslationBlueprint, createExtensionInput, } from '@backstage/frontend-plugin-api'; +import { TranslationBlueprint } from '@backstage/plugin-app-react'; import { appLanguageApiRef, translationApiRef, diff --git a/plugins/app/src/extensions/components.tsx b/plugins/app/src/extensions/components.tsx index 99f6291e87..72a77e2446 100644 --- a/plugins/app/src/extensions/components.tsx +++ b/plugins/app/src/extensions/components.tsx @@ -17,9 +17,8 @@ import { NotFoundErrorPage as SwappableNotFoundErrorPage, Progress as SwappableProgress, ErrorDisplay as SwappableErrorDisplay, - SwappableComponentBlueprint, } from '@backstage/frontend-plugin-api'; - +import { SwappableComponentBlueprint } from '@backstage/plugin-app-react'; import { ErrorPage, ErrorPanel, diff --git a/yarn.lock b/yarn.lock index 675eba1c8c..a47cb186d9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3571,6 +3571,7 @@ __metadata: "@backstage/frontend-app-api": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/frontend-test-utils": "workspace:^" + "@backstage/plugin-app-react": "workspace:^" "@backstage/plugin-catalog": "workspace:^" "@backstage/plugin-catalog-react": "workspace:^" "@backstage/test-utils": "workspace:^" @@ -3861,6 +3862,7 @@ __metadata: "@backstage/frontend-app-api": "workspace:^" "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-app": "workspace:^" + "@backstage/plugin-app-react": "workspace:^" "@backstage/test-utils": "workspace:^" "@react-hookz/web": "npm:^24.0.0" "@testing-library/jest-dom": "npm:^6.0.0" @@ -30966,6 +30968,7 @@ __metadata: "@backstage/integration-react": "workspace:^" "@backstage/plugin-api-docs": "workspace:^" "@backstage/plugin-app": "workspace:^" + "@backstage/plugin-app-react": "workspace:^" "@backstage/plugin-app-visualizer": "workspace:^" "@backstage/plugin-auth": "workspace:^" "@backstage/plugin-auth-react": "workspace:^" From f73d947c85ebb6578248517c944b722cedf36665 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 23 Jan 2026 11:16:00 +0100 Subject: [PATCH 059/211] docs/frontend-system: document internal option for createExtensionInput Signed-off-by: Patrik Oldsberg --- docs/frontend-system/architecture/20-extensions.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/frontend-system/architecture/20-extensions.md b/docs/frontend-system/architecture/20-extensions.md index a6d00a3131..c13a54427a 100644 --- a/docs/frontend-system/architecture/20-extensions.md +++ b/docs/frontend-system/architecture/20-extensions.md @@ -174,6 +174,8 @@ const navigationExtension = createExtension({ The input (see [1] above) is an object that we create using `createExtensionInput`. The first argument is the set of extension data that we accept via this input, and works just like the `output` option. The second argument is optional, and it allows us to put constraints on the extensions that are attached to our input. If the `singleton: true` option is set, only a single extension can be attached at a time, and unless the `optional: true` option is set it will also be required that there is exactly one attached extension. +Another option that can be used when creating an extension input is the `internal: true` option, which restricts the input to only accept extensions from the same plugin as the extension defining the input. Extensions from other plugins that attempt to attach to an internal input will be ignored, and a warning will be reported. This is useful when you want to limit extensibility to overrides and modules of your plugin, rather than letting it be open to any plugin. + So how can we now attach the output to the parent extension's input? If we think about a navigation component, like the Sidebar in Backstage, there might be plugins that want to attach a link to their plugin to this navigation component. In this case the plugin only needs to know the extension `id` and the name of the extension `input` to attach the extension `output` returned by the `factory` to the specified extension: ```tsx From 6539d9a15ca38cfeb49da656b7a953f9eda20445 Mon Sep 17 00:00:00 2001 From: Jack Palmer Date: Fri, 23 Jan 2026 10:46:51 +0000 Subject: [PATCH 060/211] feat: Add organization option to auth0 auth provider Signed-off-by: Jack Palmer --- .../config.d.ts | 1 + .../package.json | 2 + .../src/authenticator.ts | 2 + .../src/strategy.ts | 16 +++ .../src/types.d.ts | 100 ++++++++++++++++++ yarn.lock | 2 + 6 files changed, 123 insertions(+) create mode 100644 plugins/auth-backend-module-auth0-provider/src/types.d.ts diff --git a/plugins/auth-backend-module-auth0-provider/config.d.ts b/plugins/auth-backend-module-auth0-provider/config.d.ts index 661409a592..d2b1c66389 100644 --- a/plugins/auth-backend-module-auth0-provider/config.d.ts +++ b/plugins/auth-backend-module-auth0-provider/config.d.ts @@ -32,6 +32,7 @@ export interface Config { audience?: string; connection?: string; connectionScope?: string; + organization?: string; sessionDuration?: HumanDuration | string; }; }; diff --git a/plugins/auth-backend-module-auth0-provider/package.json b/plugins/auth-backend-module-auth0-provider/package.json index 8b749bc420..7ca877a6a2 100644 --- a/plugins/auth-backend-module-auth0-provider/package.json +++ b/plugins/auth-backend-module-auth0-provider/package.json @@ -37,6 +37,7 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "express": "^4.22.0", + "passport": "^0.7.0", "passport-auth0": "^1.4.3", "passport-oauth2": "^1.6.1" }, @@ -46,6 +47,7 @@ "@backstage/cli": "workspace:^", "@backstage/plugin-auth-backend": "workspace:^", "@backstage/types": "workspace:^", + "@types/passport": "^1.0.3", "@types/passport-auth0": "^1.0.5", "@types/passport-oauth2": "^1.4.15", "supertest": "^7.0.0" diff --git a/plugins/auth-backend-module-auth0-provider/src/authenticator.ts b/plugins/auth-backend-module-auth0-provider/src/authenticator.ts index a042f113f8..5ed5fc580c 100644 --- a/plugins/auth-backend-module-auth0-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-auth0-provider/src/authenticator.ts @@ -35,6 +35,7 @@ export const auth0Authenticator = createOAuthAuthenticator({ const connection = config.getOptionalString('connection'); const connectionScope = config.getOptionalString('connectionScope'); const callbackURL = config.getOptionalString('callbackUrl') ?? callbackUrl; + const organization = config.getOptionalString('organization'); // Due to passport-auth0 forcing options.state = true, // passport-oauth2 requires express-session to be installed // so that the 'state' parameter of the oauth2 flow can be stored. @@ -58,6 +59,7 @@ export const auth0Authenticator = createOAuthAuthenticator({ callbackURL, domain, store, + organization, // We need passReqToCallback set to false to get params, but there's // no matching type signature for that, so instead behold this beauty passReqToCallback: false as true, diff --git a/plugins/auth-backend-module-auth0-provider/src/strategy.ts b/plugins/auth-backend-module-auth0-provider/src/strategy.ts index 039885dddb..a8699555a7 100644 --- a/plugins/auth-backend-module-auth0-provider/src/strategy.ts +++ b/plugins/auth-backend-module-auth0-provider/src/strategy.ts @@ -25,10 +25,13 @@ export interface Auth0StrategyOptionsWithRequest { domain: string; passReqToCallback: true; store: StateStore; + organization?: string; } /** @public */ export class Auth0Strategy extends Auth0InternalStrategy { + private organization: string | undefined; + constructor( options: Auth0StrategyOptionsWithRequest, verify: Auth0InternalStrategy.VerifyFunction, @@ -41,5 +44,18 @@ export class Auth0Strategy extends Auth0InternalStrategy { apiUrl: `https://${options.domain}/api`, }; super(optionsWithURLs, verify); + this.organization = options.organization; + } + + authorizationParams(options: Record): Record { + const params = super.authorizationParams(options); + + if (this.organization) { + return { + ...params, + organization: this.organization, + }; + } + return params; } } diff --git a/plugins/auth-backend-module-auth0-provider/src/types.d.ts b/plugins/auth-backend-module-auth0-provider/src/types.d.ts new file mode 100644 index 0000000000..f0efc49347 --- /dev/null +++ b/plugins/auth-backend-module-auth0-provider/src/types.d.ts @@ -0,0 +1,100 @@ +/* + * Copyright 2026 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +declare module 'passport-auth0' { + import passport from 'passport'; + import express from 'express'; + + declare class StrategyInternal extends passport.Strategy { + constructor( + options: StrategyInternal.StrategyOptionWithRequest, + verify: StrategyInternal.VerifyFunctionWithRequest, + ); + constructor( + options: StrategyInternal.StrategyOption, + verify: StrategyInternal.VerifyFunction, + ); + + name: string; + authenticate(req: express.Request, options?: object): void; + authorizationParams(options: Record): Record; + } + + declare namespace StrategyInternal { + interface Profile extends passport.Profile { + id: string; + displayName: string; + gender?: string | undefined; + ageRange?: + | { + min: number; + max?: number | undefined; + } + | undefined; + profileUrl?: string | undefined; + username?: string | undefined; + birthday: string; + + _raw: string; + _json: any; + } + + interface AuthenticateOptions extends passport.AuthenticateOptions { + authType?: string | undefined; + } + + interface StrategyOption { + clientID: string; + clientSecret: string; + callbackURL: string; + domain: string; + scopeSeparator?: string | undefined; + enableProof?: boolean | undefined; + profileFields?: string[] | undefined; + state?: boolean | undefined; + } + + interface StrategyOptionWithRequest extends StrategyOption { + passReqToCallback: true; + } + interface ExtraVerificationParams { + audience?: string | undefined; + connection?: string | undefined; + prompt?: string | undefined; + } + + type VerifyFunction = ( + accessToken: string, + refreshToken: string, + extraParams: ExtraVerificationParams, + profile: Profile, + done: (error: any, user?: any, info?: any) => void, + ) => void; + + type VerifyFunctionWithRequest = ( + req: express.Request, + accessToken: string, + refreshToken: string, + extraParams: ExtraVerificationParams, + profile: Profile, + done: (error: any, user?: any, info?: any) => void, + ) => void; + + export import Strategy = StrategyInternal; + } + + export = StrategyInternal; +} diff --git a/yarn.lock b/yarn.lock index 675eba1c8c..8a2dfb2568 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4283,9 +4283,11 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/types": "workspace:^" + "@types/passport": "npm:^1.0.3" "@types/passport-auth0": "npm:^1.0.5" "@types/passport-oauth2": "npm:^1.4.15" express: "npm:^4.22.0" + passport: "npm:^0.7.0" passport-auth0: "npm:^1.4.3" passport-oauth2: "npm:^1.6.1" supertest: "npm:^7.0.0" From 36804fed6ef3b6e25bcdfcfcfa80d32a7d5ac4f6 Mon Sep 17 00:00:00 2001 From: Jack Palmer Date: Fri, 23 Jan 2026 10:48:38 +0000 Subject: [PATCH 061/211] chore: add changeset Signed-off-by: Jack Palmer --- .changeset/chatty-tips-stop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chatty-tips-stop.md diff --git a/.changeset/chatty-tips-stop.md b/.changeset/chatty-tips-stop.md new file mode 100644 index 0000000000..7b4c435ffc --- /dev/null +++ b/.changeset/chatty-tips-stop.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-auth0-provider': minor +--- + +feat: Added organization option to authorization params of the strategy From 59c06a215edaf6cf627753462f522b7d8a2ee4ca Mon Sep 17 00:00:00 2001 From: Jack Palmer Date: Fri, 23 Jan 2026 10:53:35 +0000 Subject: [PATCH 062/211] chore: update documentation Signed-off-by: Jack Palmer --- docs/auth/auth0/provider.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/auth/auth0/provider.md b/docs/auth/auth0/provider.md index 90ac36f07d..b836bf01f6 100644 --- a/docs/auth/auth0/provider.md +++ b/docs/auth/auth0/provider.md @@ -44,6 +44,7 @@ auth: audience: ${AUTH_AUTH0_AUDIENCE} connection: ${AUTH_AUTH0_CONNECTION} connectionScope: ${AUTH_AUTH0_CONNECTION_SCOPE} + organization: ${AUTH_AUTH0_ORGANIZATION_ID} ## uncomment to set lifespan of user session # sessionDuration: { hours: 24 } # supports `ms` library format (e.g. '24h', '2 days'), ISO duration, "human duration" as used in code session: @@ -69,6 +70,7 @@ Auth0 requires a session, so you need to give the session a secret key. - `connection`: Social identity provider name. To check the available social connections, please visit [Auth0 Social Connections](https://marketplace.auth0.com/features/social-connections). - `connectionScope`: Additional scopes in the interactive token request. It should always be used in combination with the `connection` parameter. - `sessionDuration`: Lifespan of the user session. +- `organization`: Specify a specific organization ID to be targeted as part of the login flow. ### Resolvers From 409af724bfae50d67c8dff096b7e38f4ddea7f42 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 23 Jan 2026 11:46:20 +0100 Subject: [PATCH 063/211] test and review fixes Signed-off-by: Patrik Oldsberg --- .changeset/lemon-eyes-grin.md | 2 +- .changeset/tired-sides-share.md | 5 +++ .../frontend-defaults/src/createApp.test.tsx | 4 +-- .../src/blueprints/ApiBlueprint.test.ts | 1 + .../src/wiring/createExtensionInput.ts | 4 +-- .../AppRootWrapperBlueprint.test.tsx | 31 +++++++++++++------ 6 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 .changeset/tired-sides-share.md diff --git a/.changeset/lemon-eyes-grin.md b/.changeset/lemon-eyes-grin.md index 890a143c47..7cbd4b29f1 100644 --- a/.changeset/lemon-eyes-grin.md +++ b/.changeset/lemon-eyes-grin.md @@ -2,4 +2,4 @@ '@backstage/create-app': patch --- -Switched `next-app` template to use blueprint from `@backstage/plugin-app-react. +Switched `next-app` template to use blueprint from `@backstage/plugin-app-react`. diff --git a/.changeset/tired-sides-share.md b/.changeset/tired-sides-share.md new file mode 100644 index 0000000000..661a7aee50 --- /dev/null +++ b/.changeset/tired-sides-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app-react': patch +--- + +Internal refactor to move implementation of blueprints from `@backstage/frontend-plugin-api` to this package. diff --git a/packages/frontend-defaults/src/createApp.test.tsx b/packages/frontend-defaults/src/createApp.test.tsx index 47f6b7895d..3a9ea91bec 100644 --- a/packages/frontend-defaults/src/createApp.test.tsx +++ b/packages/frontend-defaults/src/createApp.test.tsx @@ -60,8 +60,8 @@ describe('createApp', () => { }), }, features: [ - createFrontendPlugin({ - pluginId: 'test', + createFrontendModule({ + pluginId: 'app', extensions: [ ThemeBlueprint.make({ name: 'derp', diff --git a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts index 6d16a215e1..8043752687 100644 --- a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts @@ -200,6 +200,7 @@ describe('ApiBlueprint', () => { "test": { "$$type": "@backstage/ExtensionInput", "config": { + "internal": false, "optional": false, "singleton": false, }, diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts index b4fe9983b9..760bd4b984 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts @@ -60,11 +60,11 @@ export interface ExtensionInput< * plugin. The following options are available: * * - `singleton`: If set to `true`, only one extension can be attached to the - * input at a time. Additional extension will trigger an app error and be + * input at a time. Additional extensions will trigger an app error and be * ignored. * - `optional`: If set to `true`, the input is optional and can be omitted, * this only has an effect if the `singleton` is set to `true`. - * - `internal`: If set to `true`, only extensions from the same plugins will be + * - `internal`: If set to `true`, only extensions from the same plugin will be * allowed to attach to this input. Other extensions will trigger an app error * and be ignored. * diff --git a/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.test.tsx b/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.test.tsx index cd459b371f..a61f583ca8 100644 --- a/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.test.tsx +++ b/plugins/app-react/src/blueprints/AppRootWrapperBlueprint.test.tsx @@ -20,6 +20,7 @@ import { coreExtensionData, createExtension, createExtensionInput, + createFrontendModule, } from '@backstage/frontend-plugin-api'; import { renderTestApp } from '@backstage/frontend-test-utils'; @@ -63,7 +64,11 @@ describe('AppRootWrapperBlueprint', () => { }, }); - renderTestApp({ extensions: [extension] }); + renderTestApp({ + features: [ + createFrontendModule({ pluginId: 'app', extensions: [extension] }), + ], + }); await waitFor(() => expect(screen.getByText('Hello')).toBeInTheDocument()); }); @@ -95,20 +100,28 @@ describe('AppRootWrapperBlueprint', () => { }); renderTestApp({ - extensions: [ - extension, - createExtension({ - name: 'test-child', - attachTo: { id: 'app-root-wrapper:test', input: 'children' }, - output: [coreExtensionData.reactElement], - factory: () => [coreExtensionData.reactElement(
Its Me
)], + extensions: [], + features: [ + createFrontendModule({ + pluginId: 'app', + extensions: [ + extension, + createExtension({ + name: 'test-child', + attachTo: extension.inputs.children, + output: [coreExtensionData.reactElement], + factory: () => [ + coreExtensionData.reactElement(
Its Me
), + ], + }), + ], }), ], config: { app: { extensions: [ { - 'app-root-wrapper:test': { config: { name: 'Robin' } }, + 'app-root-wrapper:app': { config: { name: 'Robin' } }, }, ], }, From 29ddd3cf43929aafda99eec612157f1fd1aa90b7 Mon Sep 17 00:00:00 2001 From: Jack Palmer Date: Fri, 23 Jan 2026 11:33:26 +0000 Subject: [PATCH 064/211] chore: fix types Signed-off-by: Jack Palmer --- plugins/auth-backend-module-auth0-provider/src/types.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/auth-backend-module-auth0-provider/src/types.d.ts b/plugins/auth-backend-module-auth0-provider/src/types.d.ts index f0efc49347..8d4d182728 100644 --- a/plugins/auth-backend-module-auth0-provider/src/types.d.ts +++ b/plugins/auth-backend-module-auth0-provider/src/types.d.ts @@ -18,7 +18,7 @@ declare module 'passport-auth0' { import passport from 'passport'; import express from 'express'; - declare class StrategyInternal extends passport.Strategy { + class StrategyInternal extends passport.Strategy { constructor( options: StrategyInternal.StrategyOptionWithRequest, verify: StrategyInternal.VerifyFunctionWithRequest, @@ -33,7 +33,7 @@ declare module 'passport-auth0' { authorizationParams(options: Record): Record; } - declare namespace StrategyInternal { + namespace StrategyInternal { interface Profile extends passport.Profile { id: string; displayName: string; From 492503a244cc9e5afa0544a4df386282f14844ed Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 23 Jan 2026 13:45:54 +0100 Subject: [PATCH 065/211] frontend-app-api: attribute attachemnt data failures to the attached extension Signed-off-by: Patrik Oldsberg --- .changeset/good-eggs-tell.md | 8 ++ .../src/tree/instantiateAppNodeTree.test.ts | 136 +++++++++++++----- .../src/tree/instantiateAppNodeTree.ts | 27 +++- 3 files changed, 130 insertions(+), 41 deletions(-) create mode 100644 .changeset/good-eggs-tell.md diff --git a/.changeset/good-eggs-tell.md b/.changeset/good-eggs-tell.md new file mode 100644 index 0000000000..86767c24a4 --- /dev/null +++ b/.changeset/good-eggs-tell.md @@ -0,0 +1,8 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Updated error reporting and app tree resolution logic to attribute errors to the correct extension and allow app startup to proceed more optimistically: + +- If an attachment fails to provide the required input data, the error is now attributed to the attachment rather than the parent extension. +- Singleton extension inputs will now only forward attachment errors if the input is required. diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index a07fdf352d..d15873f5d1 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -1574,41 +1574,109 @@ describe('instantiateAppNodeTree', () => { ]); }); - it('should refuse to create an instance with multiple inputs that did not provide required data', () => { - const node = makeNode( - resolveExtensionDefinition( - createExtension({ - name: 'test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput([otherDataRef], { - singleton: true, - }), - }, - output: [], - factory: () => [], - }), - { namespace: 'app' }, - ), + describe('with attachment failures', () => { + const inputCountRef = createExtensionDataRef().with({ + id: 'input-count', + }); + + const attachmentWithoutRequiredData = makeInstanceWithId( + simpleExtension, + undefined, ); - expect( - createAppNodeInstance({ - apis: testApis, - attachments: new Map([ - ['singleton', [makeInstanceWithId(simpleExtension, undefined)]], - ]), - node, - collector, - }), - ).toBeUndefined(); - expect(collector.collectErrors()).toEqual([ - { - code: 'EXTENSION_INPUT_DATA_MISSING', - message: - "extension 'app/test' could not be attached because its output data ('test') does not match what the input 'singleton' requires ('other')", - context: { node, inputName: 'singleton' }, - }, - ]); + + it('should proceed if input is optional', () => { + const node = makeNode( + resolveExtensionDefinition( + createExtension({ + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput([otherDataRef], { + singleton: true, + optional: true, + }), + }, + output: [inputCountRef], + factory: ({ inputs }) => [ + inputCountRef(inputs.singleton ? 1 : 0), + ], + }), + { namespace: 'app' }, + ), + ); + expect( + createAppNodeInstance({ + apis: testApis, + attachments: new Map([ + ['singleton', [attachmentWithoutRequiredData]], + ]), + node, + collector, + })?.getData(inputCountRef), + ).toBe(0); + + expect(collector.collectErrors()).toEqual([ + { + code: 'EXTENSION_INPUT_DATA_MISSING', + message: + "extension 'app/test' could not be attached because its output data ('test') does not match what the input 'singleton' requires ('other')", + context: { + node: attachmentWithoutRequiredData, + inputName: 'singleton', + }, + }, + ]); + }); + + it('should fail if input is required', () => { + const node = makeNode( + resolveExtensionDefinition( + createExtension({ + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput([otherDataRef], { + singleton: true, + }), + }, + output: [inputCountRef], + factory: ({ inputs }) => [ + inputCountRef(inputs.singleton ? 1 : 0), + ], + }), + { namespace: 'app' }, + ), + ); + + expect( + createAppNodeInstance({ + apis: testApis, + attachments: new Map([ + ['singleton', [attachmentWithoutRequiredData]], + ]), + node, + collector, + })?.getData(inputCountRef), + ).toBeUndefined(); + + expect(collector.collectErrors()).toEqual([ + { + code: 'EXTENSION_ATTACHMENT_MISSING', + message: + "input 'singleton' is required but it failed to be instantiated", + context: { inputName: 'singleton', node }, + }, + { + code: 'EXTENSION_INPUT_DATA_MISSING', + message: + "extension 'app/test' could not be attached because its output data ('test') does not match what the input 'singleton' requires ('other')", + context: { + node: attachmentWithoutRequiredData, + inputName: 'singleton', + }, + }, + ]); + }); }); }); }); diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 04082f8520..55bea60274 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -119,7 +119,7 @@ function resolveInputDataContainer( .map(r => `'${r.id}'`) .join(', '); - collector.report({ + collector.child({ node: attachment }).report({ code: 'EXTENSION_INPUT_DATA_MISSING', message: `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`, }); @@ -271,12 +271,25 @@ function resolveV2Inputs( } return undefined; } - return resolveInputDataContainer( - input.extensionData, - attachedNodes[0], - inputName, - collector, - ); + try { + return resolveInputDataContainer( + input.extensionData, + attachedNodes[0], + inputName, + collector, + ); + } catch (error) { + if (error === INSTANTIATION_FAILED) { + if (input.config.optional) { + return undefined; + } + collector.report({ + code: 'EXTENSION_ATTACHMENT_MISSING', + message: `input '${inputName}' is required but it failed to be instantiated`, + }); + } + throw error; + } } return mapWithFailures(attachedNodes, attachment => From efea76beb0df3e23ecdac6fc8b3e5b1d3c5067f7 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 23 Jan 2026 13:52:47 +0100 Subject: [PATCH 066/211] frontend-app-api: filter out failed attachments Signed-off-by: Patrik Oldsberg --- .changeset/good-eggs-tell.md | 1 + .../src/tree/instantiateAppNodeTree.test.ts | 49 +++++++++++++++++++ .../src/tree/instantiateAppNodeTree.ts | 28 ++++++----- 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/.changeset/good-eggs-tell.md b/.changeset/good-eggs-tell.md index 86767c24a4..04ce63db7d 100644 --- a/.changeset/good-eggs-tell.md +++ b/.changeset/good-eggs-tell.md @@ -6,3 +6,4 @@ Updated error reporting and app tree resolution logic to attribute errors to the - If an attachment fails to provide the required input data, the error is now attributed to the attachment rather than the parent extension. - Singleton extension inputs will now only forward attachment errors if the input is required. +- Array extension inputs will now filter out failed attachments instead of failing the entire app tree resolution. diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index d15873f5d1..485e9d95bd 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -1677,6 +1677,55 @@ describe('instantiateAppNodeTree', () => { }, ]); }); + + it('should filter out failed attachments for non-singleton inputs', () => { + const node = makeNode( + resolveExtensionDefinition( + createExtension({ + name: 'test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + children: createExtensionInput([otherDataRef]), + }, + output: [inputCountRef], + factory: ({ inputs }) => [ + inputCountRef(inputs.children.length), + ], + }), + { namespace: 'app' }, + ), + ); + expect( + createAppNodeInstance({ + apis: testApis, + attachments: new Map([ + [ + 'children', + [ + attachmentWithoutRequiredData, + makeInstanceWithId(simpleExtension, { + other: 42, + }), + ], + ], + ]), + node, + collector, + })?.getData(inputCountRef), + ).toBe(1); + + expect(collector.collectErrors()).toEqual([ + { + code: 'EXTENSION_INPUT_DATA_MISSING', + message: + "extension 'app/test' could not be attached because its output data ('test') does not match what the input 'children' requires ('other')", + context: { + node: attachmentWithoutRequiredData, + inputName: 'children', + }, + }, + ]); + }); }); }); }); diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 55bea60274..ca0d785fee 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -38,21 +38,22 @@ const INSTANTIATION_FAILED = new Error('Instantiation failed'); function mapWithFailures( iterable: Iterable, callback: (item: T) => U, + options?: { ignoreFailures?: boolean }, ): U[] { let failed = false; - const results = Array.from(iterable).map(item => { + const results = []; + for (const item of iterable) { try { - return callback(item); + results.push(callback(item)); } catch (error) { if (error === INSTANTIATION_FAILED) { failed = true; } else { throw error; } - return null as any; } - }); - if (failed) { + } + if (failed && !options?.ignoreFailures) { throw INSTANTIATION_FAILED; } return results; @@ -292,13 +293,16 @@ function resolveV2Inputs( } } - return mapWithFailures(attachedNodes, attachment => - resolveInputDataContainer( - input.extensionData, - attachment, - inputName, - collector, - ), + return mapWithFailures( + attachedNodes, + attachment => + resolveInputDataContainer( + input.extensionData, + attachment, + inputName, + collector, + ), + { ignoreFailures: true }, ); }) as ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }>; } From caeb9ada1d11589ef3e2fc485a4789507f7ab33b Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Thu, 22 Jan 2026 12:14:15 +0100 Subject: [PATCH 067/211] fix(ui)!: remove Fragment wrapper in Table to prevent id prop error React Aria's Collection component injects an `id` prop into rendered children. When children were wrapped in Fragment, this caused "Invalid prop `id` supplied to `React.Fragment`" errors on render. BREAKING CHANGE: The `cell` and `header` properties in `ColumnConfig` now return `ReactElement` instead of `ReactNode`. Cell and header functions that return non-element values must be updated to always return a valid element. Signed-off-by: Johan Persson --- .changeset/quiet-carpets-arrive.md | 22 +++++++++++++++++++ packages/ui/report.api.md | 4 ++-- .../src/components/Table/components/Table.tsx | 8 +++---- packages/ui/src/components/Table/types.ts | 6 ++--- 4 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 .changeset/quiet-carpets-arrive.md diff --git a/.changeset/quiet-carpets-arrive.md b/.changeset/quiet-carpets-arrive.md new file mode 100644 index 0000000000..e0104cc286 --- /dev/null +++ b/.changeset/quiet-carpets-arrive.md @@ -0,0 +1,22 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: The `cell` and `header` properties in `ColumnConfig` now return `ReactElement` instead of `ReactNode`. + +This fixes an issue where React Aria's Collection component would inject an `id` prop into Fragment wrappers, causing "Invalid prop `id` supplied to `React.Fragment`" errors on render. + +Migration: + +```diff +const columns: ColumnConfig[] = [ + { + id: 'name', + label: 'Name', +- cell: (item) => item.name, ++ cell: (item) => , +- header: () => 'Name', ++ header: () => Name, + }, +]; +``` diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index fa50111cde..47d306fefb 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -479,11 +479,11 @@ export const Column: (props: ColumnProps) => JSX_2.Element; // @public (undocumented) export interface ColumnConfig { // (undocumented) - cell: (item: T) => ReactNode; + cell: (item: T) => ReactElement; // (undocumented) defaultWidth?: ColumnSize | null; // (undocumented) - header?: () => ReactNode; + header?: () => ReactElement; // (undocumented) id: string; // (undocumented) diff --git a/packages/ui/src/components/Table/components/Table.tsx b/packages/ui/src/components/Table/components/Table.tsx index f417063e74..c65340c026 100644 --- a/packages/ui/src/components/Table/components/Table.tsx +++ b/packages/ui/src/components/Table/components/Table.tsx @@ -29,7 +29,7 @@ import type { RowRenderFn, TablePaginationType, } from '../types'; -import { Fragment, useMemo } from 'react'; +import { useMemo } from 'react'; import { VisuallyHidden } from '../../VisuallyHidden'; import { Flex } from '../../Flex'; @@ -158,7 +158,7 @@ export function Table({ {column => column.header ? ( - <>{column.header()} + column.header() ) : ( ({ : undefined } > - {column => ( - {column.cell(item)} - )} + {column => column.cell(item)} ); }} diff --git a/packages/ui/src/components/Table/types.ts b/packages/ui/src/components/Table/types.ts index 6ba44b6697..6d94d58fe6 100644 --- a/packages/ui/src/components/Table/types.ts +++ b/packages/ui/src/components/Table/types.ts @@ -19,7 +19,7 @@ import { ColumnProps as ReactAriaColumnProps, TableProps as ReactAriaTableProps, } from 'react-aria-components'; -import type { ReactNode } from 'react'; +import type { ReactElement, ReactNode } from 'react'; import type { SortDescriptor as ReactStatelySortDescriptor } from 'react-stately'; import type { ColumnSize, ColumnStaticSize } from '@react-types/table'; import type { TextColors } from '../../types'; @@ -89,8 +89,8 @@ export type TablePaginationType = NoPagination | PagePagination; export interface ColumnConfig { id: string; label: string; - cell: (item: T) => ReactNode; - header?: () => ReactNode; + cell: (item: T) => ReactElement; + header?: () => ReactElement; isSortable?: boolean; isHidden?: boolean; width?: ColumnSize | null; From 40a30551a088bd54623aa2bca39742c0fd0aa84b Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Thu, 22 Jan 2026 12:18:59 +0100 Subject: [PATCH 068/211] feat(docs-ui): improve documentation components - Add ReactAriaLink component for linking to React Aria docs - Make PropsTable columns configurable - Improve Chip styling to support multiline content - Allow ReactNode in PropDef descriptions Signed-off-by: Johan Persson --- docs-ui/src/components/Chip/styles.module.css | 6 +- .../src/components/PropsTable/PropsTable.tsx | 146 +++++++++++------- .../ReactAriaLink/ReactAriaLink.tsx | 26 ++++ docs-ui/src/components/ReactAriaLink/index.ts | 1 + .../ReactAriaLink/styles.module.css | 15 ++ docs-ui/src/utils/propDefs.ts | 3 +- 6 files changed, 138 insertions(+), 59 deletions(-) create mode 100644 docs-ui/src/components/ReactAriaLink/ReactAriaLink.tsx create mode 100644 docs-ui/src/components/ReactAriaLink/index.ts create mode 100644 docs-ui/src/components/ReactAriaLink/styles.module.css diff --git a/docs-ui/src/components/Chip/styles.module.css b/docs-ui/src/components/Chip/styles.module.css index 01ee29311d..80c37d8899 100644 --- a/docs-ui/src/components/Chip/styles.module.css +++ b/docs-ui/src/components/Chip/styles.module.css @@ -1,11 +1,9 @@ .chip { - display: inline-flex; - align-items: center; + display: inline; font-family: monospace; font-size: 13px; border-radius: 6px; - padding: 0px 8px; - height: 24px; + padding: 4px 8px; margin-right: 4px; background-color: #f0f0f0; color: #5d5d5d; diff --git a/docs-ui/src/components/PropsTable/PropsTable.tsx b/docs-ui/src/components/PropsTable/PropsTable.tsx index 86b7b274ef..e1324b88b9 100644 --- a/docs-ui/src/components/PropsTable/PropsTable.tsx +++ b/docs-ui/src/components/PropsTable/PropsTable.tsx @@ -6,77 +6,115 @@ import { TypePopup } from './TypePopup'; import { PropDef } from '@/utils/propDefs'; -// Use the proper PropDef type type PropData = PropDef; -// Modify the PropsTable component to use the new type +type ColumnType = 'prop' | 'type' | 'default' | 'description' | 'responsive'; + +interface ColumnConfig { + key: ColumnType; + width: string; +} + +const defaultColumns: ColumnConfig[] = [ + { key: 'prop', width: '16%' }, + { key: 'type', width: '50%' }, + { key: 'default', width: '20%' }, + { key: 'responsive', width: '14%' }, +]; + +const columnLabels: Record = { + prop: 'Prop', + type: 'Type', + default: 'Default', + description: 'Description', + responsive: 'Responsive', +}; + export const PropsTable = >({ data, + columns = defaultColumns, }: { data: T; + columns?: ColumnConfig[]; }) => { if (!data) return null; + const renderCell = ( + propName: string, + propData: PropData, + column: ColumnType, + ) => { + const enumValues = + Array.isArray(propData.values) && + propData.values.map(t => {t}); + + switch (column) { + case 'prop': + return {propName}; + + case 'type': + return ( +
+ {propData.type === 'string' && string} + {propData.type === 'number' && number} + {propData.type === 'boolean' && boolean} + {propData.type === 'enum' && enumValues} + {propData.type === 'spacing' && ( + <> + 0.5, 1, 1.5, 2, 3, ..., 14 + string + + )} + {propData.type === 'complex' && propData.complexType && ( + + )} + {propData.type === 'enum | string' && ( + <> + {enumValues} + string + + )} +
+ ); + + case 'default': + return propData.default ? {propData.default} : null; + + case 'description': + return propData.description || null; + + case 'responsive': + return {propData.responsive ? 'Yes' : 'No'}; + + default: + return null; + } + }; + return ( - Prop - Type - Default - - Responsive - + {columns.map(col => ( + + {columnLabels[col.key]} + + ))} - {Object.keys(data).map(n => { - const enumValues = - Array.isArray(data[n].values) && - data[n].values.map(t => {t}); - - return ( - - - {n} + {Object.keys(data).map(n => ( + + {columns.map(col => ( + + {renderCell(n, data[n], col.key)} - -
- {data[n].type === 'string' && string} - {data[n].type === 'number' && number} - {data[n].type === 'boolean' && boolean} - {data[n].type === 'enum' && enumValues} - {data[n].type === 'spacing' && ( - <> - 0.5, 1, 1.5, 2, 3, ..., 14 - string - - )} - {data[n].type === 'complex' && data[n].complexType && ( - - )} - {data[n].type === 'enum | string' && ( - <> - {enumValues} - string - - )} -
-
- - {data[n].default ? data[n].default : '-'} - - - {data[n].responsive ? 'Yes' : 'No'} - -
- ); - })} + ))} +
+ ))}
); diff --git a/docs-ui/src/components/ReactAriaLink/ReactAriaLink.tsx b/docs-ui/src/components/ReactAriaLink/ReactAriaLink.tsx new file mode 100644 index 0000000000..37656c482d --- /dev/null +++ b/docs-ui/src/components/ReactAriaLink/ReactAriaLink.tsx @@ -0,0 +1,26 @@ +import styles from './styles.module.css'; + +interface ReactAriaLinkProps { + /** The React Aria component name (e.g., "Cell", "Table") */ + component: string; + /** The documentation URL */ + href: string; +} + +/** + * Displays a standardized note linking to React Aria documentation. + * + * Usage: + * + */ +export function ReactAriaLink({ component, href }: ReactAriaLinkProps) { + return ( +

+ Inherits all{' '} + + React Aria {component} + {' '} + props. +

+ ); +} diff --git a/docs-ui/src/components/ReactAriaLink/index.ts b/docs-ui/src/components/ReactAriaLink/index.ts new file mode 100644 index 0000000000..8fa7b70e73 --- /dev/null +++ b/docs-ui/src/components/ReactAriaLink/index.ts @@ -0,0 +1 @@ +export { ReactAriaLink } from './ReactAriaLink'; diff --git a/docs-ui/src/components/ReactAriaLink/styles.module.css b/docs-ui/src/components/ReactAriaLink/styles.module.css new file mode 100644 index 0000000000..b74436fee3 --- /dev/null +++ b/docs-ui/src/components/ReactAriaLink/styles.module.css @@ -0,0 +1,15 @@ +.note { + font-size: 1rem; + color: var(--secondary); + margin-top: 0.5rem; + margin-bottom: 1rem; +} + +.note a { + color: var(--link); + text-decoration: none; +} + +.note a:hover { + text-decoration: underline; +} diff --git a/docs-ui/src/utils/propDefs.ts b/docs-ui/src/utils/propDefs.ts index ed4a07e7ee..2dd4f5c25a 100644 --- a/docs-ui/src/utils/propDefs.ts +++ b/docs-ui/src/utils/propDefs.ts @@ -1,3 +1,4 @@ +import type { ReactNode } from 'react'; import type { Breakpoint } from '@backstage/ui/src/types'; const breakpoints = ['initial', 'xs', 'sm', 'md', 'lg', 'xl'] as Breakpoint[]; @@ -28,7 +29,7 @@ export type PropDef = { default?: string; required?: boolean; responsive?: boolean; - description?: string; + description?: ReactNode; }; export { breakpoints }; From ec66afb03e1d9b66e99ab46886b0dfff61b37fae Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Thu, 22 Jan 2026 12:23:11 +0100 Subject: [PATCH 069/211] docs(table): rewrite Table component documentation Comprehensive rewrite of the Table component documentation including: - Updated prop definitions with JSX descriptions - New interactive examples for sorting, search, pagination, selection - Expanded code snippets covering all major use cases - Added custom row and primitives examples - Improved MDX structure and styling Signed-off-by: Johan Persson --- .../src/app/components/table/components.tsx | 554 +++++++++++++++--- docs-ui/src/app/components/table/page.mdx | 345 +++++++---- .../app/components/table/props-definition.ts | 268 --------- .../app/components/table/props-definition.tsx | 462 +++++++++++++++ docs-ui/src/app/components/table/snippets.ts | 437 +++++++++++--- docs-ui/src/css/mdx.module.css | 38 +- docs-ui/src/mdx-components.tsx | 7 +- 7 files changed, 1546 insertions(+), 565 deletions(-) delete mode 100644 docs-ui/src/app/components/table/props-definition.ts create mode 100644 docs-ui/src/app/components/table/props-definition.tsx diff --git a/docs-ui/src/app/components/table/components.tsx b/docs-ui/src/app/components/table/components.tsx index cb158579a1..d26deeb5d3 100644 --- a/docs-ui/src/app/components/table/components.tsx +++ b/docs-ui/src/app/components/table/components.tsx @@ -1,31 +1,46 @@ 'use client'; +import { useState } from 'react'; import { Table, + TableRoot, + TableHeader, + TableBody, + Column, + Row, CellProfile, CellText, - type ColumnConfig, useTable, + type ColumnConfig, } from '../../../../../packages/ui/src/components/Table'; import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex'; import { Text } from '../../../../../packages/ui/src/components/Text/Text'; -import { RadioGroup } from '../../../../../packages/ui/src/components/RadioGroup/RadioGroup'; -import { Radio } from '../../../../../packages/ui/src/components/RadioGroup'; -import { data as data4 } from '../../../../../packages/ui/src/components/Table/stories/mocked-data4'; -import { useState } from 'react'; +import { SearchField } from '../../../../../packages/ui/src/components/SearchField/SearchField'; import { - selectionData, - selectionColumns, -} from '../../../../../packages/ui/src/components/Table/stories/utils'; + RadioGroup, + Radio, +} from '../../../../../packages/ui/src/components/RadioGroup'; +import { data as rockBandData } from '../../../../../packages/ui/src/components/Table/stories/mocked-data4'; +import { data as catalogData } from '../../../../../packages/ui/src/components/Table/stories/mocked-data1'; import { MemoryRouter } from 'react-router-dom'; -type Data4Item = (typeof data4)[0]; +// ============================================================================= +// Types +// ============================================================================= -const columns: ColumnConfig[] = [ +type RockBandItem = (typeof rockBandData)[0]; +type CatalogItem = (typeof catalogData)[0]; + +// ============================================================================= +// Hero Example +// ============================================================================= + +const heroColumns: ColumnConfig[] = [ { id: 'name', label: 'Band name', isRowHeader: true, + defaultWidth: '3fr', cell: item => ( ), @@ -33,112 +48,183 @@ const columns: ColumnConfig[] = [ { id: 'genre', label: 'Genre', + defaultWidth: '3fr', cell: item => , }, { id: 'yearFormed', label: 'Year formed', + defaultWidth: '1fr', cell: item => , }, { id: 'albums', label: 'Albums', + defaultWidth: '1fr', cell: item => , }, ]; -export const TableRockBand = () => { +export function HeroExample() { const { tableProps } = useTable({ mode: 'complete', - getData: () => data4, + getData: () => rockBandData, paginationOptions: { pageSize: 5 }, }); return ( - +
); -}; +} -export const SelectionToggleWithActions = () => { - const [selected, setSelected] = useState | 'all'>( - new Set(), - ); +// ============================================================================= +// Sorting Example +// ============================================================================= +const sortingColumns: ColumnConfig[] = [ + { + id: 'name', + label: 'Name', + isRowHeader: true, + isSortable: true, + cell: item => , + }, + { + id: 'owner', + label: 'Owner', + isSortable: true, + cell: item => , + }, + { + id: 'type', + label: 'Type', + isSortable: true, + cell: item => , + }, + { + id: 'lifecycle', + label: 'Lifecycle', + isSortable: true, + cell: item => , + }, +]; + +export function SortingExample() { const { tableProps } = useTable({ mode: 'complete', - getData: () => selectionData, - paginationOptions: { pageSize: 10 }, + getData: () => catalogData, + paginationOptions: { pageSize: 5 }, + initialSort: { column: 'name', direction: 'ascending' }, + sortFn: (items, { column, direction }) => { + return [...items].sort((a, b) => { + let aVal: string; + let bVal: string; + if (column === 'owner') { + aVal = a.owner.name; + bVal = b.owner.name; + } else { + aVal = String(a[column as keyof CatalogItem]); + bVal = String(b[column as keyof CatalogItem]); + } + const cmp = aVal.localeCompare(bVal); + return direction === 'descending' ? -cmp : cmp; + }); + }, }); return ( -
alert(`Clicked: ${item.name}`) }} - /> +
); -}; +} -export const SelectionModePlayground = () => { - const [selectionMode, setSelectionMode] = useState<'single' | 'multiple'>( - 'multiple', - ); - const [selected, setSelected] = useState | 'all'>( - new Set(), - ); +// ============================================================================= +// Search Example +// ============================================================================= - const { tableProps } = useTable({ +const searchColumns: ColumnConfig[] = [ + { + id: 'name', + label: 'Name', + isRowHeader: true, + cell: item => , + }, + { + id: 'owner', + label: 'Owner', + cell: item => , + }, + { id: 'type', label: 'Type', cell: item => }, +]; + +export function SearchExample() { + const { tableProps, search } = useTable({ mode: 'complete', - getData: () => selectionData, - paginationOptions: { pageSize: 10 }, + getData: () => catalogData, + paginationOptions: { pageSize: 5 }, + searchFn: (items, query) => { + const lowerQuery = query.toLocaleLowerCase('en-US'); + return items.filter( + item => + item.name.toLocaleLowerCase('en-US').includes(lowerQuery) || + item.owner.name.toLocaleLowerCase('en-US').includes(lowerQuery) || + item.type.toLocaleLowerCase('en-US').includes(lowerQuery), + ); + }, }); return ( - -
+ +
No results found for "{search.value}" + ) : ( + No data available + ) + } + {...tableProps} /> -
- - Selection mode: - - { - setSelectionMode(value as 'single' | 'multiple'); - setSelected(new Set()); - }} - > - single - multiple - -
); -}; +} -export const SelectionBehaviorPlayground = () => { +// ============================================================================= +// Selection Example +// ============================================================================= + +const selectionColumns: ColumnConfig[] = [ + { + id: 'name', + label: 'Name', + isRowHeader: true, + cell: item => , + }, + { + id: 'owner', + label: 'Owner', + cell: item => , + }, + { id: 'type', label: 'Type', cell: item => }, +]; + +export function SelectionExample() { + const [selectionMode, setSelectionMode] = useState<'single' | 'multiple'>( + 'multiple', + ); const [selectionBehavior, setSelectionBehavior] = useState< 'toggle' | 'replace' >('toggle'); @@ -148,41 +234,321 @@ export const SelectionBehaviorPlayground = () => { const { tableProps } = useTable({ mode: 'complete', - getData: () => selectionData, - paginationOptions: { pageSize: 10 }, + getData: () => catalogData.slice(0, 5), }); return ( - +
-
- - Selection behavior: - - { - setSelectionBehavior(value as 'toggle' | 'replace'); - setSelected(new Set()); - }} - > - toggle - replace - -
+ +
+ + Selection mode: + + { + setSelectionMode(value as 'single' | 'multiple'); + setSelected(new Set()); + }} + > + single + multiple + +
+
+ + Selection behavior: + + { + setSelectionBehavior(value as 'toggle' | 'replace'); + setSelected(new Set()); + }} + > + toggle + replace + +
+
); -}; +} + +// ============================================================================= +// Row Actions Example +// ============================================================================= + +export function RowActionsExample() { + const [selected, setSelected] = useState | 'all'>( + new Set(), + ); + + const { tableProps } = useTable({ + mode: 'complete', + getData: () => catalogData.slice(0, 5), + }); + + return ( + +
alert(`Clicked: ${item.name}`), + }} + selection={{ + mode: 'multiple', + behavior: 'toggle', + selected, + onSelectionChange: setSelected, + }} + {...tableProps} + /> + + ); +} + +// ============================================================================= +// Empty State Example +// ============================================================================= + +const emptyStateColumns: ColumnConfig[] = [ + { + id: 'name', + label: 'Name', + isRowHeader: true, + cell: item => , + }, + { + id: 'owner', + label: 'Owner', + cell: item => , + }, + { id: 'type', label: 'Type', cell: item => }, +]; + +export function EmptyStateExample() { + const emptyData: CatalogItem[] = []; + + const { tableProps } = useTable({ + mode: 'complete', + getData: () => emptyData, + }); + + return ( + +
No items yet. Create one to get started.} + {...tableProps} + /> + + ); +} + +// ============================================================================= +// Combined Example +// ============================================================================= + +const combinedColumns: ColumnConfig[] = [ + { + id: 'name', + label: 'Name', + isRowHeader: true, + isSortable: true, + cell: item => , + }, + { + id: 'owner', + label: 'Owner', + isSortable: true, + cell: item => , + }, + { + id: 'type', + label: 'Type', + isSortable: true, + cell: item => , + }, +]; + +export function CombinedExample() { + const [selected, setSelected] = useState | 'all'>( + new Set(), + ); + + const { tableProps, search } = useTable({ + mode: 'offset', + getData: async ({ offset, pageSize, search: query, sort }) => { + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Filter data + let filtered = catalogData; + if (query) { + const lowerQuery = query.toLocaleLowerCase('en-US'); + filtered = filtered.filter( + item => + item.name.toLocaleLowerCase('en-US').includes(lowerQuery) || + item.owner.name.toLocaleLowerCase('en-US').includes(lowerQuery) || + item.type.toLocaleLowerCase('en-US').includes(lowerQuery), + ); + } + + // Sort data + if (sort?.column) { + filtered = [...filtered].sort((a, b) => { + let aVal: string; + let bVal: string; + if (sort.column === 'owner') { + aVal = a.owner.name; + bVal = b.owner.name; + } else { + aVal = String(a[sort.column as keyof CatalogItem]); + bVal = String(b[sort.column as keyof CatalogItem]); + } + const cmp = aVal.localeCompare(bVal); + return sort.direction === 'descending' ? -cmp : cmp; + }); + } + + // Paginate + const data = filtered.slice(offset, offset + pageSize); + + return { data, totalCount: filtered.length }; + }, + paginationOptions: { pageSize: 5, pageSizeOptions: [5, 10, 20] }, + initialSort: { column: 'name', direction: 'ascending' }, + }); + + return ( + + + +
alert(`Clicked: ${item.name}`), + }} + selection={{ + mode: 'multiple', + behavior: 'toggle', + selected, + onSelectionChange: setSelected, + }} + emptyState={ + search.value ? ( + No results match your search + ) : ( + No items available + ) + } + {...tableProps} + /> + + + ); +} + +// ============================================================================= +// Custom Row Example +// ============================================================================= + +const customRowColumns: ColumnConfig[] = [ + { + id: 'name', + label: 'Name', + isRowHeader: true, + cell: item => , + }, + { + id: 'owner', + label: 'Owner', + cell: item => , + }, + { + id: 'lifecycle', + label: 'Lifecycle', + cell: item => , + }, +]; + +export function CustomRowExample() { + const { tableProps } = useTable({ + mode: 'complete', + getData: () => catalogData.slice(0, 5), + }); + + return ( + +
( + + {column => column.cell(item)} + + )} + {...tableProps} + /> + + ); +} + +// ============================================================================= +// Primitives Example +// ============================================================================= + +export function PrimitivesExample() { + const items = catalogData.slice(0, 5); + + return ( + + + + Name + Owner + Type + + + {items.map(item => ( + + + + + + ))} + + + + ); +} diff --git a/docs-ui/src/app/components/table/page.mdx b/docs-ui/src/app/components/table/page.mdx index f9b27cd8d3..e39f89f5e9 100644 --- a/docs-ui/src/app/components/table/page.mdx +++ b/docs-ui/src/app/components/table/page.mdx @@ -2,161 +2,294 @@ import { PropsTable } from '@/components/PropsTable'; import { Snippet } from '@/components/Snippet'; import { CodeBlock } from '@/components/CodeBlock'; import { - TableRockBand, - SelectionModePlayground, - SelectionBehaviorPlayground, - SelectionToggleWithActions, + HeroExample, + SortingExample, + SearchExample, + SelectionExample, + RowActionsExample, + EmptyStateExample, + CombinedExample, + CustomRowExample, + PrimitivesExample, } from './components'; import { + tablePropsColumns, + tableReturnColumns, + useTableOptionsPropDefs, + useTableReturnPropDefs, tablePropDefs, - tableHeaderPropDefs, - tableBodyPropDefs, tablePaginationPropDefs, + columnConfigPropDefs, + cellTextPropDefs, + cellProfilePropDefs, + tableRootPropDefs, columnPropDefs, rowPropDefs, - cellPropDefs, } from './props-definition'; import { tableUsageSnippet, - tableBasicSnippet, - tableSelectionActionsSnippet, - tableSelectionModeSnippet, - tableSelectionBehaviorSnippet, + tableHeroSnippet, + tableConceptsSnippet, + tableSortingSnippet, + tablePaginationSnippet, + tableSearchSnippet, + tableSelectionSnippet, + tableRowActionsHrefSnippet, + tableRowActionsClickSnippet, + tableRowActionsDisabledSnippet, + tableEmptyStateSnippet, + tableOffsetPaginationSnippet, + tableCursorPaginationSnippet, + tableCombinedSnippet, + tableCustomRowSnippet, + tablePrimitivesSnippet, } from './snippets'; import { ChangelogComponent } from '@/components/ChangelogComponent'; import { PageTitle } from '@/components/PageTitle'; import { Theming } from '@/components/Theming'; +import { ReactAriaLink } from '@/components/ReactAriaLink'; import { TableDefinition } from '../../../utils/definitions'; +export const reactAriaUrls = { + table: 'https://react-aria.adobe.com/Table#table', + tableHeader: 'https://react-aria.adobe.com/Table#tableheader', + tableBody: 'https://react-aria.adobe.com/Table#tablebody', + column: 'https://react-aria.adobe.com/Table#column', + row: 'https://react-aria.adobe.com/Table#row', + cell: 'https://react-aria.adobe.com/Table#cell', +}; + -} code={tableBasicSnippet} /> +} code={tableHeroSnippet} /> ## Usage -## API reference +## Core Concepts -### Table +The Table component is designed around a hook + component pattern: -The main table component that renders data in a structured grid format. +- **`useTable`** manages data fetching, pagination state, sorting, and filtering. It returns `tableProps` that you spread onto the Table component. +- **`Table`** handles rendering - columns, rows, cells, and interactions. - +The hook supports three modes for different data scenarios: -### TableHeader +- **`complete`** - You have all data available upfront (client-side) +- **`offset`** - Your API uses offset/limit pagination +- **`cursor`** - Your API uses cursor-based pagination -The header section of the table that contains the column definitions. + - +**Controlled vs uncontrolled state** -### Column +By default, `useTable` manages sort, search, and filter state internally. Pass `initialSort`, `initialSearch`, or `initialFilter` to set starting values. -A column definition that describes how a column should be rendered. +For full control over state, use the controlled props instead: - +- `sort` / `onSortChange` +- `search` / `onSearchChange` +- `filter` / `onFilterChange` -### TableBody - -The body section of the table that contains the rows. - - - -### Row - -A row definition that describes how a row should be rendered. - - - -### Cell - -A cell definition that describes how a cell should be rendered. - - - -### TablePagination - -A pagination component designed to work with the Table component. - - - -## Examples - -### Basic Table - -Coming soon. - -### Row selection - -Tables support row selection with two configuration options: selection mode and selection behavior. - -#### Selection mode - -Use `selectionMode` to control how many rows can be selected. With `single`, only one row can be selected at a time. With `multiple`, any number of rows can be selected, and a header checkbox provides select-all functionality. - -} - code={tableSelectionModeSnippet} -/> - -#### Selection behavior - -Use `selectionBehavior` to control how selection is indicated and interacted with. With `toggle`, checkboxes appear for selection. With `replace`, selection is indicated by row background color—click to select, Cmd/Ctrl+click for multiple. - -} - code={tableSelectionBehaviorSnippet} -/> - -#### With row actions - -With toggle behavior, clicking a row triggers its action when nothing is selected. Once any row is selected, clicking toggles selection instead. - -With replace behavior, clicking selects the row and double-clicking triggers the action. - -} - code={tableSelectionActionsSnippet} -/> - -### Row Clicks - -Coming soon. - -### Pagination - -Coming soon. +## Common Patterns ### Sorting -Coming soon. +Columns can be made sortable by adding `isSortable: true` to the column configuration. When sortable, clicking a column header cycles through ascending, descending, and unsorted states. A visual indicator shows the current sort direction. -### Asynchronous loading +With `mode: 'complete'`, sorting happens client-side. Provide a `sortFn` that receives the full dataset and the current sort descriptor, and returns the sorted array. You can also set an `initialSort` to define the default sort when the table first renders. For server-side sorting with `offset` or `cursor` modes, see [Server-Side Data](#server-side-data). -Coming soon. +} code={tableSortingSnippet} /> -### Empty state +### Pagination -Coming soon. +Configure page size and available options through `paginationOptions`. The table displays navigation controls automatically. -### Column resizing + -This feature is not available yet — let us know if you'd like us to explore it! +### Search -### Column reordering +The `useTable` hook returns a `search` object with `value` and `onChange` properties, ready to connect to a search input. With `mode: 'complete'`, provide a `searchFn` that filters the dataset based on the search query. -This feature is not available yet — let us know if you'd like us to explore it! +The search state is debounced internally, so rapid typing doesn't trigger excessive re-filtering. When the search query changes, pagination resets to the first page automatically. -### Column pinning +For server-side search with `offset` or `cursor` modes, the search query is passed to your `getData` function. See [Server-Side Data](#server-side-data). -This feature is not available yet — let us know if you'd like us to explore it! +} code={tableSearchSnippet} /> -### Column visibility +### Row Selection -This feature is not available yet — let us know if you'd like us to explore it! +Tables support row selection with two configuration options: `mode` and `behavior`. + +Selection mode controls how many rows can be selected: + +- `single` - Only one row can be selected at a time +- `multiple` - Any number of rows can be selected, with a header checkbox for select-all + +Selection behavior controls the interaction style: + +- `toggle` - Checkboxes appear for selection. Click a checkbox to select/deselect. +- `replace` - No checkboxes. Click a row to select it (replacing previous selection). Use Cmd/Ctrl+click to select multiple rows. + +Selection state can be managed in two ways: + +- **Controlled** - Pass both `selected` and `onSelectionChange` to fully manage state externally +- **Uncontrolled** - Pass only `onSelectionChange` to let the Table manage state while receiving change notifications + +} code={tableSelectionSnippet} /> + +### Row Actions + +Rows can respond to user interaction in two ways: navigating to a URL or triggering a callback. + +Use `getHref` when rows should link to detail pages. The entire row becomes clickable and navigates on click. Links within cells remain independently clickable. + + + +Use `onClick` for custom actions like opening a panel or triggering a dialog. + + + +When combining row actions with selection, the interaction depends on selection behavior: + +- **`toggle`**: Clicking a row triggers its action when nothing is selected. Once any row is selected, clicking anywhere on the row toggles selection instead. +- **`replace`**: Single click selects the row. Double-click triggers the row action. + +You can also disable specific rows from being clicked using `getIsDisabled`: + + + +} /> + +### Empty State + +When the table has no data to display, provide an `emptyState` to show a helpful message instead of an empty table body. Consider providing different messages depending on context - an empty search result is different from a table with no data at all. + +} code={tableEmptyStateSnippet} /> + +## Server-Side Data + +When your data comes from an API with server-side pagination, use `offset` or `cursor` mode instead of `complete`. The key difference: instead of providing all data upfront, you provide a `getData` function that fetches data for the current page. + +### Offset Pagination + +Use `mode: 'offset'` when your API accepts `offset` and `limit` (or `skip` and `take`) parameters. + + + +The `signal` parameter is an `AbortSignal` - pass it to `fetch` so in-flight requests are cancelled when the user navigates away or triggers a new query. + +### Cursor Pagination + +Use `mode: 'cursor'` when your API uses cursor-based pagination (common with GraphQL or APIs that return `nextCursor`/`prevCursor` tokens). + + + +### Loading States + +When fetching data, the table shows a loading state. If the user triggers a new query (by paginating, sorting, or searching) while previous data is displayed, the table enters a "stale" state where it continues showing the previous data until new data arrives. This prevents jarring layout shifts. + +You can access these states via `tableProps.loading` and `tableProps.isStale` if you need to render additional loading indicators. + +## Combining Features + +Real-world tables often combine multiple features. Here's an example with search, sorting, pagination, and row selection working together. + +} code={tableCombinedSnippet} /> + +## Custom Tables + +For most use cases, `useTable` + `Table` provides everything you need. When you need more control, there are several ways you can customize your table. + +### Custom Row and Header Rendering + +Use a render function for `rowConfig` when you need to customize individual rows based on their data - for example, highlighting rows that require attention. Use `header` in column config to customize header cell content. + +} code={tableCustomRowSnippet} /> + +### Using Primitives Directly + +When the `Table` component doesn't support your use case, you can compose with the lower-level primitives: `TableRoot`, `TableHeader`, `TableBody`, `Column`, and `Row`. + +} code={tablePrimitivesSnippet} /> + +## API Reference + +### useTable + +The `useTable` hook manages data fetching, pagination, sorting, and filtering. + +**Options** + + + +**Return Value** + + + +### Table + +The main table component. + + + +### ColumnConfig + + + +### CellText + + + + + +### CellProfile + + + + + +### TablePagination + + + +### Primitives + +Low-level components for building custom table layouts. + +#### TableRoot + + + + + +#### TableHeader + + + +#### TableBody + + + +#### Column + + + + + +#### Row + + + + + +#### Cell + + diff --git a/docs-ui/src/app/components/table/props-definition.ts b/docs-ui/src/app/components/table/props-definition.ts deleted file mode 100644 index e4ef0ffe8b..0000000000 --- a/docs-ui/src/app/components/table/props-definition.ts +++ /dev/null @@ -1,268 +0,0 @@ -import { - classNamePropDefs, - stylePropDefs, - type PropDef, -} from '@/utils/propDefs'; - -export const tablePropDefs: Record = { - selectionBehavior: { - type: 'enum', - values: ['toggle', 'replace'], - default: 'toggle', - description: 'How multiple selection should behave in the collection.', - }, - disabledBehavior: { - type: 'enum', - values: ['selection', 'all'], - default: 'selection', - description: - 'Whether disabledKeys applies to all interactions, or only selection.', - }, - disabledKeys: { - type: 'enum', - values: ['Iterable'], - description: 'A list of row keys to disable.', - }, - selectionMode: { - type: 'enum', - values: ['single', 'multiple'], - description: 'The type of selection that is allowed in the collection.', - }, - selectedKeys: { - type: 'enum', - values: ['all', 'Iterable'], - description: 'The currently selected keys in the collection (controlled).', - }, - defaultSelectedKeys: { - type: 'enum', - values: ['all', 'Iterable'], - description: 'The initial selected keys in the collection (uncontrolled).', - }, - onRowAction: { - type: 'enum', - values: ['(key: Key) => void'], - description: - 'Handler that is called when a user performs an action on the row.', - }, - onSelectionChange: { - type: 'enum', - values: ['(keys: Selection) => void'], - description: 'Handler that is called when the selection changes.', - }, - onSortChange: { - type: 'enum', - values: ['(descriptor: SortDescriptor) => any'], - description: - 'Handler that is called when the sorted column or direction changes.', - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const tableHeaderPropDefs: Record = { - onHoverStart: { - type: 'enum', - values: ['(e: HoverEvent) => void'], - description: 'Handler that is called when a hover interaction starts.', - }, - onHoverEnd: { - type: 'enum', - values: ['(e: HoverEvent) => void'], - description: 'Handler that is called when a hover interaction ends.', - }, - onHoverChange: { - type: 'enum', - values: ['(isHovering: boolean) => void'], - description: 'Handler that is called when the hover state changes.', - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const columnPropDefs: Record = { - id: { - type: 'enum', - values: ['Key'], - description: 'The unique id of the column.', - }, - allowsSorting: { - type: 'boolean', - description: 'Whether the column allows sorting.', - }, - isRowHeader: { - type: 'boolean', - description: - 'Whether a column is a row header and should be announced by assistive technology during row navigation.', - }, - textValue: { - type: 'string', - description: - "A string representation of the column's contents, used for accessibility announcements.", - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const tableBodyPropDefs: Record = { - renderEmptyState: { - type: 'enum', - values: ['(props) => ReactNode'], - description: - 'Provides content to display when there are no rows in the table.', - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const rowPropDefs: Record = { - textValue: { - type: 'string', - description: - "A string representation of the row's contents, used for accessibility announcements.", - }, - isDisabled: { - type: 'boolean', - description: 'Whether the row is disabled.', - }, - id: { - type: 'enum', - values: ['Key'], - description: 'The unique id of the row.', - }, - href: { - type: 'string', - description: 'The URL to navigate to when the row is clicked.', - }, - hrefLang: { - type: 'string', - description: - 'The language of the URL to navigate to when the row is clicked.', - }, - target: { - type: 'string', - description: - 'The target of the URL to navigate to when the row is clicked.', - }, - rel: { - type: 'string', - description: - 'The relationship of the URL to navigate to when the row is clicked.', - }, - onAction: { - type: 'enum', - values: ['() => void'], - description: - "Handler that is called when a user performs an action on the row. The exact user event depends on the collection's selectionBehavior prop and the interaction modality.", - }, - onHoverStart: { - type: 'enum', - values: ['(e: HoverEvent) => void'], - description: 'Handler that is called when a hover interaction starts.', - }, - onHoverEnd: { - type: 'enum', - values: ['(e: HoverEvent) => void'], - description: 'Handler that is called when a hover interaction ends.', - }, - onHoverChange: { - type: 'enum', - values: ['(isHovering: boolean) => void'], - description: 'Handler that is called when the hover state changes.', - }, - onPress: { - type: 'enum', - values: ['(e: PressEvent) => void'], - description: - 'Handler that is called when the press is released over the target.', - }, - onPressStart: { - type: 'enum', - values: ['(e: PressEvent) => void'], - description: 'Handler that is called when a press interaction starts.', - }, - onPressEnd: { - type: 'enum', - values: ['(e: PressEvent) => void'], - description: - 'Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.', - }, - onPressChange: { - type: 'enum', - values: ['(isPressed: boolean) => void'], - description: 'Handler that is called when the press state changes.', - }, - onPressUp: { - type: 'enum', - values: ['(e: PressEvent) => void'], - description: - 'Handler that is called when a press is released over the target, regardless of whether it started on the target or not.', - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const cellPropDefs: Record = { - id: { - type: 'enum', - values: ['Key'], - description: 'The unique id of the cell.', - }, - textValue: { - type: 'string', - description: - "A string representation of the cell's contents, used for features like typeahead.", - }, - leadingIcon: { - type: 'enum', - values: ['ReactNode'], - description: 'Optional icon to display before the cell content.', - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const tablePaginationPropDefs: Record = { - offset: { - type: 'number', - description: 'The current offset (starting index) for pagination.', - }, - pageSize: { - type: 'number', - description: 'The number of items per page.', - }, - setOffset: { - type: 'enum', - values: ['(offset: number) => void'], - description: 'Handler that is called when the offset changes.', - }, - setPageSize: { - type: 'enum', - values: ['(pageSize: number) => void'], - description: 'Handler that is called when the page size changes.', - }, - rowCount: { - type: 'number', - description: 'The total number of rows in the table.', - }, - onNextPage: { - type: 'enum', - values: ['() => void'], - description: 'Handler that is called when the next page is requested.', - }, - onPreviousPage: { - type: 'enum', - values: ['() => void'], - description: 'Handler that is called when the previous page is requested.', - }, - onPageSizeChange: { - type: 'enum', - values: ['(pageSize: number) => void'], - description: 'Handler that is called when the page size changes.', - }, - showPageSizeOptions: { - type: 'boolean', - description: 'Whether to show the page size options.', - }, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/docs-ui/src/app/components/table/props-definition.tsx b/docs-ui/src/app/components/table/props-definition.tsx new file mode 100644 index 0000000000..58c8f197a7 --- /dev/null +++ b/docs-ui/src/app/components/table/props-definition.tsx @@ -0,0 +1,462 @@ +import { + classNamePropDefs, + stylePropDefs, + type PropDef, +} from '@/utils/propDefs'; +import { Chip } from '@/components/Chip'; + +// ============================================================================= +// PropsTable Column Configuration (Table docs use description instead of responsive) +// ============================================================================= + +export const tablePropsColumns = [ + { key: 'prop' as const, width: '15%' }, + { key: 'type' as const, width: '25%' }, + { key: 'default' as const, width: '15%' }, + { key: 'description' as const, width: '45%' }, +]; + +// For return values (no default column) +export const tableReturnColumns = [ + { key: 'prop' as const, width: '15%' }, + { key: 'type' as const, width: '25%' }, + { key: 'description' as const, width: '60%' }, +]; + +// ============================================================================= +// useTable Hook +// ============================================================================= + +export const useTableOptionsPropDefs: Record = { + mode: { + type: 'enum', + values: ['complete', 'offset', 'cursor'], + description: ( + <> + Data fetching strategy (required). Use complete for + client-side data, offset for offset/limit APIs,{' '} + cursor for cursor-based pagination. + + ), + }, + getData: { + type: 'enum', + values: ['function'], + description: + 'Function that returns or fetches data (required). Signature varies by mode.', + }, + paginationOptions: { + type: 'enum', + values: ['object'], + description: ( + <> + Pagination configuration including pageSize,{' '} + pageSizeOptions, and initialOffset. + + ), + }, + // Uncontrolled state + initialSort: { + type: 'enum', + values: ['SortDescriptor'], + description: 'Default sort configuration on first render (uncontrolled).', + }, + initialSearch: { + type: 'string', + description: 'Default search value on first render (uncontrolled).', + }, + initialFilter: { + type: 'enum', + values: ['TFilter'], + description: 'Default filter value on first render (uncontrolled).', + }, + // Controlled state + sort: { + type: 'enum', + values: ['SortDescriptor'], + description: 'Current sort state (controlled).', + }, + onSortChange: { + type: 'enum', + values: ['(sort: SortDescriptor) => void'], + description: 'Sort change handler (controlled).', + }, + search: { + type: 'string', + description: 'Current search value (controlled).', + }, + onSearchChange: { + type: 'enum', + values: ['(search: string) => void'], + description: 'Search change handler (controlled).', + }, + filter: { + type: 'enum', + values: ['TFilter'], + description: 'Current filter value (controlled).', + }, + onFilterChange: { + type: 'enum', + values: ['(filter: TFilter) => void'], + description: 'Filter change handler (controlled).', + }, + // Client-side functions + sortFn: { + type: 'enum', + values: ['(data, sort) => data'], + description: ( + <> + Client-side sort function. Only used with complete mode. + + ), + }, + searchFn: { + type: 'enum', + values: ['(data, query) => data'], + description: ( + <> + Client-side search function. Only used with complete mode. + + ), + }, + filterFn: { + type: 'enum', + values: ['(data, filter) => data'], + description: ( + <> + Client-side filter function. Only used with complete mode. + + ), + }, +}; + +export const useTableReturnPropDefs: Record = { + tableProps: { + type: 'enum', + values: ['object'], + description: ( + <> + Props to spread onto the Table component. Includes data, + loading, error, pagination, and sort state. + + ), + }, + reload: { + type: 'enum', + values: ['() => void'], + description: 'Function to trigger a data refetch.', + }, + search: { + type: 'enum', + values: ['{ value, onChange }'], + description: ( + <> + Search state object for binding to a SearchField component. + + ), + }, + filter: { + type: 'enum', + values: ['{ value, onChange }'], + description: 'Filter state object for binding to filter controls.', + }, +}; + +// ============================================================================= +// Table Component +// ============================================================================= + +export const tablePropDefs: Record = { + columnConfig: { + type: 'enum', + values: ['ColumnConfig[]'], + description: + 'Array of column configurations defining how each column renders.', + }, + data: { + type: 'enum', + values: ['T[]'], + description: 'Array of data items to display in the table.', + }, + loading: { + type: 'boolean', + default: 'false', + description: 'Whether the table is in a loading state.', + }, + isStale: { + type: 'boolean', + default: 'false', + description: + 'Whether the displayed data is stale while new data is loading.', + }, + error: { + type: 'enum', + values: ['Error'], + description: 'Error object if data fetching failed.', + }, + pagination: { + type: 'enum', + values: ['TablePaginationType'], + description: ( + <> + Pagination configuration (required). Use{' '} + {'{ type: "none" }'} to disable or{' '} + {'{ type: "page", ...props }'} for pagination. + + ), + }, + sort: { + type: 'enum', + values: ['SortState'], + description: 'Sort state including current descriptor and change handler.', + }, + rowConfig: { + type: 'enum', + values: ['RowConfig | RowRenderFn'], + description: ( + <> + Row configuration object with getHref, onClick + , getIsDisabled, or a render function for custom rows. + + ), + }, + selection: { + type: 'enum', + values: ['TableSelection'], + description: + 'Selection configuration including mode, behavior, selected keys, and change handler.', + }, + emptyState: { + type: 'enum', + values: ['ReactNode'], + description: 'Content to display when the table has no data.', + }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +// ============================================================================= +// ColumnConfig +// ============================================================================= + +export const columnConfigPropDefs: Record = { + id: { + type: 'string', + description: 'Unique identifier for the column.', + }, + label: { + type: 'string', + description: 'Display label for the column header.', + }, + cell: { + type: 'enum', + values: ['(item) => ReactNode'], + description: 'Render function for cell content.', + }, + header: { + type: 'enum', + values: ['() => ReactNode'], + description: 'Optional custom render function for the header cell.', + }, + isSortable: { + type: 'boolean', + default: 'false', + description: 'Whether the column supports sorting.', + }, + isHidden: { + type: 'boolean', + default: 'false', + description: 'Whether the column is hidden.', + }, + isRowHeader: { + type: 'boolean', + default: 'false', + description: 'Whether this column is the row header for accessibility.', + }, + width: { + type: 'enum', + values: ['ColumnSize'], + description: 'Current width of the column.', + }, + defaultWidth: { + type: 'enum', + values: ['ColumnSize'], + description: ( + <> + Default width of the column (e.g., 1fr, 200px + ). + + ), + }, + minWidth: { + type: 'enum', + values: ['ColumnStaticSize'], + description: 'Minimum width of the column.', + }, + maxWidth: { + type: 'enum', + values: ['ColumnStaticSize'], + description: 'Maximum width of the column.', + }, +}; + +// ============================================================================= +// CellText +// Note: Extends React Aria Cell props +// ============================================================================= + +export const cellTextPropDefs: Record = { + title: { + type: 'string', + description: 'Primary text content of the cell.', + }, + description: { + type: 'string', + description: 'Secondary description text displayed below the title.', + }, + color: { + type: 'enum', + values: ['TextColors'], + description: 'Text color variant.', + }, + leadingIcon: { + type: 'enum', + values: ['ReactNode'], + description: 'Icon displayed before the text content.', + }, + href: { + type: 'string', + description: 'URL to navigate to when the cell is clicked.', + }, +}; + +// ============================================================================= +// CellProfile +// Note: Extends React Aria Cell props +// ============================================================================= + +export const cellProfilePropDefs: Record = { + name: { + type: 'string', + description: 'Name to display.', + }, + src: { + type: 'string', + description: 'URL of the avatar image.', + }, + description: { + type: 'string', + description: 'Secondary text displayed below the name.', + }, + href: { + type: 'string', + description: 'URL to navigate to when clicked.', + }, + color: { + type: 'enum', + values: ['TextColors'], + description: 'Text color variant.', + }, +}; + +// ============================================================================= +// TablePagination +// ============================================================================= + +export const tablePaginationPropDefs: Record = { + pageSize: { + type: 'number', + description: 'Number of items per page.', + }, + pageSizeOptions: { + type: 'enum', + values: ['number[]'], + description: 'Available page size options for the dropdown.', + }, + offset: { + type: 'number', + description: 'Current offset (starting index) in the data.', + }, + totalCount: { + type: 'number', + description: 'Total number of items across all pages.', + }, + hasNextPage: { + type: 'boolean', + description: 'Whether there is a next page available.', + }, + hasPreviousPage: { + type: 'boolean', + description: 'Whether there is a previous page available.', + }, + onNextPage: { + type: 'enum', + values: ['() => void'], + description: 'Handler called when navigating to the next page.', + }, + onPreviousPage: { + type: 'enum', + values: ['() => void'], + description: 'Handler called when navigating to the previous page.', + }, + onPageSizeChange: { + type: 'enum', + values: ['(size: number) => void'], + description: 'Handler called when the page size changes.', + }, + showPageSizeOptions: { + type: 'boolean', + default: 'true', + description: 'Whether to show the page size dropdown.', + }, + getLabel: { + type: 'enum', + values: ['(props) => string'], + description: 'Custom function to generate the pagination label text.', + }, +}; + +// ============================================================================= +// Primitives +// ============================================================================= + +export const tableRootPropDefs: Record = { + stale: { + type: 'boolean', + default: 'false', + description: ( + <> + Whether the table data is stale (e.g., while fetching new data). Adds{' '} + aria-busy attribute. + + ), + }, +}; + +export const columnPropDefs: Record = { + isRowHeader: { + type: 'boolean', + description: 'Whether this column is a row header for accessibility.', + }, + children: { + type: 'enum', + values: ['ReactNode'], + description: 'Column header content.', + }, +}; + +export const rowPropDefs: Record = { + id: { + type: 'enum', + values: ['string | number'], + description: 'Unique identifier for the row.', + }, + children: { + type: 'enum', + values: ['ReactNode | ((column) => ReactNode)'], + description: + 'Row content. Can be a render function receiving column config.', + }, + ...classNamePropDefs, + ...stylePropDefs, +}; diff --git a/docs-ui/src/app/components/table/snippets.ts b/docs-ui/src/app/components/table/snippets.ts index 0c8e029330..cba479b59f 100644 --- a/docs-ui/src/app/components/table/snippets.ts +++ b/docs-ui/src/app/components/table/snippets.ts @@ -1,108 +1,359 @@ -export const tableUsageSnippet = `import { Cell, CellText, ..., TableHeader, TablePagination } from '@backstage/ui'; +// ============================================================================= +// Usage +// ============================================================================= -
- - - - - - - - - -
-`; +export const tableUsageSnippet = `import { Table, useTable, CellText, type ColumnConfig } from '@backstage/ui'; -export const tableBasicSnippet = `import { Table, TableHeader, Column, TableBody, Row, CellText, CellProfile, TablePagination, useTable } from '@backstage/ui'; - -const data = [ - { - name: 'The Beatles', - image: 'https://upload.wikimedia.org/wikipedia/en/thumb/4/42/Beatles_-...jpg', - genre: 'Rock, Pop, Psychedelic Rock', - yearFormed: 1960, - albums: 13 - }, - // ... more data +const columns: ColumnConfig[] = [ + { id: 'name', label: 'Name', isRowHeader: true, cell: item => }, + { id: 'owner', label: 'Owner', cell: item => }, ]; -// Uncontrolled pagination (easiest) -const { data: paginatedData, paginationProps } = useTable({ - data, - pagination: { - defaultPageSize: 5, +function MyTable() { + const { tableProps } = useTable({ + mode: 'complete', + getData: () => data, + }); + + return ; +}`; + +// ============================================================================= +// Hero / Quick Start +// ============================================================================= + +export const tableHeroSnippet = `import { Table, CellText, useTable, type ColumnConfig } from '@backstage/ui'; + +const data = [ + { id: 1, name: 'Service A', owner: 'Team Alpha', type: 'service' }, + { id: 2, name: 'Service B', owner: 'Team Beta', type: 'website' }, + { id: 3, name: 'Library C', owner: 'Team Gamma', type: 'library' }, +]; + +const columns: ColumnConfig[] = [ + { id: 'name', label: 'Name', isRowHeader: true, cell: item => }, + { id: 'owner', label: 'Owner', cell: item => }, + { id: 'type', label: 'Type', cell: item => }, +]; + +function MyTable() { + const { tableProps } = useTable({ + mode: 'complete', + getData: () => data, + }); + + return
; +}`; + +// ============================================================================= +// Core Concepts +// ============================================================================= + +export const tableConceptsSnippet = `// What useTable returns +const { + tableProps, // Spread onto
+ reload, // Trigger data refetch + search, // { value, onChange } for search input + filter, // { value, onChange } for filters +} = useTable({ ... });`; + +// ============================================================================= +// Common Patterns +// ============================================================================= + +export const tableSortingSnippet = `const columns: ColumnConfig[] = [ + { id: 'name', label: 'Name', isSortable: true, cell: item => }, + { id: 'owner', label: 'Owner', isSortable: true, cell: item => }, + { id: 'type', label: 'Type', cell: item => }, +]; + +const { tableProps } = useTable({ + mode: 'complete', + getData: () => data, + initialSort: { column: 'name', direction: 'ascending' }, + sortFn: (items, { column, direction }) => { + return [...items].sort((a, b) => { + const aVal = String(a[column]); + const bVal = String(b[column]); + const cmp = aVal.localeCompare(bVal); + return direction === 'descending' ? -cmp : cmp; + }); }, }); -
- - Band name - Genre - Year formed - Albums - - - {paginatedData?.map(item => ( - - - - - - - ))} - -
-`; +return ;`; -export const tableSelectionActionsSnippet = `import { Table, TableHeader, TableBody, Column, Row, CellText } from '@backstage/ui'; +export const tablePaginationSnippet = `const { tableProps } = useTable({ + mode: 'complete', + getData: () => data, + paginationOptions: { + pageSize: 10, + pageSizeOptions: [10, 25, 50], + }, +});`; -function MyTable() { - const [selectedKeys, setSelectedKeys] = React.useState(new Set([])); +export const tableSearchSnippet = `const { tableProps, search } = useTable({ + mode: 'complete', + getData: () => data, + searchFn: (items, query) => { + const lowerQuery = query.toLowerCase(); + return items.filter(item => + item.name.toLowerCase().includes(lowerQuery) || + item.owner.toLowerCase().includes(lowerQuery) + ); + }, +}); + +return ( + <> + +
+ +);`; + +export const tableSelectionSnippet = `const [selected, setSelected] = useState | 'all'>(new Set()); + +const { tableProps } = useTable({ + mode: 'complete', + getData: () => data, +}); + +return ( +
+);`; + +export const tableRowActionsHrefSnippet = `
\`/catalog/\${item.namespace}/\${item.name}\` + }} + {...tableProps} +/>`; + +export const tableRowActionsClickSnippet = `
openDetailPanel(item) + }} + {...tableProps} +/>`; + +export const tableRowActionsDisabledSnippet = `
openDetailPanel(item), + getIsDisabled: item => item.status === 'archived', + }} + {...tableProps} +/>`; + +export const tableEmptyStateSnippet = `const { tableProps, search } = useTable({ + mode: 'complete', + getData: () => data, + searchFn: (items, query) => { /* ... */ }, +}); + +return ( +
No results match "{search.value}" + : No items yet. Create one to get started. + } + {...tableProps} + /> +);`; + +// ============================================================================= +// Server-Side Data +// ============================================================================= + +export const tableOffsetPaginationSnippet = `const { tableProps } = useTable({ + mode: 'offset', + getData: async ({ offset, pageSize, sort, search, filter, signal }) => { + const response = await fetch( + \`/api/items?offset=\${offset}&limit=\${pageSize}&q=\${search}\`, + { signal } + ); + const { items, totalCount } = await response.json(); + + return { + data: items, + totalCount, + }; + }, + paginationOptions: { + pageSize: 20, + pageSizeOptions: [20, 50, 100], + }, +});`; + +export const tableCursorPaginationSnippet = `const { tableProps } = useTable({ + mode: 'cursor', + getData: async ({ cursor, pageSize, sort, search, signal }) => { + const response = await fetch( + \`/api/items?cursor=\${cursor ?? ''}&limit=\${pageSize}&q=\${search}\`, + { signal } + ); + const { items, nextCursor, prevCursor, totalCount } = await response.json(); + + return { + data: items, + nextCursor, + prevCursor, + totalCount, // optional - enables "X of Y" display + }; + }, + paginationOptions: { + pageSize: 20, + pageSizeOptions: [20, 50, 100], + }, +});`; + +// ============================================================================= +// Combining Features +// ============================================================================= + +export const tableCombinedSnippet = `interface TypeFilter { + type: string | null; +} + +const columns: ColumnConfig[] = [ + { id: 'name', label: 'Name', isRowHeader: true, isSortable: true, + cell: item => }, + { id: 'owner', label: 'Owner', isSortable: true, + cell: item => }, + { id: 'type', label: 'Type', isSortable: true, + cell: item => }, +]; + +function ItemsTable() { + const [selected, setSelected] = useState | 'all'>(new Set()); + + const { tableProps, search, filter } = useTable({ + mode: 'offset', + initialSort: { column: 'name', direction: 'ascending' }, + getData: async ({ offset, pageSize, sort, search, filter, signal }) => { + const params = new URLSearchParams({ + offset: String(offset), + limit: String(pageSize), + q: search, + ...(sort && { sortBy: sort.column, sortDir: sort.direction }), + ...(filter?.type && { type: filter.type }), + }); + + const response = await fetch(\`/api/items?\${params}\`, { signal }); + const { items, totalCount } = await response.json(); + + return { data: items, totalCount }; + }, + }); return ( -
console.log('Opening', key)} - > - - Name - Status - - - - - - - - - - - -
+ + + + - Admin - User - Viewer - + `; diff --git a/docs-ui/src/app/components/skeleton/components.tsx b/docs-ui/src/app/components/skeleton/components.tsx index 277aa17ae3..9f83a013db 100644 --- a/docs-ui/src/app/components/skeleton/components.tsx +++ b/docs-ui/src/app/components/skeleton/components.tsx @@ -2,30 +2,43 @@ import { Skeleton } from '../../../../../packages/ui/src/components/Skeleton/Skeleton'; import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex'; +import { Box } from '../../../../../packages/ui/src/components/Box/Box'; -export const Demo1 = () => { +export const CardPlaceholder = () => { return ( - - + - - - - - - + + + + + + ); +}; + +export const AvatarWithText = () => { + return ( + + + + + + + - +
); }; -export const Demo2 = () => { +export const Rounded = () => { return ( - - - - - + + + + + + + ); }; diff --git a/docs-ui/src/app/components/skeleton/page.mdx b/docs-ui/src/app/components/skeleton/page.mdx index 42f8975b5a..b7d218af89 100644 --- a/docs-ui/src/app/components/skeleton/page.mdx +++ b/docs-ui/src/app/components/skeleton/page.mdx @@ -2,8 +2,13 @@ import { PropsTable } from '@/components/PropsTable'; import { Snippet } from '@/components/Snippet'; import { CodeBlock } from '@/components/CodeBlock'; import { skeletonPropDefs } from './props-definition'; -import { skeletonUsageSnippet, demo1Snippet, demo2Snippet } from './snippets'; -import { Demo1, Demo2 } from './components'; +import { + skeletonUsageSnippet, + cardPlaceholderSnippet, + avatarWithTextSnippet, + roundedSnippet, +} from './snippets'; +import { CardPlaceholder, AvatarWithText, Rounded } from './components'; import { PageTitle } from '@/components/PageTitle'; import { Theming } from '@/components/Theming'; import { ChangelogComponent } from '@/components/ChangelogComponent'; @@ -14,7 +19,12 @@ import { SkeletonDefinition } from '../../../utils/definitions'; description="Use to show a placeholder while content is loading." /> -} code={demo2Snippet} /> +} + code={cardPlaceholderSnippet} +/> ## Usage @@ -22,21 +32,29 @@ import { SkeletonDefinition } from '../../../utils/definitions'; ## API reference +Skeleton extends standard HTML div attributes. + ## Examples -### Demo 1 +### Rounded -You can use a mix of different sizes to create a more complex skeleton. +} + code={roundedSnippet} +/> -} code={demo1Snippet} open /> +### Avatar with text -### Demo 2 - -You can use a mix of different sizes to create a more complex skeleton. - -} code={demo2Snippet} open /> +} + code={avatarWithTextSnippet} +/> diff --git a/docs-ui/src/app/components/skeleton/props-definition.ts b/docs-ui/src/app/components/skeleton/props-definition.ts index bb80024bcd..8c14c4c0ec 100644 --- a/docs-ui/src/app/components/skeleton/props-definition.ts +++ b/docs-ui/src/app/components/skeleton/props-definition.ts @@ -6,19 +6,22 @@ import { export const skeletonPropDefs: Record = { width: { - type: 'number', + type: 'string', default: '80', - responsive: false, + description: + 'The width of the skeleton. Accepts a number (pixels) or CSS string value.', }, height: { - type: 'number', + type: 'string', default: '24', - responsive: false, + description: + 'The height of the skeleton. Accepts a number (pixels) or CSS string value.', }, rounded: { type: 'boolean', default: 'false', - responsive: false, + description: + 'Whether to apply fully rounded corners (for circular shapes).', }, ...classNamePropDefs, ...stylePropDefs, diff --git a/docs-ui/src/app/components/skeleton/snippets.ts b/docs-ui/src/app/components/skeleton/snippets.ts index 981ac5ff0c..84bbec1616 100644 --- a/docs-ui/src/app/components/skeleton/snippets.ts +++ b/docs-ui/src/app/components/skeleton/snippets.ts @@ -1,26 +1,30 @@ -export const skeletonUsageSnippet = `import { Flex, Skeleton } from '@backstage/ui'; +export const skeletonUsageSnippet = `import { Skeleton } from '@backstage/ui'; - - - - -`; +`; -export const demo1Snippet = ` - +export const cardPlaceholderSnippet = ` - - - - - - + + + + +`; + +export const avatarWithTextSnippet = ` + + + + + + -`; +`; -export const demo2Snippet = ` - - - -`; +export const roundedSnippet = ` + + + + + +`; diff --git a/docs-ui/src/app/components/switch/page.mdx b/docs-ui/src/app/components/switch/page.mdx index 494a046f47..1f893aea36 100644 --- a/docs-ui/src/app/components/switch/page.mdx +++ b/docs-ui/src/app/components/switch/page.mdx @@ -1,6 +1,7 @@ import { PropsTable } from '@/components/PropsTable'; import { Snippet } from '@/components/Snippet'; import { CodeBlock } from '@/components/CodeBlock'; +import { ReactAriaLink } from '@/components/ReactAriaLink'; import { switchPropDefs } from './props-definition'; import { snippetUsage, defaultSnippet, disabledSnippet } from './snippets'; import { Default, Disabled } from './components'; @@ -9,9 +10,13 @@ import { Theming } from '@/components/Theming'; import { ChangelogComponent } from '@/components/ChangelogComponent'; import { SwitchDefinition } from '../../../utils/definitions'; +export const reactAriaUrls = { + switch: 'https://react-aria.adobe.com/Switch', +}; + } code={defaultSnippet} /> @@ -24,19 +29,13 @@ import { SwitchDefinition } from '../../../utils/definitions'; + + ## Examples ### Disabled -A switch can be disabled using the `isDisabled` prop. - -} - code={disabledSnippet} -/> +} code={disabledSnippet} /> diff --git a/docs-ui/src/app/components/switch/props-definition.ts b/docs-ui/src/app/components/switch/props-definition.ts index 769c93aac8..eeab3e64b8 100644 --- a/docs-ui/src/app/components/switch/props-definition.ts +++ b/docs-ui/src/app/components/switch/props-definition.ts @@ -2,66 +2,44 @@ import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs'; import type { PropDef } from '@/utils/propDefs'; export const switchPropDefs: Record = { - autoFocus: { - type: 'boolean', - }, - defaultSelected: { - type: 'boolean', - }, - ...classNamePropDefs, - isDisabled: { - type: 'boolean', - }, - isReadOnly: { - type: 'boolean', + label: { + type: 'string', + description: 'Text label displayed next to the switch.', }, isSelected: { type: 'boolean', + description: + 'Controlled selected state. Use with onChange for controlled behavior.', }, - label: { - type: 'string', - }, - name: { - type: 'string', + defaultSelected: { + type: 'boolean', + description: 'Initial selected state for uncontrolled usage.', }, onChange: { type: 'enum', values: ['(isSelected: boolean) => void'], + description: 'Called when the switch state changes.', }, - onFocus: { - type: 'enum', - values: ['(e: FocusEvent) => void'], + isDisabled: { + type: 'boolean', + description: 'Prevents user interaction when true.', }, - onBlur: { - type: 'enum', - values: ['(e: FocusEvent) => void'], + isReadOnly: { + type: 'boolean', + description: 'Makes the switch non-interactive but still focusable.', }, - onFocusChange: { - type: 'enum', - values: ['(isFocused: boolean) => void'], + name: { + type: 'string', + description: 'Form field name for form submission.', }, - onKeyDown: { - type: 'enum', - values: ['(e: KeyboardEvent) => void'], - }, - onKeyUp: { - type: 'enum', - values: ['(e: KeyboardEvent) => void'], - }, - onHoverStart: { - type: 'enum', - values: ['(e: HoverEvent) => void'], - }, - onHoverEnd: { - type: 'enum', - values: ['(e: HoverEvent) => void'], - }, - onHoverChange: { - type: 'enum', - values: ['(isHovered: boolean) => void'], - }, - ...stylePropDefs, value: { type: 'string', + description: 'Form field value submitted when selected.', }, + autoFocus: { + type: 'boolean', + description: 'Focuses the switch on mount.', + }, + ...classNamePropDefs, + ...stylePropDefs, }; diff --git a/docs-ui/src/app/components/table/page.mdx b/docs-ui/src/app/components/table/page.mdx index e39f89f5e9..6eb280cbe2 100644 --- a/docs-ui/src/app/components/table/page.mdx +++ b/docs-ui/src/app/components/table/page.mdx @@ -13,7 +13,6 @@ import { PrimitivesExample, } from './components'; import { - tablePropsColumns, tableReturnColumns, useTableOptionsPropDefs, useTableReturnPropDefs, @@ -163,7 +162,7 @@ You can also disable specific rows from being clicked using `getIsDisabled`: -} /> +} code={tableRowActionsHrefSnippet} /> ### Empty State @@ -225,7 +224,7 @@ The `useTable` hook manages data fetching, pagination, sorting, and filtering. **Options** - + **Return Value** @@ -235,27 +234,27 @@ The `useTable` hook manages data fetching, pagination, sorting, and filtering. The main table component. - + ### ColumnConfig - + ### CellText - + ### CellProfile - + ### TablePagination - + ### Primitives @@ -263,7 +262,7 @@ Low-level components for building custom table layouts. #### TableRoot - + @@ -277,13 +276,13 @@ Low-level components for building custom table layouts. #### Column - + #### Row - + diff --git a/docs-ui/src/app/components/table/props-definition.tsx b/docs-ui/src/app/components/table/props-definition.tsx index 58c8f197a7..e653f67c8a 100644 --- a/docs-ui/src/app/components/table/props-definition.tsx +++ b/docs-ui/src/app/components/table/props-definition.tsx @@ -9,13 +9,6 @@ import { Chip } from '@/components/Chip'; // PropsTable Column Configuration (Table docs use description instead of responsive) // ============================================================================= -export const tablePropsColumns = [ - { key: 'prop' as const, width: '15%' }, - { key: 'type' as const, width: '25%' }, - { key: 'default' as const, width: '15%' }, - { key: 'description' as const, width: '45%' }, -]; - // For return values (no default column) export const tableReturnColumns = [ { key: 'prop' as const, width: '15%' }, diff --git a/docs-ui/src/app/components/tabs/page.mdx b/docs-ui/src/app/components/tabs/page.mdx index 0b9478a503..6925127943 100644 --- a/docs-ui/src/app/components/tabs/page.mdx +++ b/docs-ui/src/app/components/tabs/page.mdx @@ -1,6 +1,7 @@ import { PropsTable } from '@/components/PropsTable'; import { Snippet } from '@/components/Snippet'; import { CodeBlock } from '@/components/CodeBlock'; +import { ReactAriaLink } from '@/components/ReactAriaLink'; import { tabsPropDefs, tabListPropDefs, @@ -13,6 +14,7 @@ import { defaultSelectedKeySnippet, disabledTabsSnippet, orientationSnippet, + urlNavigationSnippet, } from './snippets'; import { Default, @@ -25,9 +27,13 @@ import { Theming } from '@/components/Theming'; import { TabsDefinition } from '../../../utils/definitions'; import { ChangelogComponent } from '@/components/ChangelogComponent'; +export const reactAriaUrls = { + tabs: 'https://react-aria.adobe.com/Tabs', +}; + } code={defaultSnippet} /> @@ -40,40 +46,70 @@ import { ChangelogComponent } from '@/components/ChangelogComponent'; ### Tabs -Groups the tabs and the corresponding panels. Renders a `
` element. +Container that groups the tab list and panels. + + +### TabList + +Container for the tab buttons. + + + + + ### Tab -An individual interactive tab button that toggles the corresponding panel. Renders a ` - I am a tooltip + + Helpful information ); }; + +export const Placement = () => { + return ( + + + + Top tooltip + + + + Right tooltip + + + + Bottom tooltip + + + + Left tooltip + + + ); +}; diff --git a/docs-ui/src/app/components/tooltip/page.mdx b/docs-ui/src/app/components/tooltip/page.mdx index 35d650c99b..8a9711d56c 100644 --- a/docs-ui/src/app/components/tooltip/page.mdx +++ b/docs-ui/src/app/components/tooltip/page.mdx @@ -1,17 +1,26 @@ import { PropsTable } from '@/components/PropsTable'; import { Snippet } from '@/components/Snippet'; import { CodeBlock } from '@/components/CodeBlock'; +import { ReactAriaLink } from '@/components/ReactAriaLink'; import { tooltipTriggerPropDefs, tooltipPropDefs } from './props-definition'; -import { tooltipUsageSnippet, defaultSnippet } from './snippets'; -import { Default } from './components'; +import { + tooltipUsageSnippet, + defaultSnippet, + placementSnippet, +} from './snippets'; +import { Default, Placement } from './components'; import { PageTitle } from '@/components/PageTitle'; import { Theming } from '@/components/Theming'; import { TooltipDefinition } from '../../../utils/definitions'; import { ChangelogComponent } from '@/components/ChangelogComponent'; +export const reactAriaUrls = { + tooltip: 'https://react-aria.adobe.com/Tooltip', +}; + } code={defaultSnippet} align="center" /> @@ -24,16 +33,33 @@ import { ChangelogComponent } from '@/components/ChangelogComponent'; ### TooltipTrigger -The trigger will wrap both the trigger and the tooltip. +Wraps the trigger element and tooltip content. + + ### Tooltip -The tooltip will wrap the content of the tooltip. +The tooltip content that appears on hover or focus. + + +## Examples + +### Placement + +The tooltip appears in the specified direction relative to the trigger. + +} + code={placementSnippet} + align="center" +/> + diff --git a/docs-ui/src/app/components/tooltip/props-definition.ts b/docs-ui/src/app/components/tooltip/props-definition.ts deleted file mode 100644 index dc0919f4cc..0000000000 --- a/docs-ui/src/app/components/tooltip/props-definition.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - childrenPropDefs, - classNamePropDefs, - stylePropDefs, -} from '@/utils/propDefs'; -import type { PropDef } from '@/utils/propDefs'; - -export const tooltipTriggerPropDefs: Record = { - isDisabled: { - type: 'boolean', - }, - delay: { - type: 'number', - default: '600', - }, - closeDelay: { - type: 'number', - default: '500', - }, - isOpen: { - type: 'boolean', - }, - defaultOpen: { - type: 'boolean', - }, - ...childrenPropDefs, -}; - -export const tooltipPropDefs: Record = { - triggerRef: { - type: 'enum', - values: ['RefObject'], - }, - isEntering: { - type: 'boolean', - }, - isExiting: { - type: 'boolean', - }, - placement: { - type: 'enum', - values: ['top', 'right', 'bottom', 'left'], - }, - containerPadding: { - type: 'number', - default: '12', - }, - offset: { - type: 'number', - default: '0', - }, - ...childrenPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/docs-ui/src/app/components/tooltip/props-definition.tsx b/docs-ui/src/app/components/tooltip/props-definition.tsx new file mode 100644 index 0000000000..16dc9133dd --- /dev/null +++ b/docs-ui/src/app/components/tooltip/props-definition.tsx @@ -0,0 +1,79 @@ +import { + childrenPropDefs, + classNamePropDefs, + stylePropDefs, +} from '@/utils/propDefs'; +import type { PropDef } from '@/utils/propDefs'; +import { Chip } from '@/components/Chip'; + +export const tooltipTriggerPropDefs: Record = { + delay: { + type: 'number', + default: 600, + description: 'Milliseconds before tooltip appears on hover.', + }, + closeDelay: { + type: 'number', + default: 0, + description: 'Milliseconds before tooltip hides after leaving trigger.', + }, + trigger: { + type: 'enum', + values: ['focus'], + description: ( + <> + Set to focus for focus-only tooltips that do not appear on + hover. + + ), + }, + isOpen: { + type: 'boolean', + description: 'Controlled open state. Use with onOpenChange.', + }, + defaultOpen: { + type: 'boolean', + description: 'Initial open state for uncontrolled usage.', + }, + isDisabled: { + type: 'boolean', + description: 'Prevents the tooltip from appearing.', + }, + ...childrenPropDefs, +}; + +export const tooltipPropDefs: Record = { + placement: { + type: 'enum', + values: [ + 'top', + 'top start', + 'top end', + 'bottom', + 'bottom start', + 'bottom end', + 'left', + 'left top', + 'left bottom', + 'right', + 'right top', + 'right bottom', + ], + default: 'top', + description: + 'Position relative to the trigger element. Compound placements include alignment.', + }, + offset: { + type: 'number', + default: 4, + description: 'Distance in pixels from the trigger element.', + }, + containerPadding: { + type: 'number', + default: 12, + description: 'Padding from viewport edge when repositioning.', + }, + ...childrenPropDefs, + ...classNamePropDefs, + ...stylePropDefs, +}; diff --git a/docs-ui/src/app/components/tooltip/snippets.ts b/docs-ui/src/app/components/tooltip/snippets.ts index 1cf9fb66da..68b7d1b0c7 100644 --- a/docs-ui/src/app/components/tooltip/snippets.ts +++ b/docs-ui/src/app/components/tooltip/snippets.ts @@ -1,11 +1,30 @@ export const tooltipUsageSnippet = `import { TooltipTrigger, Tooltip, Button } from '@backstage/ui'; - - I am a tooltip + + Helpful information `; export const defaultSnippet = ` - - I am a tooltip + + Helpful information `; + +export const placementSnippet = ` + + + Top tooltip + + + + Right tooltip + + + + Bottom tooltip + + + + Left tooltip + +`; diff --git a/docs-ui/src/app/components/visually-hidden/page.mdx b/docs-ui/src/app/components/visually-hidden/page.mdx index 07b4bff28d..c72250231f 100644 --- a/docs-ui/src/app/components/visually-hidden/page.mdx +++ b/docs-ui/src/app/components/visually-hidden/page.mdx @@ -28,11 +28,11 @@ import { ChangelogComponent } from '@/components/ChangelogComponent'; +This component also accepts all standard HTML `div` attributes. + ## Examples -### Example Usage - -Here's an example of providing screen reader context for a list of links in a footer. +### Hidden headings = { type: 'enum', values: ['ReactNode'], responsive: false, + description: + 'Content to hide visually while remaining accessible to screen readers.', }, ...classNamePropDefs, ...stylePropDefs, diff --git a/docs-ui/src/components/Chip/styles.module.css b/docs-ui/src/components/Chip/styles.module.css index 80c37d8899..d752ae2602 100644 --- a/docs-ui/src/components/Chip/styles.module.css +++ b/docs-ui/src/components/Chip/styles.module.css @@ -1,5 +1,5 @@ .chip { - display: inline; + display: inline-block; font-family: monospace; font-size: 13px; border-radius: 6px; diff --git a/docs-ui/src/components/PropsTable/PropsTable.tsx b/docs-ui/src/components/PropsTable/PropsTable.tsx index e1324b88b9..25311a2ec3 100644 --- a/docs-ui/src/components/PropsTable/PropsTable.tsx +++ b/docs-ui/src/components/PropsTable/PropsTable.tsx @@ -16,10 +16,10 @@ interface ColumnConfig { } const defaultColumns: ColumnConfig[] = [ - { key: 'prop', width: '16%' }, - { key: 'type', width: '50%' }, - { key: 'default', width: '20%' }, - { key: 'responsive', width: '14%' }, + { key: 'prop' as const, width: '15%' }, + { key: 'type' as const, width: '25%' }, + { key: 'default' as const, width: '15%' }, + { key: 'description' as const, width: '45%' }, ]; const columnLabels: Record = { diff --git a/docs-ui/src/components/Snippet/client.tsx b/docs-ui/src/components/Snippet/client.tsx index 04284c3249..2041218a12 100644 --- a/docs-ui/src/components/Snippet/client.tsx +++ b/docs-ui/src/components/Snippet/client.tsx @@ -12,6 +12,7 @@ interface SnippetProps { py?: number; open?: boolean; height?: string | number; + layout?: 'stacked' | 'side-by-side'; } export const SnippetClient = ({ @@ -22,9 +23,28 @@ export const SnippetClient = ({ py = 2, open = false, height = 'auto', + layout = 'stacked', }: SnippetProps) => { const [isOpen, setIsOpen] = useState(open); + if (layout === 'side-by-side') { + return ( +
+
+
{codeContent}
+
+
+
+ {preview} +
+
+
+ ); + } + return ( { return ( ); }; diff --git a/docs-ui/src/components/Snippet/styles.module.css b/docs-ui/src/components/Snippet/styles.module.css index ed5bd22def..20f74c4df8 100644 --- a/docs-ui/src/components/Snippet/styles.module.css +++ b/docs-ui/src/components/Snippet/styles.module.css @@ -60,3 +60,43 @@ opacity: 1; } } + +/* Side-by-side layout */ +.sideBySide { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 16px; + align-items: stretch; +} + +.sideBySideCode { + min-width: 0; + overflow: auto; + display: flex; + flex-direction: column; +} + +.codeWrapper { + flex: 1; + display: flex; + flex-direction: column; +} + +.codeWrapper > div { + flex: 1; + margin-bottom: 0; +} + +.sideBySidePreview { + border-radius: 8px; + box-shadow: inset 0 0 0 1px var(--border); + background-color: var(--bui-bg-surface-0); + padding: 1px; + min-width: 0; + display: flex; + flex-direction: column; +} + +.sideBySidePreview > .previewContent { + flex: 1; +} diff --git a/docs-ui/src/utils/propDefs.ts b/docs-ui/src/utils/propDefs.ts index 2dd4f5c25a..575d53e81e 100644 --- a/docs-ui/src/utils/propDefs.ts +++ b/docs-ui/src/utils/propDefs.ts @@ -36,6 +36,7 @@ export { breakpoints }; export type { Breakpoint }; export const spacingValues = [ + '0', '0.5', '1', '1.5', @@ -50,8 +51,7 @@ export const spacingValues = [ '10', '11', '12', - '13', - '14', + 'auto', ]; export const paddingPropDefs = ( @@ -61,36 +61,43 @@ export const paddingPropDefs = ( type: 'enum | string', values: spacingValues, responsive: true, + description: 'Padding on all sides.', }, px: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Horizontal padding (left and right).', }, py: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Vertical padding (top and bottom).', }, pt: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Padding on the top.', }, pr: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Padding on the right.', }, pb: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Padding on the bottom.', }, pl: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Padding on the left.', }, }); @@ -101,36 +108,43 @@ export const marginPropDefs = ( type: 'enum | string', values: spacingValues, responsive: true, + description: 'Margin on all sides.', }, mx: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Horizontal margin (left and right).', }, my: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Vertical margin (top and bottom).', }, mt: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Margin on the top.', }, mr: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Margin on the right.', }, mb: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Margin on the bottom.', }, ml: { type: 'enum | string', values: spacingValues, responsive: true, + description: 'Margin on the left.', }, }); @@ -160,36 +174,36 @@ export const gapPropDefs: Record = { export const widthPropDefs: Record = { width: { type: 'string', - default: '0', responsive: true, + description: 'Sets the width of the element. Accepts CSS values.', }, minWidth: { type: 'string', - default: '0', responsive: true, + description: 'Sets the minimum width. Element cannot shrink below this.', }, maxWidth: { type: 'string', - default: '0', responsive: true, + description: 'Sets the maximum width. Element cannot grow beyond this.', }, }; export const heightPropDefs: Record = { height: { type: 'string', - default: '0', responsive: true, + description: 'Sets the height of the element. Accepts CSS values.', }, minHeight: { type: 'string', - default: '0', responsive: true, + description: 'Sets the minimum height. Element cannot shrink below this.', }, maxHeight: { type: 'string', - default: '0', responsive: true, + description: 'Sets the maximum height. Element cannot grow beyond this.', }, }; @@ -197,8 +211,8 @@ export const positionPropDefs: Record = { position: { type: 'enum', values: ['static', 'relative', 'absolute', 'fixed', 'sticky'], - default: 'static', responsive: true, + description: 'CSS positioning scheme for the element.', }, }; @@ -206,6 +220,7 @@ export const classNamePropDefs: Record = { className: { type: 'string', responsive: false, + description: 'Additional CSS class name for custom styling.', }, }; @@ -214,6 +229,7 @@ export const stylePropDefs: Record = { type: 'enum', values: ['CSSProperties'], responsive: false, + description: 'Inline CSS styles object.', }, }; From 058ffd913f8bd16aed970ae6580167b7838791fd Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Tue, 27 Jan 2026 17:24:48 +0100 Subject: [PATCH 166/211] fix(ui): remove non-existent large size from Button types Signed-off-by: Johan Persson --- .changeset/tired-queens-cross.md | 14 ++++++++++++++ packages/ui/report.api.md | 2 +- packages/ui/src/components/Button/types.ts | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .changeset/tired-queens-cross.md diff --git a/.changeset/tired-queens-cross.md b/.changeset/tired-queens-cross.md new file mode 100644 index 0000000000..2f1be86f15 --- /dev/null +++ b/.changeset/tired-queens-cross.md @@ -0,0 +1,14 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Removed `large` size variant from Button component as it was never implemented. + +**Migration:** + +```diff +- ++ +``` + +**Affected components:** Button diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index 9a65455ee0..b6f1a173ef 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -443,7 +443,7 @@ export interface ButtonLinkProps // @public (undocumented) export type ButtonOwnProps = LeafSurfaceProps & { - size?: Responsive<'small' | 'medium' | 'large'>; + size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; iconStart?: ReactElement; iconEnd?: ReactElement; diff --git a/packages/ui/src/components/Button/types.ts b/packages/ui/src/components/Button/types.ts index 8278cb932d..00a964a3f2 100644 --- a/packages/ui/src/components/Button/types.ts +++ b/packages/ui/src/components/Button/types.ts @@ -20,7 +20,7 @@ import type { LeafSurfaceProps, Responsive } from '../../types'; /** @public */ export type ButtonOwnProps = LeafSurfaceProps & { - size?: Responsive<'small' | 'medium' | 'large'>; + size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; iconStart?: ReactElement; iconEnd?: ReactElement; From 74c5a76990f942ca2dd421d89dc909cdbea66693 Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Wed, 28 Jan 2026 15:12:54 +0100 Subject: [PATCH 167/211] fix(ui): improve disabled Switch styling Add proper visual feedback for disabled Switch component by applying `not-allowed` cursor and disabled text color. Signed-off-by: Johan Persson --- .changeset/twenty-clubs-itch.md | 7 +++++++ packages/ui/src/components/Switch/Switch.module.css | 5 +++++ 2 files changed, 12 insertions(+) create mode 100644 .changeset/twenty-clubs-itch.md diff --git a/.changeset/twenty-clubs-itch.md b/.changeset/twenty-clubs-itch.md new file mode 100644 index 0000000000..c69aabec7d --- /dev/null +++ b/.changeset/twenty-clubs-itch.md @@ -0,0 +1,7 @@ +--- +'@backstage/ui': patch +--- + +Fixed Switch component disabled state styling to show `not-allowed` cursor and disabled text color. + +**Affected components:** Switch diff --git a/packages/ui/src/components/Switch/Switch.module.css b/packages/ui/src/components/Switch/Switch.module.css index 447597c6d1..d35caf41a6 100644 --- a/packages/ui/src/components/Switch/Switch.module.css +++ b/packages/ui/src/components/Switch/Switch.module.css @@ -27,6 +27,11 @@ color: var(--bui-fg-primary); cursor: pointer; + &[data-disabled] { + cursor: not-allowed; + color: var(--bui-fg-disabled); + } + &[data-pressed] .bui-SwitchIndicator { &:before { background: var(--bui-fg-solid); From 5af9e14bb0f188b98e77e38c3647ef17588400c9 Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Wed, 28 Jan 2026 16:12:15 +0100 Subject: [PATCH 168/211] fix(ui): prevent literal "undefined" class name in useDefinition When no className prop was passed to a component using useDefinition, the hook would add a literal "undefined" string as a CSS class. This happened because clsx treats object keys as class names, and `{ [undefined]: true }` creates an "undefined" key. Fixed by using conditional expressions instead of object syntax, which clsx correctly ignores when falsy. Signed-off-by: Johan Persson --- .changeset/gold-lions-stick.md | 5 +++++ packages/ui/src/hooks/useDefinition/useDefinition.tsx | 6 ++---- 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 .changeset/gold-lions-stick.md diff --git a/.changeset/gold-lions-stick.md b/.changeset/gold-lions-stick.md new file mode 100644 index 0000000000..da18dd6cf6 --- /dev/null +++ b/.changeset/gold-lions-stick.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fixed `useDefinition` hook adding literal "undefined" class name when no className prop was passed. diff --git a/packages/ui/src/hooks/useDefinition/useDefinition.tsx b/packages/ui/src/hooks/useDefinition/useDefinition.tsx index 7060ef4a1a..0de2eb8358 100644 --- a/packages/ui/src/hooks/useDefinition/useDefinition.tsx +++ b/packages/ui/src/hooks/useDefinition/useDefinition.tsx @@ -103,10 +103,8 @@ export function useDefinition< classes[name] = clsx( cssKey as string, definition.styles[cssKey as keyof typeof definition.styles], - { - [utilityClasses]: utilityTarget === name, - [ownPropsResolved.className]: classNameTarget === name, - }, + utilityTarget === name && utilityClasses, + classNameTarget === name && ownPropsResolved.className, ); } From ce3639c588e3e2419d78b6af74250cc90baffa37 Mon Sep 17 00:00:00 2001 From: KoB <63000843+kingbj940429@users.noreply.github.com> Date: Thu, 29 Jan 2026 04:31:01 +0900 Subject: [PATCH 169/211] feat(kubernetes-plugin): add pv and pvc rendering (#31355) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add persistent volumes rendering Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add pv status Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add volume status and volume type Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add test for pv Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add persistent volume claims Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add persistent volume status pending Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add persistent volume metadata Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add persistent volume claims metadata Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: add persistent volume claims test code Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: changeset Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: remove gcp and gke.io Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: yarn build:api-reports Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: update pv to table drawer like pod Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * feat: update pvc to table drawer like pod Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: remove user event at test code Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: exclude pv,pvc from default objects Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: remove duplication import for pv Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: yarn build:api-reports Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: retrigger required checks Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: retrigger required checks Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: retrigger required checks Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> * chore: retrigger required checks Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> --------- Signed-off-by: ęš€ëŗ‘ė¤€ Signed-off-by: KoB <63000843+kingbj940429@users.noreply.github.com> --- .changeset/good-cameras-repair.md | 8 + .../src/service/KubernetesFanOutHandler.ts | 12 + plugins/kubernetes-common/report.api.md | 26 +- plugins/kubernetes-common/src/types.ts | 20 +- .../kubernetes-common/src/util/response.ts | 8 + plugins/kubernetes-node/report.api.md | 4 +- plugins/kubernetes-node/src/types/types.ts | 4 +- .../1-persistentvolumeclaims.json | 37 +++ .../src/__fixtures__/1-persistentvolumes.json | 33 +++ .../2-persistentvolumeclaims.json | 160 ++++++++++ .../src/__fixtures__/2-persistentvolumes.json | 274 ++++++++++++++++++ .../src/components/Cluster/Cluster.tsx | 12 + .../PersistentVolumesAccordions.test.tsx | 50 ++++ .../PersistentVolumesAccordions.tsx | 137 +++++++++ .../PersistentVolumesDrawer.test.tsx | 38 +++ .../PersistentVolumesDrawer.tsx | 64 ++++ .../PersistentVolumesTable.test.tsx | 81 ++++++ .../PersistentVolumesTable.tsx | 152 ++++++++++ .../PersistentVolumesAccordions/index.ts | 18 ++ .../PersistentVolumeClaimsAccordions.test.tsx | 51 ++++ .../PersistentVolumeClaimsAccordions.tsx | 139 +++++++++ .../PersistentVolumeClaimsDrawer.test.tsx | 39 +++ .../PersistentVolumeClaimsDrawer.tsx | 64 ++++ .../PersistentVolumeClaimsTable.test.tsx | 68 +++++ .../PersistentVolumeClaimsTable.tsx | 136 +++++++++ .../index.ts | 18 ++ .../src/hooks/GroupedResponses.ts | 2 + .../src/utils/persistentVolumes.test.ts | 83 ++++++ .../src/utils/persistentVolumes.ts | 39 +++ 29 files changed, 1773 insertions(+), 4 deletions(-) create mode 100644 .changeset/good-cameras-repair.md create mode 100644 plugins/kubernetes-react/src/__fixtures__/1-persistentvolumeclaims.json create mode 100644 plugins/kubernetes-react/src/__fixtures__/1-persistentvolumes.json create mode 100644 plugins/kubernetes-react/src/__fixtures__/2-persistentvolumeclaims.json create mode 100644 plugins/kubernetes-react/src/__fixtures__/2-persistentvolumes.json create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.test.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.test.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.test.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesAccordions/index.ts create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.test.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.test.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.test.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.tsx create mode 100644 plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/index.ts create mode 100644 plugins/kubernetes-react/src/utils/persistentVolumes.test.ts create mode 100644 plugins/kubernetes-react/src/utils/persistentVolumes.ts diff --git a/.changeset/good-cameras-repair.md b/.changeset/good-cameras-repair.md new file mode 100644 index 0000000000..e994112e94 --- /dev/null +++ b/.changeset/good-cameras-repair.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-kubernetes-common': patch +'@backstage/plugin-kubernetes-react': patch +'@backstage/plugin-kubernetes-node': patch +--- + +Add PersistentVolume and PersistentVolumeClaims Rendering diff --git a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts index a265a142bb..4c165c17f4 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesFanOutHandler.ts @@ -148,6 +148,18 @@ export const ALL_OBJECTS: ObjectToFetch[] = [ plural: 'secrets', objectType: 'secrets', }, + { + group: '', + apiVersion: 'v1', + plural: 'persistentvolumes', + objectType: 'persistentvolumes', + }, + { + group: '', + apiVersion: 'v1', + plural: 'persistentvolumeclaims', + objectType: 'persistentvolumeclaims', + }, ...DEFAULT_OBJECTS, ]; diff --git a/plugins/kubernetes-common/report.api.md b/plugins/kubernetes-common/report.api.md index bf99cdaddd..da989ceeca 100644 --- a/plugins/kubernetes-common/report.api.md +++ b/plugins/kubernetes-common/report.api.md @@ -17,6 +17,8 @@ import type { V1Deployment } from '@kubernetes/client-node'; import type { V1Ingress } from '@kubernetes/client-node'; import type { V1Job } from '@kubernetes/client-node'; import type { V1LimitRange } from '@kubernetes/client-node'; +import type { V1PersistentVolume } from '@kubernetes/client-node'; +import type { V1PersistentVolumeClaim } from '@kubernetes/client-node'; import type { V1Pod } from '@kubernetes/client-node'; import type { V1ReplicaSet } from '@kubernetes/client-node'; import type { V1ResourceQuota } from '@kubernetes/client-node'; @@ -267,7 +269,9 @@ export type FetchResponse = | StatefulSetsFetchResponse | DaemonSetsFetchResponse | PodStatusFetchResponse - | SecretsFetchResponse; + | SecretsFetchResponse + | PersistentVolumeFetchResponse + | PersistentVolumeClaimsFetchResponse; // @public (undocumented) export interface GroupedResponses extends DeploymentResources { @@ -284,6 +288,10 @@ export interface GroupedResponses extends DeploymentResources { // (undocumented) jobs: V1Job[]; // (undocumented) + persistentVolumeClaims: V1PersistentVolumeClaim[]; + // (undocumented) + persistentVolumes: V1PersistentVolume[]; + // (undocumented) secrets: V1Secret[]; // (undocumented) services: V1Service[]; @@ -385,6 +393,22 @@ export interface ObjectsByEntityResponse { items: ClusterObjects[]; } +// @public (undocumented) +export interface PersistentVolumeClaimsFetchResponse { + // (undocumented) + resources: Array; + // (undocumented) + type: 'persistentvolumeclaims'; +} + +// @public (undocumented) +export interface PersistentVolumeFetchResponse { + // (undocumented) + resources: Array; + // (undocumented) + type: 'persistentvolumes'; +} + // @public (undocumented) export interface PodFetchResponse { // (undocumented) diff --git a/plugins/kubernetes-common/src/types.ts b/plugins/kubernetes-common/src/types.ts index 30846e3b8e..9791216604 100644 --- a/plugins/kubernetes-common/src/types.ts +++ b/plugins/kubernetes-common/src/types.ts @@ -31,6 +31,8 @@ import type { V1Service, V1StatefulSet, V1Secret, + V1PersistentVolume, + V1PersistentVolumeClaim, } from '@kubernetes/client-node'; import { Entity } from '@backstage/catalog-model'; @@ -142,7 +144,9 @@ export type FetchResponse = | StatefulSetsFetchResponse | DaemonSetsFetchResponse | PodStatusFetchResponse - | SecretsFetchResponse; + | SecretsFetchResponse + | PersistentVolumeFetchResponse + | PersistentVolumeClaimsFetchResponse; /** @public */ export interface PodFetchResponse { @@ -246,6 +250,18 @@ export interface SecretsFetchResponse { resources: Array; } +/** @public */ +export interface PersistentVolumeFetchResponse { + type: 'persistentvolumes'; + resources: Array; +} + +/** @public */ +export interface PersistentVolumeClaimsFetchResponse { + type: 'persistentvolumeclaims'; + resources: Array; +} + /** @public */ export type KubernetesFetchError = StatusError | RawFetchError; @@ -311,4 +327,6 @@ export interface GroupedResponses extends DeploymentResources { customResources: any[]; statefulsets: V1StatefulSet[]; daemonSets: V1DaemonSet[]; + persistentVolumes: V1PersistentVolume[]; + persistentVolumeClaims: V1PersistentVolumeClaim[]; } diff --git a/plugins/kubernetes-common/src/util/response.ts b/plugins/kubernetes-common/src/util/response.ts index ba181c48e2..7f7aacfb0b 100644 --- a/plugins/kubernetes-common/src/util/response.ts +++ b/plugins/kubernetes-common/src/util/response.ts @@ -64,6 +64,12 @@ export const groupResponses = ( case 'daemonsets': prev.daemonSets.push(...next.resources); break; + case 'persistentvolumes': + prev.persistentVolumes.push(...next.resources); + break; + case 'persistentvolumeclaims': + prev.persistentVolumeClaims.push(...next.resources); + break; default: } return prev; @@ -82,6 +88,8 @@ export const groupResponses = ( customResources: [], statefulsets: [], daemonSets: [], + persistentVolumes: [], + persistentVolumeClaims: [], } as GroupedResponses, ); }; diff --git a/plugins/kubernetes-node/report.api.md b/plugins/kubernetes-node/report.api.md index c23a0b5bb8..901d1ff782 100644 --- a/plugins/kubernetes-node/report.api.md +++ b/plugins/kubernetes-node/report.api.md @@ -221,7 +221,9 @@ export type KubernetesObjectTypes = | 'customresources' | 'statefulsets' | 'daemonsets' - | 'secrets'; + | 'secrets' + | 'persistentvolumes' + | 'persistentvolumeclaims'; // @public export interface KubernetesRouterExtensionPoint { diff --git a/plugins/kubernetes-node/src/types/types.ts b/plugins/kubernetes-node/src/types/types.ts index 3789835d52..1a2370f7d7 100644 --- a/plugins/kubernetes-node/src/types/types.ts +++ b/plugins/kubernetes-node/src/types/types.ts @@ -187,7 +187,9 @@ export type KubernetesObjectTypes = | 'customresources' | 'statefulsets' | 'daemonsets' - | 'secrets'; + | 'secrets' + | 'persistentvolumes' + | 'persistentvolumeclaims'; // If updating this list, also make sure to update // `objectTypes` and `apiVersionOverrides` in config.d.ts on @backstage/plugin-kubernetes-backend! diff --git a/plugins/kubernetes-react/src/__fixtures__/1-persistentvolumeclaims.json b/plugins/kubernetes-react/src/__fixtures__/1-persistentvolumeclaims.json new file mode 100644 index 0000000000..0da7fec9c9 --- /dev/null +++ b/plugins/kubernetes-react/src/__fixtures__/1-persistentvolumeclaims.json @@ -0,0 +1,37 @@ +{ + "persistentVolumeClaims": [ + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "pvc-web-storage", + "namespace": "default", + "uid": "1ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "web-app" + }, + "annotations": {} + }, + "spec": { + "accessModes": ["ReadWriteOnce"], + "resources": { + "requests": { + "storage": "10Gi" + } + }, + "storageClassName": "standard", + "volumeName": "pv-web-volume" + }, + "status": { + "phase": "Bound", + "accessModes": ["ReadWriteOnce"], + "capacity": { + "storage": "10Gi" + } + } + } + ] +} diff --git a/plugins/kubernetes-react/src/__fixtures__/1-persistentvolumes.json b/plugins/kubernetes-react/src/__fixtures__/1-persistentvolumes.json new file mode 100644 index 0000000000..cddf92224f --- /dev/null +++ b/plugins/kubernetes-react/src/__fixtures__/1-persistentvolumes.json @@ -0,0 +1,33 @@ +{ + "persistentVolumes": [ + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-hostpath", + "uid": "1ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "type": "local" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "10Gi" + }, + "accessModes": ["ReadWriteOnce"], + "persistentVolumeReclaimPolicy": "Retain", + "storageClassName": "manual", + "hostPath": { + "path": "/tmp/data" + } + }, + "status": { + "phase": "Available" + } + } + ] +} diff --git a/plugins/kubernetes-react/src/__fixtures__/2-persistentvolumeclaims.json b/plugins/kubernetes-react/src/__fixtures__/2-persistentvolumeclaims.json new file mode 100644 index 0000000000..d418e6f691 --- /dev/null +++ b/plugins/kubernetes-react/src/__fixtures__/2-persistentvolumeclaims.json @@ -0,0 +1,160 @@ +{ + "persistentVolumeClaims": [ + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "pvc-web-storage", + "namespace": "default", + "uid": "1ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "web-app" + }, + "annotations": {} + }, + "spec": { + "accessModes": ["ReadWriteOnce"], + "resources": { + "requests": { + "storage": "10Gi" + } + }, + "storageClassName": "standard", + "volumeName": "pv-web-volume" + }, + "status": { + "phase": "Bound", + "accessModes": ["ReadWriteOnce"], + "capacity": { + "storage": "10Gi" + } + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "pvc-database-storage", + "namespace": "production", + "uid": "2fb184cd-8b5c-5c10-9432-1416cdf96679", + "resourceVersion": "1362732553", + "creationTimestamp": "2021-07-17T10:20:15Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "database" + }, + "annotations": {} + }, + "spec": { + "accessModes": ["ReadWriteOnce"], + "resources": { + "requests": { + "storage": "50Gi" + } + }, + "storageClassName": "fast-ssd", + "volumeName": "pv-database-volume" + }, + "status": { + "phase": "Bound", + "accessModes": ["ReadWriteOnce"], + "capacity": { + "storage": "50Gi" + } + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "pvc-cache-storage", + "namespace": "staging", + "uid": "3gc295de-9c6d-6d21-a543-2527def07790", + "resourceVersion": "1362732554", + "creationTimestamp": "2021-07-18T15:30:45Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "cache-service" + }, + "annotations": {} + }, + "spec": { + "accessModes": ["ReadWriteMany"], + "resources": { + "requests": { + "storage": "5Gi" + } + }, + "storageClassName": "shared-storage" + }, + "status": { + "phase": "Pending" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "pvc-logs-storage", + "namespace": "logging", + "uid": "4hd3a6ef-ad7e-7e32-b654-3638eff18801", + "resourceVersion": "1362732555", + "creationTimestamp": "2021-07-19T09:15:30Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "log-aggregator" + }, + "annotations": {} + }, + "spec": { + "accessModes": ["ReadWriteMany"], + "resources": { + "requests": { + "storage": "100Gi" + } + }, + "storageClassName": "nfs-storage", + "volumeName": "pv-logs-volume" + }, + "status": { + "phase": "Bound", + "accessModes": ["ReadWriteMany"], + "capacity": { + "storage": "100Gi" + } + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "pvc-backup-storage", + "namespace": "backup", + "uid": "5ie4b7f0-be8f-8f43-c765-4749f0029912", + "resourceVersion": "1362732556", + "creationTimestamp": "2021-07-20T16:45:22Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "backup-service" + }, + "annotations": {} + }, + "spec": { + "accessModes": ["ReadWriteOnce"], + "resources": { + "requests": { + "storage": "200Gi" + } + }, + "storageClassName": "backup-storage", + "volumeName": "pv-backup-volume-deleted" + }, + "status": { + "phase": "Lost" + } + } + ] +} diff --git a/plugins/kubernetes-react/src/__fixtures__/2-persistentvolumes.json b/plugins/kubernetes-react/src/__fixtures__/2-persistentvolumes.json new file mode 100644 index 0000000000..46165b78ac --- /dev/null +++ b/plugins/kubernetes-react/src/__fixtures__/2-persistentvolumes.json @@ -0,0 +1,274 @@ +{ + "persistentVolumes": [ + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-aws-ebs", + "uid": "1ea073bc-7a4b-4b99-8321-0305bce85568", + "resourceVersion": "1362732552", + "creationTimestamp": "2021-07-16T22:39:58Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "100Gi" + }, + "accessModes": ["ReadWriteOnce"], + "persistentVolumeReclaimPolicy": "Delete", + "storageClassName": "gp3", + "csi": { + "driver": "ebs.csi.aws.com", + "volumeHandle": "vol-1234567890abcdef0" + } + }, + "status": { + "phase": "Bound" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-aws-efs", + "uid": "2fb184cd-8b5c-5c10-9432-1416cdf96679", + "resourceVersion": "1362732553", + "creationTimestamp": "2021-07-17T10:20:15Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "5Ti" + }, + "accessModes": ["ReadWriteMany"], + "persistentVolumeReclaimPolicy": "Retain", + "storageClassName": "efs-sc", + "csi": { + "driver": "efs.csi.aws.com", + "volumeHandle": "fs-0123456789abcdef0" + } + }, + "status": { + "phase": "Available" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-aws-s3", + "uid": "3gc295de-9c6d-6d21-a543-2527def07790", + "resourceVersion": "1362732554", + "creationTimestamp": "2021-07-18T08:45:30Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "10Ti" + }, + "accessModes": ["ReadWriteMany"], + "persistentVolumeReclaimPolicy": "Retain", + "storageClassName": "s3-csi", + "csi": { + "driver": "s3.csi.aws.com", + "volumeHandle": "my-s3-bucket" + } + }, + "status": { + "phase": "Bound" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-gcp-pd", + "uid": "4hd3a6ef-ad7e-7e32-b654-3638eff18801", + "resourceVersion": "1362732555", + "creationTimestamp": "2021-07-18T15:30:45Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "200Gi" + }, + "accessModes": ["ReadWriteOnce"], + "persistentVolumeReclaimPolicy": "Delete", + "storageClassName": "ssd", + "csi": { + "driver": "pd.csi.storage.gke.io", + "volumeHandle": "projects/my-project/zones/us-central1-a/disks/my-disk" + } + }, + "status": { + "phase": "Bound" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-gcp-filestore", + "uid": "5ie4b7f0-be8f-8f43-c765-4749f0029912", + "resourceVersion": "1362732556", + "creationTimestamp": "2021-07-19T09:15:30Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "1Ti" + }, + "accessModes": ["ReadWriteMany"], + "persistentVolumeReclaimPolicy": "Retain", + "storageClassName": "filestore-csi", + "csi": { + "driver": "filestore.csi.storage.gke.io", + "volumeHandle": "projects/my-project/locations/us-central1-c/instances/my-filestore" + } + }, + "status": { + "phase": "Available" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-gcp-gcsfuse", + "uid": "6jf5c8g1-cf90-9054-d876-5850g1130a23", + "resourceVersion": "1362732557", + "creationTimestamp": "2021-07-20T12:30:45Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "50Ti" + }, + "accessModes": ["ReadWriteMany"], + "persistentVolumeReclaimPolicy": "Retain", + "storageClassName": "gcsfuse", + "csi": { + "driver": "gcsfuse.csi.storage.gke.io", + "volumeHandle": "my-gcs-bucket" + } + }, + "status": { + "phase": "Available" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-azure-disk", + "uid": "7kg6d9h2-dg01-0165-e987-6961h2241b34", + "resourceVersion": "1362732558", + "creationTimestamp": "2021-07-20T14:45:20Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "128Gi" + }, + "accessModes": ["ReadWriteOnce"], + "persistentVolumeReclaimPolicy": "Delete", + "storageClassName": "managed-premium", + "csi": { + "driver": "disk.csi.azure.com", + "volumeHandle": "/subscriptions/subscription-id/resourceGroups/rg/providers/Microsoft.Compute/disks/my-disk" + } + }, + "status": { + "phase": "Bound" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-azure-file", + "uid": "8lh7e0i3-eh12-1276-f098-7072i3352c45", + "resourceVersion": "1362732559", + "creationTimestamp": "2021-07-21T11:20:10Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "100Gi" + }, + "accessModes": ["ReadWriteMany"], + "persistentVolumeReclaimPolicy": "Retain", + "storageClassName": "azurefile", + "csi": { + "driver": "file.csi.azure.com", + "volumeHandle": "unique-volumeid" + } + }, + "status": { + "phase": "Available" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": "pv-azure-blob", + "uid": "9mi8f1j4-fi23-2387-g109-8183j4463d56", + "resourceVersion": "1362732560", + "creationTimestamp": "2021-07-22T16:30:25Z", + "labels": { + "backstage.io/kubernetes-id": "dice-roller", + "app": "dice-roller" + }, + "annotations": {} + }, + "spec": { + "capacity": { + "storage": "500Gi" + }, + "accessModes": ["ReadWriteMany"], + "persistentVolumeReclaimPolicy": "Delete", + "storageClassName": "azureblob", + "csi": { + "driver": "blob.csi.azure.com", + "volumeHandle": "my-storage-account/my-container" + } + }, + "status": { + "phase": "Bound" + } + } + ] +} diff --git a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx index ca0f57e855..7fcdf70d6e 100644 --- a/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx +++ b/plugins/kubernetes-react/src/components/Cluster/Cluster.tsx @@ -34,6 +34,8 @@ import { ConfigmapsAccordions } from '../ConfigmapsAccordions'; import { CronJobsAccordions } from '../CronJobsAccordions'; import { CustomResources } from '../CustomResources'; import { DaemonSetsAccordions } from '../DaemonSetsAccordions'; +import { PersistentVolumesAccordions } from '../PersistentVolumesAccordions'; +import { PersistentVolumeClaimsAccordions } from '../PersistentVolumesClaimsAccordions'; import { ClusterContext, GroupedResponsesContext, @@ -187,6 +189,16 @@ export const Cluster = ({ clusterObjects, podsWithErrors }: ClusterProps) => { ) : undefined} + {groupedResponses.persistentVolumes.length > 0 ? ( + + + + ) : undefined} + {groupedResponses.persistentVolumeClaims.length > 0 ? ( + + + + ) : undefined} {groupedResponses.cronJobs.length > 0 ? ( diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.test.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.test.tsx new file mode 100644 index 0000000000..6561cf20c2 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.test.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { screen } from '@testing-library/react'; +import { PersistentVolumesAccordions } from './PersistentVolumesAccordions'; +import * as onePersistentVolumesFixture from '../../__fixtures__/1-persistentvolumes.json'; +import * as twoPersistentVolumesFixture from '../../__fixtures__/2-persistentvolumes.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { kubernetesProviders } from '../../hooks/test-utils'; + +describe('PersistentVolumesAccordions', () => { + it('should render 1 persistent volume with summary', async () => { + const wrapper = kubernetesProviders( + onePersistentVolumesFixture, + new Set(), + ); + + await renderInTestApp(wrapper()); + + expect(screen.getByText('PersistentVolumes')).toBeInTheDocument(); + expect(screen.getByText('1 volumes')).toBeInTheDocument(); + expect(screen.getByText('0 bound')).toBeInTheDocument(); + }); + + it('should render 9 persistent volumes with summary', async () => { + const wrapper = kubernetesProviders( + twoPersistentVolumesFixture, + new Set(), + ); + + await renderInTestApp(wrapper()); + + expect(screen.getByText('PersistentVolumes')).toBeInTheDocument(); + expect(screen.getByText('9 volumes')).toBeInTheDocument(); + expect(screen.getByText('5 bound')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.tsx new file mode 100644 index 0000000000..2d486bc383 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesAccordions.tsx @@ -0,0 +1,137 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ReactNode, useContext } from 'react'; +import Accordion from '@material-ui/core/Accordion'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import type { V1PersistentVolume } from '@kubernetes/client-node'; +import { PersistentVolumesTable } from './PersistentVolumesTable'; +import { GroupedResponsesContext } from '../../hooks'; +import { StatusOK, StatusError } from '@backstage/core-components'; + +type PersistentVolumesAccordionsProps = { + children?: ReactNode; +}; + +type PersistentVolumesAccordionProps = { + persistentVolumes: V1PersistentVolume[]; + children?: ReactNode; +}; + +type PersistentVolumesSummaryProps = { + numberOfPersistentVolumes: number; + numberOfBoundVolumes: number; + numberOfFailedVolumes: number; + children?: ReactNode; +}; + +const PersistentVolumesSummary = ({ + numberOfPersistentVolumes, + numberOfBoundVolumes, + numberOfFailedVolumes, +}: PersistentVolumesSummaryProps) => { + return ( + + + + PersistentVolumes + + + + + {numberOfPersistentVolumes} volumes + + + {numberOfBoundVolumes} bound + + {numberOfFailedVolumes > 0 && ( + + + {numberOfFailedVolumes} volume + {numberOfFailedVolumes > 1 ? 's' : ''} failed + + + )} + + + ); +}; + +const PersistentVolumesAccordion = ({ + persistentVolumes, +}: PersistentVolumesAccordionProps) => { + const boundVolumes = persistentVolumes.filter( + pv => pv.status?.phase === 'Bound', + ); + const failedVolumes = persistentVolumes.filter( + pv => pv.status?.phase === 'Failed', + ); + + return ( + + }> + + + + + + + ); +}; + +export const PersistentVolumesAccordions = + ({}: PersistentVolumesAccordionsProps) => { + const groupedResponses = useContext(GroupedResponsesContext); + + return ( + + + + + + + + ); + }; diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.test.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.test.tsx new file mode 100644 index 0000000000..85911016ac --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.test.tsx @@ -0,0 +1,38 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as onePersistentVolumesFixture from '../../__fixtures__/1-persistentvolumes.json'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { PersistentVolumesDrawer } from './PersistentVolumesDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; + +describe('PersistentVolumesDrawer', () => { + it('should render persistent volume drawer', async () => { + const { getByText, getAllByText } = await renderInTestApp( + + + , + ); + + expect(getAllByText('pv-hostpath')).toHaveLength(3); + expect(getAllByText('PersistentVolume')).toHaveLength(3); + expect(getByText('YAML')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.tsx new file mode 100644 index 0000000000..7da0d16842 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesDrawer.tsx @@ -0,0 +1,64 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { V1PersistentVolume } from '@kubernetes/client-node'; +import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import Chip from '@material-ui/core/Chip'; + +export const PersistentVolumesDrawer = ({ + persistentVolume, + expanded, +}: { + persistentVolume: V1PersistentVolume; + expanded?: boolean; +}) => { + const namespace = persistentVolume.metadata?.namespace; + return ( + { + return persistentVolumeObject || {}; + }} + > + + + + {persistentVolume.metadata?.name ?? 'unknown object'} + + + + + PersistentVolume + + + {namespace && ( + + + + )} + + + ); +}; diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.test.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.test.tsx new file mode 100644 index 0000000000..a1684748cb --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { screen } from '@testing-library/react'; +import * as onePersistentVolumesFixture from '../../__fixtures__/1-persistentvolumes.json'; +import * as twoPersistentVolumesFixture from '../../__fixtures__/2-persistentvolumes.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { PersistentVolumesTable } from './PersistentVolumesTable'; + +describe('PersistentVolumesTable', () => { + it('should render persistent volume table with columns', async () => { + await renderInTestApp( + , + ); + + expect(screen.getByText('name')).toBeInTheDocument(); + expect(screen.getByText('phase')).toBeInTheDocument(); + expect(screen.getByText('status')).toBeInTheDocument(); + expect(screen.getByText('capacity')).toBeInTheDocument(); + expect(screen.getByText('type')).toBeInTheDocument(); + expect(screen.getByText('claim')).toBeInTheDocument(); + + expect(screen.getByText('pv-hostpath')).toBeInTheDocument(); + expect(screen.getAllByText('Available').length).toBeGreaterThanOrEqual(1); + expect(screen.getByText('10Gi')).toBeInTheDocument(); + }); + + it('should render multiple persistent volumes with cloud provider types', async () => { + await renderInTestApp( + , + ); + + expect(screen.getByText('name')).toBeInTheDocument(); + expect(screen.getByText('phase')).toBeInTheDocument(); + expect(screen.getByText('status')).toBeInTheDocument(); + expect(screen.getByText('capacity')).toBeInTheDocument(); + expect(screen.getByText('type')).toBeInTheDocument(); + expect(screen.getByText('claim')).toBeInTheDocument(); + + expect(screen.getByText('pv-aws-ebs')).toBeInTheDocument(); + expect(screen.getByText('pv-aws-efs')).toBeInTheDocument(); + expect(screen.getByText('pv-aws-s3')).toBeInTheDocument(); + expect(screen.getByText('pv-gcp-pd')).toBeInTheDocument(); + expect(screen.getByText('pv-gcp-filestore')).toBeInTheDocument(); + + expect(screen.getByText('AWS EBS Volume')).toBeInTheDocument(); + expect(screen.getByText('AWS EFS')).toBeInTheDocument(); + expect(screen.getByText('S3 Bucket')).toBeInTheDocument(); + expect(screen.getByText('GCP Persistent Disk')).toBeInTheDocument(); + expect(screen.getByText('GCP Filestore')).toBeInTheDocument(); + + expect(screen.getAllByText('Bound').length).toBeGreaterThanOrEqual(3); + expect(screen.getAllByText('Available').length).toBeGreaterThanOrEqual(2); + + expect(screen.getByText('100Gi')).toBeInTheDocument(); + expect(screen.getByText('5Ti')).toBeInTheDocument(); + expect(screen.getByText('10Ti')).toBeInTheDocument(); + expect(screen.getByText('200Gi')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.tsx new file mode 100644 index 0000000000..6c9ed7c339 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/PersistentVolumesTable.tsx @@ -0,0 +1,152 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ReactNode } from 'react'; +import { PersistentVolumesDrawer } from './PersistentVolumesDrawer'; +import { Table, TableColumn } from '@backstage/core-components'; +import type { V1PersistentVolume } from '@kubernetes/client-node'; +import { + StatusError, + StatusOK, + StatusPending, +} from '@backstage/core-components'; +import { getPersistentVolumeType } from '../../utils/persistentVolumes'; + +export type PersistentVolumesTableProps = { + persistentVolumes: V1PersistentVolume[]; + children?: ReactNode; +}; + +const PersistentVolumeDrawerTrigger = ({ + persistentVolume, +}: { + persistentVolume: V1PersistentVolume; +}) => { + return ; +}; + +const renderPhaseStatus = (persistentVolume: V1PersistentVolume) => { + const phase = persistentVolume.status?.phase; + + if (phase === 'Bound') { + return Bound; + } + if (phase === 'Available') { + return Available; + } + if (phase === 'Released') { + return Released; + } + if (phase === 'Pending') { + return Pending; + } + if (phase === 'Failed') { + return Failed; + } + return <>{phase ?? 'Unknown'}; +}; + +export const PersistentVolumesTable = ({ + persistentVolumes, +}: PersistentVolumesTableProps) => { + const defaultColumns: TableColumn[] = [ + { + title: 'ID', + field: 'metadata.uid', + hidden: true, + }, + { + title: 'name', + highlight: true, + render: (persistentVolume: V1PersistentVolume) => { + return ( + + ); + }, + cellStyle: { + width: '20%', + }, + }, + { + title: 'phase', + render: renderPhaseStatus, + cellStyle: { + width: '9%', + }, + }, + { + title: 'status', + render: (persistentVolume: V1PersistentVolume) => + persistentVolume.status?.phase ?? 'Unknown', + cellStyle: { + width: '9%', + }, + }, + { + title: 'capacity', + render: (persistentVolume: V1PersistentVolume) => + persistentVolume.spec?.capacity?.storage ?? 'N/A', + cellStyle: { + width: '10%', + }, + }, + { + title: 'type', + render: (persistentVolume: V1PersistentVolume) => { + const driver = persistentVolume.spec?.csi?.driver; + return getPersistentVolumeType(driver) ?? 'N/A'; + }, + cellStyle: { + width: '15%', + }, + }, + { + title: 'claim', + render: (persistentVolume: V1PersistentVolume) => { + const claim = persistentVolume.spec?.claimRef; + if (claim?.namespace && claim?.name) { + return `${claim.namespace}/${claim.name}`; + } + return 'N/A'; + }, + cellStyle: { + width: '37%', + }, + }, + ]; + + const columns: TableColumn[] = [...defaultColumns]; + + const tableStyle = { + minWidth: '0', + width: '100%', + }; + + return ( +
+ ({ + ...pv, + id: pv?.metadata?.uid, + })) as any as V1PersistentVolume[] + } + columns={columns} + /> + + ); +}; diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/index.ts b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/index.ts new file mode 100644 index 0000000000..29123cc6fc --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesAccordions/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './PersistentVolumesAccordions'; +export * from './PersistentVolumesTable'; +export * from './PersistentVolumesDrawer'; diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.test.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.test.tsx new file mode 100644 index 0000000000..2497b2fa04 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.test.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { screen } from '@testing-library/react'; +import { PersistentVolumeClaimsAccordions } from './PersistentVolumeClaimsAccordions'; +import * as onePersistentVolumeClaimsFixture from '../../__fixtures__/1-persistentvolumeclaims.json'; +import * as twoPersistentVolumeClaimsFixture from '../../__fixtures__/2-persistentvolumeclaims.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { kubernetesProviders } from '../../hooks/test-utils'; + +describe('PersistentVolumeClaimsAccordions', () => { + it('should render 1 persistent volume claim with summary', async () => { + const wrapper = kubernetesProviders( + onePersistentVolumeClaimsFixture, + new Set(), + ); + + await renderInTestApp(wrapper()); + + expect(screen.getByText('PersistentVolumeClaims')).toBeInTheDocument(); + expect(screen.getByText('1 claims')).toBeInTheDocument(); + expect(screen.getByText('1 bound')).toBeInTheDocument(); + }); + + it('should render multiple persistent volume claims with summary', async () => { + const wrapper = kubernetesProviders( + twoPersistentVolumeClaimsFixture, + new Set(), + ); + + await renderInTestApp(wrapper()); + + expect(screen.getByText('PersistentVolumeClaims')).toBeInTheDocument(); + expect(screen.getByText('5 claims')).toBeInTheDocument(); + expect(screen.getByText('3 bound')).toBeInTheDocument(); + expect(screen.getByText('1 claim lost')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.tsx new file mode 100644 index 0000000000..c16b1399f1 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsAccordions.tsx @@ -0,0 +1,139 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ReactNode, useContext } from 'react'; +import Accordion from '@material-ui/core/Accordion'; +import AccordionDetails from '@material-ui/core/AccordionDetails'; +import AccordionSummary from '@material-ui/core/AccordionSummary'; +import Grid from '@material-ui/core/Grid'; +import Typography from '@material-ui/core/Typography'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import type { V1PersistentVolumeClaim } from '@kubernetes/client-node'; +import { PersistentVolumeClaimsTable } from './PersistentVolumeClaimsTable'; +import { GroupedResponsesContext } from '../../hooks'; +import { StatusOK, StatusError } from '@backstage/core-components'; + +type PersistentVolumeClaimsAccordionsProps = { + children?: ReactNode; +}; + +type PersistentVolumeClaimsAccordionProps = { + persistentVolumeClaims: V1PersistentVolumeClaim[]; + children?: ReactNode; +}; + +type PersistentVolumeClaimsSummaryProps = { + numberOfPersistentVolumeClaims: number; + numberOfBoundClaims: number; + numberOfLostClaims: number; + children?: ReactNode; +}; + +const PersistentVolumeClaimsSummary = ({ + numberOfPersistentVolumeClaims, + numberOfBoundClaims, + numberOfLostClaims, +}: PersistentVolumeClaimsSummaryProps) => { + return ( + + + + PersistentVolumeClaims + + + + + {numberOfPersistentVolumeClaims} claims + + + {numberOfBoundClaims} bound + + {numberOfLostClaims > 0 && ( + + + {numberOfLostClaims} claim + {numberOfLostClaims > 1 ? 's' : ''} lost + + + )} + + + ); +}; + +const PersistentVolumeClaimsAccordion = ({ + persistentVolumeClaims, +}: PersistentVolumeClaimsAccordionProps) => { + const boundClaims = persistentVolumeClaims.filter( + pvc => pvc.status?.phase === 'Bound', + ); + const lostClaims = persistentVolumeClaims.filter( + pvc => pvc.status?.phase === 'Lost', + ); + + return ( + + }> + + + + + + + ); +}; + +export const PersistentVolumeClaimsAccordions = + ({}: PersistentVolumeClaimsAccordionsProps) => { + const groupedResponses = useContext(GroupedResponsesContext); + + return ( + + + + + + + + ); + }; diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.test.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.test.tsx new file mode 100644 index 0000000000..1340a176b5 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.test.tsx @@ -0,0 +1,39 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import * as onePersistentVolumeClaimsFixture from '../../__fixtures__/1-persistentvolumeclaims.json'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { PersistentVolumeClaimsDrawer } from './PersistentVolumeClaimsDrawer'; +import { kubernetesClusterLinkFormatterApiRef } from '../../api'; + +describe('PersistentVolumeClaimsDrawer', () => { + it('should render persistent volume claim drawer', async () => { + const { getByText, getAllByText } = await renderInTestApp( + + + , + ); + + expect(getAllByText('pvc-web-storage')).toHaveLength(3); + expect(getAllByText('PersistentVolumeClaim')).toHaveLength(3); + expect(getByText('YAML')).toBeInTheDocument(); + expect(getByText('namespace: default')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.tsx new file mode 100644 index 0000000000..d0af9caf48 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsDrawer.tsx @@ -0,0 +1,64 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { V1PersistentVolumeClaim } from '@kubernetes/client-node'; +import { KubernetesStructuredMetadataTableDrawer } from '../KubernetesDrawer'; +import Typography from '@material-ui/core/Typography'; +import Grid from '@material-ui/core/Grid'; +import Chip from '@material-ui/core/Chip'; + +export const PersistentVolumeClaimsDrawer = ({ + persistentVolumeClaim, + expanded, +}: { + persistentVolumeClaim: V1PersistentVolumeClaim; + expanded?: boolean; +}) => { + const namespace = persistentVolumeClaim.metadata?.namespace; + return ( + { + return persistentVolumeClaimObject || {}; + }} + > + + + + {persistentVolumeClaim.metadata?.name ?? 'unknown object'} + + + + + PersistentVolumeClaim + + + {namespace && ( + + + + )} + + + ); +}; diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.test.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.test.tsx new file mode 100644 index 0000000000..06230a85b7 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.test.tsx @@ -0,0 +1,68 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { screen } from '@testing-library/react'; +import * as onePersistentVolumeClaimsFixture from '../../__fixtures__/1-persistentvolumeclaims.json'; +import * as twoPersistentVolumeClaimsFixture from '../../__fixtures__/2-persistentvolumeclaims.json'; +import { renderInTestApp } from '@backstage/test-utils'; +import { PersistentVolumeClaimsTable } from './PersistentVolumeClaimsTable'; + +describe('PersistentVolumeClaimsTable', () => { + it('should render persistent volume claim table with columns', async () => { + await renderInTestApp( + , + ); + + expect(screen.getByText('name')).toBeInTheDocument(); + expect(screen.getByText('phase')).toBeInTheDocument(); + expect(screen.getByText('status')).toBeInTheDocument(); + expect(screen.getByText('capacity')).toBeInTheDocument(); + expect(screen.getByText('volume')).toBeInTheDocument(); + + expect(screen.getByText('pvc-web-storage')).toBeInTheDocument(); + expect(screen.getAllByText('Bound').length).toBeGreaterThanOrEqual(1); + expect(screen.getByText('10Gi')).toBeInTheDocument(); + }); + + it('should render multiple persistent volume claims', async () => { + await renderInTestApp( + , + ); + + expect(screen.getByText('name')).toBeInTheDocument(); + expect(screen.getByText('phase')).toBeInTheDocument(); + expect(screen.getByText('status')).toBeInTheDocument(); + expect(screen.getByText('capacity')).toBeInTheDocument(); + expect(screen.getByText('volume')).toBeInTheDocument(); + + expect(screen.getByText('pvc-web-storage')).toBeInTheDocument(); + expect(screen.getByText('pvc-database-storage')).toBeInTheDocument(); + + expect(screen.getAllByText('Bound').length).toBeGreaterThanOrEqual(1); + expect(screen.getAllByText('Pending').length).toBeGreaterThanOrEqual(1); + + expect(screen.getByText('10Gi')).toBeInTheDocument(); + expect(screen.getByText('50Gi')).toBeInTheDocument(); + }); +}); diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.tsx b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.tsx new file mode 100644 index 0000000000..eb801fc1b8 --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/PersistentVolumeClaimsTable.tsx @@ -0,0 +1,136 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ReactNode } from 'react'; +import { PersistentVolumeClaimsDrawer } from './PersistentVolumeClaimsDrawer'; +import { Table, TableColumn } from '@backstage/core-components'; +import type { V1PersistentVolumeClaim } from '@kubernetes/client-node'; +import { + StatusError, + StatusOK, + StatusPending, +} from '@backstage/core-components'; + +export type PersistentVolumeClaimsTableProps = { + persistentVolumeClaims: V1PersistentVolumeClaim[]; + children?: ReactNode; +}; + +const PersistentVolumeClaimDrawerTrigger = ({ + persistentVolumeClaim, +}: { + persistentVolumeClaim: V1PersistentVolumeClaim; +}) => { + return ( + + ); +}; + +const renderPhaseStatus = (persistentVolumeClaim: V1PersistentVolumeClaim) => { + const phase = persistentVolumeClaim.status?.phase; + + if (phase === 'Bound') { + return Bound; + } + if (phase === 'Pending') { + return Pending; + } + if (phase === 'Lost') { + return Lost; + } + return <>{phase ?? 'Unknown'}; +}; + +export const PersistentVolumeClaimsTable = ({ + persistentVolumeClaims, +}: PersistentVolumeClaimsTableProps) => { + const defaultColumns: TableColumn[] = [ + { + title: 'ID', + field: 'metadata.uid', + hidden: true, + }, + { + title: 'name', + highlight: true, + render: (persistentVolumeClaim: V1PersistentVolumeClaim) => { + return ( + + ); + }, + cellStyle: { + width: '25%', + }, + }, + { + title: 'phase', + render: renderPhaseStatus, + cellStyle: { + width: '10%', + }, + }, + { + title: 'status', + render: (persistentVolumeClaim: V1PersistentVolumeClaim) => + persistentVolumeClaim.status?.phase ?? 'Unknown', + cellStyle: { + width: '10%', + }, + }, + { + title: 'capacity', + render: (persistentVolumeClaim: V1PersistentVolumeClaim) => + persistentVolumeClaim.status?.capacity?.storage ?? 'N/A', + cellStyle: { + width: '10%', + }, + }, + { + title: 'volume', + render: (persistentVolumeClaim: V1PersistentVolumeClaim) => + persistentVolumeClaim.spec?.volumeName ?? 'N/A', + cellStyle: { + width: '45%', + }, + }, + ]; + + const columns: TableColumn[] = [...defaultColumns]; + + const tableStyle = { + minWidth: '0', + width: '100%', + }; + + return ( +
+
({ + ...pvc, + id: pvc?.metadata?.uid, + })) as any as V1PersistentVolumeClaim[] + } + columns={columns} + /> + + ); +}; diff --git a/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/index.ts b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/index.ts new file mode 100644 index 0000000000..63308960fe --- /dev/null +++ b/plugins/kubernetes-react/src/components/PersistentVolumesClaimsAccordions/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './PersistentVolumeClaimsAccordions'; +export * from './PersistentVolumeClaimsTable'; +export * from './PersistentVolumeClaimsDrawer'; diff --git a/plugins/kubernetes-react/src/hooks/GroupedResponses.ts b/plugins/kubernetes-react/src/hooks/GroupedResponses.ts index 750ee58846..81ad9af048 100644 --- a/plugins/kubernetes-react/src/hooks/GroupedResponses.ts +++ b/plugins/kubernetes-react/src/hooks/GroupedResponses.ts @@ -35,4 +35,6 @@ export const GroupedResponsesContext = createContext({ cronJobs: [], customResources: [], statefulsets: [], + persistentVolumes: [], + persistentVolumeClaims: [], }); diff --git a/plugins/kubernetes-react/src/utils/persistentVolumes.test.ts b/plugins/kubernetes-react/src/utils/persistentVolumes.test.ts new file mode 100644 index 0000000000..ab583b7c9d --- /dev/null +++ b/plugins/kubernetes-react/src/utils/persistentVolumes.test.ts @@ -0,0 +1,83 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getPersistentVolumeType } from './persistentVolumes'; + +describe('getPersistentVolumeType', () => { + it('should return null for undefined driver', () => { + expect(getPersistentVolumeType(undefined)).toBeNull(); + }); + + it('should return null for empty driver', () => { + expect(getPersistentVolumeType('')).toBeNull(); + }); + + describe('AWS drivers', () => { + it('should return AWS EBS Volume for aws ebs driver', () => { + expect(getPersistentVolumeType('ebs.csi.aws.com')).toBe('AWS EBS Volume'); + }); + + it('should return AWS EFS for aws efs driver', () => { + expect(getPersistentVolumeType('efs.csi.aws.com')).toBe('AWS EFS'); + }); + + it('should return S3 Bucket for aws s3 driver', () => { + expect(getPersistentVolumeType('s3.csi.aws.com')).toBe('S3 Bucket'); + }); + }); + + describe('GCP drivers', () => { + it('should return GCP Persistent Disk for gcp pd driver', () => { + expect(getPersistentVolumeType('pd.csi.storage.gke.io')).toBe( + 'GCP Persistent Disk', + ); + }); + + it('should return GCP Filestore for gcp filestore driver', () => { + expect(getPersistentVolumeType('filestore.csi.storage.gke.io')).toBe( + 'GCP Filestore', + ); + }); + + it('should return GCS Fuse for gcp gcsfuse driver', () => { + expect(getPersistentVolumeType('gcsfuse.csi.storage.gke.io')).toBe( + 'GCS Fuse', + ); + }); + }); + + describe('Azure drivers', () => { + it('should return Azure Disk for azure disk driver', () => { + expect(getPersistentVolumeType('disk.csi.azure.com')).toBe('Azure Disk'); + }); + + it('should return Azure File for azure file driver', () => { + expect(getPersistentVolumeType('file.csi.azure.com')).toBe('Azure File'); + }); + + it('should return Azure Blob for azure blob driver', () => { + expect(getPersistentVolumeType('blob.csi.azure.com')).toBe('Azure Blob'); + }); + }); + + it('should return the original driver for unknown drivers', () => { + expect(getPersistentVolumeType('unknown.driver.com')).toBe( + 'unknown.driver.com', + ); + expect(getPersistentVolumeType('local-storage')).toBe('local-storage'); + expect(getPersistentVolumeType('nfs')).toBe('nfs'); + }); +}); diff --git a/plugins/kubernetes-react/src/utils/persistentVolumes.ts b/plugins/kubernetes-react/src/utils/persistentVolumes.ts new file mode 100644 index 0000000000..348e442cb1 --- /dev/null +++ b/plugins/kubernetes-react/src/utils/persistentVolumes.ts @@ -0,0 +1,39 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const getPersistentVolumeType = (driver?: string): string | null => { + if (!driver) return null; + + if (driver.includes('aws')) { + if (driver.includes('ebs')) return `AWS EBS Volume`; + if (driver.includes('efs')) return `AWS EFS`; + if (driver.includes('s3')) return `S3 Bucket`; + } + + if (driver.includes('gke')) { + if (driver.includes('gcsfuse')) return `GCS Fuse`; + if (driver.includes('filestore')) return `GCP Filestore`; + if (driver.includes('pd')) return `GCP Persistent Disk`; + } + + if (driver.includes('azure')) { + if (driver.includes('disk')) return `Azure Disk`; + if (driver.includes('file')) return `Azure File`; + if (driver.includes('blob')) return `Azure Blob`; + } + + return driver; +}; From bdf05eac2fa82396cf73429478df9b89ce52fed5 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 29 Jan 2026 10:08:27 +0100 Subject: [PATCH 170/211] Revert "frontend-app-api: switch new API conflict error to be a warning" This reverts commit 17e0eb3e5f0c76558c0f4d1c2686ac5861892d14. Signed-off-by: Patrik Oldsberg --- .../api-override-deprecation-warning-defaults.md | 5 ----- .changeset/api-override-deprecation-warning.md | 5 ----- .patches/pr-32522.txt | 1 - .../src/wiring/createSpecializedApp.test.tsx | 9 ++++----- .../src/wiring/createSpecializedApp.tsx | 13 ++++++------- .../frontend-defaults/src/maybeCreateErrorPage.tsx | 1 - 6 files changed, 10 insertions(+), 24 deletions(-) delete mode 100644 .changeset/api-override-deprecation-warning-defaults.md delete mode 100644 .changeset/api-override-deprecation-warning.md delete mode 100644 .patches/pr-32522.txt diff --git a/.changeset/api-override-deprecation-warning-defaults.md b/.changeset/api-override-deprecation-warning-defaults.md deleted file mode 100644 index 8616460390..0000000000 --- a/.changeset/api-override-deprecation-warning-defaults.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/frontend-defaults': patch ---- - -The `API_FACTORY_CONFLICT` error is now treated as a warning and will not prevent the app from starting. diff --git a/.changeset/api-override-deprecation-warning.md b/.changeset/api-override-deprecation-warning.md deleted file mode 100644 index 91ed83efaf..0000000000 --- a/.changeset/api-override-deprecation-warning.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/frontend-app-api': patch ---- - -Updated the behavior of the new API override logic to log a deprecation warning instead of rejecting the override and blocking app startup, as was originally intended. diff --git a/.patches/pr-32522.txt b/.patches/pr-32522.txt deleted file mode 100644 index 06f33f31b1..0000000000 --- a/.patches/pr-32522.txt +++ /dev/null @@ -1 +0,0 @@ -Rolls back the immediate breaking change of API factory conflicts in the new frontend system, making it a deprecation warning instead. \ No newline at end of file diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx index fe807a1384..6c79002ad9 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.test.tsx @@ -289,7 +289,7 @@ describe('createSpecializedApp', () => { expect(mockAnalyticsApi).toHaveBeenCalled(); }); - it('should warn when API overrides would be blocked by new logic', () => { + it('should select the API factory from the owning plugin on conflict', () => { const testApiRef = createApiRef<{ value: string }>({ id: 'test.api' }); const app = createSpecializedApp({ @@ -303,7 +303,7 @@ describe('createSpecializedApp', () => { defineParams({ api: testApiRef, deps: {}, - factory: () => ({ value: 'other-before' }), + factory: () => ({ value: 'other' }), }), }), ], @@ -329,7 +329,7 @@ describe('createSpecializedApp', () => { defineParams({ api: testApiRef, deps: {}, - factory: () => ({ value: 'other-after' }), + factory: () => ({ value: 'other' }), }), }), ], @@ -348,8 +348,7 @@ describe('createSpecializedApp', () => { }), ]); - // Old behavior: last factory wins - expect(app.apis.get(testApiRef)).toEqual({ value: 'other-after' }); + expect(app.apis.get(testApiRef)).toEqual({ value: 'owner' }); }); it('should allow API overrides within the same plugin', () => { diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx index f1d70a95a7..57c91969de 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -428,14 +428,13 @@ function createApiFactories(options: { existingPluginId: acceptedPluginId, }, }); - if (!shouldReplace) { - // eslint-disable-next-line no-console - console.warn( - `DEPRECATION WARNING: Plugin '${rejectedPluginId}' is overriding API '${apiRefId}' ` + - `from plugin '${acceptedPluginId}'. This will be blocked in a future release. ` + - `Please use a module for plugin '${acceptedPluginId}' instead.`, - ); + if (shouldReplace) { + factoriesById.set(apiRefId, { + pluginId, + factory: apiFactory, + }); } + continue; } factoriesById.set(apiRefId, { pluginId, factory: apiFactory }); diff --git a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx index 806c618e5c..152c37fdd3 100644 --- a/packages/frontend-defaults/src/maybeCreateErrorPage.tsx +++ b/packages/frontend-defaults/src/maybeCreateErrorPage.tsx @@ -24,7 +24,6 @@ const DEFAULT_WARNING_CODES: Array = [ 'EXTENSION_INPUT_DATA_IGNORED', 'EXTENSION_INPUT_INTERNAL_IGNORED', 'EXTENSION_OUTPUT_IGNORED', - 'API_FACTORY_CONFLICT', ]; function AppErrorItem(props: { error: AppError }): JSX.Element { From 2c219b98172209a245550ca85fc3f4d870885467 Mon Sep 17 00:00:00 2001 From: Johan Persson Date: Wed, 28 Jan 2026 16:27:01 +0100 Subject: [PATCH 171/211] feat(ui): add danger variant to Button component Added a `danger` variant for destructive actions like delete or remove. Uses solid red background with white text in both light and dark modes. - Added CSS styles with custom properties for future token migration - Updated Storybook stories to include danger variant examples - Updated docs-ui documentation to reflect the new variant Signed-off-by: Johan Persson --- .changeset/floppy-actors-show.md | 7 + .../src/app/components/button/components.tsx | 16 ++ docs-ui/src/app/components/button/page.mdx | 17 +- .../components/button/props-definition.tsx | 6 + docs-ui/src/app/components/button/snippets.ts | 12 ++ packages/ui/report.api.md | 4 + .../src/components/Button/Button.module.css | 86 ++++++++ .../src/components/Button/Button.stories.tsx | 184 ++++++++++++++++-- .../ui/src/components/Button/definition.ts | 1 + packages/ui/src/components/Button/types.ts | 1 + 10 files changed, 322 insertions(+), 12 deletions(-) create mode 100644 .changeset/floppy-actors-show.md diff --git a/.changeset/floppy-actors-show.md b/.changeset/floppy-actors-show.md new file mode 100644 index 0000000000..95bb711660 --- /dev/null +++ b/.changeset/floppy-actors-show.md @@ -0,0 +1,7 @@ +--- +'@backstage/ui': patch +--- + +Added `destructive` prop to Button for dangerous actions like delete or remove. Works with all variants (primary, secondary, tertiary). + +**Affected components:** Button diff --git a/docs-ui/src/app/components/button/components.tsx b/docs-ui/src/app/components/button/components.tsx index 558c852e9c..1fe13f6630 100644 --- a/docs-ui/src/app/components/button/components.tsx +++ b/docs-ui/src/app/components/button/components.tsx @@ -59,6 +59,22 @@ export const Disabled = () => { ); }; +export const Destructive = () => { + return ( + + + + + + ); +}; + export const Loading = () => { return ( `; +export const destructiveSnippet = ` + + + +`; + export const loadingSnippet = ``; diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index b6f1a173ef..b4211d2499 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -330,6 +330,9 @@ export const ButtonDefinition: { readonly dataAttribute: true; readonly default: 'primary'; }; + readonly destructive: { + readonly dataAttribute: true; + }; readonly loading: { readonly dataAttribute: true; }; @@ -445,6 +448,7 @@ export interface ButtonLinkProps export type ButtonOwnProps = LeafSurfaceProps & { size?: Responsive<'small' | 'medium'>; variant?: Responsive<'primary' | 'secondary' | 'tertiary'>; + destructive?: boolean; iconStart?: ReactElement; iconEnd?: ReactElement; loading?: boolean; diff --git a/packages/ui/src/components/Button/Button.module.css b/packages/ui/src/components/Button/Button.module.css index fd57682406..740e18f59e 100644 --- a/packages/ui/src/components/Button/Button.module.css +++ b/packages/ui/src/components/Button/Button.module.css @@ -79,6 +79,39 @@ } } + .bui-Button[data-variant='primary'][data-destructive='true'] { + /* Custom properties matching future token names (without bui- prefix) */ + --bg-solid-danger: #dc2626; + --bg-solid-danger-hover: #b91c1c; + --bg-solid-danger-pressed: #991b1b; + --bg-solid-danger-disabled: #fca5a5; + --fg-solid-danger: var(--bui-white); + + [data-theme-mode='dark'] & { + --bg-solid-danger: #ef4444; + --bg-solid-danger-hover: #dc2626; + --bg-solid-danger-pressed: #b91c1c; + --bg-solid-danger-disabled: #7f1d1d; + } + + --bg: var(--bg-solid-danger); + --bg-hover: var(--bg-solid-danger-hover); + --bg-active: var(--bg-solid-danger-pressed); + --fg: var(--fg-solid-danger); + + &[data-disabled='true'], + &[data-loading='true'] { + --bg: var(--bg-solid-danger-disabled); + --bg-hover: var(--bg-solid-danger-disabled); + --bg-active: var(--bg-solid-danger-disabled); + } + + &:focus-visible { + outline: 2px solid var(--bui-border-danger); + outline-offset: 2px; + } + } + .bui-Button[data-variant='secondary'] { --bg: var(--bui-bg-neutral-on-surface-0); --bg-hover: var(--bui-bg-neutral-on-surface-0-hover); @@ -117,6 +150,33 @@ } } + .bui-Button[data-variant='secondary'][data-destructive='true'] { + /* Custom properties for hover/active states (no tokens exist yet) */ + --bg-danger-hover: #fecaca; + --bg-danger-pressed: #fca5a5; + + [data-theme-mode='dark'] & { + --bg-danger-hover: #450a0a; + --bg-danger-pressed: #7f1d1d; + } + + --bg: var(--bui-bg-danger); + --bg-hover: var(--bg-danger-hover); + --bg-active: var(--bg-danger-pressed); + --fg: var(--bui-fg-danger); + + &[data-disabled='true'], + &[data-loading='true'] { + --bg-hover: var(--bg); + --bg-active: var(--bg); + --fg: var(--bui-fg-disabled); + } + + &:focus-visible { + box-shadow: inset 0 0 0 2px var(--bui-border-danger); + } + } + .bui-Button[data-variant='tertiary'] { --bg-hover: var(--bui-bg-neutral-on-surface-0-hover); --bg-active: var(--bui-bg-neutral-on-surface-0-pressed); @@ -151,6 +211,32 @@ } } + .bui-Button[data-variant='tertiary'][data-destructive='true'] { + /* Custom properties for hover/active states (no tokens exist yet) */ + --bg-danger-hover: #fecaca; + --bg-danger-pressed: #fca5a5; + + [data-theme-mode='dark'] & { + --bg-danger-hover: #450a0a; + --bg-danger-pressed: #7f1d1d; + } + + --bg-hover: var(--bui-bg-danger); + --bg-active: var(--bg-danger-pressed); + --fg: var(--bui-fg-danger); + + &[data-disabled='true'], + &[data-loading='true'] { + --bg-hover: var(--bg); + --bg-active: var(--bg); + --fg: var(--bui-fg-disabled); + } + + &:focus-visible { + box-shadow: inset 0 0 0 2px var(--bui-border-danger); + } + } + .bui-Button[data-size='small'] { font-size: var(--bui-font-size-3); padding: 0 var(--bui-space-2); diff --git a/packages/ui/src/components/Button/Button.stories.tsx b/packages/ui/src/components/Button/Button.stories.tsx index 22d21b08f9..8d5a67c9fe 100644 --- a/packages/ui/src/components/Button/Button.stories.tsx +++ b/packages/ui/src/components/Button/Button.stories.tsx @@ -31,7 +31,10 @@ const meta = preview.meta({ }, variant: { control: 'select', - options: ['primary', 'secondary'], + options: ['primary', 'secondary', 'tertiary'], + }, + destructive: { + control: 'boolean', }, }, }); @@ -67,6 +70,15 @@ export const Variants = meta.story({ + + + @@ -81,6 +93,15 @@ export const Variants = meta.story({ + + + @@ -95,6 +116,15 @@ export const Variants = meta.story({ + + + @@ -109,6 +139,15 @@ export const Variants = meta.story({ + + + @@ -123,6 +162,98 @@ export const Variants = meta.story({ + + + + + + + ), +}); + +export const Destructive = meta.story({ + render: () => ( + + + Primary Destructive + + + + + + + + + Secondary Destructive + + + + + + + + + Tertiary Destructive + + + + + + + + + On Surface 1 + + + + + + + + Sizes + + + @@ -181,16 +312,29 @@ export const FullWidth = meta.story({ export const Disabled = meta.story({ render: () => ( - - - - + + + + + + + + + + + ), }); @@ -270,6 +414,24 @@ export const LoadingVariants = meta.story({ + Primary Destructive + + + + + + Loading vs Disabled