diff --git a/.changeset/angry-candies-draw.md b/.changeset/angry-candies-draw.md new file mode 100644 index 0000000000..fdf4d31994 --- /dev/null +++ b/.changeset/angry-candies-draw.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +The `source` property of `AppNodeSpec` has been renamed to `plugin`. The old property has been deprecated and will be removed in a future release. diff --git a/.changeset/clean-otters-allow.md b/.changeset/clean-otters-allow.md new file mode 100644 index 0000000000..5e0aeb598f --- /dev/null +++ b/.changeset/clean-otters-allow.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +fix: enable `lazyCompilation` and `refreshOptions` for rspack diff --git a/.changeset/cool-groups-fail.md b/.changeset/cool-groups-fail.md new file mode 100644 index 0000000000..459358676b --- /dev/null +++ b/.changeset/cool-groups-fail.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-defaults': patch +--- + +`GithubUrlReader`'s search detects glob-patterns supported by `minimatch`, instead of just detecting +`*` and `?` characters. + +For example, this allows to search for patterns like `{C,c}atalog-info.yaml`. diff --git a/.changeset/create-app-1747144256.md b/.changeset/create-app-1747144256.md new file mode 100644 index 0000000000..b50d431d4b --- /dev/null +++ b/.changeset/create-app-1747144256.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Bumped create-app version. diff --git a/.changeset/dry-shirts-film.md b/.changeset/dry-shirts-film.md new file mode 100644 index 0000000000..35ada3969a --- /dev/null +++ b/.changeset/dry-shirts-film.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Added the Catalog presentation API to the HomePageRecentlyVisited and HomePageTopVisited components diff --git a/.changeset/eleven-beans-relax.md b/.changeset/eleven-beans-relax.md new file mode 100644 index 0000000000..fd01394d2d --- /dev/null +++ b/.changeset/eleven-beans-relax.md @@ -0,0 +1,9 @@ +--- +'@backstage/release-manifests': patch +--- + +This expands the configurability of `release-manifests` to pave the road for more configuration options in the `cli`. + +Specifically it allows the specification of mirrored, proxied, or air-gapped hosts when upgrading across releases when +working in restricted or heavily governed development environments (common in large enterprises and government +entities). diff --git a/.changeset/every-bottles-grow.md b/.changeset/every-bottles-grow.md new file mode 100644 index 0000000000..a61a7b42b9 --- /dev/null +++ b/.changeset/every-bottles-grow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +"Added a note clarifying that `entity-fetch` audit events are not visible by default in the logs and are only displayed when the log severity level is adjusted." diff --git a/.changeset/evil-lies-punch.md b/.changeset/evil-lies-punch.md new file mode 100644 index 0000000000..e878c65731 --- /dev/null +++ b/.changeset/evil-lies-punch.md @@ -0,0 +1,6 @@ +--- +'@backstage/frontend-app-api': patch +'@backstage/core-compat-api': patch +--- + +Updates to use the new `plugin` property of `AppNodeSpec`. diff --git a/.changeset/fifty-trains-bow.md b/.changeset/fifty-trains-bow.md new file mode 100644 index 0000000000..37d05c177e --- /dev/null +++ b/.changeset/fifty-trains-bow.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Add missing modules to the Backstage CLI alpha entrypoint. diff --git a/.changeset/fluffy-lands-stand.md b/.changeset/fluffy-lands-stand.md new file mode 100644 index 0000000000..8b6510652e --- /dev/null +++ b/.changeset/fluffy-lands-stand.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Add a warning for React 17 deprecation that triggers when frontend packages and plugins start. diff --git a/.changeset/four-snails-argue.md b/.changeset/four-snails-argue.md new file mode 100644 index 0000000000..905e52a559 --- /dev/null +++ b/.changeset/four-snails-argue.md @@ -0,0 +1,26 @@ +--- +'@backstage/integration': minor +--- + +Added support for federated credentials using managed identities in the Azure DevOps integration. Federated credentials are only available for Azure DevOps organizations that have been configured to use Entra ID for authentication. + +```diff +integrations: + azure: + - host: dev.azure.com + credentials: ++ - clientId: ${APP_REGISTRATION_CLIENT_ID} ++ managedIdentityClientId: system-assigned ++ tenantId: ${AZURE_TENANT_ID} +``` + +This also adds support for automatically using the system-assigned managed identity of an Azure resource by specifying `system-assigned` as the client ID of the managed identity. + +```diff +integrations: + azure: + - host: dev.azure.com + credentials: +- - clientId: ${AZURE_CLIENT_ID} ++ - clientId: system-assigned +``` diff --git a/.changeset/grumpy-phones-kiss.md b/.changeset/grumpy-phones-kiss.md new file mode 100644 index 0000000000..96b6ff05d9 --- /dev/null +++ b/.changeset/grumpy-phones-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': patch +--- + +Fixed an issue where the `search.elasticsearch.queryOptions` config were not picked up by the `ElasticSearchSearchEngine`. diff --git a/.changeset/honest-states-repeat.md b/.changeset/honest-states-repeat.md new file mode 100644 index 0000000000..06b459b673 --- /dev/null +++ b/.changeset/honest-states-repeat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Added optional title prop to `customHomePageGrid` diff --git a/.changeset/honest-teams-shave.md b/.changeset/honest-teams-shave.md new file mode 100644 index 0000000000..6afbf37039 --- /dev/null +++ b/.changeset/honest-teams-shave.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-home-react': patch +'@backstage/plugin-home': patch +--- + +Export ContentModal from `@backstage/plugin-home-react` so people can use this in other scenarios. +Renamed `CatalogReactComponentsNameToClassKey` to `PluginHomeComponentsNameToClassKey` in `overridableComponents.ts` + +Made QuickStartCard `docsLinkTitle` prop more flexible to allow for any React.JSX.Element instead of just a string. +Added QuickStartCard prop `additionalContent` which can eventually replace the prop `video`. diff --git a/.changeset/long-ends-shop.md b/.changeset/long-ends-shop.md new file mode 100644 index 0000000000..f57c3ccdbd --- /dev/null +++ b/.changeset/long-ends-shop.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new Tabs component to Canon diff --git a/.changeset/moody-women-itch.md b/.changeset/moody-women-itch.md new file mode 100644 index 0000000000..f68fddb848 --- /dev/null +++ b/.changeset/moody-women-itch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +--- + +Enable the ability to configure the endpoint for the SES connection used in the notifications email module. This enables the configuration of alternate endpoints as required, for example for local testing or alternative stacks. diff --git a/.changeset/ninety-donkeys-shop.md b/.changeset/ninety-donkeys-shop.md new file mode 100644 index 0000000000..53e78afc6a --- /dev/null +++ b/.changeset/ninety-donkeys-shop.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +Fixed an issue causing `OAuthRequestDialog` to re-render on mount. diff --git a/.changeset/old-parents-cut.md b/.changeset/old-parents-cut.md new file mode 100644 index 0000000000..d2fdaac564 --- /dev/null +++ b/.changeset/old-parents-cut.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github': patch +--- + +Added filter to include archived repositories diff --git a/.changeset/orange-banks-deny.md b/.changeset/orange-banks-deny.md new file mode 100644 index 0000000000..3719206865 --- /dev/null +++ b/.changeset/orange-banks-deny.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitea': minor +--- + +add new gitea provider module diff --git a/.changeset/orange-women-fry.md b/.changeset/orange-women-fry.md new file mode 100644 index 0000000000..493a1aed57 --- /dev/null +++ b/.changeset/orange-women-fry.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-catalog': patch +--- + +A new `filter` parameter has been added to `EntityContextMenuItemBlueprint` to make it easier to configure which entities a menu item should appear for. The `filter` parameter is a function which accepts an entity and returns a boolean. diff --git a/.changeset/polite-shoes-allow.md b/.changeset/polite-shoes-allow.md new file mode 100644 index 0000000000..b7e06bce6f --- /dev/null +++ b/.changeset/polite-shoes-allow.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Added the ability to ignore unknown extension config by passing `{ flags: { allowUnknownExtensionConfig: true } }` to `createSpecializedApp`. diff --git a/.changeset/pre.json b/.changeset/pre.json index 51d53067bd..c2037b7c8a 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -1,5 +1,5 @@ { - "mode": "pre", + "mode": "exit", "tag": "next", "initialVersions": { "example-app": "0.2.108", @@ -198,51 +198,65 @@ "@backstage/plugin-user-settings": "0.8.21", "@backstage/plugin-user-settings-backend": "0.3.1", "@backstage/plugin-user-settings-common": "0.0.1", - "@backstage/plugin-events-backend-module-google-pubsub": "0.0.0" + "@backstage/plugin-events-backend-module-google-pubsub": "0.0.0", + "@backstage/plugin-catalog-backend-module-gitea": "0.0.0" }, "changesets": [ "angry-sites-fold", "beige-kiwis-flow", "brave-donuts-sink", "brave-eggs-mate", + "brave-pandas-beam", "brave-toes-switch", "breezy-hotels-deny", "bright-moles-sort", "bumpy-showers-design", "busy-badgers-hang", "calm-toys-occur", + "chatty-months-grow", "chatty-showers-cheat", "chilly-trams-cheer", "chubby-cougars-run", "chubby-needles-vanish", + "clean-otters-allow", "cold-humans-check", "common-goats-raise", "cool-bikes-push", "cool-cities-grab", "cool-colts-float", + "cool-groups-fail", "cool-knives-design", "crazy-chefs-sin", "create-app-1745325336", "create-app-1745936753", + "create-app-1747144256", "cruel-lights-sip", "cyan-pots-appear", "deep-ties-move", "dirty-grapes-vanish", "dry-carpets-hope", + "dry-shirts-film", "dull-doodles-trade", "early-colts-accept", "early-dryers-teach", "eight-toys-feel", + "eleven-beans-relax", "eleven-ghosts-strive", + "every-bottles-grow", "famous-cities-stand", "fancy-frogs-like", "floppy-days-tell", "four-peaches-talk", + "four-snails-argue", "fruity-bags-flow", + "funny-papayas-tell", "good-islands-drive", "green-trainers-float", + "grumpy-phones-kiss", "heavy-baths-rule", "heavy-onions-swim", + "honest-states-repeat", + "honest-teams-shave", "huge-olives-do", "icy-mugs-glow", "khaki-grapes-sink", @@ -252,18 +266,26 @@ "lovely-cats-take", "mean-parents-build", "mighty-carrots-decide", + "moody-women-itch", "neat-glasses-occur", "neat-glasses-occured", "new-hands-scream", "nice-vans-vanish", + "ninety-donkeys-shop", "old-crews-serve", + "old-parents-cut", "open-ghosts-fix", "open-lands-shop", + "orange-banks-deny", + "polite-shoes-allow", "pretty-corners-speak", "pretty-seas-hug", + "proud-women-hammer", "public-socks-agree", + "puny-rice-sneeze", "real-rings-smoke", "real-sheep-chew", + "renovate-29506de", "renovate-4fc113d", "renovate-68baea0", "renovate-e32145b", @@ -280,21 +302,32 @@ "slimy-peas-post", "slow-drinks-enjoy", "small-eggs-develop", + "soft-ravens-build", + "spicy-camels-bet", "spicy-steaks-swim", "spotty-doors-design", "stale-symbols-joke", + "sweet-papayas-slide", + "tall-suits-share-auth-backend", + "tall-suits-share-auth-node", + "tall-suits-share", "tame-areas-behave", "ten-spies-explode", "ten-tables-build", "thick-hotels-rhyme", + "tidy-paths-count", "true-breads-rhyme", + "true-trains-tie", "twenty-kiwis-punch", + "violet-breads-obey", "warm-cases-bathe", "warm-llamas-return", + "wicked-clubs-dream", "wicked-dingos-stand", "wise-cobras-sink", "wise-pillows-smile", "yellow-beans-eat", - "yellow-cows-tickle" + "yellow-cows-tickle", + "young-dancers-shave" ] } diff --git a/.changeset/proud-women-hammer.md b/.changeset/proud-women-hammer.md new file mode 100644 index 0000000000..030fc5bf7b --- /dev/null +++ b/.changeset/proud-women-hammer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-node': patch +--- + +trim leading and trailing slashes from parseRepoUrl query parameters diff --git a/.changeset/puny-garlics-bathe.md b/.changeset/puny-garlics-bathe.md new file mode 100644 index 0000000000..97071ac2b7 --- /dev/null +++ b/.changeset/puny-garlics-bathe.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +We are modifying the way we treat custom render using 'useRender()' under the hood from BaseUI. diff --git a/.changeset/puny-rice-sneeze.md b/.changeset/puny-rice-sneeze.md new file mode 100644 index 0000000000..1967b68dab --- /dev/null +++ b/.changeset/puny-rice-sneeze.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Export root page route from the home plugin to enable adding links/nav to it from outside the plugin diff --git a/.changeset/renovate-29506de.md b/.changeset/renovate-29506de.md new file mode 100644 index 0000000000..1260f3b453 --- /dev/null +++ b/.changeset/renovate-29506de.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Updated dependency `typedoc` to `^0.28.0`. diff --git a/.changeset/shaggy-gifts-beam.md b/.changeset/shaggy-gifts-beam.md new file mode 100644 index 0000000000..f2957fc2c3 --- /dev/null +++ b/.changeset/shaggy-gifts-beam.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-common': patch +--- + +Fixed an issue causing `PermissionClient` to throw an error when authorizing basic permissions with the `permission.EXPERIMENTAL_enableBatchedRequests` config enabled. diff --git a/.changeset/spotty-papers-call.md b/.changeset/spotty-papers-call.md new file mode 100644 index 0000000000..3e39e8e1f8 --- /dev/null +++ b/.changeset/spotty-papers-call.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +The icon prop in TextField now accept a ReactNode instead of an icon name. We also updated the icon sizes for each input sizes. diff --git a/.changeset/tall-suits-share-auth-backend.md b/.changeset/tall-suits-share-auth-backend.md new file mode 100644 index 0000000000..3f5230c4e3 --- /dev/null +++ b/.changeset/tall-suits-share-auth-backend.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Added support for the new `dangerousEntityRefFallback` option for `signInWithCatalogUser` in `AuthResolverContext`. diff --git a/.changeset/tall-suits-share-auth-node.md b/.changeset/tall-suits-share-auth-node.md new file mode 100644 index 0000000000..cccd3537f7 --- /dev/null +++ b/.changeset/tall-suits-share-auth-node.md @@ -0,0 +1,38 @@ +--- +'@backstage/plugin-auth-node': patch +--- + +Added a new `dangerousEntityRefFallback` option to the `signInWithCatalogUser` method in `AuthResolverContext`. The option will cause the provided entity reference to be used as a fallback in case the user is not found in the catalog. It is up to the caller to provide the fallback entity reference. + +Auth providers that include pre-defined sign-in resolvers are encouraged to define a flag named `dangerouslyAllowSignInWithoutUserInCatalog` in their config, which in turn enables use of the `dangerousEntityRefFallback` option. For example: + +```ts +export const usernameMatchingUserEntityName = createSignInResolverFactory({ + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { + return async ( + info: SignInInfo>, + ctx, + ) => { + const { username } = info.result.fullProfile; + if (!username) { + throw new Error('User profile does not contain a username'); + } + + return ctx.signInWithCatalogUser( + { entityRef: { name: username } }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: username } } + : undefined, + }, + ); + }; + }, +}); +``` diff --git a/.changeset/tall-suits-share.md b/.changeset/tall-suits-share.md new file mode 100644 index 0000000000..05b74bc277 --- /dev/null +++ b/.changeset/tall-suits-share.md @@ -0,0 +1,21 @@ +--- +'@backstage/plugin-auth-backend-module-cloudflare-access-provider': patch +'@backstage/plugin-auth-backend-module-bitbucket-server-provider': patch +'@backstage/plugin-auth-backend-module-azure-easyauth-provider': patch +'@backstage/plugin-auth-backend-module-oauth2-proxy-provider': patch +'@backstage/plugin-auth-backend-module-vmware-cloud-provider': patch +'@backstage/plugin-auth-backend-module-atlassian-provider': patch +'@backstage/plugin-auth-backend-module-bitbucket-provider': patch +'@backstage/plugin-auth-backend-module-microsoft-provider': patch +'@backstage/plugin-auth-backend-module-onelogin-provider': patch +'@backstage/plugin-auth-backend-module-aws-alb-provider': patch +'@backstage/plugin-auth-backend-module-gcp-iap-provider': patch +'@backstage/plugin-auth-backend-module-github-provider': patch +'@backstage/plugin-auth-backend-module-gitlab-provider': patch +'@backstage/plugin-auth-backend-module-google-provider': patch +'@backstage/plugin-auth-backend-module-oauth2-provider': patch +'@backstage/plugin-auth-backend-module-oidc-provider': patch +'@backstage/plugin-auth-backend-module-okta-provider': patch +--- + +Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. diff --git a/.changeset/tidy-paths-count.md b/.changeset/tidy-paths-count.md new file mode 100644 index 0000000000..190df58043 --- /dev/null +++ b/.changeset/tidy-paths-count.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +docs: Update default for `preventCollision` prop diff --git a/.changeset/upset-papayas-flash.md b/.changeset/upset-papayas-flash.md new file mode 100644 index 0000000000..fae7347e0d --- /dev/null +++ b/.changeset/upset-papayas-flash.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Update Menu component in Canon to make the UI more condensed. We are also adding a new Combobox option for nested navigation. diff --git a/.changeset/violet-breads-obey.md b/.changeset/violet-breads-obey.md new file mode 100644 index 0000000000..f892e91ee2 --- /dev/null +++ b/.changeset/violet-breads-obey.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Migrate custom fields to new schema factory function; +standardize field descriptions to prefer ui:description and present consistently, +utilizing ScaffolderField component where possible. diff --git a/.changeset/witty-weeks-boil.md b/.changeset/witty-weeks-boil.md new file mode 100644 index 0000000000..0e49315962 --- /dev/null +++ b/.changeset/witty-weeks-boil.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new truncate prop to Text and Heading components in Canon. diff --git a/.changeset/young-dancers-shave.md b/.changeset/young-dancers-shave.md new file mode 100644 index 0000000000..f7cdc3f3c2 --- /dev/null +++ b/.changeset/young-dancers-shave.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +--- + +Generating new tokens on each Scaffolder Task Retry diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 41f409a651..2685926549 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -25,6 +25,7 @@ yarn.lock @backstage/maintainers @backst /docs/tooling @backstage/tooling-maintainers /microsite @backstage/documentation-maintainers /microsite/data/plugins @backstage/maintainers +/microsite/static @backstage/maintainers @backstage/documentation-maintainers /packages @backstage/framework-maintainers /packages/backend-openapi-utils @backstage/maintainers @backstage/reviewers @backstage/openapi-tooling-maintainers /packages/canon @backstage/design-system-maintainers @@ -51,6 +52,7 @@ yarn.lock @backstage/maintainers @backst /plugins/catalog-backend-module-aws @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @pjungermann /plugins/catalog-backend-module-bitbucket-cloud @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @pjungermann /plugins/catalog-backend-module-backstage-openapi @backstage/maintainers @backstage/reviewers @backstage/openapi-tooling-maintainers +/plugins/catalog-backend-module-gitea @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers /plugins/catalog-backend-module-msgraph @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @pjungermann /plugins/catalog-backend-module-puppetdb @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers /plugins/catalog-graph @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @backstage/sda-se-reviewers diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index 0ca8986140..36f9335b1f 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -304,6 +304,7 @@ oidc Okta Olausson Oldsberg +onboarded onboarding Onboarding onelogin diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 4a33b2241e..42dadb5a57 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -24,13 +24,10 @@ jobs: fetch-tags: true token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} - - name: Fetch tags # See https://github.com/actions/checkout/issues/2041 - run: git fetch --tags - - name: Install Dependencies run: yarn --immutable - name: Create Release Pull Request - uses: backstage/changesets-action@291bfc1f76d1dcfbf967f5810dc0423592eae09a # v2.3.1 + uses: backstage/changesets-action@a39baf18913e669734ffb00c2fd9900472cfa240 # v2.3.2 with: # Calls out to `changeset version`, but also runs prettier version: yarn release diff --git a/.gitignore b/.gitignore index 25280b40b0..fa4184ecf2 100644 --- a/.gitignore +++ b/.gitignore @@ -183,4 +183,4 @@ knip.json # Typedocs temporary files type-docs docs.json -tsconfig.typedoc.tmp.json \ No newline at end of file +tsconfig.typedoc.tmp.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6a91fc4196..9413206197 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -388,7 +388,7 @@ Once you've submitted a Pull Request (PR) the various bots will come out and do - adding labels to help make reviewing PRs easier - checking for missing changesets or confirming them - checking for commits for their DCO (Developer Certificate of Origin) -- kick of the various CI builds +- kick off the various CI builds Once these steps are completed, it's just a matter of being patient. As the reviewers have time, they will begin reviewing your PR. When the review process begins, there may be a few layers to this, but the general rule is that you need approval from one of the core maintainers and one from the specific area impacted by your PR. You may also have someone from the community review your changes. This can really help speed things up as they may catch some early items making the review for the maintainers simpler. Once you have the two (2) approvals, it's ready to be merged, a task that is also performed by the maintainers. diff --git a/OWNERS.md b/OWNERS.md index b29889fec1..85ffadae42 100644 --- a/OWNERS.md +++ b/OWNERS.md @@ -168,14 +168,15 @@ Team: @backstage/tooling-maintainers Scope: All published Backstage CLI tools in the main `backstage` repository that do not belong to other areas, including `@backstage/cli` and `@backstage/repo-tools`. -| Name | Organization | Team | GitHub | Discord | -| -------------------- | ------------ | ------------- | ----------------------------------------------- | --------------- | -| Patrik Oldsberg | Spotify | Cubic Belugas | [Rugvip](https://github.com/Rugvip) | `Rugvip#0019` | -| Ben Lambert | Spotify | Cubic Belugas | [benjdlambert](https://github.com/benjdlambert) | `blam#2159` | -| Camila Loiola | Spotify | Cubic Belugas | [camilaibs](http://github.com/camilaibs) | `camilal#0226` | -| Fredrik Adelöw | Spotify | Cubic Belugas | [freben](https://github.com/freben) | `freben#3926` | -| Mihai Tabara | Spotify | Cubic Belugas | [MihaiTabara](http://github.com/MihaiTabara) | `mihait#3107` | -| Vincenzo Scamporlino | Spotify | Cubic Belugas | [vinzscam](http://github.com/vinzscam) | `vinzscam#6944` | +| Name | Organization | Team | GitHub | Discord | +| -------------------- | ------------ | ------------- | ----------------------------------------------------- | --------------- | +| Patrik Oldsberg | Spotify | Cubic Belugas | [Rugvip](https://github.com/Rugvip) | `Rugvip#0019` | +| Ben Lambert | Spotify | Cubic Belugas | [benjdlambert](https://github.com/benjdlambert) | `blam#2159` | +| Camila Loiola | Spotify | Cubic Belugas | [camilaibs](http://github.com/camilaibs) | `camilal#0226` | +| Fredrik Adelöw | Spotify | Cubic Belugas | [freben](https://github.com/freben) | `freben#3926` | +| Mihai Tabara | Spotify | Cubic Belugas | [MihaiTabara](http://github.com/MihaiTabara) | `mihait#3107` | +| Vincenzo Scamporlino | Spotify | Cubic Belugas | [vinzscam](http://github.com/vinzscam) | `vinzscam#6944` | +| Aramis Sennyey | DoorDash | | [aramissennyeydd](https://github.com/aramissennyeydd) | `Aramis#7984` | ## Incubating Project Areas diff --git a/canon-docs/src/app/(docs)/components/heading/page.mdx b/canon-docs/src/app/(docs)/components/heading/page.mdx index 10fd60bef1..e09bae9945 100644 --- a/canon-docs/src/app/(docs)/components/heading/page.mdx +++ b/canon-docs/src/app/(docs)/components/heading/page.mdx @@ -62,6 +62,22 @@ appearance of the heading. `} /> +### Truncate + +The `Heading` component has a `truncate` prop that can be used to truncate the +heading. + +} + code={` + A man looks at a painting in a museum and says, “Brothers and sisters I + have none, but that man's father is my father's son.” Who is + in the painting? + `} +/> + ### Responsive You can also use the `variant` prop to change the appearance of the text based diff --git a/canon-docs/src/app/(docs)/components/heading/props.ts b/canon-docs/src/app/(docs)/components/heading/props.ts index a3dd3288ff..6d62d2dee1 100644 --- a/canon-docs/src/app/(docs)/components/heading/props.ts +++ b/canon-docs/src/app/(docs)/components/heading/props.ts @@ -12,6 +12,10 @@ export const headingPropDefs: Record = { values: ['ReactNode'], responsive: false, }, + truncate: { + type: 'boolean', + default: 'false', + }, ...classNamePropDefs, ...stylePropDefs, }; diff --git a/canon-docs/src/app/(docs)/components/menu/page.mdx b/canon-docs/src/app/(docs)/components/menu/page.mdx index 98c4949438..524db26cd1 100644 --- a/canon-docs/src/app/(docs)/components/menu/page.mdx +++ b/canon-docs/src/app/(docs)/components/menu/page.mdx @@ -155,3 +155,48 @@ You can additionally configure how quickly the menu opens on hover using the `de `} /> + +### Nested navigation + +You can nest menus to create a more complex navigation structure. + +} + code={` ( + + )} + /> + + + + Settings + Invite new members + Download app + Log out + + Submenu + + + + Submenu Item 1 + Submenu Item 2 + Submenu Item 3 + + + + + + + `} +/> diff --git a/canon-docs/src/app/(docs)/components/tabs/page.mdx b/canon-docs/src/app/(docs)/components/tabs/page.mdx new file mode 100644 index 0000000000..478608d718 --- /dev/null +++ b/canon-docs/src/app/(docs)/components/tabs/page.mdx @@ -0,0 +1,92 @@ +import { PropsTable } from '@/components/PropsTable'; +import { TabsSnippet } from '@/snippets/stories-snippets'; +import { Snippet } from '@/components/Snippet'; +import { Tabs } from '@/components/Tabs'; +import { CodeBlock } from '@/components/CodeBlock'; +import { + tabsRootPropDefs, + tabsListPropDefs, + tabsTabPropDefs, + tabsPanelPropDefs, +} from './props'; +import { BaseUI } from '@/components/HeadlessBanners/BaseUI'; + +# Tabs + +A component for toggling between related panels on the same page. + +} + code={` + + Tab 1 + Tab 2 + Tab 3 With long title + + Content for Tab 1 + Content for Tab 2 + Content for Tab 3 + `} +/> + + + + Usage + Theming + + + + + Tab 1 + Tab 2 + Tab 3 + + Content for Tab 1 + Content for Tab 2 + Content for Tab 3 + +`} + /> + + + We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with + your brand. For additional flexibility, you can use the provided class names for each element listed below. + - `canon-TabsRoot` + - `canon-TabsList` + - `canon-TabsTab` + - `canon-TabsPanel` + - `canon-TabsIndicator` + + + +## API reference + + + +### Tabs.Root + +Groups the tabs and the corresponding panels. Renders a `
` element. + + + +### Tabs.List + +Groups the individual tab buttons. Renders a `
` element. + + + +### Tabs.Tab + +An individual interactive tab button that toggles the corresponding panel. Renders a ` + )} + /> + + + + Settings + Invite new members + Download app + Log out + + Submenu + + + + Submenu Item 1 + Submenu Item 2 + Submenu Item 3 + + + + + + + + + ), + }, +}; + +export const SubmenuCombobox = () => { + const [selectedValues, setSelectedValues] = useState([]); + + return ( + + + {selectedValues.length === 0 + ? 'Which is your favorite fruit?' + : `Yum, ${selectedValues[0]} is delicious!`} + + + ( + + )} + /> + + + + Regular Item + + Fruits + + + + + + + + + Another Item + + + + + + ); +}; + +export const SubmenuComboboxMultiselect = () => { + const [selectedValues, setSelectedValues] = useState([]); + + return ( + + + {selectedValues.length === 0 + ? 'Tell us what fruits you like.' + : `${selectedValues.join( + ', ', + )} would make for a great, healthy smoothy!`} + + + ( + + )} + /> + + + + Regular Item + + Fruits + + + + + + + + + Another Item + + + + + + ); +}; diff --git a/packages/canon/src/components/Menu/Menu.styles.css b/packages/canon/src/components/Menu/Menu.styles.css index 403d34294e..07d257f57f 100644 --- a/packages/canon/src/components/Menu/Menu.styles.css +++ b/packages/canon/src/components/Menu/Menu.styles.css @@ -3,16 +3,20 @@ } .canon-MenuPopup { - padding: var(--canon-space-2); + display: flex; + flex-direction: column; border-radius: 0.375rem; background-color: var(--canon-bg-surface-1); border: 1px solid var(--canon-border); color: var(--canon-fg-primary); outline: none; - overflow: hidden; + overflow: auto; transform-origin: var(--transform-origin); - max-width: 340px; + max-width: min(var(--available-width), 340px); + max-height: min(var(--available-height), 500px); transition: transform 150ms, opacity 150ms; + position: relative; + padding-bottom: var(--canon-space-1); &[data-starting-style], &[data-ending-style] { @@ -23,25 +27,61 @@ .canon-MenuItem { outline: 0; - cursor: default; user-select: none; display: flex; + height: 32px; + align-items: center; gap: var(--canon-space-2); color: var(--canon-fg-primary); text-decoration: none; border-radius: var(--canon-radius-2); - padding: var(--canon-space-2) var(--canon-space-2); - padding-right: var(--canon-space-4); + margin-inline: var(--canon-space-1); + padding-inline: var(--canon-space-2); + font-size: var(--canon-font-size-3); cursor: pointer; + flex-shrink: 0; - &:last-child { - border-bottom: none; + &:first-child { + margin-top: var(--canon-space-1); } &[data-highlighted] { - z-index: 0; - position: relative; - background-color: var(--canon-bg-tint-hover); + background-color: var(--canon-gray-3); + } +} + +.canon-MenuSubmenuTrigger { + outline: 0; + user-select: none; + display: flex; + height: 32px; + align-items: center; + justify-content: space-between; + gap: var(--canon-space-2); + color: var(--canon-fg-primary); + text-decoration: none; + border-radius: var(--canon-radius-2); + margin-inline: var(--canon-space-1); + padding-inline: var(--canon-space-2); + font-size: var(--canon-font-size-3); + cursor: pointer; + flex-shrink: 0; + + & .canon-Icon { + color: var(--canon-fg-secondary); + } + + &:first-child { + margin-top: var(--canon-space-1); + } + + &[data-popup-open], + &[data-highlighted] { + background-color: var(--canon-gray-3); + + .canon-Icon { + color: var(--canon-fg-primary); + } } } @@ -50,3 +90,91 @@ height: 1px; background-color: var(--color-gray-200); } + +.canon-SubmenuComboboxSearch { + padding-inline: var(--canon-space-3); + height: 32px; + border: none; + border-bottom: 1px solid var(--canon-border); + background-color: var(--canon-bg-surface-1); + color: var(--canon-fg-primary); + line-height: 140%; + outline: none; + font-size: var(--canon-font-size-3); + position: sticky; + top: 0; + z-index: 1; + + &::placeholder { + color: var(--canon-fg-secondary); + } + + &:disabled { + opacity: 0.6; + cursor: not-allowed; + } +} + +.canon-SubmenuComboboxItems { + overflow-y: auto; + display: flex; + flex-direction: column; + padding-top: var(--canon-space-2); + outline: none; +} + +.canon-SubmenuComboboxNoResults { + padding-inline: var(--canon-space-3); + padding-top: var(--canon-space-2); + padding-bottom: var(--canon-space-4); + color: var(--canon-fg-secondary); + font-size: var(--canon-font-size-3); +} + +.canon-SubmenuComboboxItem { + outline: 0; + user-select: none; + display: flex; + height: 32px; + align-items: center; + justify-content: space-between; + gap: var(--canon-space-2); + color: var(--canon-fg-primary); + text-decoration: none; + border-radius: var(--canon-radius-2); + margin-inline: var(--canon-space-1); + padding-inline: var(--canon-space-2); + font-size: var(--canon-font-size-3); + cursor: pointer; + user-select: none; + flex-shrink: 0; + + &[data-highlighted] { + background-color: var(--canon-gray-3); + } + + &[data-disabled] { + opacity: 0.5; + cursor: not-allowed; + } +} + +.canon-SubmenuComboboxItemCheckbox { + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + color: var(--canon-fg-primary); + flex-shrink: 0; + border-radius: var(--canon-radius-2); + border: 1px solid var(--canon-border); + background: var(--canon-bg-surface-1); +} + +.canon-SubmenuComboboxItemLabel { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/packages/canon/src/components/Menu/Menu.tsx b/packages/canon/src/components/Menu/Menu.tsx index 6b93d14865..123b01b283 100644 --- a/packages/canon/src/components/Menu/Menu.tsx +++ b/packages/canon/src/components/Menu/Menu.tsx @@ -18,6 +18,8 @@ import { forwardRef } from 'react'; import { Menu as MenuPrimitive } from '@base-ui-components/react/menu'; import clsx from 'clsx'; import { MenuComponent } from './types'; +import { Combobox } from './Combobox'; +import { Icon } from '../Icon'; const MenuTrigger = forwardRef< React.ElementRef, @@ -180,12 +182,15 @@ MenuCheckboxItemIndicator.displayName = const MenuSubmenuTrigger = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( +>(({ className, children, ...props }, ref) => ( + > +
{children}
+ +
)); MenuSubmenuTrigger.displayName = MenuPrimitive.SubmenuTrigger.displayName; @@ -220,4 +225,5 @@ export const Menu: MenuComponent = { CheckboxItemIndicator: MenuCheckboxItemIndicator, SubmenuTrigger: MenuSubmenuTrigger, Separator: MenuSeparator, + Combobox, }; diff --git a/packages/canon/src/components/Menu/types.ts b/packages/canon/src/components/Menu/types.ts index 63f8069f61..9071f9a921 100644 --- a/packages/canon/src/components/Menu/types.ts +++ b/packages/canon/src/components/Menu/types.ts @@ -13,7 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Menu as MenuPrimitive } from '@base-ui-components/react/menu'; +import { + ForwardRefExoticComponent, + RefAttributes, + ComponentProps, +} from 'react'; + +/** @public */ +export type MenuComboboxOption = { + label: string; + value: string; + disabled?: boolean; +}; + +/** @public */ +export interface MenuComboboxProps extends ComponentProps<'div'> { + options: MenuComboboxOption[]; + value?: string[]; + onValueChange?: (value: string[]) => void; + multiselect?: boolean; + closeParentOnEsc?: boolean; +} /** @public */ export type MenuComponent = { @@ -34,4 +56,7 @@ export type MenuComponent = { CheckboxItemIndicator: typeof MenuPrimitive.CheckboxItemIndicator; SubmenuTrigger: typeof MenuPrimitive.SubmenuTrigger; Separator: typeof MenuPrimitive.Separator; + Combobox: ForwardRefExoticComponent< + MenuComboboxProps & RefAttributes + >; }; diff --git a/packages/canon/src/components/Tabs/Tabs.stories.tsx b/packages/canon/src/components/Tabs/Tabs.stories.tsx new file mode 100644 index 0000000000..31945b1e02 --- /dev/null +++ b/packages/canon/src/components/Tabs/Tabs.stories.tsx @@ -0,0 +1,48 @@ +/* + * Copyright 2024 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 { Meta, StoryObj } from '@storybook/react'; +import { Tabs } from './Tabs'; + +const meta = { + title: 'Components/Tabs', + component: Tabs.Root, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const panelStyles = { + padding: 'var(--canon-space-3)', + fontSize: 'var(--canon-font-size-2)', +}; + +export const Default: Story = { + args: { + children: ( + + + Tab 1 + Tab 2 + Tab 3 With long title + + Content for Tab 1 + Content for Tab 2 + Content for Tab 3 + + ), + }, +}; diff --git a/packages/canon/src/components/Tabs/Tabs.styles.css b/packages/canon/src/components/Tabs/Tabs.styles.css new file mode 100644 index 0000000000..84a4d378c1 --- /dev/null +++ b/packages/canon/src/components/Tabs/Tabs.styles.css @@ -0,0 +1,76 @@ +.canon-TabsRoot { + border: 1px solid var(--color-gray-200); + border-radius: 0.375rem; +} + +.canon-TabsList { + display: flex; + position: relative; + z-index: 0; +} + +.canon-TabsTab { + display: flex; + align-items: center; + justify-content: center; + border: 0; + margin: 0; + outline: 0; + background: none; + appearance: none; + color: var(--canon-fg-secondary); + font-family: inherit; + font-size: 0.875rem; + line-height: 1.25rem; + font-weight: 500; + user-select: none; + padding-inline: var(--canon-space-3); + padding-block: 0; + height: 2rem; + font-size: var(--canon-font-size-2); + transition: color 200ms ease-in-out; + cursor: pointer; + + &[data-selected] { + color: var(--canon-fg-primary); + } + + @media (hover: hover) { + &:hover { + color: var(--canon-fg-primary); + } + } + + &:focus-visible { + position: relative; + + &::before { + content: ''; + position: absolute; + inset: 0.25rem 0; + border-radius: 0.25rem; + outline: 1px solid var(--canon-ring); + outline-offset: -1px; + } + } +} + +.canon-TabsIndicator { + position: absolute; + z-index: -1; + left: 0; + bottom: 0; + translate: var(--active-tab-left) -50%; + width: var(--active-tab-width); + height: 1px; + background-color: var(--canon-bg-solid); + transition-property: translate, width; + transition-duration: 200ms; + transition-timing-function: ease-in-out; +} + +.canon-TabsPanel { + &[hidden] { + display: none; + } +} diff --git a/packages/canon/src/components/Tabs/Tabs.tsx b/packages/canon/src/components/Tabs/Tabs.tsx new file mode 100644 index 0000000000..b4f93a5b31 --- /dev/null +++ b/packages/canon/src/components/Tabs/Tabs.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2024 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 { forwardRef } from 'react'; +import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs'; +import type { TabsRootWithoutOrientation } from './types'; +import clsx from 'clsx'; + +const TabsRoot = forwardRef< + React.ElementRef, + TabsRootWithoutOrientation +>(({ className, ...props }, ref) => ( + +)); +TabsRoot.displayName = TabsPrimitive.Root.displayName; + +const TabsList = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + {children} + + +)); +TabsList.displayName = TabsPrimitive.List.displayName; + +const TabsTab = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsTab.displayName = TabsPrimitive.Tab.displayName; + +const TabsPanel = forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +TabsPanel.displayName = TabsPrimitive.Panel.displayName; + +/** @public */ +export const Tabs = { + Root: TabsRoot, + List: TabsList, + Tab: TabsTab, + Panel: TabsPanel, +}; diff --git a/packages/canon/src/components/Tabs/index.ts b/packages/canon/src/components/Tabs/index.ts new file mode 100644 index 0000000000..ba6ca91de7 --- /dev/null +++ b/packages/canon/src/components/Tabs/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2025 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 { Tabs } from './Tabs'; +export type { TabsRootWithoutOrientation } from './types'; diff --git a/packages/canon/src/components/Tabs/types.ts b/packages/canon/src/components/Tabs/types.ts new file mode 100644 index 0000000000..f3b6342761 --- /dev/null +++ b/packages/canon/src/components/Tabs/types.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2025 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 { Tabs } from '@base-ui-components/react/tabs'; + +// We are not supporting the orientation prop, so we need to omit it + +/** @public */ +export interface TabsRootWithoutOrientation + extends Omit< + React.ComponentPropsWithoutRef, + 'orientation' + > {} diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx index 0b0ffd2712..5fe0c12cb5 100644 --- a/packages/canon/src/components/Text/Text.stories.tsx +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -78,6 +78,13 @@ export const AllColors: Story = { ), }; +export const Truncate: Story = { + args: { + ...Default.args, + truncate: true, + }, +}; + export const Responsive: Story = { args: { ...Default.args, @@ -101,6 +108,13 @@ export const WrappedInLink: Story = { ], }; +export const CustomRender: Story = { + args: { + ...Default.args, + render: , + }, +}; + export const Playground: Story = { render: () => ( diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx index 76f5adbb43..7e4dc74720 100644 --- a/packages/canon/src/components/Text/Text.tsx +++ b/packages/canon/src/components/Text/Text.tsx @@ -14,8 +14,9 @@ * limitations under the License. */ -import { forwardRef } from 'react'; +import { forwardRef, useRef } from 'react'; import { useResponsiveValue } from '../../hooks/useResponsiveValue'; +import { useRender } from '@base-ui-components/react/use-render'; import clsx from 'clsx'; import type { TextProps } from './types'; @@ -24,12 +25,12 @@ import type { TextProps } from './types'; export const Text = forwardRef( (props, ref) => { const { - children, variant = 'body', weight = 'regular', color = 'primary', - style, className, + truncate, + render =

, ...restProps } = props; @@ -37,20 +38,22 @@ export const Text = forwardRef( const responsiveVariant = useResponsiveValue(variant); const responsiveWeight = useResponsiveValue(weight); const responsiveColor = useResponsiveValue(color); + const internalRef = useRef(null); - return ( -

- {children} -

- ); + const { renderElement } = useRender({ + render, + props: { + className: clsx('canon-Text', className), + ['data-variant']: responsiveVariant, + ['data-weight']: responsiveWeight, + ['data-color']: responsiveColor, + ['data-truncate']: truncate, + ...restProps, + }, + refs: [ref, internalRef], + }); + + return renderElement(); }, ); diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css index 9f0ed703d9..c0cd781007 100644 --- a/packages/canon/src/components/Text/styles.css +++ b/packages/canon/src/components/Text/styles.css @@ -67,3 +67,9 @@ .canon-Text[data-color='success'] { color: var(--canon-fg-success); } + +.canon-Text[data-truncate] { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/packages/canon/src/components/Text/types.ts b/packages/canon/src/components/Text/types.ts index d4f1615694..f9c3c0aaca 100644 --- a/packages/canon/src/components/Text/types.ts +++ b/packages/canon/src/components/Text/types.ts @@ -14,12 +14,13 @@ * limitations under the License. */ -import type { CSSProperties, ReactNode } from 'react'; +import type { CSSProperties } from 'react'; import type { Breakpoint } from '../../types'; +import type { useRender } from '@base-ui-components/react/use-render'; /** @public */ -export interface TextProps { - children: ReactNode; +export interface TextProps + extends Omit, 'color'> { variant?: | 'subtitle' | 'body' @@ -39,6 +40,7 @@ export interface TextProps { 'primary' | 'secondary' | 'danger' | 'warning' | 'success' > >; + truncate?: boolean; className?: string; style?: CSSProperties; } diff --git a/packages/canon/src/components/TextField/TextField.stories.tsx b/packages/canon/src/components/TextField/TextField.stories.tsx index 0615a157a1..7e625dad24 100644 --- a/packages/canon/src/components/TextField/TextField.stories.tsx +++ b/packages/canon/src/components/TextField/TextField.stories.tsx @@ -17,6 +17,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TextField } from './TextField'; import { Flex } from '../Flex'; +import { Icon } from '../Icon'; const meta = { title: 'Components/TextField', @@ -79,8 +80,8 @@ export const Sizes: Story = { }, render: args => ( - - + } /> + } /> ), }; @@ -114,7 +115,7 @@ export const WithIcon: Story = { args: { ...WithLabel.args, placeholder: 'Search...', - icon: 'search', + icon: , }, }; diff --git a/packages/canon/src/components/TextField/TextField.styles.css b/packages/canon/src/components/TextField/TextField.styles.css index ad3ccc931b..be7ec3379c 100644 --- a/packages/canon/src/components/TextField/TextField.styles.css +++ b/packages/canon/src/components/TextField/TextField.styles.css @@ -59,15 +59,24 @@ background-color: var(--canon-bg-surface-1); } -.canon-TextFieldInputIcon { - display: block; - padding-right: var(--canon-space-1); - width: 1.5rem; - height: 1.5rem; +.canon-TextFieldIcon { + margin-right: var(--canon-space-1); color: var(--canon-fg-primary); flex-shrink: 0; } +.canon-TextFieldIcon[data-size='small'], +.canon-TextFieldIcon[data-size='small'] svg { + width: 1rem; + height: 1rem; +} + +.canon-TextFieldIcon[data-size='medium'], +.canon-TextFieldIcon[data-size='medium'] svg { + width: 1.25rem; + height: 1.25rem; +} + .canon-TextFieldInput { border: none; background: none; diff --git a/packages/canon/src/components/TextField/TextField.tsx b/packages/canon/src/components/TextField/TextField.tsx index bb32033da4..e4cf43a711 100644 --- a/packages/canon/src/components/TextField/TextField.tsx +++ b/packages/canon/src/components/TextField/TextField.tsx @@ -27,7 +27,7 @@ export const TextField = forwardRef( (props: TextFieldProps, ref) => { const { className, - size = 'medium', + size = 'small', label, description, error, @@ -61,7 +61,15 @@ export const TextField = forwardRef( )}
- {icon && } + {icon && ( + + )} , 'size'> { @@ -54,10 +53,10 @@ export interface TextFieldProps /** * An icon to render before the input */ - icon?: IconNames; + icon?: ReactNode; /** * Handler to call when the clear button is pressed */ - onClear?: React.MouseEventHandler; + onClear?: MouseEventHandler; } diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 53c2882302..7cee946e3b 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -30,6 +30,7 @@ @import '../components/Table/TableCellText/TableCellText.styles.css'; @import '../components/Table/TableCellLink/TableCellLink.styles.css'; @import '../components/Table/TableCellProfile/TableCellProfile.styles.css'; +@import '../components/Tabs/Tabs.styles.css'; @import '../components/Text/styles.css'; @import '../components/Heading/styles.css'; @import '../components/IconButton/styles.css'; diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts index 7677a539c7..cab4cc4acb 100644 --- a/packages/canon/src/index.ts +++ b/packages/canon/src/index.ts @@ -40,6 +40,7 @@ export * from './components/Icon'; export * from './components/IconButton'; export * from './components/Checkbox'; export * from './components/Table'; +export * from './components/Tabs'; export * from './components/TextField'; export * from './components/Tooltip'; export * from './components/Menu'; diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index faddf38926..fb537faff4 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/cli +## 0.32.1-next.3 + +### Patch Changes + +- 674def9: fix: enable `lazyCompilation` and `refreshOptions` for rspack +- 19a4e7c: Internal refactor to move things closer to home +- Updated dependencies + - @backstage/release-manifests@0.0.13-next.0 + - @backstage/integration@1.17.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/types@1.2.1 + ## 0.32.1-next.2 ### Patch Changes diff --git a/packages/cli/cli-report.backstage-cli-alpha.md b/packages/cli/cli-report.backstage-cli-alpha.md index 386a642c21..331385f6f6 100644 --- a/packages/cli/cli-report.backstage-cli-alpha.md +++ b/packages/cli/cli-report.backstage-cli-alpha.md @@ -241,8 +241,12 @@ Options: Commands: build + clean help [command] lint + postpack + prepack + start test ``` @@ -260,6 +264,15 @@ Options: -h, --help ``` +### `backstage-cli-alpha package clean` + +``` +Usage: program [options] + +Options: + -h, --help +``` + ### `backstage-cli-alpha package lint` ``` @@ -273,6 +286,40 @@ Options: -h, --help ``` +### `backstage-cli-alpha package postpack` + +``` +Usage: program [options] + +Options: + -h, --help +``` + +### `backstage-cli-alpha package prepack` + +``` +Usage: program [options] + +Options: + -h, --help +``` + +### `backstage-cli-alpha package start` + +``` +Usage: program [options] + +Options: + --check + --config + --inspect [host] + --inspect-brk [host] + --link + --require + --role + -h, --help +``` + ### `backstage-cli-alpha package test` ``` @@ -397,8 +444,12 @@ Options: Commands: build + clean + fix help [command] lint + list-deprecations + start test ``` @@ -414,6 +465,26 @@ Options: -h, --help ``` +### `backstage-cli-alpha repo clean` + +``` +Usage: program [options] + +Options: + -h, --help +``` + +### `backstage-cli-alpha repo fix` + +``` +Usage: program [options] + +Options: + --check + --publish + -h, --help +``` + ### `backstage-cli-alpha repo lint` ``` @@ -430,6 +501,31 @@ Options: -h, --help ``` +### `backstage-cli-alpha repo list-deprecations` + +``` +Usage: program [options] + +Options: + --json + -h, --help +``` + +### `backstage-cli-alpha repo start` + +``` +Usage: program [options] [...packageNameOrPath] + +Options: + --config + --inspect [host] + --inspect-brk [host] + --link + --plugin + --require + -h, --help +``` + ### `backstage-cli-alpha repo test` ``` diff --git a/packages/cli/package.json b/packages/cli/package.json index 616556b5b6..7afa1637b8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.32.1-next.2", + "version": "0.32.1-next.3", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" @@ -180,9 +180,9 @@ "@backstage/plugin-scaffolder-node-test-utils": "workspace:^", "@backstage/test-utils": "workspace:^", "@backstage/theme": "workspace:^", - "@rspack/core": "^1.0.10", - "@rspack/dev-server": "^1.0.9", - "@rspack/plugin-react-refresh": "^1.0.0", + "@rspack/core": "^1.3.9", + "@rspack/dev-server": "^1.1.1", + "@rspack/plugin-react-refresh": "^1.4.2", "@types/cross-spawn": "^6.0.2", "@types/ejs": "^3.1.3", "@types/express": "^4.17.6", @@ -204,7 +204,7 @@ "nodemon": "^3.0.1" }, "peerDependencies": { - "@rspack/core": "^1.0.10", + "@rspack/core": "^1.2.8", "@rspack/dev-server": "^1.0.9", "@rspack/plugin-react-refresh": "^1.0.0" }, diff --git a/packages/cli/src/alpha.ts b/packages/cli/src/alpha.ts index 3324ed8d75..a29594e135 100644 --- a/packages/cli/src/alpha.ts +++ b/packages/cli/src/alpha.ts @@ -25,13 +25,15 @@ import chalk from 'chalk'; ), ); const initializer = new CliInitializer(); - initializer.add(import('./modules/new/alpha')); + initializer.add(import('./modules/build/alpha')); + initializer.add(import('./modules/config/alpha')); initializer.add(import('./modules/create-github-app/alpha')); initializer.add(import('./modules/info/alpha')); - initializer.add(import('./modules/config/alpha')); - initializer.add(import('./modules/build/alpha')); - initializer.add(import('./modules/migrate/alpha')); - initializer.add(import('./modules/test/alpha')); initializer.add(import('./modules/lint/alpha')); + initializer.add(import('./modules/maintenance/alpha')); + initializer.add(import('./modules/migrate/alpha')); + initializer.add(import('./modules/new/alpha')); + initializer.add(import('./modules/start/alpha')); + initializer.add(import('./modules/test/alpha')); await initializer.run(); })(); diff --git a/packages/cli/src/modules/build/lib/bundler/config.ts b/packages/cli/src/modules/build/lib/bundler/config.ts index 6b7b6543ae..d7c824e3fa 100644 --- a/packages/cli/src/modules/build/lib/bundler/config.ts +++ b/packages/cli/src/modules/build/lib/bundler/config.ts @@ -142,19 +142,19 @@ export async function createConfig( options.moduleFederation, ); + const refreshOptions = { + overlay: { + sockProtocol: 'ws', + sockHost: host, + sockPort: port, + }, + } as const; + if (rspack) { const RspackReactRefreshPlugin = require('@rspack/plugin-react-refresh'); - plugins.push(new RspackReactRefreshPlugin()); + plugins.push(new RspackReactRefreshPlugin(refreshOptions)); } else { - plugins.push( - new ReactRefreshPlugin({ - overlay: { - sockProtocol: 'ws', - sockHost: host, - sockPort: port, - }, - }), - ); + plugins.push(new ReactRefreshPlugin(refreshOptions)); } } @@ -466,7 +466,7 @@ export async function createConfig( : {}), }, experiments: { - lazyCompilation: !rspack && yn(process.env.EXPERIMENTAL_LAZY_COMPILATION), + lazyCompilation: yn(process.env.EXPERIMENTAL_LAZY_COMPILATION), ...(rspack && { // We're still using `style-loader` for custom `insert` option css: false, diff --git a/packages/cli/src/modules/start/commands/package/start/startFrontend.ts b/packages/cli/src/modules/start/commands/package/start/startFrontend.ts index d4b2f6a1cd..aaadf3ed95 100644 --- a/packages/cli/src/modules/start/commands/package/start/startFrontend.ts +++ b/packages/cli/src/modules/start/commands/package/start/startFrontend.ts @@ -22,6 +22,7 @@ import { } from '../../../../build/lib/bundler'; import { paths } from '../../../../../lib/paths'; import { BackstagePackageJson } from '@backstage/cli-node'; +import { hasReactDomClient } from '../../../../build/lib/bundler/hasReactDomClient'; interface StartAppOptions { verifyVersions?: boolean; @@ -40,6 +41,12 @@ export async function startFrontend(options: StartAppOptions) { resolvePath(options.targetDir ?? paths.targetDir, 'package.json'), )) as BackstagePackageJson; + if (!hasReactDomClient()) { + console.warn( + 'React 17 is now deprecated! Please follow the Backstage migration guide to update to React 18: https://backstage.io/docs/tutorials/react18-migration/', + ); + } + const waitForExit = await serveBundle({ entry: options.entry, targetDir: options.targetDir, diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 39a4f5b63b..7e874a1774 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/core-app-api +## 1.17.0-next.1 + +### Minor Changes + +- 1e0230e: Support custom `AuthConnector` for `OAuth2`. + + A user can pass their own `AuthConnector` implementation in `OAuth2` constructor. + In which case the session manager will use that instead of the `DefaultAuthConnector` to interact with the + authentication provider. + + A custom `AuthConnector` may call the authentication provider from the front-end, store and retrieve tokens + in the session storage, for example, and otherwise send custom requests to the authentication provider and + handle its responses. + + Note, that if the custom `AuthConnector` transforms scopes returned from the authentication provider, + the transformation must be the same as `OAuth2CreateOptions#scopeTransform` passed to `OAuth2` constructor. + See creating `DefaultAuthConnector` in `OAuth2#create(...)` for an example. + +### Patch Changes + +- cc119b2: Fixed an issue causing `OAuthRequestDialog` to re-render on mount. +- Updated dependencies + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 1.16.2-next.0 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index be8f0592eb..2eb53d7d87 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-app-api", - "version": "1.16.2-next.0", + "version": "1.17.0-next.1", "description": "Core app API used by Backstage apps", "backstage": { "role": "web-library" diff --git a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts index 193c474361..b5261bd1e2 100644 --- a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts +++ b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/MockOAuthApi.ts @@ -17,11 +17,19 @@ import { OAuthRequestApi, OAuthRequesterOptions, + PendingOAuthRequest, } from '@backstage/core-plugin-api'; import { OAuthRequestManager } from './OAuthRequestManager'; export default class MockOAuthApi implements OAuthRequestApi { private readonly real = new OAuthRequestManager(); + private requests: PendingOAuthRequest[] = []; + + constructor() { + this.authRequest$().subscribe(requests => { + this.requests = requests; + }); + } createAuthRequester(options: OAuthRequesterOptions) { return this.real.createAuthRequester(options); @@ -34,25 +42,12 @@ export default class MockOAuthApi implements OAuthRequestApi { async triggerAll() { await Promise.resolve(); // Wait a tick to allow new requests to get forwarded - return new Promise(resolve => { - const subscription = this.authRequest$().subscribe(requests => { - subscription.unsubscribe(); - Promise.all(requests.map(request => request.trigger())).then(() => - resolve(), - ); - }); - }); + return Promise.all(this.requests.map(request => request.trigger())); } async rejectAll() { await Promise.resolve(); // Wait a tick to allow new requests to get forwarded - return new Promise(resolve => { - const subscription = this.authRequest$().subscribe(requests => { - subscription.unsubscribe(); - requests.map(request => request.reject()); - resolve(); - }); - }); + this.requests.forEach(request => request.reject()); } } diff --git a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts index 4dd6af715e..226ca5eb28 100644 --- a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts +++ b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.test.ts @@ -34,12 +34,12 @@ describe('OAuthRequestManager', () => { expect(reqSpy).toHaveBeenCalledTimes(0); await 'a tick'; - expect(reqSpy).toHaveBeenCalledTimes(2); + expect(reqSpy).toHaveBeenCalledTimes(1); expect(reqSpy).toHaveBeenLastCalledWith([]); const req = requester(new Set(['my-scope'])); - expect(reqSpy).toHaveBeenCalledTimes(3); + expect(reqSpy).toHaveBeenCalledTimes(2); expect(reqSpy).toHaveBeenLastCalledWith([ expect.objectContaining({ reject: expect.any(Function), @@ -51,7 +51,7 @@ describe('OAuthRequestManager', () => { 'not yet', ); - const [request] = reqSpy.mock.calls[2][0]; + const [request] = reqSpy.mock.calls[1][0]; request.trigger(); await expect(req).resolves.toBe('hello'); diff --git a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts index 5d467be7b4..0f7ad0f2c7 100644 --- a/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts +++ b/packages/core-app-api/src/apis/implementations/OAuthRequestApi/OAuthRequestManager.ts @@ -22,7 +22,7 @@ import { } from '@backstage/core-plugin-api'; import { Observable } from '@backstage/types'; import { OAuthPendingRequests, PendingRequest } from './OAuthPendingRequests'; -import { BehaviorSubject } from '../../../lib/subjects'; +import { PublishSubject } from '../../../lib/subjects'; /** * The OAuthRequestManager is an implementation of the OAuthRequestApi. @@ -34,7 +34,7 @@ import { BehaviorSubject } from '../../../lib/subjects'; * @public */ export class OAuthRequestManager implements OAuthRequestApi { - private readonly subject = new BehaviorSubject([]); + private readonly subject = new PublishSubject(); private currentRequests: PendingOAuthRequest[] = []; private handlerCount = 0; diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index e06676f2e3..430117e719 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/core-compat-api +## 0.4.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/version-bridge@1.0.11 + ## 0.4.2-next.2 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 6f20d04c5f..77650e4196 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.4.2-next.2", + "version": "0.4.2-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx b/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx index 151fb21067..0a5dd68aa3 100644 --- a/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx +++ b/packages/core-compat-api/src/compatWrapper/BackwardsCompatProvider.tsx @@ -119,7 +119,7 @@ function LegacyAppContextProvider(props: { children: ReactNode }) { const pluginSet = new Set(); for (const node of tree.nodes.values()) { - const plugin = node.spec.source; + const plugin = node.spec.plugin; if (plugin) { pluginSet.add(toLegacyPlugin(plugin)); } diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 004799767d..14c327f183 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/create-app +## 0.6.2-next.3 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + ## 0.6.2-next.2 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 55432ddc6b..20981dc781 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.6.2-next.2", + "version": "0.6.2-next.3", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 1481726448..d1c08c394e 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/dev-utils +## 1.1.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.17.0-next.1 + - @backstage/app-defaults@1.6.2-next.2 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/theme@0.6.6-next.0 + ## 1.1.10-next.2 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 8e68e50aed..e71af05c9a 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.10-next.2", + "version": "1.1.10-next.3", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 487eb771f1..b760a4d651 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.28-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.6.2-next.3 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + ## 0.2.28-next.2 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 7287232a01..7e37bb477d 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.28-next.2", + "version": "0.2.28-next.3", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 3e119d7b2f..84fb2dbaa1 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/frontend-app-api +## 0.11.2-next.3 + +### Patch Changes + +- 1f04491: Added the ability to ignore unknown extension config by passing `{ flags: { allowUnknownExtensionConfig: true } }` to `createSpecializedApp`. +- Updated dependencies + - @backstage/core-app-api@1.17.0-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.2.2-next.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.11.2-next.2 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index ea086e29ee..6cfbccf639 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-app-api", - "version": "0.11.2-next.2", + "version": "0.11.2-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-app-api/report.api.md b/packages/frontend-app-api/report.api.md index f24d662df4..d2a3ac4d3b 100644 --- a/packages/frontend-app-api/report.api.md +++ b/packages/frontend-app-api/report.api.md @@ -34,6 +34,9 @@ export function createSpecializedApp(options?: { extensionFactoryMiddleware?: | ExtensionFactoryMiddleware | ExtensionFactoryMiddleware[]; + flags?: { + allowUnknownExtensionConfig?: boolean; + }; }): { apis: ApiHolder; tree: AppTree; diff --git a/packages/frontend-app-api/src/routing/RouteTracker.test.tsx b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx index 08a9673afc..5cee727075 100644 --- a/packages/frontend-app-api/src/routing/RouteTracker.test.tsx +++ b/packages/frontend-app-api/src/routing/RouteTracker.test.tsx @@ -49,7 +49,7 @@ describe('RouteTracker', () => { appNode: { spec: { extension: { id: 'home.page.index' }, - source: { id: 'home' }, + plugin: { id: 'home' }, }, } as AppNode, }, @@ -63,7 +63,7 @@ describe('RouteTracker', () => { appNode: { spec: { extension: { id: 'plugin1.page.index' }, - source: { id: 'plugin1' }, + plugin: { id: 'plugin1' }, }, } as AppNode, }, @@ -77,7 +77,7 @@ describe('RouteTracker', () => { appNode: { spec: { extension: { id: 'plugin2.page.index' }, - source: { id: 'plugin2' }, + plugin: { id: 'plugin2' }, }, } as AppNode, }, diff --git a/packages/frontend-app-api/src/routing/RouteTracker.tsx b/packages/frontend-app-api/src/routing/RouteTracker.tsx index 4a218a0900..0068f87d4d 100644 --- a/packages/frontend-app-api/src/routing/RouteTracker.tsx +++ b/packages/frontend-app-api/src/routing/RouteTracker.tsx @@ -63,7 +63,7 @@ const getExtensionContext = ( return acc; }, {}); - const plugin = routeObject.appNode?.spec.source; + const plugin = routeObject.appNode?.spec.plugin; const extension = routeObject.appNode?.spec.extension; return { diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts index 17afbeb4c9..a1abaf1599 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts @@ -124,8 +124,8 @@ export function extractRouteInfoFromAppNode(node: AppNode): { routeParents.set(routeRef, newParentRef); currentObj?.routeRefs.add(routeRef); - if (current.spec.source) { - currentObj?.plugins.add(toLegacyPlugin(current.spec.source)); + if (current.spec.plugin) { + currentObj?.plugins.add(toLegacyPlugin(current.spec.plugin)); } } diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index a000e016be..8801e2070e 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -57,6 +57,7 @@ function makeSpec( disabled: extension.disabled, extension: extension as Extension, source: undefined, + plugin: undefined, ...spec, }; } diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts index c486370563..979c851a47 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts @@ -126,6 +126,7 @@ describe('resolveAppNodeSpecs', () => { extension: makeExt('test/a'), attachTo: { id: 'root', input: 'default' }, source: pluginA, + plugin: pluginA, disabled: false, }, ]); @@ -162,6 +163,7 @@ describe('resolveAppNodeSpecs', () => { id: 'test/a', extension: a, attachTo: { id: 'root', input: 'default' }, + plugin, source: plugin, config: { foo: { bar: 1 } }, disabled: false, @@ -170,6 +172,7 @@ describe('resolveAppNodeSpecs', () => { id: 'test/b', extension: b, attachTo: { id: 'root', input: 'default' }, + plugin, source: plugin, config: { foo: { qux: 3 } }, disabled: false, @@ -305,6 +308,7 @@ describe('resolveAppNodeSpecs', () => { id: 'test/a', extension: expect.objectContaining(aOverride), attachTo: { id: 'other', input: 'default' }, + plugin, source: plugin, disabled: false, }, @@ -312,6 +316,7 @@ describe('resolveAppNodeSpecs', () => { id: 'test/b', extension: expect.objectContaining(bOverride), attachTo: { id: 'other', input: 'default' }, + plugin, source: plugin, disabled: true, }, @@ -319,6 +324,7 @@ describe('resolveAppNodeSpecs', () => { id: 'test/c', extension: expect.objectContaining(cOverride), attachTo: { id: 'root', input: 'default' }, + plugin, source: plugin, disabled: false, }, diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 0abbbb3560..399e6ccb7b 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -33,34 +33,36 @@ export function resolveAppNodeSpecs(options: { builtinExtensions?: Extension[]; parameters?: Array; forbidden?: Set; + allowUnknownExtensionConfig?: boolean; }): AppNodeSpec[] { const { builtinExtensions = [], parameters = [], forbidden = new Set(), features = [], + allowUnknownExtensionConfig = false, } = options; const plugins = features.filter(OpaqueFrontendPlugin.isType); const modules = features.filter(isInternalFrontendModule); - const pluginExtensions = plugins.flatMap(source => { - return OpaqueFrontendPlugin.toInternal(source).extensions.map( + const pluginExtensions = plugins.flatMap(plugin => { + return OpaqueFrontendPlugin.toInternal(plugin).extensions.map( extension => ({ ...extension, - source, + plugin, }), ); }); const moduleExtensions = modules.flatMap(mod => toInternalFrontendModule(mod).extensions.flatMap(extension => { // Modules for plugins that are not installed are ignored - const source = plugins.find(p => p.id === mod.pluginId); - if (!source) { + const plugin = plugins.find(p => p.id === mod.pluginId); + if (!plugin) { return []; } - return [{ ...extension, source }]; + return [{ ...extension, plugin }]; }), ); @@ -68,7 +70,7 @@ export function resolveAppNodeSpecs(options: { if (pluginExtensions.some(({ id }) => forbidden.has(id))) { const pluginsStr = pluginExtensions .filter(({ id }) => forbidden.has(id)) - .map(({ source }) => `'${source.id}'`) + .map(({ plugin }) => `'${plugin.id}'`) .join(', '); const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); throw new Error( @@ -78,7 +80,7 @@ export function resolveAppNodeSpecs(options: { if (moduleExtensions.some(({ id }) => forbidden.has(id))) { const pluginsStr = moduleExtensions .filter(({ id }) => forbidden.has(id)) - .map(({ source }) => `'${source.id}'`) + .map(({ plugin }) => `'${plugin.id}'`) .join(', '); const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); throw new Error( @@ -87,12 +89,13 @@ export function resolveAppNodeSpecs(options: { } const configuredExtensions = [ - ...pluginExtensions.map(({ source, ...extension }) => { + ...pluginExtensions.map(({ plugin, ...extension }) => { const internalExtension = toInternalExtension(extension); return { extension: internalExtension, params: { - source, + plugin, + source: plugin, attachTo: internalExtension.attachTo, disabled: internalExtension.disabled, config: undefined as unknown, @@ -105,6 +108,7 @@ export function resolveAppNodeSpecs(options: { extension: internalExtension, params: { source: undefined, + plugin: undefined, attachTo: internalExtension.attachTo, disabled: internalExtension.disabled, config: undefined as unknown, @@ -131,7 +135,8 @@ export function resolveAppNodeSpecs(options: { configuredExtensions.push({ extension: internalExtension, params: { - source: extension.source, + plugin: extension.plugin, + source: extension.plugin, attachTo: internalExtension.attachTo, disabled: internalExtension.disabled, config: undefined, @@ -202,7 +207,7 @@ export function resolveAppNodeSpecs(options: { existing.params.disabled = Boolean(overrideParam.disabled); } order.set(extensionId, existing); - } else { + } else if (!allowUnknownExtensionConfig) { throw new Error(`Extension ${extensionId} does not exist`); } } @@ -217,6 +222,7 @@ export function resolveAppNodeSpecs(options: { attachTo: param.params.attachTo, extension: param.extension, disabled: param.params.disabled, + plugin: param.params.plugin, source: param.params.source, config: param.params.config, })); diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx index 1a4c7dbc78..5f6692d600 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -208,6 +208,7 @@ export function createSpecializedApp(options?: { extensionFactoryMiddleware?: | ExtensionFactoryMiddleware | ExtensionFactoryMiddleware[]; + flags?: { allowUnknownExtensionConfig?: boolean }; }): { apis: ApiHolder; tree: AppTree } { const config = options?.config ?? new ConfigReader({}, 'empty-config'); const features = deduplicateFeatures(options?.features ?? []); @@ -221,6 +222,7 @@ export function createSpecializedApp(options?: { ], parameters: readAppExtensionsConfig(config), forbidden: new Set(['root']), + allowUnknownExtensionConfig: options?.flags?.allowUnknownExtensionConfig, }), ); diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index df3f24050b..e9f64904c5 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/frontend-defaults +## 0.2.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.11.2-next.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/plugin-app@0.1.9-next.3 + ## 0.2.2-next.2 ### Patch Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index b1b85f3eb1..5525daa3bb 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.2.2-next.2", + "version": "0.2.2-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index cd5d89401d..1c01e0ac83 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -235,6 +235,8 @@ export interface AppNodeSpec { // (undocumented) readonly id: string; // (undocumented) + readonly plugin?: FrontendPlugin; + // @deprecated (undocumented) readonly source?: FrontendPlugin; } diff --git a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts index c9d9896600..0c6dde3cc8 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts @@ -37,6 +37,10 @@ export interface AppNodeSpec { readonly extension: Extension; readonly disabled: boolean; readonly config?: unknown; + readonly plugin?: FrontendPlugin; + /** + * @deprecated Use {@link AppNodeSpec.plugin} instead. + */ readonly source?: FrontendPlugin; } diff --git a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx index 46b3956e0e..a55afeeffd 100644 --- a/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx +++ b/packages/frontend-plugin-api/src/components/ExtensionBoundary.tsx @@ -69,14 +69,14 @@ export function ExtensionBoundary(props: ExtensionBoundaryProps) { node.instance?.getData(coreExtensionData.routePath), ); - const plugin = node.spec.source; + const plugin = node.spec.plugin; const Progress = useComponentRef(coreComponentRefs.progress); const fallback = useComponentRef(coreComponentRefs.errorBoundaryFallback); // Skipping "routeRef" attribute in the new system, the extension "id" should provide more insight const attributes = { extensionId: node.spec.id, - pluginId: node.spec.source?.id ?? 'app', + pluginId: node.spec.plugin?.id ?? 'app', }; return ( diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 3534a15653..fba6e9afb5 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/frontend-test-utils +## 0.3.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.11.2-next.3 + - @backstage/test-utils@1.7.8-next.2 + - @backstage/config@1.3.2 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-app@0.1.9-next.3 + ## 0.3.2-next.2 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index b065490006..833030129f 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-test-utils", - "version": "0.3.2-next.2", + "version": "0.3.2-next.3", "backstage": { "role": "web-library" }, diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index 58fe14bfb1..fb44067948 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/integration-react +## 1.2.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/config@1.3.2 + ## 1.2.7-next.2 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index bf99892817..529f5011db 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-react", - "version": "1.2.7-next.2", + "version": "1.2.7-next.3", "description": "Frontend package for managing integrations towards external systems", "backstage": { "role": "web-library" diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 6a67d797c8..832f45e230 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/integration +## 1.17.0-next.3 + +### Minor Changes + +- d945206: Added support for federated credentials using managed identities in the Azure DevOps integration. Federated credentials are only available for Azure DevOps organizations that have been configured to use Entra ID for authentication. + + ```diff + integrations: + azure: + - host: dev.azure.com + credentials: + + - clientId: ${APP_REGISTRATION_CLIENT_ID} + + managedIdentityClientId: system-assigned + + tenantId: ${AZURE_TENANT_ID} + ``` + + This also adds support for automatically using the system-assigned managed identity of an Azure resource by specifying `system-assigned` as the client ID of the managed identity. + + ```diff + integrations: + azure: + - host: dev.azure.com + credentials: + - - clientId: ${AZURE_CLIENT_ID} + + - clientId: system-assigned + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 1.17.0-next.2 ### Minor Changes diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index 3bfcf42203..4955d0fac3 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -62,6 +62,7 @@ export interface Config { clientSecret?: string; tenantId?: string; personalAccessToken?: string; + managedIdentityClientId?: string; }[]; /** * PGP signing key for signing commits. diff --git a/packages/integration/package.json b/packages/integration/package.json index 053a3e793a..142f17c74a 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "1.17.0-next.2", + "version": "1.17.0-next.3", "description": "Helpers for managing integrations towards external systems", "backstage": { "role": "common-library" diff --git a/packages/integration/report.api.md b/packages/integration/report.api.md index 9c1ad3ae82..d700c13429 100644 --- a/packages/integration/report.api.md +++ b/packages/integration/report.api.md @@ -137,6 +137,7 @@ export interface AzureCredentialsManager { // @public export type AzureDevOpsCredential = | AzureClientSecretCredential + | AzureManagedIdentityClientAssertionCredential | AzureManagedIdentityCredential | PersonalAccessTokenCredential; @@ -144,11 +145,13 @@ export type AzureDevOpsCredential = export type AzureDevOpsCredentialKind = | 'PersonalAccessToken' | 'ClientSecret' - | 'ManagedIdentity'; + | 'ManagedIdentity' + | 'ManagedIdentityClientAssertion'; // @public export type AzureDevOpsCredentialLike = Omit< Partial & + Partial & Partial & Partial, 'kind' @@ -204,10 +207,19 @@ export type AzureIntegrationConfig = { commitSigningKey?: string; }; +// @public +export type AzureManagedIdentityClientAssertionCredential = + AzureCredentialBase & { + kind: 'ManagedIdentityClientAssertion'; + tenantId: string; + clientId: string; + managedIdentityClientId: 'system-assigned' | string; + }; + // @public export type AzureManagedIdentityCredential = AzureCredentialBase & { kind: 'ManagedIdentity'; - clientId: string; + clientId: 'system-assigned' | string; }; // @public diff --git a/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.test.ts b/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.test.ts index c9fbb4c324..e65d127ce8 100644 --- a/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.test.ts +++ b/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.test.ts @@ -16,6 +16,7 @@ import { CachedAzureDevOpsCredentialsProvider } from './CachedAzureDevOpsCredentialsProvider'; import { AccessToken, + ClientAssertionCredential, ClientSecretCredential, ManagedIdentityCredential, } from '@azure/identity'; @@ -24,6 +25,11 @@ const MockedClientSecretCredential = ClientSecretCredential as jest.MockedClass< typeof ClientSecretCredential >; +const MockedClientAssertionCredential = + ClientAssertionCredential as jest.MockedClass< + typeof ClientAssertionCredential + >; + const MockedManagedIdentityCredential = ManagedIdentityCredential as jest.MockedClass< typeof ManagedIdentityCredential @@ -46,6 +52,13 @@ describe('CachedAzureDevOpsCredentialsProvider', () => { } as AccessToken), ); + MockedClientAssertionCredential.prototype.getToken.mockImplementation(() => + Promise.resolve({ + expiresOnTimestamp: Date.now() + hours(8), + token: 'fake-client-assertion-token', + } as AccessToken), + ); + MockedManagedIdentityCredential.prototype.getToken.mockImplementation(() => Promise.resolve({ expiresOnTimestamp: Date.now() + hours(8), @@ -89,6 +102,25 @@ describe('CachedAzureDevOpsCredentialsProvider', () => { expect(token).toBe('fake-client-secret-token'); }); + it('Should return a bearer credential when a managed identity client assertion is configured', async () => { + const manager = + CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential({ + kind: 'ManagedIdentityClientAssertion', + clientId: 'id', + managedIdentityClientId: 'managedIdentityClientId', + tenantId: 'tenantId', + }); + + const { headers, type, token } = await manager.getCredentials(); + + expect(headers).toStrictEqual({ + Authorization: `Bearer fake-client-assertion-token`, + }); + + expect(type).toBe('bearer'); + expect(token).toBe('fake-client-assertion-token'); + }); + it('Should return a bearer credential when a managed identity is configured', async () => { const manager = CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential({ @@ -124,6 +156,24 @@ describe('CachedAzureDevOpsCredentialsProvider', () => { ).toHaveBeenCalledTimes(1); }); + it('Should not refresh the managed identity client assertion token when it does not expire within 10 minutes', async () => { + const manager = + CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential({ + kind: 'ManagedIdentityClientAssertion', + clientId: 'id', + managedIdentityClientId: 'managedIdentityClientId', + tenantId: 'tenantId', + }); + + await manager.getCredentials(); + await manager.getCredentials(); + await manager.getCredentials(); + + expect( + MockedClientAssertionCredential.prototype.getToken, + ).toHaveBeenCalledTimes(1); + }); + it('Should return the managed identity token when it does not expire within 10 minutes', async () => { const manager = CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential({ @@ -164,6 +214,30 @@ describe('CachedAzureDevOpsCredentialsProvider', () => { ).toHaveBeenCalledTimes(2); }); + it('Should refresh the managed identity client assertion token when it expires within 10 minutes', async () => { + const manager = + CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential({ + kind: 'ManagedIdentityClientAssertion', + clientId: 'id', + managedIdentityClientId: 'managedIdentityClientId', + tenantId: 'tenantId', + }); + + MockedClientAssertionCredential.prototype.getToken.mockImplementation(() => + Promise.resolve({ + expiresOnTimestamp: Date.now() + minutes(9) + seconds(59), + token: 'fake-client-assertion-token', + } as AccessToken), + ); + + await manager.getCredentials(); + await manager.getCredentials(); + + expect( + MockedClientAssertionCredential.prototype.getToken, + ).toHaveBeenCalledTimes(2); + }); + it('Should refresh the managed identity token when it expires within 10 minutes', async () => { const manager = CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential({ diff --git a/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.ts b/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.ts index aee4cefc41..be721e483e 100644 --- a/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.ts +++ b/packages/integration/src/azure/CachedAzureDevOpsCredentialsProvider.ts @@ -15,6 +15,7 @@ */ import { AzureDevOpsCredential, PersonalAccessTokenCredential } from './config'; import { + ClientAssertionCredential, ClientSecretCredential, ManagedIdentityCredential, TokenCredential, @@ -23,6 +24,7 @@ import { AzureDevOpsCredentials, AzureDevOpsCredentialsProvider, } from './types'; +import { ManagedIdentityClientAssertion } from './ManagedIdentityClientAssertion'; type CachedAzureDevOpsCredentials = AzureDevOpsCredentials & { expiresAt?: number; @@ -59,9 +61,26 @@ export class CachedAzureDevOpsCredentialsProvider credential.clientSecret, ), ); + + case 'ManagedIdentityClientAssertion': { + const clientAssertion = new ManagedIdentityClientAssertion({ + clientId: credential.managedIdentityClientId, + }); + + return CachedAzureDevOpsCredentialsProvider.fromTokenCredential( + new ClientAssertionCredential( + credential.tenantId, + credential.clientId, + () => clientAssertion.getSignedAssertion(), + ), + ); + } + case 'ManagedIdentity': return CachedAzureDevOpsCredentialsProvider.fromTokenCredential( - new ManagedIdentityCredential(credential.clientId), + credential.clientId === 'system-assigned' + ? new ManagedIdentityCredential() + : new ManagedIdentityCredential(credential.clientId), ); default: exhaustiveCheck(credential); diff --git a/packages/integration/src/azure/ClientAssertion.ts b/packages/integration/src/azure/ClientAssertion.ts new file mode 100644 index 0000000000..752dcf4dd1 --- /dev/null +++ b/packages/integration/src/azure/ClientAssertion.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2025 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 type ClientAssertion = { + /** + * The signed assertion token + */ + signedAssertion: string; + /** + * The assertion's expiration timestamp in milliseconds, UNIX epoch time. + */ + expiresOnTimestamp: number; +}; diff --git a/packages/integration/src/azure/ManagedIdentityClientAssertion.test.ts b/packages/integration/src/azure/ManagedIdentityClientAssertion.test.ts new file mode 100644 index 0000000000..102081c698 --- /dev/null +++ b/packages/integration/src/azure/ManagedIdentityClientAssertion.test.ts @@ -0,0 +1,124 @@ +/* + * Copyright 2025 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 { ManagedIdentityClientAssertion } from './ManagedIdentityClientAssertion'; +import { ManagedIdentityCredential, AccessToken } from '@azure/identity'; + +const seconds = (s: number) => s * 1000; +const minutes = (m: number) => seconds(60) * m; +const hours = (h: number) => minutes(60) * h; +const MockedManagedIdentityCredential = + ManagedIdentityCredential as jest.MockedClass< + typeof ManagedIdentityCredential + >; + +jest.mock('@azure/identity'); + +describe('ManagedIdentityClientAssertion', () => { + beforeEach(() => { + jest.resetAllMocks(); + + MockedManagedIdentityCredential.prototype.getToken.mockImplementation(() => + Promise.resolve({ + expiresOnTimestamp: Date.now() + hours(8), + token: 'fake-managed-identity-token', + } as AccessToken), + ); + }); + + it('Should return a cached token if it does not expire within 5 minutes', async () => { + const clientAssertion = new ManagedIdentityClientAssertion({ + clientId: 'clientId', + }); + + // First call to getToken to cache the token + await clientAssertion.getSignedAssertion(); + // Second call should return the cached token + const token = await clientAssertion.getSignedAssertion(); + + expect(token).toBe('fake-managed-identity-token'); + expect( + MockedManagedIdentityCredential.prototype.getToken, + ).toHaveBeenCalledTimes(1); + }); + + it('Should obtain a new token if the cached token expires within 5 minutes', async () => { + const clientAssertion = new ManagedIdentityClientAssertion({ + clientId: 'clientId', + }); + + MockedManagedIdentityCredential.prototype.getToken.mockImplementationOnce( + () => + Promise.resolve({ + expiresOnTimestamp: Date.now() + minutes(4), + token: 'expiring-soon-token', + } as AccessToken), + ); + + // First call to getToken to cache the expiring token + await clientAssertion.getSignedAssertion(); + + MockedManagedIdentityCredential.prototype.getToken.mockImplementationOnce( + () => + Promise.resolve({ + expiresOnTimestamp: Date.now() + hours(8), + token: 'new-managed-identity-token', + } as AccessToken), + ); + + // Second call should obtain a new token + const token = await clientAssertion.getSignedAssertion(); + + expect(token).toBe('new-managed-identity-token'); + expect( + MockedManagedIdentityCredential.prototype.getToken, + ).toHaveBeenCalledTimes(2); + }); + + it('Should obtain a new token if no token is cached', async () => { + const clientAssertion = new ManagedIdentityClientAssertion({ + clientId: 'clientId', + }); + + const token = await clientAssertion.getSignedAssertion(); + + expect(token).toBe('fake-managed-identity-token'); + expect( + MockedManagedIdentityCredential.prototype.getToken, + ).toHaveBeenCalledTimes(1); + }); + + it('Should request a token for the correct scope', async () => { + const clientAssertion = new ManagedIdentityClientAssertion({ + clientId: 'clientId', + }); + + await clientAssertion.getSignedAssertion(); + + expect( + MockedManagedIdentityCredential.prototype.getToken, + ).toHaveBeenCalledWith('api://AzureADTokenExchange'); + }); + + it('Should handle system-assigned managed identity', async () => { + const clientAssertion = new ManagedIdentityClientAssertion(); + + await clientAssertion.getSignedAssertion(); + + expect( + MockedManagedIdentityCredential.prototype.getToken, + ).toHaveBeenCalledWith('api://AzureADTokenExchange'); + }); +}); diff --git a/packages/integration/src/azure/ManagedIdentityClientAssertion.ts b/packages/integration/src/azure/ManagedIdentityClientAssertion.ts new file mode 100644 index 0000000000..5fca816634 --- /dev/null +++ b/packages/integration/src/azure/ManagedIdentityClientAssertion.ts @@ -0,0 +1,75 @@ +/* + * Copyright 2025 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 { ManagedIdentityCredential } from '@azure/identity'; +import { ClientAssertion } from './ClientAssertion'; + +export type ManagedIdentityClientAssertionOptions = { + clientId?: string; +}; + +const fiveMinutes = 5 * 60 * 1000; // 5 minutes in milliseconds +const expiresWithinFiveMinutes = (clientAssertion: ClientAssertion) => + clientAssertion.expiresOnTimestamp - Date.now() <= fiveMinutes; + +/** + * Class representing a Managed Identity Client Assertion. + * This class is responsible for obtaining a signed client assertion using Azure Managed Identity. + */ +export class ManagedIdentityClientAssertion { + private credential: ManagedIdentityCredential; + private clientAssertion?: ClientAssertion; + + /** + * Creates an instance of ManagedIdentityClientAssertion. + * @param options - Optional parameters for the ManagedIdentityClientAssertion. + * - clientId: The client ID of the managed identity. If not provided, 'system-assigned' is used. + */ + constructor(options?: ManagedIdentityClientAssertionOptions) { + let { clientId } = options || {}; + clientId ??= 'system-assigned'; + + this.credential = + clientId === 'system-assigned' + ? new ManagedIdentityCredential() + : new ManagedIdentityCredential(clientId); + } + + /** + * Gets a signed client assertion. + * If a valid client assertion is already cached which doesn't expire soon, it returns the cached assertion. + * Otherwise, it obtains a new access token and creates a new client assertion. + * @returns A promise that resolves to the signed client assertion. + */ + public async getSignedAssertion(): Promise { + if ( + this.clientAssertion !== undefined && + !expiresWithinFiveMinutes(this.clientAssertion) + ) { + return this.clientAssertion.signedAssertion; + } + + const accessToken = await this.credential.getToken( + 'api://AzureADTokenExchange', + ); + + this.clientAssertion = { + signedAssertion: accessToken.token, + expiresOnTimestamp: accessToken.expiresOnTimestamp, + }; + + return accessToken.token; + } +} diff --git a/packages/integration/src/azure/config.test.ts b/packages/integration/src/azure/config.test.ts index dc6781c45e..c1ddf1590f 100644 --- a/packages/integration/src/azure/config.test.ts +++ b/packages/integration/src/azure/config.test.ts @@ -167,7 +167,7 @@ describe('readAzureIntegrationConfig', () => { }); }); - it('reads all values when using a managed identity credential', () => { + it('reads all values when using a managed identity client assertion credential', () => { const output = readAzureIntegrationConfig( buildConfig({ host: 'dev.azure.com', @@ -175,6 +175,62 @@ describe('readAzureIntegrationConfig', () => { { organizations: ['org1', 'org2'], clientId: 'id', + managedIdentityClientId: 'system-assigned', + tenantId: 'tenant', + }, + ], + }), + ); + + expect(output).toEqual({ + host: 'dev.azure.com', + credentials: [ + { + kind: 'ManagedIdentityClientAssertion', + organizations: ['org1', 'org2'], + clientId: 'id', + managedIdentityClientId: 'system-assigned', + tenantId: 'tenant', + }, + ], + }); + }); + + it('reads all values when using a managed identity client assertion credential (without organizations)', () => { + const output = readAzureIntegrationConfig( + buildConfig({ + host: 'dev.azure.com', + credentials: [ + { + clientId: 'id', + managedIdentityClientId: 'system-assigned', + tenantId: 'tenant', + }, + ], + }), + ); + + expect(output).toEqual({ + host: 'dev.azure.com', + credentials: [ + { + kind: 'ManagedIdentityClientAssertion', + clientId: 'id', + managedIdentityClientId: 'system-assigned', + tenantId: 'tenant', + }, + ], + }); + }); + + it('reads all values when using a managed identity credential', () => { + const output = readAzureIntegrationConfig( + buildConfig({ + host: 'dev.azure.com', + credentials: [ + { + organizations: ['org1', 'org2'], + clientId: 'system-assigned', }, ], }), @@ -186,7 +242,7 @@ describe('readAzureIntegrationConfig', () => { { kind: 'ManagedIdentity', organizations: ['org1', 'org2'], - clientId: 'id', + clientId: 'system-assigned', }, ], }); diff --git a/packages/integration/src/azure/config.ts b/packages/integration/src/azure/config.ts index 2910387829..09b97755ec 100644 --- a/packages/integration/src/azure/config.ts +++ b/packages/integration/src/azure/config.ts @@ -71,7 +71,8 @@ export type AzureIntegrationConfig = { export type AzureDevOpsCredentialKind = | 'PersonalAccessToken' | 'ClientSecret' - | 'ManagedIdentity'; + | 'ManagedIdentity' + | 'ManagedIdentityClientAssertion'; /** * Common fields for the Azure DevOps credentials. @@ -109,6 +110,31 @@ export type AzureClientSecretCredential = AzureCredentialBase & { clientSecret: string; }; +/** + * A client assertion credential that uses a managed identity to generate a client assertion (JWT). + * @public + */ +export type AzureManagedIdentityClientAssertionCredential = + AzureCredentialBase & { + kind: 'ManagedIdentityClientAssertion'; + /** + * The Entra ID tenant + */ + tenantId: string; + + /** + * The client ID of the app registration you want to authenticate as. + */ + clientId: string; + + /** + * The client ID of the managed identity used to generate a client assertion (JWT). + * Set to "system-assigned" to automatically use the system-assigned managed identity. + * For user-assigned managed identities, specify the client ID of the managed identity you want to use. + */ + managedIdentityClientId: 'system-assigned' | string; + }; + /** * A managed identity credential. * @public @@ -118,7 +144,7 @@ export type AzureManagedIdentityCredential = AzureCredentialBase & { /** * The clientId */ - clientId: string; + clientId: 'system-assigned' | string; }; /** @@ -136,6 +162,7 @@ export type PersonalAccessTokenCredential = AzureCredentialBase & { */ export type AzureDevOpsCredentialLike = Omit< Partial & + Partial & Partial & Partial, 'kind' @@ -147,12 +174,14 @@ export type AzureDevOpsCredentialLike = Omit< */ export type AzureDevOpsCredential = | AzureClientSecretCredential + | AzureManagedIdentityClientAssertionCredential | AzureManagedIdentityCredential | PersonalAccessTokenCredential; const AzureDevOpsCredentialFields = [ 'clientId', 'clientSecret', + 'managedIdentityClientId', 'tenantId', 'personalAccessToken', ] as const; @@ -164,6 +193,10 @@ const AzureDevopsCredentialFieldMap = new Map< >([ ['ClientSecret', ['clientId', 'clientSecret', 'tenantId']], ['ManagedIdentity', ['clientId']], + [ + 'ManagedIdentityClientAssertion', + ['clientId', 'managedIdentityClientId', 'tenantId'], + ], ['PersonalAccessToken', ['personalAccessToken']], ]); @@ -213,9 +246,12 @@ export function readAzureIntegrationConfig( personalAccessToken: credential .getOptionalString('personalAccessToken') ?.trim(), - tenantId: credential.getOptionalString('tenantId'), - clientId: credential.getOptionalString('clientId'), + tenantId: credential.getOptionalString('tenantId')?.trim(), + clientId: credential.getOptionalString('clientId')?.trim(), clientSecret: credential.getOptionalString('clientSecret')?.trim(), + managedIdentityClientId: credential + .getOptionalString('managedIdentityClientId') + ?.trim(), }; return result; diff --git a/packages/integration/src/azure/index.ts b/packages/integration/src/azure/index.ts index b5f5c0a316..a447c8ffd8 100644 --- a/packages/integration/src/azure/index.ts +++ b/packages/integration/src/azure/index.ts @@ -25,6 +25,7 @@ export type { AzureCredentialBase, AzureClientSecretCredential, AzureManagedIdentityCredential, + AzureManagedIdentityClientAssertionCredential, PersonalAccessTokenCredential, AzureDevOpsCredentialLike, AzureDevOpsCredential, diff --git a/packages/release-manifests/CHANGELOG.md b/packages/release-manifests/CHANGELOG.md index e88324250f..a7f41cbdf8 100644 --- a/packages/release-manifests/CHANGELOG.md +++ b/packages/release-manifests/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/release-manifests +## 0.0.13-next.0 + +### Patch Changes + +- 163f3da: This expands the configurability of `release-manifests` to pave the road for more configuration options in the `cli`. + + Specifically it allows the specification of mirrored, proxied, or air-gapped hosts when upgrading across releases when + working in restricted or heavily governed development environments (common in large enterprises and government + entities). + ## 0.0.12 ### Patch Changes diff --git a/packages/release-manifests/package.json b/packages/release-manifests/package.json index 0b24a93c6a..ec67298ec2 100644 --- a/packages/release-manifests/package.json +++ b/packages/release-manifests/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/release-manifests", - "version": "0.0.12", + "version": "0.0.13-next.0", "description": "Helper library for receiving release manifests", "backstage": { "role": "common-library" diff --git a/packages/release-manifests/report.api.md b/packages/release-manifests/report.api.md index 2d2cc00767..5ef8345ec0 100644 --- a/packages/release-manifests/report.api.md +++ b/packages/release-manifests/report.api.md @@ -11,6 +11,14 @@ export function getManifestByReleaseLine( // @public export type GetManifestByReleaseLineOptions = { releaseLine: string; + fetch?: ( + url: string, + options?: { + signal?: AbortSignal; + }, + ) => Promise>; + gitHubRawBaseUrl?: string; + versionsBaseUrl?: string; }; // @public @@ -27,6 +35,8 @@ export type GetManifestByVersionOptions = { signal?: AbortSignal; }, ) => Promise>; + gitHubRawBaseUrl?: string; + versionsBaseUrl?: string; }; // @public diff --git a/packages/release-manifests/src/manifest.test.ts b/packages/release-manifests/src/manifest.test.ts index ad5bf13496..9cb100376a 100644 --- a/packages/release-manifests/src/manifest.test.ts +++ b/packages/release-manifests/src/manifest.test.ts @@ -86,6 +86,71 @@ describe('Release Manifests', () => { getManifestByVersion({ version: '0.0.0', fetch: mockFetch }), ).rejects.toThrow('No release found for 0.0.0 version'); }); + + it('should allow overriding the versions host', async () => { + const mockFetch = jest.fn().mockImplementation(async url => ({ + status: 200, + url, + json: () => ({ + packages: [{ name: '@backstage/core', version: '2.3.4' }], + }), + })); + + const pkgs = await getManifestByVersion({ + version: '0.0.0', + fetch: mockFetch, + versionsBaseUrl: 'https://versions.some-test-host.com', + }); + + expect(pkgs.packages).toEqual([ + { + name: '@backstage/core', + version: '2.3.4', + }, + ]); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://versions.some-test-host.com/v1/releases/0.0.0/manifest.json', + expect.anything(), + ); + }); + + it('should allow overriding github host', async () => { + const mockFetch = jest.fn().mockImplementation(async url => { + if ( + url === + 'https://versions.some-test-host.com/v1/releases/0.0.0/manifest.json' + ) { + return { + status: 200, + url, + json: () => ({ + packages: [{ name: '@backstage/core', version: '2.3.4' }], + }), + }; + } + + throw new Error('Host not found'); + }); + + const pkgs = await getManifestByVersion({ + version: '0.0.0', + fetch: mockFetch, + gitHubRawBaseUrl: 'https://versions.some-test-host.com', + }); + + expect(pkgs.packages).toEqual([ + { + name: '@backstage/core', + version: '2.3.4', + }, + ]); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://versions.some-test-host.com/v1/releases/0.0.0/manifest.json', + expect.anything(), + ); + }); }); describe('getManifestByReleaseLine', () => { @@ -119,6 +184,121 @@ describe('Release Manifests', () => { getManifestByReleaseLine({ releaseLine: 'foo' }), ).rejects.toThrow("No 'foo' release line found"); }); + + it('should allow overriding the fetch implementation', async () => { + const mockFetch = jest.fn().mockImplementation(async url => ({ + status: 200, + url, + json: () => ({ + packages: [{ name: '@backstage/core', version: '1.2.3' }], + }), + })); + + const pkgs = await getManifestByReleaseLine({ + releaseLine: 'main', + fetch: mockFetch, + }); + + expect(pkgs.packages).toEqual([ + { + name: '@backstage/core', + version: '1.2.3', + }, + ]); + + mockFetch.mockImplementation(async url => ({ + status: 404, + url, + })); + + await expect( + getManifestByReleaseLine({ releaseLine: 'foo', fetch: mockFetch }), + ).rejects.toThrow("No 'foo' release line found"); + }); + + it('should allow overriding the versions host', async () => { + const mockFetch = jest.fn().mockImplementation(async url => ({ + status: 200, + url, + json: () => ({ + packages: [{ name: '@backstage/core', version: '1.2.3' }], + }), + })); + + const pkgs = await getManifestByReleaseLine({ + releaseLine: 'main', + fetch: mockFetch, + versionsBaseUrl: 'https://versions.some-test-host.com', + }); + + expect(pkgs.packages).toEqual([ + { + name: '@backstage/core', + version: '1.2.3', + }, + ]); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://versions.some-test-host.com/v1/tags/main/manifest.json', + expect.anything(), + ); + }); + + it('should allow overriding github host', async () => { + const mockFetch = jest.fn().mockImplementation(async url => { + if ( + url === + 'https://hosted.raw.internal-github.com/backstage/versions/main/v1/tags/main' + ) { + return { + ok: true, + status: 200, + url, + text: () => + 'https://hosted.raw.internal-github.com/backstage/versions/tags/main', + }; + } + + if ( + url === + 'https://hosted.raw.internal-github.com/backstage/versions/tags/main/manifest.json' + ) { + return { + status: 200, + url, + json: () => ({ + packages: [{ name: '@backstage/core', version: '1.2.3' }], + }), + }; + } + + throw new Error('Host Not Found'); + }); + + const pkgs = await getManifestByReleaseLine({ + releaseLine: 'main', + fetch: mockFetch, + gitHubRawBaseUrl: + 'https://hosted.raw.internal-github.com/backstage/versions/main', + }); + + expect(pkgs.packages).toEqual([ + { + name: '@backstage/core', + version: '1.2.3', + }, + ]); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://hosted.raw.internal-github.com/backstage/versions/main/v1/tags/main', + expect.anything(), + ); + + expect(mockFetch).toHaveBeenCalledWith( + 'https://hosted.raw.internal-github.com/backstage/versions/tags/main/manifest.json', + expect.anything(), + ); + }); }); }); diff --git a/packages/release-manifests/src/manifest.ts b/packages/release-manifests/src/manifest.ts index 66a5f62151..8db9d59d39 100644 --- a/packages/release-manifests/src/manifest.ts +++ b/packages/release-manifests/src/manifest.ts @@ -37,6 +37,8 @@ export type GetManifestByVersionOptions = { url: string, options?: { signal?: AbortSignal }, ) => Promise>; + gitHubRawBaseUrl?: string; + versionsBaseUrl?: string; }; // Wait for waitMs, or until signal is aborted. @@ -88,19 +90,18 @@ export async function getManifestByVersion( const versionEnc = encodeURIComponent(options.version); const fetchFn = options.fetch ?? fetch; + const versionsHost = options.versionsBaseUrl ?? VERSIONS_BASE_URL; + const gitHubRawBaseUrl = options.gitHubRawBaseUrl ?? GITHUB_RAW_BASE_URL; const res = await withFallback( signal => - fetchFn(`${VERSIONS_BASE_URL}/v1/releases/${versionEnc}/manifest.json`, { + fetchFn(`${versionsHost}/v1/releases/${versionEnc}/manifest.json`, { signal, }), signal => - fetchFn( - `${GITHUB_RAW_BASE_URL}/v1/releases/${versionEnc}/manifest.json`, - { - signal, - }, - ), + fetchFn(`${gitHubRawBaseUrl}/v1/releases/${versionEnc}/manifest.json`, { + signal, + }), 500, ); if (res.status === 404) { @@ -120,6 +121,12 @@ export async function getManifestByVersion( */ export type GetManifestByReleaseLineOptions = { releaseLine: string; + fetch?: ( + url: string, + options?: { signal?: AbortSignal }, + ) => Promise>; + gitHubRawBaseUrl?: string; + versionsBaseUrl?: string; }; /** @@ -130,20 +137,27 @@ export async function getManifestByReleaseLine( options: GetManifestByReleaseLineOptions, ): Promise { const releaseEnc = encodeURIComponent(options.releaseLine); + + const fetchFn = options.fetch ?? fetch; + const versionsHost = options.versionsBaseUrl ?? VERSIONS_BASE_URL; + const gitHubRawBaseUrl = options.gitHubRawBaseUrl ?? GITHUB_RAW_BASE_URL; + const res = await withFallback( signal => - fetch(`${VERSIONS_BASE_URL}/v1/tags/${releaseEnc}/manifest.json`, { + fetchFn(`${versionsHost}/v1/tags/${releaseEnc}/manifest.json`, { signal, }), async signal => { // The release tags are symlinks, which we need to follow manually when fetching from GitHub. - const baseUrl = `${GITHUB_RAW_BASE_URL}/v1/tags/${releaseEnc}`; - const linkRes = await fetch(baseUrl, { signal }); + const baseUrl = `${gitHubRawBaseUrl}/v1/tags/${releaseEnc}`; + const linkRes = await fetchFn(baseUrl, { signal }); if (!linkRes.ok) { return linkRes; } const link = (await linkRes.text()).trim(); - return fetch(new URL(`${link}/manifest.json`, baseUrl), { signal }); + return fetchFn(new URL(`${link}/manifest.json`, baseUrl).toString(), { + signal, + }); }, 1000, ); diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 917b364d2e..ed05f6dc0e 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/repo-tools +## 0.13.3-next.3 + +### Patch Changes + +- 659f2ce: Updated dependency `typedoc` to `^0.28.0`. +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config-loader@1.10.1-next.0 + - @backstage/errors@1.2.7 + ## 0.13.3-next.2 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 1ec1d1a48f..2aa4e1d80d 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.13.3-next.2", + "version": "0.13.3-next.3", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" @@ -90,7 +90,7 @@ "@types/is-glob": "^4.0.2", "@types/node": "^20.16.0", "@types/prettier": "^2.0.0", - "typedoc": "^0.27.6" + "typedoc": "^0.28.0" }, "peerDependencies": { "@microsoft/api-extractor-model": "*", @@ -98,7 +98,7 @@ "@microsoft/tsdoc-config": "*", "@useoptic/optic": "^1.0.0", "prettier": "^2.8.1", - "typedoc": "^0.27.0", + "typedoc": "^0.28.0", "typescript": "> 3.0.0" }, "peerDependenciesMeta": { diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index 9ba8836fff..01824c4df6 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,13 @@ # @internal/scaffolder +## 0.0.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.16.0-next.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + ## 0.0.9-next.2 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index 6dda7d0b41..dc727b99d3 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.9-next.2", + "version": "0.0.9-next.3", "backstage": { "role": "web-library", "inline": true diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 8afd99d6fb..4e504b8f60 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,24 @@ # techdocs-cli-embedded-app +## 0.2.108-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.17.0-next.1 + - @backstage/cli@0.32.1-next.3 + - @backstage/plugin-catalog@1.30.0-next.3 + - @backstage/app-defaults@1.6.2-next.2 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/test-utils@1.7.8-next.2 + - @backstage/plugin-techdocs@1.12.6-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/theme@0.6.6-next.0 + - @backstage/plugin-techdocs-react@1.2.17-next.1 + ## 0.2.108-next.2 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index cd95f9e37e..0683378df9 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,6 +1,6 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.108-next.2", + "version": "0.2.108-next.3", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index e9a719087e..3ce409bbc4 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.9.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.3-next.3 + ## 1.9.3-next.2 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index e44b6fb107..3034be2715 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.3-next.2", + "version": "1.9.3-next.3", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 2561b80f57..e8acf8edd1 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/test-utils +## 1.7.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.17.0-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/config@1.3.2 + - @backstage/theme@0.6.6-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-react@0.4.34-next.1 + ## 1.7.8-next.1 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 0839e44b69..0824422425 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/test-utils", - "version": "1.7.8-next.1", + "version": "1.7.8-next.2", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/packages/yarn-plugin/CHANGELOG.md b/packages/yarn-plugin/CHANGELOG.md index 41ebade0a3..7a05151bb0 100644 --- a/packages/yarn-plugin/CHANGELOG.md +++ b/packages/yarn-plugin/CHANGELOG.md @@ -1,5 +1,13 @@ # yarn-plugin-backstage +## 0.0.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/release-manifests@0.0.13-next.0 + - @backstage/cli-common@0.1.15 + ## 0.0.5-next.0 ### Patch Changes diff --git a/packages/yarn-plugin/package.json b/packages/yarn-plugin/package.json index 78d34c2788..7de8d140bb 100644 --- a/packages/yarn-plugin/package.json +++ b/packages/yarn-plugin/package.json @@ -1,6 +1,6 @@ { "name": "yarn-plugin-backstage", - "version": "0.0.5-next.0", + "version": "0.0.5-next.1", "description": "Yarn plugin for working with Backstage monorepos", "backstage": { "role": "node-library" diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index ea622deaba..e126781b9c 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-api-docs +## 0.12.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.30.0-next.3 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-permission-react@0.4.34-next.1 + ## 0.12.7-next.2 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 4854695e23..db9b774cc0 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.12.7-next.2", + "version": "0.12.7-next.3", "description": "A Backstage plugin that helps represent API entities in the frontend", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 966860f088..2230b0ed43 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-app-backend +## 0.5.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.33-next.2 + ## 0.5.2-next.1 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 432e17151a..82cbcf2e94 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-backend", - "version": "0.5.2-next.1", + "version": "0.5.2-next.2", "description": "A Backstage backend plugin that serves the Backstage frontend app", "backstage": { "role": "backend-plugin", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index 6d1e1a2368..9b85efb09a 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-app-node +## 0.1.33-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config-loader@1.10.1-next.0 + ## 0.1.33-next.1 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index b92d045fd8..aec12abb23 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-node", - "version": "0.1.33-next.1", + "version": "0.1.33-next.2", "description": "Node.js library for the app plugin", "backstage": { "role": "node-library", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index 790b735bb1..4c96752e76 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-app +## 0.1.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/theme@0.6.6-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-react@0.4.34-next.1 + ## 0.1.9-next.2 ### Patch Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index fed1f47f4c..c1759b138b 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.1.9-next.2", + "version": "0.1.9-next.3", "backstage": { "role": "frontend-plugin", "pluginId": "app", diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 2ce29207f7..fa0658554a 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.4.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-atlassian-provider/config.d.ts b/plugins/auth-backend-module-atlassian-provider/config.d.ts index f6433c123e..2729ad31fd 100644 --- a/plugins/auth-backend-module-atlassian-provider/config.d.ts +++ b/plugins/auth-backend-module-atlassian-provider/config.d.ts @@ -32,12 +32,19 @@ export interface Config { additionalScopes?: string | string[]; signIn?: { resolvers: Array< - | { resolver: 'usernameMatchingUserEntityName' } + | { + resolver: 'usernameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index e458540b8f..fe708925bd 100644 --- a/plugins/auth-backend-module-atlassian-provider/package.json +++ b/plugins/auth-backend-module-atlassian-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-atlassian-provider", - "version": "0.4.3-next.1", + "version": "0.4.3-next.2", "description": "The atlassian-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -38,7 +38,8 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "passport": "^0.7.0", - "passport-atlassian-oauth2": "^2.1.0" + "passport-atlassian-oauth2": "^2.1.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-atlassian-provider/report.api.md b/plugins/auth-backend-module-atlassian-provider/report.api.md index 7abdf7ee70..c9e184aeac 100644 --- a/plugins/auth-backend-module-atlassian-provider/report.api.md +++ b/plugins/auth-backend-module-atlassian-provider/report.api.md @@ -20,7 +20,10 @@ export const atlassianAuthenticator: OAuthAuthenticator< export namespace atlassianSignInResolvers { const usernameMatchingUserEntityName: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } diff --git a/plugins/auth-backend-module-atlassian-provider/src/resolvers.ts b/plugins/auth-backend-module-atlassian-provider/src/resolvers.ts index 1f3090bfdd..949f25db9c 100644 --- a/plugins/auth-backend-module-atlassian-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-atlassian-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Atlassian auth provider. @@ -31,7 +32,12 @@ export namespace atlassianSignInResolvers { * Looks up the user by matching their Atlassian username to the entity name. */ export const usernameMatchingUserEntityName = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -43,7 +49,15 @@ export namespace atlassianSignInResolvers { throw new Error(`Atlassian user profile does not contain a username`); } - return ctx.signInWithCatalogUser({ entityRef: { name: id } }); + return ctx.signInWithCatalogUser( + { entityRef: { name: id } }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: id } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md index c14a7d28bd..c9ccfe7d41 100644 --- a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-auth0-provider +## 0.2.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.2.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-auth0-provider/package.json b/plugins/auth-backend-module-auth0-provider/package.json index 8a8b878ff2..ce5d1b6cb1 100644 --- a/plugins/auth-backend-module-auth0-provider/package.json +++ b/plugins/auth-backend-module-auth0-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-auth0-provider", - "version": "0.2.3-next.1", + "version": "0.2.3-next.2", "description": "The auth0-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md index 687bd3a63f..cb47375143 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.4.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-backend@0.25.0-next.2 + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/errors@1.2.7 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/config.d.ts b/plugins/auth-backend-module-aws-alb-provider/config.d.ts index 41c84872de..3a5d2d5eca 100644 --- a/plugins/auth-backend-module-aws-alb-provider/config.d.ts +++ b/plugins/auth-backend-module-aws-alb-provider/config.d.ts @@ -46,8 +46,12 @@ export interface Config { | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index 9b98642ab1..b418c74bc1 100644 --- a/plugins/auth-backend-module-aws-alb-provider/package.json +++ b/plugins/auth-backend-module-aws-alb-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-aws-alb-provider", - "version": "0.4.3-next.1", + "version": "0.4.3-next.2", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", @@ -42,7 +42,8 @@ "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "jose": "^5.0.0", - "node-cache": "^5.1.2" + "node-cache": "^5.1.2", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/auth-backend-module-aws-alb-provider/report.api.md b/plugins/auth-backend-module-aws-alb-provider/report.api.md index 567bff993c..ebb3e8e24a 100644 --- a/plugins/auth-backend-module-aws-alb-provider/report.api.md +++ b/plugins/auth-backend-module-aws-alb-provider/report.api.md @@ -41,7 +41,10 @@ export namespace awsAlbSignInResolvers { const // (undocumented) emailMatchingUserEntityProfileEmail: SignInResolverFactory< AwsAlbResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-aws-alb-provider/src/resolvers.ts b/plugins/auth-backend-module-aws-alb-provider/src/resolvers.ts index 990c447124..38b4b60f27 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/resolvers.ts @@ -19,6 +19,8 @@ import { SignInInfo, } from '@backstage/plugin-auth-node'; import { AwsAlbResult } from './types'; +import { z } from 'zod'; + /** * Available sign-in resolvers for the AWS ALB auth provider. * @@ -27,19 +29,37 @@ import { AwsAlbResult } from './types'; export namespace awsAlbSignInResolvers { export const emailMatchingUserEntityProfileEmail = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async (info: SignInInfo, ctx) => { if (!info.result.fullProfile.emails) { throw new Error( 'Login failed, user profile does not contain an email', ); } - return ctx.signInWithCatalogUser({ - filter: { - kind: ['User'], - 'spec.profile.email': info.result.fullProfile.emails[0].value, + + return ctx.signInWithCatalogUser( + { + filter: { + kind: ['User'], + 'spec.profile.email': info.result.fullProfile.emails[0].value, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { + entityRef: { + name: info.result.fullProfile.emails[0].value, + }, + } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md index da8c7e6e81..4c41c3f427 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend-module-azure-easyauth-provider +## 0.2.8-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-azure-easyauth-provider/package.json b/plugins/auth-backend-module-azure-easyauth-provider/package.json index 837ed7c9d1..51ded2f229 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/package.json +++ b/plugins/auth-backend-module-azure-easyauth-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-azure-easyauth-provider", - "version": "0.2.8-next.1", + "version": "0.2.8-next.2", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -40,7 +40,8 @@ "@types/passport": "^1.0.16", "express": "^4.19.2", "jose": "^5.0.0", - "passport": "^0.7.0" + "passport": "^0.7.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/auth-backend-module-azure-easyauth-provider/report.api.md b/plugins/auth-backend-module-azure-easyauth-provider/report.api.md index 9d337e3f34..717864f422 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/report.api.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/report.api.md @@ -32,7 +32,10 @@ export namespace azureEasyAuthSignInResolvers { const // (undocumented) idMatchingUserEntityAnnotation: SignInResolverFactory< AzureEasyAuthResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } diff --git a/plugins/auth-backend-module-azure-easyauth-provider/src/resolvers.ts b/plugins/auth-backend-module-azure-easyauth-provider/src/resolvers.ts index e9e35420d1..94afa90c5f 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-azure-easyauth-provider/src/resolvers.ts @@ -19,11 +19,17 @@ import { SignInInfo, } from '@backstage/plugin-auth-node'; import { AzureEasyAuthResult } from './types'; +import { z } from 'zod'; /** @public */ export namespace azureEasyAuthSignInResolvers { export const idMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async (info: SignInInfo, ctx) => { const { fullProfile: { id }, @@ -32,12 +38,19 @@ export namespace azureEasyAuthSignInResolvers { if (!id) { throw new Error('User profile contained no id'); } - - return await ctx.signInWithCatalogUser({ - annotations: { - 'graph.microsoft.com/user-id': id, + return ctx.signInWithCatalogUser( + { + annotations: { + 'graph.microsoft.com/user-id': id, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: id } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index fe706216c2..b8fb09bac0 100644 --- a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-bitbucket-provider +## 0.3.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/config.d.ts b/plugins/auth-backend-module-bitbucket-provider/config.d.ts index f3d82608bd..56303b5215 100644 --- a/plugins/auth-backend-module-bitbucket-provider/config.d.ts +++ b/plugins/auth-backend-module-bitbucket-provider/config.d.ts @@ -30,12 +30,23 @@ export interface Config { additionalScopes?: string | string[]; signIn?: { resolvers: Array< - | { resolver: 'userIdMatchingUserEntityAnnotation' } + | { + resolver: 'userIdMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'usernameMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index 55182829cb..2a4630e623 100644 --- a/plugins/auth-backend-module-bitbucket-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-provider", - "version": "0.3.3-next.1", + "version": "0.3.3-next.2", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -38,7 +38,8 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "passport": "^0.7.0", - "passport-bitbucket-oauth2": "^0.1.2" + "passport-bitbucket-oauth2": "^0.1.2", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-bitbucket-provider/report.api.md b/plugins/auth-backend-module-bitbucket-provider/report.api.md index ceb4141fd9..86cff50a0f 100644 --- a/plugins/auth-backend-module-bitbucket-provider/report.api.md +++ b/plugins/auth-backend-module-bitbucket-provider/report.api.md @@ -24,11 +24,17 @@ export const bitbucketAuthenticator: OAuthAuthenticator< export namespace bitbucketSignInResolvers { const userIdMatchingUserEntityAnnotation: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; const usernameMatchingUserEntityAnnotation: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-bitbucket-provider/src/resolvers.ts b/plugins/auth-backend-module-bitbucket-provider/src/resolvers.ts index f9d834a3a7..6691806e63 100644 --- a/plugins/auth-backend-module-bitbucket-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-bitbucket-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Bitbucket auth provider. @@ -32,7 +33,12 @@ export namespace bitbucketSignInResolvers { */ export const userIdMatchingUserEntityAnnotation = createSignInResolverFactory( { - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -44,11 +50,19 @@ export namespace bitbucketSignInResolvers { throw new Error('Bitbucket user profile does not contain an ID'); } - return ctx.signInWithCatalogUser({ - annotations: { - 'bitbucket.org/user-id': id, + return ctx.signInWithCatalogUser( + { + annotations: { + 'bitbucket.org/user-id': id, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: id } } + : undefined, + }, + ); }; }, }, @@ -59,7 +73,12 @@ export namespace bitbucketSignInResolvers { */ export const usernameMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -73,11 +92,19 @@ export namespace bitbucketSignInResolvers { ); } - return ctx.signInWithCatalogUser({ - annotations: { - 'bitbucket.org/username': username, + return ctx.signInWithCatalogUser( + { + annotations: { + 'bitbucket.org/username': username, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: username } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md index e4fc051072..486e011126 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-bitbucket-server-provider +## 0.2.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.2.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-server-provider/config.d.ts b/plugins/auth-backend-module-bitbucket-server-provider/config.d.ts index 8e63581625..26afb46f09 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/config.d.ts +++ b/plugins/auth-backend-module-bitbucket-server-provider/config.d.ts @@ -29,6 +29,27 @@ export interface Config { clientSecret: string; host: string; callbackUrl?: string; + signIn?: { + resolvers: Array< + | { + resolver: 'userIdMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'usernameMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailLocalPartMatchingUserEntityName'; + allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + >; + }; sessionDuration?: HumanDuration | string; }; }; diff --git a/plugins/auth-backend-module-bitbucket-server-provider/package.json b/plugins/auth-backend-module-bitbucket-server-provider/package.json index dff0e32895..c61880fbe9 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/package.json +++ b/plugins/auth-backend-module-bitbucket-server-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-bitbucket-server-provider", - "version": "0.2.3-next.1", + "version": "0.2.3-next.2", "description": "The bitbucket-server-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -37,7 +37,8 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "passport": "^0.7.0", - "passport-oauth2": "^1.6.1" + "passport-oauth2": "^1.6.1", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-bitbucket-server-provider/report.api.md b/plugins/auth-backend-module-bitbucket-server-provider/report.api.md index d5c484f6e3..d9409bd36d 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/report.api.md +++ b/plugins/auth-backend-module-bitbucket-server-provider/report.api.md @@ -27,7 +27,10 @@ export const bitbucketServerAuthenticator: OAuthAuthenticator< export namespace bitbucketServerSignInResolvers { const emailMatchingUserEntityProfileEmail: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-bitbucket-server-provider/src/resolvers.ts b/plugins/auth-backend-module-bitbucket-server-provider/src/resolvers.ts index dc2ceef7e9..2e92d8c6ad 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-bitbucket-server-provider/src/resolvers.ts @@ -19,6 +19,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Bitbucket Server auth provider. @@ -31,7 +32,12 @@ export namespace bitbucketServerSignInResolvers { */ export const emailMatchingUserEntityProfileEmail = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -44,11 +50,19 @@ export namespace bitbucketServerSignInResolvers { ); } - return ctx.signInWithCatalogUser({ - filter: { - 'spec.profile.email': profile.email, + return ctx.signInWithCatalogUser( + { + filter: { + 'spec.profile.email': profile.email, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: profile.email } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index 1495904a13..168292d2f8 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend-module-cloudflare-access-provider +## 0.4.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts b/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts index 39a4ca3eb6..c4ea052db3 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts +++ b/plugins/auth-backend-module-cloudflare-access-provider/config.d.ts @@ -34,8 +34,12 @@ export interface Config { | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; }; diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index 3f8e18b5bb..cd4b800bfc 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/package.json +++ b/plugins/auth-backend-module-cloudflare-access-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-cloudflare-access-provider", - "version": "0.4.3-next.1", + "version": "0.4.3-next.2", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -39,7 +39,8 @@ "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", - "jose": "^5.0.0" + "jose": "^5.0.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-cloudflare-access-provider/report.api.md b/plugins/auth-backend-module-cloudflare-access-provider/report.api.md index 1547190d39..98c55e80bf 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/report.api.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/report.api.md @@ -52,7 +52,10 @@ export type CloudflareAccessResult = { export namespace cloudflareAccessSignInResolvers { const emailMatchingUserEntityProfileEmail: SignInResolverFactory< CloudflareAccessResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } diff --git a/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.test.ts b/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.test.ts index 27cdfa001c..2ab49c313a 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.test.ts +++ b/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.test.ts @@ -37,8 +37,13 @@ describe('resolvers', () => { } satisfies Partial; await resolver(info, context as any); - expect(context.signInWithCatalogUser).toHaveBeenCalledWith({ - filter: { 'spec.profile.email': 'hello@example.com' }, - }); + expect(context.signInWithCatalogUser).toHaveBeenCalledWith( + { + filter: { 'spec.profile.email': 'hello@example.com' }, + }, + { + dangerousEntityRefFallback: undefined, + }, + ); }); }); diff --git a/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.ts b/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.ts index ad0b512a65..21cb124be4 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-cloudflare-access-provider/src/resolvers.ts @@ -19,6 +19,7 @@ import { SignInInfo, } from '@backstage/plugin-auth-node'; import { CloudflareAccessResult } from './types'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Cloudflare Access auth provider. @@ -31,7 +32,12 @@ export namespace cloudflareAccessSignInResolvers { */ export const emailMatchingUserEntityProfileEmail = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async (info: SignInInfo, ctx) => { const { profile } = info; @@ -41,11 +47,19 @@ export namespace cloudflareAccessSignInResolvers { ); } - return ctx.signInWithCatalogUser({ - filter: { - 'spec.profile.email': profile.email, + return ctx.signInWithCatalogUser( + { + filter: { + 'spec.profile.email': profile.email, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: profile.email } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index 6af6a5844b..a280efab1a 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.4.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/config.d.ts b/plugins/auth-backend-module-gcp-iap-provider/config.d.ts index d99d890b50..572afdb0e6 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/config.d.ts +++ b/plugins/auth-backend-module-gcp-iap-provider/config.d.ts @@ -36,13 +36,23 @@ export interface Config { signIn?: { resolvers: Array< - | { resolver: 'emailMatchingUserEntityAnnotation' } - | { resolver: 'idMatchingUserEntityAnnotation' } + | { + resolver: 'emailMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'idMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index 7ee3097075..6366642ec8 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", - "version": "0.4.3-next.1", + "version": "0.4.3-next.2", "description": "A GCP IAP auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", @@ -42,7 +42,8 @@ "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/types": "workspace:^", - "google-auth-library": "^9.0.0" + "google-auth-library": "^9.0.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/auth-backend-module-gcp-iap-provider/report.api.md b/plugins/auth-backend-module-gcp-iap-provider/report.api.md index 1476694f99..5cad28ac87 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/report.api.md +++ b/plugins/auth-backend-module-gcp-iap-provider/report.api.md @@ -35,11 +35,17 @@ export type GcpIapResult = { export namespace gcpIapSignInResolvers { const emailMatchingUserEntityAnnotation: SignInResolverFactory< GcpIapResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; const idMatchingUserEntityAnnotation: SignInResolverFactory< GcpIapResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } diff --git a/plugins/auth-backend-module-gcp-iap-provider/src/resolvers.ts b/plugins/auth-backend-module-gcp-iap-provider/src/resolvers.ts index 557ee8fdfe..77dc7b1062 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-gcp-iap-provider/src/resolvers.ts @@ -19,6 +19,7 @@ import { SignInInfo, } from '@backstage/plugin-auth-node'; import { GcpIapResult } from './types'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Google auth provider. @@ -30,7 +31,12 @@ export namespace gcpIapSignInResolvers { * Looks up the user by matching their email to the `google.com/email` annotation. */ export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async (info: SignInInfo, ctx) => { const email = info.result.iapToken.email; @@ -38,11 +44,19 @@ export namespace gcpIapSignInResolvers { throw new Error('Google IAP sign-in result is missing email'); } - return ctx.signInWithCatalogUser({ - annotations: { - 'google.com/email': email, + return ctx.signInWithCatalogUser( + { + annotations: { + 'google.com/email': email, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: email } } + : undefined, + }, + ); }; }, }); @@ -51,15 +65,28 @@ export namespace gcpIapSignInResolvers { * Looks up the user by matching their user ID to the `google.com/user-id` annotation. */ export const idMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async (info: SignInInfo, ctx) => { const userId = info.result.iapToken.sub.split(':')[1]; - return ctx.signInWithCatalogUser({ - annotations: { - 'google.com/user-id': userId, + return ctx.signInWithCatalogUser( + { + annotations: { + 'google.com/user-id': userId, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: userId } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index dc2635956e..93a0865d63 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.3.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/config.d.ts b/plugins/auth-backend-module-github-provider/config.d.ts index 69abfa0d12..15ed412a6f 100644 --- a/plugins/auth-backend-module-github-provider/config.d.ts +++ b/plugins/auth-backend-module-github-provider/config.d.ts @@ -32,12 +32,18 @@ export interface Config { additionalScopes?: string | string[]; signIn?: { resolvers: Array< - | { resolver: 'usernameMatchingUserEntityName' } | { - resolver: 'emailLocalPartMatchingUserEntityName'; - allowedDomains?: string[]; + resolver: 'usernameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'preferredUsernameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index dabe2c4c5b..bf6529a971 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", - "version": "0.3.3-next.1", + "version": "0.3.3-next.2", "description": "The github-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -36,7 +36,8 @@ "dependencies": { "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", - "passport-github2": "^0.1.12" + "passport-github2": "^0.1.12", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-github-provider/report.api.md b/plugins/auth-backend-module-github-provider/report.api.md index 222305adfd..6bad840d00 100644 --- a/plugins/auth-backend-module-github-provider/report.api.md +++ b/plugins/auth-backend-module-github-provider/report.api.md @@ -24,7 +24,10 @@ export const githubAuthenticator: OAuthAuthenticator< export namespace githubSignInResolvers { const usernameMatchingUserEntityName: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-github-provider/src/resolvers.ts b/plugins/auth-backend-module-github-provider/src/resolvers.ts index 496080a33c..5a6934439f 100644 --- a/plugins/auth-backend-module-github-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-github-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the GitHub auth provider. @@ -31,7 +32,12 @@ export namespace githubSignInResolvers { * Looks up the user by matching their GitHub username to the entity name. */ export const usernameMatchingUserEntityName = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -43,7 +49,17 @@ export namespace githubSignInResolvers { throw new Error(`GitHub user profile does not contain a username`); } - return ctx.signInWithCatalogUser({ entityRef: { name: userId } }); + return ctx.signInWithCatalogUser( + { + entityRef: { name: userId }, + }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: userId } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index 6ecb16c14a..4001ca4ac0 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.3.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/config.d.ts b/plugins/auth-backend-module-gitlab-provider/config.d.ts index 8d47421255..8cbb253e2c 100644 --- a/plugins/auth-backend-module-gitlab-provider/config.d.ts +++ b/plugins/auth-backend-module-gitlab-provider/config.d.ts @@ -32,12 +32,19 @@ export interface Config { additionalScopes?: string | string[]; signIn?: { resolvers: Array< - | { resolver: 'usernameMatchingUserEntityName' } + | { + resolver: 'usernameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index fd726ebed5..677bac514e 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", - "version": "0.3.3-next.1", + "version": "0.3.3-next.2", "description": "The gitlab-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -38,7 +38,8 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "passport": "^0.7.0", - "passport-gitlab2": "^5.0.0" + "passport-gitlab2": "^5.0.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-gitlab-provider/report.api.md b/plugins/auth-backend-module-gitlab-provider/report.api.md index 44c5ffb214..cb914deeea 100644 --- a/plugins/auth-backend-module-gitlab-provider/report.api.md +++ b/plugins/auth-backend-module-gitlab-provider/report.api.md @@ -24,7 +24,10 @@ export const gitlabAuthenticator: OAuthAuthenticator< export namespace gitlabSignInResolvers { const usernameMatchingUserEntityName: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-gitlab-provider/src/resolvers.ts b/plugins/auth-backend-module-gitlab-provider/src/resolvers.ts index 755ed08aa0..d5715f7e91 100644 --- a/plugins/auth-backend-module-gitlab-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-gitlab-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the GitLab auth provider. @@ -31,7 +32,12 @@ export namespace gitlabSignInResolvers { * Looks up the user by matching their GitLab username to the entity name. */ export const usernameMatchingUserEntityName = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -43,7 +49,17 @@ export namespace gitlabSignInResolvers { throw new Error(`GitLab user profile does not contain a username`); } - return ctx.signInWithCatalogUser({ entityRef: { name: id } }); + return ctx.signInWithCatalogUser( + { + entityRef: { name: id }, + }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: id } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 6271e93dec..08ba55f539 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.3.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/config.d.ts b/plugins/auth-backend-module-google-provider/config.d.ts index ec3cd92b31..842635480c 100644 --- a/plugins/auth-backend-module-google-provider/config.d.ts +++ b/plugins/auth-backend-module-google-provider/config.d.ts @@ -32,12 +32,19 @@ export interface Config { additionalScopes?: string | string[]; signIn?: { resolvers: Array< - | { resolver: 'emailMatchingUserEntityAnnotation' } + | { + resolver: 'emailMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index cdc365ddfb..69f95614f6 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", - "version": "0.3.3-next.1", + "version": "0.3.3-next.2", "description": "A Google auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", @@ -41,7 +41,8 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "google-auth-library": "^9.0.0", - "passport-google-oauth20": "^2.0.0" + "passport-google-oauth20": "^2.0.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/auth-backend-module-google-provider/report.api.md b/plugins/auth-backend-module-google-provider/report.api.md index eeefb8e837..f06a3c3355 100644 --- a/plugins/auth-backend-module-google-provider/report.api.md +++ b/plugins/auth-backend-module-google-provider/report.api.md @@ -24,7 +24,10 @@ export const googleAuthenticator: OAuthAuthenticator< export namespace googleSignInResolvers { const emailMatchingUserEntityAnnotation: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } diff --git a/plugins/auth-backend-module-google-provider/src/resolvers.ts b/plugins/auth-backend-module-google-provider/src/resolvers.ts index b19fc4d49f..297ac0da6e 100644 --- a/plugins/auth-backend-module-google-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-google-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Google auth provider. @@ -31,7 +32,12 @@ export namespace googleSignInResolvers { * Looks up the user by matching their email to the `google.com/email` annotation. */ export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -42,11 +48,19 @@ export namespace googleSignInResolvers { throw new Error('Google profile contained no email'); } - return ctx.signInWithCatalogUser({ - annotations: { - 'google.com/email': profile.email, + return ctx.signInWithCatalogUser( + { + annotations: { + 'google.com/email': profile.email, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: profile.email } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-guest-provider/CHANGELOG.md b/plugins/auth-backend-module-guest-provider/CHANGELOG.md index 3bb742c6ef..dd906358c1 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-guest-provider/package.json b/plugins/auth-backend-module-guest-provider/package.json index 80fe709602..1562ea78fb 100644 --- a/plugins/auth-backend-module-guest-provider/package.json +++ b/plugins/auth-backend-module-guest-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-guest-provider", - "version": "0.2.8-next.1", + "version": "0.2.8-next.2", "description": "The guest-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index 2375a09efd..a84516bb02 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.3.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/config.d.ts b/plugins/auth-backend-module-microsoft-provider/config.d.ts index 3cfcd45a2a..8ea62ddd86 100644 --- a/plugins/auth-backend-module-microsoft-provider/config.d.ts +++ b/plugins/auth-backend-module-microsoft-provider/config.d.ts @@ -34,13 +34,23 @@ export interface Config { skipUserProfile?: boolean; signIn?: { resolvers: Array< - | { resolver: 'emailMatchingUserEntityAnnotation' } + | { + resolver: 'emailMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'userIdMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } - | { resolver: 'userIdMatchingUserEntityAnnotation' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index a64b9d04d4..2e04eb41e5 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", - "version": "0.3.3-next.1", + "version": "0.3.3-next.2", "description": "The microsoft-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -38,7 +38,8 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "jose": "^5.0.0", - "passport-microsoft": "^1.0.0" + "passport-microsoft": "^1.0.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-microsoft-provider/report.api.md b/plugins/auth-backend-module-microsoft-provider/report.api.md index 6aef395d7c..9e37eee771 100644 --- a/plugins/auth-backend-module-microsoft-provider/report.api.md +++ b/plugins/auth-backend-module-microsoft-provider/report.api.md @@ -30,11 +30,17 @@ export const microsoftAuthenticator: OAuthAuthenticator< export namespace microsoftSignInResolvers { const emailMatchingUserEntityAnnotation: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; const userIdMatchingUserEntityAnnotation: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts b/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts index db7aa4f7f3..0ce276522e 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Microsoft auth provider. @@ -31,7 +32,12 @@ export namespace microsoftSignInResolvers { * Looks up the user by matching their Microsoft email to the email entity annotation. */ export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -42,11 +48,19 @@ export namespace microsoftSignInResolvers { throw new Error('Microsoft profile contained no email'); } - return ctx.signInWithCatalogUser({ - annotations: { - 'microsoft.com/email': profile.email, + return ctx.signInWithCatalogUser( + { + annotations: { + 'microsoft.com/email': profile.email, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: profile.email } } + : undefined, + }, + ); }; }, }); @@ -55,7 +69,12 @@ export namespace microsoftSignInResolvers { */ export const userIdMatchingUserEntityAnnotation = createSignInResolverFactory( { - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -68,11 +87,19 @@ export namespace microsoftSignInResolvers { throw new Error('Microsoft profile contained no id'); } - return ctx.signInWithCatalogUser({ - annotations: { - 'graph.microsoft.com/user-id': id, + return ctx.signInWithCatalogUser( + { + annotations: { + 'graph.microsoft.com/user-id': id, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: id } } + : undefined, + }, + ); }; }, }, diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index e8359616eb..34a3ea522b 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.4.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/config.d.ts b/plugins/auth-backend-module-oauth2-provider/config.d.ts index ba047e3340..8818211318 100644 --- a/plugins/auth-backend-module-oauth2-provider/config.d.ts +++ b/plugins/auth-backend-module-oauth2-provider/config.d.ts @@ -36,12 +36,19 @@ export interface Config { includeBasicAuth?: boolean; signIn?: { resolvers: Array< - | { resolver: 'usernameMatchingUserEntityName' } + | { + resolver: 'usernameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index 4b9f9b6c02..8061de65b6 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", - "version": "0.4.3-next.1", + "version": "0.4.3-next.2", "description": "The oauth2-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -37,7 +37,8 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "passport": "^0.7.0", - "passport-oauth2": "^1.6.1" + "passport-oauth2": "^1.6.1", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-oauth2-provider/report.api.md b/plugins/auth-backend-module-oauth2-provider/report.api.md index 632591ec04..b4b7d6d465 100644 --- a/plugins/auth-backend-module-oauth2-provider/report.api.md +++ b/plugins/auth-backend-module-oauth2-provider/report.api.md @@ -24,7 +24,10 @@ export const oauth2Authenticator: OAuthAuthenticator< export namespace oauth2SignInResolvers { const usernameMatchingUserEntityName: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-oauth2-provider/src/resolvers.ts b/plugins/auth-backend-module-oauth2-provider/src/resolvers.ts index c77f5afa03..bad3f015c8 100644 --- a/plugins/auth-backend-module-oauth2-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-oauth2-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the oauth2 auth provider. @@ -31,7 +32,12 @@ export namespace oauth2SignInResolvers { * Looks up the user by matching their oauth2 username to the entity name. */ export const usernameMatchingUserEntityName = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -43,7 +49,17 @@ export namespace oauth2SignInResolvers { throw new Error(`Oauth2 user profile does not contain a username`); } - return ctx.signInWithCatalogUser({ entityRef: { name: id } }); + return ctx.signInWithCatalogUser( + { + entityRef: { name: id }, + }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: id } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index a4ed6a4989..f64ab78eaa 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.2.8-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/errors@1.2.7 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/package.json b/plugins/auth-backend-module-oauth2-proxy-provider/package.json index c1df361682..b77acc0055 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/package.json +++ b/plugins/auth-backend-module-oauth2-proxy-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-proxy-provider", - "version": "0.2.8-next.1", + "version": "0.2.8-next.2", "description": "The oauth2-proxy-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -36,7 +36,8 @@ "@backstage/backend-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", - "jose": "^5.0.0" + "jose": "^5.0.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/report.api.md b/plugins/auth-backend-module-oauth2-proxy-provider/report.api.md index aa360bc9c5..2efdc52635 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/report.api.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/report.api.md @@ -6,6 +6,7 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { IncomingHttpHeaders } from 'http'; import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; +import { SignInResolverFactory } from '@backstage/plugin-auth-node'; // @public (undocumented) const authModuleOauth2ProxyProvider: BackendFeature; @@ -30,4 +31,16 @@ export type OAuth2ProxyResult = { headers: IncomingHttpHeaders; getHeader(name: string): string | undefined; }; + +// @public (undocumented) +export namespace oauth2ProxySignInResolvers { + const // (undocumented) + forwardedUserMatchingUserEntityName: SignInResolverFactory< + OAuth2ProxyResult, + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined + >; +} ``` diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/src/index.ts b/plugins/auth-backend-module-oauth2-proxy-provider/src/index.ts index b804f666c7..34617d4325 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/src/index.ts +++ b/plugins/auth-backend-module-oauth2-proxy-provider/src/index.ts @@ -20,6 +20,7 @@ * @packageDocumentation */ export { authModuleOauth2ProxyProvider as default } from './module'; +export { oauth2ProxySignInResolvers } from './resolvers'; export { oauth2ProxyAuthenticator, OAUTH2_PROXY_JWT_HEADER, diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/src/resolvers.ts b/plugins/auth-backend-module-oauth2-proxy-provider/src/resolvers.ts index 99ca8bc156..8ac639b3c0 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-oauth2-proxy-provider/src/resolvers.ts @@ -19,6 +19,7 @@ import { SignInInfo, } from '@backstage/plugin-auth-node'; import { OAuth2ProxyResult } from './types'; +import { z } from 'zod'; /** * @public @@ -26,15 +27,29 @@ import { OAuth2ProxyResult } from './types'; export namespace oauth2ProxySignInResolvers { export const forwardedUserMatchingUserEntityName = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async (info: SignInInfo, ctx) => { const name = info.result.getHeader('x-forwarded-user'); if (!name) { throw new Error('Request did not contain a user'); } - return ctx.signInWithCatalogUser({ - entityRef: { name }, - }); + + return ctx.signInWithCatalogUser( + { + entityRef: { name }, + }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 33520a8bde..f1d17a579d 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.4.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-backend@0.25.0-next.2 + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index 8a1df804e6..c11b9319ec 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -38,8 +38,16 @@ export interface Config { | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'preferredUsernameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 65c1d8f208..33f323c335 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", - "version": "0.4.3-next.1", + "version": "0.4.3-next.2", "description": "The oidc-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -41,7 +41,8 @@ "@backstage/types": "workspace:^", "express": "^4.18.2", "openid-client": "^5.5.0", - "passport": "^0.7.0" + "passport": "^0.7.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-oidc-provider/report.api.md b/plugins/auth-backend-module-oidc-provider/report.api.md index 93c000c2ee..11cde22db1 100644 --- a/plugins/auth-backend-module-oidc-provider/report.api.md +++ b/plugins/auth-backend-module-oidc-provider/report.api.md @@ -41,12 +41,17 @@ export namespace oidcSignInResolvers { unknown, | { allowedDomains?: string[] | undefined; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; } | undefined >; const emailMatchingUserEntityProfileEmail: SignInResolverFactory< unknown, - unknown + | { + allowedDomains?: string[] | undefined; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-oidc-provider/src/module.ts b/plugins/auth-backend-module-oidc-provider/src/module.ts index 5680cd8603..5e4daf20c3 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.ts @@ -16,7 +16,6 @@ import { createBackendModule } from '@backstage/backend-plugin-api'; import { authProvidersExtensionPoint, - commonSignInResolvers, createOAuthProviderFactory, } from '@backstage/plugin-auth-node'; import { oidcAuthenticator } from './authenticator'; @@ -38,7 +37,6 @@ export const authModuleOidcProvider = createBackendModule({ authenticator: oidcAuthenticator, signInResolverFactories: { ...oidcSignInResolvers, - ...commonSignInResolvers, }, }), }); diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index 3f7838609e..79660f330f 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.2.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.2.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/config.d.ts b/plugins/auth-backend-module-okta-provider/config.d.ts index 3f27e805ec..f475a1d693 100644 --- a/plugins/auth-backend-module-okta-provider/config.d.ts +++ b/plugins/auth-backend-module-okta-provider/config.d.ts @@ -34,12 +34,19 @@ export interface Config { additionalScopes?: string | string[]; signIn?: { resolvers: Array< - | { resolver: 'emailMatchingUserEntityAnnotation' } + | { + resolver: 'emailMatchingUserEntityAnnotation'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 70e528d0b1..70d8defb25 100644 --- a/plugins/auth-backend-module-okta-provider/package.json +++ b/plugins/auth-backend-module-okta-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-okta-provider", - "version": "0.2.3-next.1", + "version": "0.2.3-next.2", "description": "The okta-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -38,7 +38,8 @@ "@backstage/plugin-auth-node": "workspace:^", "@davidzemon/passport-okta-oauth": "^0.0.5", "express": "^4.18.2", - "passport": "^0.7.0" + "passport": "^0.7.0", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-okta-provider/report.api.md b/plugins/auth-backend-module-okta-provider/report.api.md index a142265100..796e0f20a6 100644 --- a/plugins/auth-backend-module-okta-provider/report.api.md +++ b/plugins/auth-backend-module-okta-provider/report.api.md @@ -24,7 +24,10 @@ export const oktaAuthenticator: OAuthAuthenticator< export namespace oktaSignInResolvers { const emailMatchingUserEntityAnnotation: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-okta-provider/src/resolvers.ts b/plugins/auth-backend-module-okta-provider/src/resolvers.ts index 8bc3f38f17..cdb37dbaae 100644 --- a/plugins/auth-backend-module-okta-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-okta-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the Okta auth provider. @@ -32,7 +33,12 @@ export namespace oktaSignInResolvers { */ export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -43,11 +49,19 @@ export namespace oktaSignInResolvers { throw new Error('Okta profile contained no email'); } - return ctx.signInWithCatalogUser({ - annotations: { - 'okta.com/email': profile.email, + return ctx.signInWithCatalogUser( + { + annotations: { + 'okta.com/email': profile.email, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: profile.email } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md index 410f2eda91..c6e5d2944a 100644 --- a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-onelogin-provider +## 0.3.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-onelogin-provider/config.d.ts b/plugins/auth-backend-module-onelogin-provider/config.d.ts index 58f471bafb..e8fd6f6749 100644 --- a/plugins/auth-backend-module-onelogin-provider/config.d.ts +++ b/plugins/auth-backend-module-onelogin-provider/config.d.ts @@ -31,12 +31,19 @@ export interface Config { callbackUrl?: string; signIn?: { resolvers: Array< - | { resolver: 'usernameMatchingUserEntityName' } + | { + resolver: 'usernameMatchingUserEntityName'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index 1e9efc21ea..6867251e54 100644 --- a/plugins/auth-backend-module-onelogin-provider/package.json +++ b/plugins/auth-backend-module-onelogin-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-onelogin-provider", - "version": "0.3.3-next.1", + "version": "0.3.3-next.2", "description": "The onelogin-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -38,7 +38,8 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "passport": "^0.7.0", - "passport-onelogin-oauth": "^0.0.1" + "passport-onelogin-oauth": "^0.0.1", + "zod": "^3.22.4" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/plugins/auth-backend-module-onelogin-provider/report.api.md b/plugins/auth-backend-module-onelogin-provider/report.api.md index 1991f00e0b..df3345f5ed 100644 --- a/plugins/auth-backend-module-onelogin-provider/report.api.md +++ b/plugins/auth-backend-module-onelogin-provider/report.api.md @@ -24,7 +24,10 @@ export const oneLoginAuthenticator: OAuthAuthenticator< export namespace oneLoginSignInResolvers { const usernameMatchingUserEntityName: SignInResolverFactory< OAuthAuthenticatorResult, - unknown + | { + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; } ``` diff --git a/plugins/auth-backend-module-onelogin-provider/src/resolvers.ts b/plugins/auth-backend-module-onelogin-provider/src/resolvers.ts index 93bfd758cb..56710472be 100644 --- a/plugins/auth-backend-module-onelogin-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-onelogin-provider/src/resolvers.ts @@ -20,6 +20,7 @@ import { PassportProfile, SignInInfo, } from '@backstage/plugin-auth-node'; +import { z } from 'zod'; /** * Available sign-in resolvers for the OneLogin auth provider. @@ -31,7 +32,12 @@ export namespace oneLoginSignInResolvers { * Looks up the user by matching their OneLogin username to the entity name. */ export const usernameMatchingUserEntityName = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async ( info: SignInInfo>, ctx, @@ -43,7 +49,17 @@ export namespace oneLoginSignInResolvers { throw new Error(`OneLogin user profile does not contain a username`); } - return ctx.signInWithCatalogUser({ entityRef: { name: id } }); + return ctx.signInWithCatalogUser( + { + entityRef: { name: id }, + }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: id } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 7c1ee827b6..865f494229 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index 940443cf7e..8c0569dbb4 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", - "version": "0.3.3-next.1", + "version": "0.3.3-next.2", "description": "The pinniped-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md index fc085d8c31..62f2a9373d 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.5.3-next.2 + +### Patch Changes + +- 5cc1f7f: Introduce `dangerouslyAllowSignInWithoutUserInCatalog` auth resolver config. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + ## 0.5.3-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts b/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts index 9886d69a41..235d2ab0f1 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts +++ b/plugins/auth-backend-module-vmware-cloud-provider/config.d.ts @@ -32,8 +32,12 @@ export interface Config { | { resolver: 'emailLocalPartMatchingUserEntityName'; allowedDomains?: string[]; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; + } + | { + resolver: 'emailMatchingUserEntityProfileEmail'; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean; } - | { resolver: 'emailMatchingUserEntityProfileEmail' } >; }; sessionDuration?: HumanDuration | string; diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index bf209f69fd..7f3c77a1e4 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/package.json +++ b/plugins/auth-backend-module-vmware-cloud-provider/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend-module-vmware-cloud-provider", - "version": "0.5.3-next.1", + "version": "0.5.3-next.2", "description": "The vmware-cloud-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 8f12cd8305..ebf41fff38 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-auth-backend +## 0.25.0-next.2 + +### Patch Changes + +- ab53e6f: Added support for the new `dangerousEntityRefFallback` option for `signInWithCatalogUser` in `AuthResolverContext`. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.25.0-next.1 ### Patch Changes diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 3fce660aa5..ce99f85b08 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.25.0-next.1", + "version": "0.25.0-next.2", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index 5268833d14..4f9b33d021 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -139,19 +139,54 @@ export class CatalogAuthResolverContext implements AuthResolverContext { return { entity: result }; } - async signInWithCatalogUser(query: AuthResolverCatalogUserQuery) { - const { entity } = await this.findCatalogUser(query); + async signInWithCatalogUser( + query: AuthResolverCatalogUserQuery, + options?: { + dangerousEntityRefFallback?: { + entityRef: + | string + | { + kind?: string; + namespace?: string; + name: string; + }; + }; + }, + ) { + try { + const { entity } = await this.findCatalogUser(query); - const { ownershipEntityRefs } = await this.resolveOwnershipEntityRefs( - entity, - ); + const { ownershipEntityRefs } = await this.resolveOwnershipEntityRefs( + entity, + ); - return await this.tokenIssuer.issueToken({ - claims: { - sub: stringifyEntityRef(entity), - ent: ownershipEntityRefs, - }, - }); + return await this.tokenIssuer.issueToken({ + claims: { + sub: stringifyEntityRef(entity), + ent: ownershipEntityRefs, + }, + }); + } catch (error) { + if ( + error?.name !== 'NotFoundError' || + !options?.dangerousEntityRefFallback + ) { + throw error; + } + const userEntityRef = stringifyEntityRef( + parseEntityRef(options.dangerousEntityRefFallback.entityRef, { + defaultKind: 'User', + defaultNamespace: DEFAULT_NAMESPACE, + }), + ); + + return await this.tokenIssuer.issueToken({ + claims: { + sub: userEntityRef, + ent: [userEntityRef], + }, + }); + } } async resolveOwnershipEntityRefs( diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 7fd817c2aa..5e60a40cd3 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,52 @@ # @backstage/plugin-auth-node +## 0.6.3-next.2 + +### Patch Changes + +- ab53e6f: Added a new `dangerousEntityRefFallback` option to the `signInWithCatalogUser` method in `AuthResolverContext`. The option will cause the provided entity reference to be used as a fallback in case the user is not found in the catalog. It is up to the caller to provide the fallback entity reference. + + Auth providers that include pre-defined sign-in resolvers are encouraged to define a flag named `dangerouslyAllowSignInWithoutUserInCatalog` in their config, which in turn enables use of the `dangerousEntityRefFallback` option. For example: + + ```ts + export const usernameMatchingUserEntityName = createSignInResolverFactory({ + optionsSchema: z + .object({ + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { + return async ( + info: SignInInfo>, + ctx, + ) => { + const { username } = info.result.fullProfile; + if (!username) { + throw new Error('User profile does not contain a username'); + } + + return ctx.signInWithCatalogUser( + { entityRef: { name: username } }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: username } } + : undefined, + }, + ); + }; + }, + }); + ``` + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.6.3-next.1 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index 744bff38ef..2c9e8c007d 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.6.3-next.1", + "version": "0.6.3-next.2", "backstage": { "role": "node-library", "pluginId": "auth", diff --git a/plugins/auth-node/report.api.md b/plugins/auth-node/report.api.md index ad0d85030c..090f981636 100644 --- a/plugins/auth-node/report.api.md +++ b/plugins/auth-node/report.api.md @@ -110,6 +110,17 @@ export type AuthResolverContext = { }>; signInWithCatalogUser( query: AuthResolverCatalogUserQuery, + options?: { + dangerousEntityRefFallback?: { + entityRef: + | string + | { + kind?: string; + namespace?: string; + name: string; + }; + }; + }, ): Promise; resolveOwnershipEntityRefs(entity: Entity): Promise<{ ownershipEntityRefs: string[]; @@ -146,12 +157,17 @@ export type ClientAuthResponse = { export namespace commonSignInResolvers { const emailMatchingUserEntityProfileEmail: SignInResolverFactory< unknown, - unknown + | { + allowedDomains?: string[] | undefined; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; + } + | undefined >; const emailLocalPartMatchingUserEntityName: SignInResolverFactory< unknown, | { allowedDomains?: string[] | undefined; + dangerouslyAllowSignInWithoutUserInCatalog?: boolean | undefined; } | undefined >; diff --git a/plugins/auth-node/src/sign-in/commonSignInResolvers.ts b/plugins/auth-node/src/sign-in/commonSignInResolvers.ts index 6f0fc7fdb2..2b7442f746 100644 --- a/plugins/auth-node/src/sign-in/commonSignInResolvers.ts +++ b/plugins/auth-node/src/sign-in/commonSignInResolvers.ts @@ -35,7 +35,13 @@ export namespace commonSignInResolvers { */ export const emailMatchingUserEntityProfileEmail = createSignInResolverFactory({ - create() { + optionsSchema: z + .object({ + allowedDomains: z.array(z.string()).optional(), + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), + }) + .optional(), + create(options = {}) { return async (info, ctx) => { const { profile } = info; @@ -59,11 +65,19 @@ export namespace commonSignInResolvers { const [_, name, _plus, domain] = m; const noPlusEmail = `${name}${domain}`; - return ctx.signInWithCatalogUser({ - filter: { - 'spec.profile.email': noPlusEmail, + return ctx.signInWithCatalogUser( + { + filter: { + 'spec.profile.email': noPlusEmail, + }, }, - }); + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: noPlusEmail } } + : undefined, + }, + ); } } // Email had no plus addressing or is missing in the catalog, forward failure @@ -82,6 +96,7 @@ export namespace commonSignInResolvers { optionsSchema: z .object({ allowedDomains: z.array(z.string()).optional(), + dangerouslyAllowSignInWithoutUserInCatalog: z.boolean().optional(), }) .optional(), create(options = {}) { @@ -102,10 +117,15 @@ export namespace commonSignInResolvers { 'Sign-in user email is not from an allowed domain', ); } - - return ctx.signInWithCatalogUser({ - entityRef: { name: localPart }, - }); + return ctx.signInWithCatalogUser( + { entityRef: { name: localPart } }, + { + dangerousEntityRefFallback: + options?.dangerouslyAllowSignInWithoutUserInCatalog + ? { entityRef: { name: localPart } } + : undefined, + }, + ); }; }, }); diff --git a/plugins/auth-node/src/types.ts b/plugins/auth-node/src/types.ts index a7202d7748..8d6b8f34fb 100644 --- a/plugins/auth-node/src/types.ts +++ b/plugins/auth-node/src/types.ts @@ -162,10 +162,26 @@ export type AuthResolverContext = { * Finds a single user in the catalog using the provided query, and then * issues an identity for that user using default ownership resolution. * + * If the user is not found, an optional `dangerousEntityRefFallback` + * entity ref can be provided to allow sign-in to proceed by issuing an + * identity based on the given ref. This bypasses the requirement for the + * user to exist in the catalog and should be used with caution. + * * See {@link AuthResolverCatalogUserQuery} for details. */ signInWithCatalogUser( query: AuthResolverCatalogUserQuery, + options?: { + dangerousEntityRefFallback?: { + entityRef: + | string + | { + kind?: string; + namespace?: string; + name: string; + }; + }; + }, ): Promise; /** diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 5cff146b61..4b6241bb7b 100644 --- a/plugins/bitbucket-cloud-common/CHANGELOG.md +++ b/plugins/bitbucket-cloud-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-bitbucket-cloud-common +## 0.3.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + ## 0.3.0-next.2 ### Patch Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 7b336e3455..40006fc8d7 100644 --- a/plugins/bitbucket-cloud-common/package.json +++ b/plugins/bitbucket-cloud-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bitbucket-cloud-common", - "version": "0.3.0-next.2", + "version": "0.3.0-next.3", "description": "Common functionalities for bitbucket-cloud plugins", "backstage": { "role": "common-library", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 600a4f7e93..d22346d163 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.11-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.16-next.0 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-kubernetes-common@0.9.5-next.0 + ## 0.4.11-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index 157fe665b0..a3cb716c29 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", - "version": "0.4.11-next.2", + "version": "0.4.11-next.3", "description": "A Backstage catalog backend module that helps integrate towards AWS", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 9ce4ec3f4e..3e9a4c6aad 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.3.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.3.5-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index 5fc27e581d..7f1d5453d7 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", - "version": "0.3.5-next.2", + "version": "0.3.5-next.3", "description": "A Backstage catalog backend module that helps integrate towards Azure", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md index 3bffb1d42e..5160254a3a 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.5.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.5.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.5.2-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json index de8dc971e5..a7f4f1aca2 100644 --- a/plugins/catalog-backend-module-backstage-openapi/package.json +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", - "version": "0.5.2-next.1", + "version": "0.5.2-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index f562d20651..06202b5624 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.4.8-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-bitbucket-cloud-common@0.3.0-next.3 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.4.8-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index 8d73b5fd74..48ff0b17d4 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", - "version": "0.4.8-next.2", + "version": "0.4.8-next.3", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index ecd451bd5a..8b6ff29b28 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.4.1-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.4.1-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 9dd5e16e55..ca241c615a 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-server", - "version": "0.4.1-next.2", + "version": "0.4.1-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 3ccb395b27..215f5c16bc 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-kubernetes-common@0.9.5-next.0 + ## 0.3.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 8b70f03bf0..7be8d3f195 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", - "version": "0.3.8-next.1", + "version": "0.3.8-next.2", "description": "A Backstage catalog backend module that helps integrate towards GCP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index e207e24dcf..d74fad602d 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.3.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.3.2-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 9d176a1a9d..a34a769b3c 100644 --- a/plugins/catalog-backend-module-gerrit/package.json +++ b/plugins/catalog-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gerrit", - "version": "0.3.2-next.2", + "version": "0.3.2-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gitea/.eslintrc.js b/plugins/catalog-backend-module-gitea/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/catalog-backend-module-gitea/CHANGELOG.md b/plugins/catalog-backend-module-gitea/CHANGELOG.md new file mode 100644 index 0000000000..35e32e6169 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/CHANGELOG.md @@ -0,0 +1,17 @@ +# @backstage/plugin-catalog-backend-module-gitea + +## 0.1.0-next.0 + +### Minor Changes + +- e4dabc6: add new gitea provider module + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 diff --git a/plugins/catalog-backend-module-gitea/README.md b/plugins/catalog-backend-module-gitea/README.md new file mode 100644 index 0000000000..6b93c9c90a --- /dev/null +++ b/plugins/catalog-backend-module-gitea/README.md @@ -0,0 +1,8 @@ +# Catalog Backend Module for Gitea + +This is an extension module to the plugin-catalog-backend plugin, providing extensions targeted at Gitea integrations. + +## Getting started + +See [Backstage documentation](https://backstage.io/docs/integrations/gitea/discovery.md) +for details on how to install and configure the plugin. diff --git a/plugins/catalog-backend-module-gitea/catalog-info.yaml b/plugins/catalog-backend-module-gitea/catalog-info.yaml new file mode 100644 index 0000000000..eb580d570b --- /dev/null +++ b/plugins/catalog-backend-module-gitea/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-catalog-backend-module-gitea + title: '@backstage/plugin-catalog-backend-module-gitea' + description: The gitea backend module for the catalog plugin provide by intive.com +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/catalog-backend-module-gitea/config.d.ts b/plugins/catalog-backend-module-gitea/config.d.ts new file mode 100644 index 0000000000..bbb1a0ee4f --- /dev/null +++ b/plugins/catalog-backend-module-gitea/config.d.ts @@ -0,0 +1,58 @@ +/* + * 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. + */ + +import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api'; + +export interface Config { + catalog?: { + /** + * List of provider-specific options and attributes + */ + providers?: { + /** + * GiteaEntityProvider configuration + * + * Maps provider id with configuration. + */ + gitea?: { + [name: string]: { + /** + * (Required) The host of the Gitea integration to use. + */ + host: string; + /** + * (Required) Name of your organization account/workspace. + */ + organization: string; + /** + * (Optional) Branch. + * The branch where the provider will try to find entities. Uses the default to "main". + */ + branch?: string; + /** + * (Optional) Path where the catalog YAML manifest file is expected in the repository. + * Defaults to "catalog-info.yaml". + */ + catalogPath?: string; + /** + * (Optional) TaskScheduleDefinition for the discovery. + */ + schedule?: SchedulerServiceTaskScheduleDefinition; + }; + }; + }; + }; +} diff --git a/plugins/catalog-backend-module-gitea/package.json b/plugins/catalog-backend-module-gitea/package.json new file mode 100644 index 0000000000..22f4dc5460 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/package.json @@ -0,0 +1,49 @@ +{ + "name": "@backstage/plugin-catalog-backend-module-gitea", + "version": "0.1.0-next.0", + "license": "Apache-2.0", + "description": "The gitea backend module for the catalog plugin.", + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/catalog-backend-module-gitea" + }, + "backstage": { + "role": "backend-plugin-module", + "pluginId": "catalog", + "pluginPackage": "@backstage/plugin-catalog-backend" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "p-limit": "^3.0.2", + "uuid": "^11.0.0" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/catalog-backend-module-gitea/report.api.md b/plugins/catalog-backend-module-gitea/report.api.md new file mode 100644 index 0000000000..1aae80271a --- /dev/null +++ b/plugins/catalog-backend-module-gitea/report.api.md @@ -0,0 +1,36 @@ +## API Report File for "@backstage/plugin-catalog-backend-module-gitea" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; +import { EntityProvider } from '@backstage/plugin-catalog-node'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { SchedulerService } from '@backstage/backend-plugin-api'; +import { SchedulerServiceTaskRunner } from '@backstage/backend-plugin-api'; + +// @public (undocumented) +const catalogModuleGitea: BackendFeature; +export default catalogModuleGitea; + +// @public (undocumented) +export class GiteaEntityProvider implements EntityProvider { + // (undocumented) + connect(connection: EntityProviderConnection): Promise; + // (undocumented) + static fromConfig( + configRoot: Config, + options: { + logger: LoggerService; + schedule?: SchedulerServiceTaskRunner; + scheduler?: SchedulerService; + }, + ): GiteaEntityProvider[]; + // (undocumented) + getProviderName(): string; + // (undocumented) + refresh(logger: LoggerService): Promise; +} +``` diff --git a/plugins/catalog-backend-module-gitea/src/index.ts b/plugins/catalog-backend-module-gitea/src/index.ts new file mode 100644 index 0000000000..b92ced7fcf --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2025 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. + */ + +/** + * The gitea backend module for the catalog plugin. + * + * @packageDocumentation + */ + +export { catalogModuleGitea as default } from './module'; +export { GiteaEntityProvider } from './providers/GiteaEntityProvider'; diff --git a/plugins/catalog-backend-module-gitea/src/module.test.ts b/plugins/catalog-backend-module-gitea/src/module.test.ts new file mode 100644 index 0000000000..0ce2d4319a --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/module.test.ts @@ -0,0 +1,86 @@ +/* + * 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. + */ + +import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { catalogModuleGitea } from './module'; +import { GiteaEntityProvider } from './providers/GiteaEntityProvider'; + +describe('catalogModuleGitea', () => { + it('should register provider at the catalog extension point', async () => { + let addedProviders: Array | undefined; + let usedSchedule: SchedulerServiceTaskScheduleDefinition | undefined; + + const extensionPoint = { + addEntityProvider: (providers: any) => { + addedProviders = providers; + }, + }; + const runner = jest.fn(); + const scheduler = mockServices.scheduler.mock({ + createScheduledTaskRunner(schedule) { + usedSchedule = schedule; + return { run: runner }; + }, + }); + + const config = { + catalog: { + providers: { + gitea: { + test: { + host: 'g.com', + organization: 'org', + branch: 'main', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + }, + integrations: { + gitea: [ + { + host: 'g.com', + baseUrl: 'https://g.com/gitea', + gitilesBaseUrl: 'https:/g.com/gitiles', + }, + ], + }, + }; + + await startTestBackend({ + extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + features: [ + catalogModuleGitea, + mockServices.rootConfig.factory({ data: config }), + mockServices.logger.factory(), + scheduler.factory, + ], + }); + + expect(usedSchedule?.frequency).toEqual({ months: 1 }); + expect(usedSchedule?.timeout).toEqual({ minutes: 3 }); + expect(addedProviders?.length).toEqual(1); + expect(addedProviders?.pop()?.getProviderName()).toEqual( + 'gitea-provider:test', + ); + expect(runner).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/catalog-backend-module-gitea/src/module.ts b/plugins/catalog-backend-module-gitea/src/module.ts new file mode 100644 index 0000000000..91e019c6fb --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/module.ts @@ -0,0 +1,47 @@ +/* + * Copyright 2025 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 { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; + +import { GiteaEntityProvider } from './providers/GiteaEntityProvider'; +/** + * @public + */ +export const catalogModuleGitea = createBackendModule({ + pluginId: 'catalog', + moduleId: 'gitea', + register(reg) { + reg.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + config: coreServices.rootConfig, + logger: coreServices.logger, + scheduler: coreServices.scheduler, + }, + + async init({ catalog, config, logger, scheduler }) { + const providers = GiteaEntityProvider.fromConfig(config, { + logger, + scheduler, + }); + catalog.addEntityProvider(providers); + }, + }); + }, +}); diff --git a/plugins/catalog-backend-module-gitea/src/providers/GiteaEntityProvider.test.ts b/plugins/catalog-backend-module-gitea/src/providers/GiteaEntityProvider.test.ts new file mode 100644 index 0000000000..d6a31899f1 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/GiteaEntityProvider.test.ts @@ -0,0 +1,472 @@ +/* + * 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. + */ +import { mockServices } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; +import { GiteaEntityProvider } from './GiteaEntityProvider'; +import * as uuid from 'uuid'; +import { readGiteaConfigs } from './config'; +import { getGiteaApiUrl } from './core'; +import { GiteaIntegration } from '@backstage/integration'; + +jest.mock('./config'); +jest.mock('./core'); +jest.mock('uuid'); + +describe('GiteaEntityProvider', () => { + const logger = mockServices.logger.mock(); + + const mockScheduler = { + createScheduledTaskRunner: jest.fn(), + triggerTask: jest.fn(), + scheduleTask: jest.fn(), + getScheduledTasks: jest.fn(), + }; + const mockTaskRunner = { + run: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + (readGiteaConfigs as jest.Mock).mockReturnValue([ + { + id: 'test-provider', + host: 'gitea.example.com', + organization: 'test-org', + catalogPath: 'catalog-info.yaml', + branch: 'main', + schedule: { + frequency: { minutes: 30 }, + timeout: { minutes: 3 }, + }, + }, + ]); + (getGiteaApiUrl as jest.Mock).mockReturnValue( + 'https://gitea.example.com/api/v1/', + ); + mockScheduler.createScheduledTaskRunner.mockReturnValue(mockTaskRunner); + (uuid.v4 as jest.Mock).mockReturnValue('test-uuid'); + + // Mock global fetch + global.fetch = jest.fn(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('fromConfig', () => { + it('should create providers from config', () => { + const config = new ConfigReader({ + integrations: { + gitea: [ + { + host: 'gitea.example.com', + token: 'test-token', + }, + ], + }, + catalog: { + providers: { + gitea: { + 'test-provider': { + host: 'gitea.example.com', + organization: 'test-org', + }, + }, + }, + }, + }); + + const providers = GiteaEntityProvider.fromConfig(config, { + logger, + scheduler: mockScheduler, + }); + + expect(providers).toHaveLength(1); + expect(providers[0].getProviderName()).toBe( + 'gitea-provider:test-provider', + ); + }); + + it('should throw if no schedule or scheduler is provided', () => { + const config = new ConfigReader({ + integrations: { + gitea: [ + { + host: 'gitea.example.com', + token: 'test-token', + }, + ], + }, + catalog: { + providers: { + gitea: { + 'test-provider': { + host: 'gitea.example.com', + organization: 'test-org', + }, + }, + }, + }, + }); + + expect(() => + GiteaEntityProvider.fromConfig(config, { + logger, + } as any), + ).toThrow('Either schedule or scheduler must be provided.'); + }); + + it('should throw if no matching integration is found', () => { + const config = new ConfigReader({ + integrations: { + gitea: [ + { + host: 'wrong-host.example.com', + token: 'test-token', + }, + ], + }, + catalog: { + providers: { + gitea: { + 'test-provider': { + host: 'gitea.example.com', + organization: 'test-org', + }, + }, + }, + }, + }); + + expect(() => + GiteaEntityProvider.fromConfig(config, { + logger, + scheduler: mockScheduler, + }), + ).toThrow( + 'No Gitea integration found that matches host gitea.example.com', + ); + }); + + it('should throw if no schedule is provided in code or config', () => { + const config = new ConfigReader({ + integrations: { + gitea: [ + { + host: 'gitea.example.com', + token: 'test-token', + }, + ], + }, + catalog: { + providers: { + gitea: { + 'test-provider': { + host: 'gitea.example.com', + organization: 'test-org', + schedule: undefined, + }, + }, + }, + }, + }); + + (readGiteaConfigs as jest.Mock).mockReturnValue([ + { + id: 'test-provider', + host: 'gitea.example.com', + organization: 'test-org', + catalogPath: 'catalog-info.yaml', + branch: 'main', + schedule: undefined, + }, + ]); + + expect(() => + GiteaEntityProvider.fromConfig(config, { + logger, + scheduler: mockScheduler, + }), + ).toThrow( + 'No schedule provided neither via code nor config for Gitea-provider:test-provider.', + ); + }); + }); + + describe('getProviderName', () => { + it('should return the correct provider name', () => { + const config = new ConfigReader({ + integrations: { + gitea: [ + { + host: 'gitea.example.com', + token: 'test-token', + }, + ], + }, + catalog: { + providers: { + gitea: { + 'test-provider': { + host: 'gitea.example.com', + organization: 'test-org', + }, + }, + }, + }, + }); + + const providers = GiteaEntityProvider.fromConfig(config, { + logger, + scheduler: mockScheduler, + }); + + expect(providers[0].getProviderName()).toBe( + 'gitea-provider:test-provider', + ); + }); + }); + + describe('connect', () => { + let provider: GiteaEntityProvider; + const mockConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + beforeEach(() => { + const config = new ConfigReader({ + integrations: { + gitea: [ + { + host: 'gitea.example.com', + token: 'test-token', + }, + ], + }, + catalog: { + providers: { + gitea: { + 'test-provider': { + host: 'gitea.example.com', + organization: 'test-org', + }, + }, + }, + }, + }); + + provider = GiteaEntityProvider.fromConfig(config, { + logger, + scheduler: mockScheduler, + })[0]; + }); + + it('should connect and schedule a refresh', async () => { + await provider.connect(mockConnection); + + expect(mockTaskRunner.run).toHaveBeenCalledWith({ + id: 'gitea-provider:test-provider:refresh', + fn: expect.any(Function), + }); + }); + }); + + describe('refresh', () => { + let provider: GiteaEntityProvider; + let integration: GiteaIntegration; + const mockConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + beforeEach(() => { + // Need to access the provider internals for refresh tests + integration = { + config: { + host: 'gitea.example.com', + token: 'test-token', + }, + } as unknown as GiteaIntegration; + + // Direct instantiation for testing the refresh method + provider = new (GiteaEntityProvider as any)( + { + id: 'test-provider', + host: 'gitea.example.com', + organization: 'test-org', + catalogPath: 'catalog-info.yaml', + branch: 'main', + }, + integration, + logger, + mockTaskRunner, + ); + }); + + it('should throw if not connected', async () => { + await expect(provider.refresh(logger)).rejects.toThrow( + 'Gitea discovery connection not initialized', + ); + }); + + it('should handle API errors gracefully', async () => { + await provider.connect(mockConnection); + + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + status: 500, + }); + + await expect(provider.refresh(logger)).rejects.toThrow( + 'Failed to list Gitea projects for organization test-org', + ); + }); + + it('should handle empty results', async () => { + await provider.connect(mockConnection); + + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: async () => [], + }); + + await provider.refresh(logger); + + expect(mockConnection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: [], + }); + }); + + it('should handle pagination and filter repos with catalog files', async () => { + await provider.connect(mockConnection); + + // Page 1 response + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: async () => [ + { + name: 'repo1', + html_url: 'https://gitea.example.com/test-org/repo1', + empty: false, + }, + { + name: 'repo2', + html_url: 'https://gitea.example.com/test-org/repo2', + empty: false, + }, + { + name: 'empty-repo', + html_url: 'https://gitea.example.com/test-org/empty-repo', + empty: true, + }, + ], + }); + + // Page 2 response + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: async () => [ + { + name: 'repo3', + html_url: 'https://gitea.example.com/test-org/repo3', + empty: false, + }, + ], + }); + + // Empty page 3 to terminate pagination + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: async () => [], + }); + + // Catalog check for repo1 - has catalog file + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + }); + + // Catalog check for repo2 - no catalog file + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: false, + }); + + // Catalog check for repo3 - has catalog file + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + }); + + await provider.refresh(logger); + + expect(mockConnection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: [ + expect.objectContaining({ + locationKey: 'gitea-provider:test-provider', + entity: expect.anything(), + }), + expect.objectContaining({ + locationKey: 'gitea-provider:test-provider', + entity: expect.anything(), + }), + ], + }); + + // Verify we made requests for all repos + expect(global.fetch).toHaveBeenCalledTimes(6); + }); + + it('should handle errors when applying mutations', async () => { + await provider.connect(mockConnection); + + // Mock repository data + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: async () => [ + { + name: 'repo1', + html_url: 'https://gitea.example.com/test-org/repo1', + empty: false, + }, + ], + }); + + // Empty page 2 + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + json: async () => [], + }); + + // Catalog check - has catalog file + (global.fetch as jest.Mock).mockResolvedValueOnce({ + ok: true, + }); + + // Mock mutation failure + (mockConnection.applyMutation as jest.Mock).mockRejectedValueOnce( + new Error('Mutation failed'), + ); + + // Should not throw but log the error + await provider.refresh(logger); + + expect(mockConnection.applyMutation).toHaveBeenCalled(); + }); + }); +}); diff --git a/plugins/catalog-backend-module-gitea/src/providers/GiteaEntityProvider.ts b/plugins/catalog-backend-module-gitea/src/providers/GiteaEntityProvider.ts new file mode 100644 index 0000000000..2f9475af26 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/GiteaEntityProvider.ts @@ -0,0 +1,266 @@ +/* + * 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. + */ +import * as uuid from 'uuid'; +import { Config } from '@backstage/config'; +import { InputError } from '@backstage/errors'; +import { + EntityProvider, + EntityProviderConnection, + locationSpecToLocationEntity, +} from '@backstage/plugin-catalog-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { + GiteaIntegration, + getGiteaRequestOptions, + ScmIntegrations, +} from '@backstage/integration'; +import { getGiteaApiUrl } from './core'; + +import { readGiteaConfigs } from './config'; +import { GiteaProjectQueryResult, GiteaProviderConfig } from './types'; +import { + LoggerService, + SchedulerService, + SchedulerServiceTaskRunner, +} from '@backstage/backend-plugin-api'; + +/** @public */ +export class GiteaEntityProvider implements EntityProvider { + private readonly config: GiteaProviderConfig; + private readonly integration: GiteaIntegration; + private readonly logger: LoggerService; + private readonly scheduleFn: () => Promise; + private connection?: EntityProviderConnection; + + static fromConfig( + configRoot: Config, + options: { + logger: LoggerService; + schedule?: SchedulerServiceTaskRunner; + scheduler?: SchedulerService; + }, + ): GiteaEntityProvider[] { + if (!options.schedule && !options.scheduler) { + throw new Error('Either schedule or scheduler must be provided.'); + } + + const providerConfigs = readGiteaConfigs(configRoot); + const integrations = ScmIntegrations.fromConfig(configRoot).gitea; + const providers: GiteaEntityProvider[] = []; + + providerConfigs.forEach(providerConfig => { + const integration = integrations.byHost(providerConfig.host); + if (!integration) { + throw new InputError( + `No Gitea integration found that matches host ${providerConfig.host}`, + ); + } + + if (!options.schedule && !providerConfig.schedule) { + throw new Error( + `No schedule provided neither via code nor config for Gitea-provider:${providerConfig.id}.`, + ); + } + + const taskRunner = + options.schedule ?? + options.scheduler!.createScheduledTaskRunner(providerConfig.schedule!); + + providers.push( + new GiteaEntityProvider( + providerConfig, + integration, + options.logger, + taskRunner, + ), + ); + }); + + return providers; + } + + private constructor( + config: GiteaProviderConfig, + integration: GiteaIntegration, + logger: LoggerService, + taskRunner: SchedulerServiceTaskRunner, + ) { + this.config = config; + this.integration = integration; + this.logger = logger.child({ + target: this.getProviderName(), + }); + this.scheduleFn = this.createScheduleFn(taskRunner); + } + + getProviderName(): string { + return `gitea-provider:${this.config.id}`; + } + + async connect(connection: EntityProviderConnection): Promise { + this.connection = connection; + await this.scheduleFn(); + } + + private async parseGiteaJsonResponse( + response: Response, + ): Promise { + if (!response.ok) { + throw new Error( + `Failed to fetch Gitea repositories, status: ${response.status}`, + ); + } + try { + return await response.json(); + } catch (error) { + throw new Error(`Failed to parse Gitea API response: ${error}`); + } + } + + private createScheduleFn( + taskRunner: SchedulerServiceTaskRunner, + ): () => Promise { + return async () => { + const taskId = `${this.getProviderName()}:refresh`; + return taskRunner.run({ + id: taskId, + fn: async () => { + const logger = this.logger.child({ + class: GiteaEntityProvider.prototype.constructor.name, + taskId, + taskInstanceId: uuid.v4(), + }); + + try { + await this.refresh(logger); + } catch (error) { + logger.error( + `${this.getProviderName()} refresh failed, ${error}`, + error as Error, + ); + } + }, + }); + }; + } + + async refresh(logger: LoggerService): Promise { + if (!this.connection) { + throw new Error('Gitea discovery connection not initialized'); + } + let allRepos: any[] = []; + + try { + // Fetch all repos for an organization + const OrgRepoApiUrl = `${getGiteaApiUrl(this.integration.config)}orgs/${ + this.config.organization + }/repos`; + let page = 1; + let hasMoreData = true; + + while (hasMoreData) { + const url = `${OrgRepoApiUrl}?page=${page}`; + const response = await fetch(url, { + method: 'GET', + ...getGiteaRequestOptions(this.integration.config), + }); + + if (!response.ok) { + throw new Error( + `Failed to fetch page ${page} of repos, status: ${response.status}`, + ); + } + + const projectsPage = (await this.parseGiteaJsonResponse( + response, + )) as GiteaProjectQueryResult; + + if (!Array.isArray(projectsPage) || projectsPage.length === 0) { + hasMoreData = false; + } else { + allRepos = allRepos.concat(projectsPage); + page++; + } + } + } catch (e) { + throw new Error( + `Failed to list Gitea projects for organization ${this.config.organization}, ${e}`, + ); + } + + // Filter for repos that have catalog-info.yaml at root + const { default: pLimit } = await import('p-limit'); + const limit = pLimit(5); + const validRepos: string[] = []; + + await Promise.all( + allRepos.map(repo => + limit(async () => { + if (repo.empty) { + logger.warn(`Repo ${repo.html_url} is empty, skipping`); + return; + } + const contentsApiUrl = `${getGiteaApiUrl( + this.integration.config, + )}repos/${this.config.organization}/${repo.name}/contents/${ + this.config.catalogPath + }`; + + const res = await fetch(contentsApiUrl, { + method: 'GET', + ...getGiteaRequestOptions(this.integration.config), + }); + if (res.ok) { + validRepos.push(repo.html_url); + } else { + logger.warn( + `Repo ${repo.html_url} does not contain a catalog-info.yaml file`, + ); + return; + } + }), + ), + ); + // Create location specs for each valid repo + const locations = await Promise.all( + validRepos.map(repo => limit(() => this.createLocationSpec(repo))), + ); + // Apply the locations to the catalog + try { + const providerName: string = this.getProviderName(); + const entities = locations.map(location => ({ + locationKey: providerName, + entity: locationSpecToLocationEntity({ location }), + })); + await this.connection.applyMutation({ + type: 'full', + entities: entities, + }); + } catch (e) { + logger.error(`Failed to apply mutation: ${e}`); + } + logger.info( + `Found ${locations.length} locations from ${allRepos.length} repos`, + ); + } + + private async createLocationSpec(repo: string): Promise { + return { + type: 'url', + target: `${repo}/src/branch/${this.config.branch}/${this.config.catalogPath}`, + }; + } +} diff --git a/plugins/catalog-backend-module-gitea/src/providers/config.test.ts b/plugins/catalog-backend-module-gitea/src/providers/config.test.ts new file mode 100644 index 0000000000..5efece4e5a --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/config.test.ts @@ -0,0 +1,125 @@ +/* + * 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. + */ + +import { readGiteaConfigs } from './config'; +import { ConfigReader } from '@backstage/config'; +import { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api'; + +jest.mock('@backstage/backend-plugin-api', () => ({ + ...jest.requireActual('@backstage/backend-plugin-api'), + readSchedulerServiceTaskScheduleDefinitionFromConfig: jest.fn(), +})); +jest.mock('p-limit'); + +describe('readGiteaConfigs', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + it('returns empty array when no gitea provider config is present', () => { + const config = new ConfigReader({}); + + const result = readGiteaConfigs(config); + + expect(result).toEqual([]); + }); + + it('parses multiple gitea provider configurations correctly', () => { + ( + readSchedulerServiceTaskScheduleDefinitionFromConfig as jest.Mock + ).mockReturnValue({ + frequency: { minutes: 5 }, + timeout: { minutes: 3 }, + }); + + const config = new ConfigReader({ + catalog: { + providers: { + gitea: { + myProvider: { + host: 'gitea.example.com', + organization: 'example-org', + branch: 'dev', + catalogPath: 'custom-path.yaml', + schedule: { + frequency: { minutes: 5 }, + timeout: { minutes: 3 }, + }, + }, + }, + }, + }, + }); + + const result = readGiteaConfigs(config); + + expect(result).toEqual([ + { + id: 'myProvider', + host: 'gitea.example.com', + organization: 'example-org', + branch: 'dev', + catalogPath: 'custom-path.yaml', + schedule: { + frequency: { minutes: 5 }, + timeout: { minutes: 3 }, + }, + }, + ]); + }); + + it('applies defaults for optional fields', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitea: { + default: { + host: 'gitea.com', + organization: 'default-org', + }, + }, + }, + }, + }); + + const result = readGiteaConfigs(config); + + expect(result[0]).toMatchObject({ + id: 'default', + host: 'gitea.com', + organization: 'default-org', + branch: 'main', + catalogPath: 'catalog-info.yaml', + schedule: undefined, + }); + }); + + it('throws if required fields are missing', () => { + const config = new ConfigReader({ + catalog: { + providers: { + gitea: { + bad: {}, + }, + }, + }, + }); + + expect(() => readGiteaConfigs(config)).toThrow( + /Missing required config value/, + ); + }); +}); diff --git a/plugins/catalog-backend-module-gitea/src/providers/config.ts b/plugins/catalog-backend-module-gitea/src/providers/config.ts new file mode 100644 index 0000000000..5863e76bf0 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/config.ts @@ -0,0 +1,58 @@ +/* + * 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. + */ + +import { readSchedulerServiceTaskScheduleDefinitionFromConfig } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; +import { GiteaProviderConfig } from './types'; + +function readGiteaConfig(id: string, config: Config): GiteaProviderConfig { + const branch = config.getOptionalString('branch') ?? 'main'; + const catalogPath = + config.getOptionalString('catalogPath') ?? 'catalog-info.yaml'; + const host = config.getString('host'); + const organization = config.getString('organization'); + + const schedule = config.has('schedule') + ? readSchedulerServiceTaskScheduleDefinitionFromConfig( + config.getConfig('schedule'), + ) + : undefined; + + return { + branch, + catalogPath, + host, + id, + schedule, + organization, + }; +} + +export function readGiteaConfigs(config: Config): GiteaProviderConfig[] { + const configs: GiteaProviderConfig[] = []; + + const providerConfigs = config.getOptionalConfig('catalog.providers.gitea'); + + if (!providerConfigs) { + return configs; + } + + for (const id of providerConfigs.keys()) { + configs.push(readGiteaConfig(id, providerConfigs.getConfig(id))); + } + + return configs; +} diff --git a/plugins/catalog-backend-module-gitea/src/providers/core.test.ts b/plugins/catalog-backend-module-gitea/src/providers/core.test.ts new file mode 100644 index 0000000000..883dab7edc --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/core.test.ts @@ -0,0 +1,42 @@ +/* + * 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. + */ + +import { getGiteaApiUrl } from './core'; +import { GiteaIntegrationConfig } from '@backstage/integration'; + +describe('getGiteaApiUrl', () => { + it('should return the correct Gitea API base URL', () => { + const config: GiteaIntegrationConfig = { + host: 'gitea.example.com', + baseUrl: 'https://gitea.example.com', + }; + + const result = getGiteaApiUrl(config); + + expect(result).toBe('https://gitea.example.com/api/v1/'); + }); + + it('should handle trailing slash in baseUrl correctly', () => { + const config: GiteaIntegrationConfig = { + host: 'gitea.example.com', + baseUrl: 'https://gitea.example.com/', + }; + + const result = getGiteaApiUrl(config); + + expect(result).toBe('https://gitea.example.com//api/v1/'); + }); +}); diff --git a/plugins/catalog-backend-module-gitea/src/providers/core.ts b/plugins/catalog-backend-module-gitea/src/providers/core.ts new file mode 100644 index 0000000000..c994f3a714 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/core.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +import { GiteaIntegrationConfig } from '@backstage/integration'; +/** + * Return the url to query available repos using the Gitea API. + * + * @param config - A Gitea provider config. + * @public + */ +export function getGiteaApiUrl(config: GiteaIntegrationConfig) { + return `${config.baseUrl}/api/v1/`; +} diff --git a/plugins/catalog-backend-module-gitea/src/providers/index.ts b/plugins/catalog-backend-module-gitea/src/providers/index.ts new file mode 100644 index 0000000000..9914fda397 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export { getGiteaApiUrl } from './core'; diff --git a/plugins/catalog-backend-module-gitea/src/providers/types.ts b/plugins/catalog-backend-module-gitea/src/providers/types.ts new file mode 100644 index 0000000000..0bbd09f9e7 --- /dev/null +++ b/plugins/catalog-backend-module-gitea/src/providers/types.ts @@ -0,0 +1,36 @@ +/* + * 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. + */ + +import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api'; + +export type GiteaProjectInfo = { + id: string; + name: string; + parent?: string; + state?: string; + branch?: string; +}; + +export type GiteaProjectQueryResult = Record; + +export type GiteaProviderConfig = { + host: string; + id: string; + branch?: string; + catalogPath?: string; + organization?: string; + schedule?: SchedulerServiceTaskScheduleDefinition; +}; diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index 14fb414c48..a3ac67d24d 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.9.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.3.10-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index ce47411b0a..ebe2e4582b 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", - "version": "0.3.10-next.2", + "version": "0.3.10-next.3", "description": "The github-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 51e1498de5..0c3b3e2a63 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-github +## 0.9.0-next.3 + +### Patch Changes + +- ee9f59f: Added filter to include archived repositories +- Updated dependencies + - @backstage/plugin-catalog-backend@2.0.0-next.3 + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.9.0-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/config.d.ts b/plugins/catalog-backend-module-github/config.d.ts index c94593b985..f7c8781c30 100644 --- a/plugins/catalog-backend-module-github/config.d.ts +++ b/plugins/catalog-backend-module-github/config.d.ts @@ -117,6 +117,11 @@ export interface Config { * (Optional) GitHub repository visibility filter. */ visibility?: Array<'private' | 'internal' | 'public'>; + /** + * (Optional) Whether to include archived repositories. + * Default: `false`. + */ + allowArchived?: boolean; }; /** * (Optional) TaskScheduleDefinition for the refresh. @@ -186,6 +191,11 @@ export interface Config { * (Optional) GitHub repository visibility filter. */ visibility?: Array<'private' | 'internal' | 'public'>; + /** + * (Optional) Whether to include archived repositories. + * Default: `false`. + */ + allowArchived?: boolean; }; /** * (Optional) TaskScheduleDefinition for the refresh. diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index bb2e436804..a0b29c1c9e 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-github", - "version": "0.9.0-next.2", + "version": "0.9.0-next.3", "description": "A Backstage catalog backend module that helps integrate towards GitHub", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts index e0837de54e..131237c581 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts @@ -256,15 +256,16 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { } private matchesFilters(repositories: Repository[]): Repository[] { - const repositoryFilter = this.config.filters?.repository; - const topicFilters = this.config.filters?.topic; - const allowForks = this.config.filters?.allowForks ?? true; - const visibilities = this.config.filters?.visibility ?? []; + const repositoryFilter = this.config.filters.repository; + const topicFilters = this.config.filters.topic; + const allowForks = this.config.filters.allowForks; + const visibilities = this.config.filters.visibility ?? []; + const allowArchived = this.config.filters.allowArchived; return repositories.filter(r => { const repoTopics: string[] = r.repositoryTopics; return ( - !r.isArchived && + (allowArchived || !r.isArchived) && (!repositoryFilter || repositoryFilter.test(r.name)) && satisfiesTopicFilter(repoTopics, topicFilters) && satisfiesForkFilter(allowForks, r.isFork) && @@ -276,7 +277,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { private createLocationUrl(repository: Repository): string { const branch = - this.config.filters?.branch || repository.defaultBranchRef || '-'; + this.config.filters.branch || repository.defaultBranchRef || '-'; const catalogFile = this.config.catalogPath.startsWith('/') ? this.config.catalogPath.substring(1) : this.config.catalogPath; @@ -334,7 +335,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { this.logger.debug(`handle github:push event for ${repoName} - ${repoUrl}`); const branch = - this.config.filters?.branch || event.repository.default_branch; + this.config.filters.branch || event.repository.default_branch; if (!event.ref.includes(branch)) { this.logger.debug(`skipping push event from ref ${event.ref}`); @@ -468,6 +469,8 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { * @param event - The repository archived event. */ private async onRepoArchived(event: RepositoryArchivedEvent) { + if (this.config.filters.allowArchived) return; + const repository = this.createRepoFromEvent(event); await this.removeEntitiesForRepo(repository); this.logger.debug( @@ -549,7 +552,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { * * Creates new entities for the repository if it matches the filters. * - * @param event - The repository unarchived event. + * @param event - The repository transferred event. */ private async onRepoTransferred(event: RepositoryTransferredEvent) { const repository = this.createRepoFromEvent(event); diff --git a/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.test.ts index bddb4d27cd..28a3f5e7dd 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.test.ts @@ -92,6 +92,12 @@ describe('readProviderConfigs', () => { visibility: ['public', 'internal'], }, }, + providerWithArchiveFilter: { + organization: 'test-org6', + filters: { + allowArchived: true, + }, + }, providerWithHost: { organization: 'test-org1', host: 'ghe.internal.com', @@ -111,7 +117,7 @@ describe('readProviderConfigs', () => { }); const providerConfigs = readProviderConfigs(config); - expect(providerConfigs).toHaveLength(9); + expect(providerConfigs).toHaveLength(10); expect(providerConfigs[0]).toEqual({ id: 'providerOrganizationOnly', organization: 'test-org1', @@ -126,6 +132,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: undefined, + allowArchived: false, }, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, validateLocationsExist: false, @@ -144,6 +151,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: undefined, + allowArchived: false, }, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, validateLocationsExist: false, @@ -162,6 +170,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: undefined, + allowArchived: false, }, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, validateLocationsExist: false, @@ -180,6 +189,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: undefined, + allowArchived: false, }, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, validateLocationsExist: false, @@ -198,6 +208,7 @@ describe('readProviderConfigs', () => { exclude: ['backstage-exclude'], }, visibility: undefined, + allowArchived: false, }, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, validateLocationsExist: false, @@ -216,6 +227,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: undefined, + allowArchived: false, }, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, validateLocationsExist: false, @@ -234,11 +246,31 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: ['public', 'internal'], + allowArchived: false, }, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, validateLocationsExist: false, }); expect(providerConfigs[7]).toEqual({ + id: 'providerWithArchiveFilter', + organization: 'test-org6', + catalogPath: '/catalog-info.yaml', + host: 'github.com', + filters: { + repository: undefined, + branch: undefined, + allowForks: true, + topic: { + include: undefined, + exclude: undefined, + }, + visibility: undefined, + allowArchived: true, + }, + schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, + validateLocationsExist: false, + }); + expect(providerConfigs[8]).toEqual({ id: 'providerWithHost', organization: 'test-org1', catalogPath: '/catalog-info.yaml', @@ -252,11 +284,12 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: undefined, + allowArchived: false, }, validateLocationsExist: false, schedule: DEFAULT_GITHUB_ENTITY_PROVIDER_CONFIG_SCHEDULE, }); - expect(providerConfigs[8]).toEqual({ + expect(providerConfigs[9]).toEqual({ id: 'providerWithSchedule', organization: 'test-org1', catalogPath: '/catalog-info.yaml', @@ -270,6 +303,7 @@ describe('readProviderConfigs', () => { exclude: undefined, }, visibility: undefined, + allowArchived: false, }, schedule: { frequency: { minutes: 30 }, diff --git a/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.ts index 781239978f..e33e7adfdd 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProviderConfig.ts @@ -37,12 +37,13 @@ export type GithubEntityProviderConfig = { catalogPath: string; organization: string; host: string; - filters?: { + filters: { repository?: RegExp; branch?: string; topic?: GithubTopicFilters; - allowForks?: boolean; + allowForks: boolean; visibility?: string[]; + allowArchived: boolean; }; validateLocationsExist: boolean; schedule?: SchedulerServiceTaskScheduleDefinition; @@ -90,6 +91,8 @@ function readProviderConfig( const topicFilterExclude = config?.getOptionalStringArray( 'filters.topic.exclude', ); + const allowArchived = + config.getOptionalBoolean('filters.allowArchived') ?? false; const validateLocationsExist = config?.getOptionalBoolean('validateLocationsExist') ?? false; @@ -132,6 +135,7 @@ function readProviderConfig( exclude: topicFilterExclude, }, visibility: visibilityFilterInclude, + allowArchived, }, schedule, validateLocationsExist, diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 7d5701a31d..61983705d7 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/plugin-catalog-backend-module-gitlab@0.6.6-next.3 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 44015c6c16..44480b3cc9 100644 --- a/plugins/catalog-backend-module-gitlab-org/package.json +++ b/plugins/catalog-backend-module-gitlab-org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab-org", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "description": "The gitlab-org backend module for the catalog plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 76e67018cf..b2cc0ee53c 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.6.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.6.6-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index e129b83cd9..108d05023e 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", - "version": "0.6.6-next.2", + "version": "0.6.6-next.3", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index 98efe68781..707a61f0d2 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.7.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/plugin-catalog-backend@2.0.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + - @backstage/plugin-permission-common@0.9.0-next.0 + ## 0.7.0-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index 60b3873e0b..074966b7a3 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", - "version": "0.7.0-next.2", + "version": "0.7.0-next.3", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 431a672828..db8ec2bec7 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.11.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.11.5-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 4d9888ac6d..79919b8d54 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.11.5-next.2", + "version": "0.11.5-next.3", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index a7e0d37921..d7c498bad0 100644 --- a/plugins/catalog-backend-module-logs/CHANGELOG.md +++ b/plugins/catalog-backend-module-logs/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-logs +## 0.1.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@2.0.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.1.10-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index a6f748e4ca..857be5ed67 100644 --- a/plugins/catalog-backend-module-logs/package.json +++ b/plugins/catalog-backend-module-logs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-logs", - "version": "0.1.10-next.2", + "version": "0.1.10-next.3", "description": "A module that subscribes to catalog related events and logs them.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index f4ea154223..9d6ca9759d 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.7.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.7.0-next.1 ### Minor Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index ae116ca435..606bf9fd51 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.7.0-next.1", + "version": "0.7.0-next.2", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index c6b53d288d..c0acf65480 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.2.10-next.2 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 5f0c106b0d..13c4e7200a 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", - "version": "0.2.10-next.2", + "version": "0.2.10-next.3", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 324a091dc4..f650e83545 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index fc830eb66e..c354a7c791 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", - "version": "0.2.10-next.1", + "version": "0.2.10-next.2", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index af44065886..4d8d4817d2 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-scaffolder-common@1.5.11-next.0 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/package.json b/plugins/catalog-backend-module-scaffolder-entity-model/package.json index dc32af8cef..06c35eba9f 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", - "version": "0.2.8-next.1", + "version": "0.2.8-next.2", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index a2341a7e40..74e023e6a1 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.6.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.8-next.0 + - @backstage/plugin-permission-common@0.9.0-next.0 + ## 0.6.0-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 98e2ba8f08..c7291b57df 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", - "version": "0.6.0-next.1", + "version": "0.6.0-next.2", "description": "Backstage Catalog module to view unprocessed entities", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 1f9802a3e8..f1023af433 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-backend +## 2.0.0-next.3 + +### Patch Changes + +- 8e0f15f: "Added a note clarifying that `entity-fetch` audit events are not visible by default in the logs and are only displayed when the log severity level is adjusted." +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-openapi-utils@0.5.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-node@0.10.0-next.2 + ## 2.0.0-next.2 ### Patch Changes diff --git a/plugins/catalog-backend/README.md b/plugins/catalog-backend/README.md index 680adc0bcd..00b88cf060 100644 --- a/plugins/catalog-backend/README.md +++ b/plugins/catalog-backend/README.md @@ -94,6 +94,7 @@ The Catalog backend emits audit events for various operations. Events are groupe **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`. diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 3b548a9f67..ad2aa6b6fd 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "2.0.0-next.2", + "version": "2.0.0-next.3", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 7f669774f8..6edba7a4fd 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-graph +## 0.4.19-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/types@1.2.1 + ## 0.4.19-next.2 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index d674ca6f95..8319e40ec9 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.4.19-next.2", + "version": "0.4.19-next.3", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 3722f55f86..42bdd25b75 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-import +## 0.13.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/plugin-catalog-common@1.1.4-next.0 + ## 0.13.0-next.2 ### Minor Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index cb046ecfb4..a9f5c79f05 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.13.0-next.2", + "version": "0.13.0-next.3", "description": "A Backstage plugin the helps you import entities into your catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index ebffdcd296..c70deebfc0 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-node +## 1.17.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-node@0.10.0-next.2 + ## 1.17.0-next.1 ### Minor Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index a222e0093d..70ec27bfc1 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-node", - "version": "1.17.0-next.1", + "version": "1.17.0-next.2", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 7ca221a40d..b598d13f5f 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-react +## 1.18.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/frontend-test-utils@0.3.2-next.3 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-react@0.4.34-next.1 + ## 1.18.0-next.2 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index bb9d1bde29..4ab0eb0b37 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "1.18.0-next.2", + "version": "1.18.0-next.3", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md index d5f85b7836..46d3030140 100644 --- a/plugins/catalog-react/report-alpha.api.md +++ b/plugins/catalog-react/report-alpha.api.md @@ -332,17 +332,36 @@ export const EntityContextMenuItemBlueprint: ExtensionBlueprint<{ kind: 'entity-context-menu-item'; name: undefined; params: EntityContextMenuItemParams; - output: ConfigurableExtensionDataRef; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + >; inputs: {}; - config: {}; - configInput: {}; - dataRefs: never; + config: { + filter: EntityPredicate | undefined; + }; + configInput: { + filter?: EntityPredicate | undefined; + }; + dataRefs: { + filterFunction: ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + {} + >; + }; }>; // @alpha (undocumented) export type EntityContextMenuItemParams = { useProps: UseProps; icon: JSX_2.Element; + filter?: EntityPredicate | ((entity: Entity) => boolean); }; // @alpha (undocumented) diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx index 3bdfc00db7..55598f5de1 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.test.tsx @@ -13,7 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { + createExtensionTester, + renderInTestApp, +} from '@backstage/frontend-test-utils'; import { EntityContextMenuItemBlueprint } from './EntityContextMenuItemBlueprint'; +import { screen, waitFor } from '@testing-library/react'; +import { EntityProvider } from '@backstage/plugin-catalog-react'; +import { Entity } from '@backstage/catalog-model'; + +jest.mock('../../hooks/useEntityContextMenu', () => ({ + useEntityContextMenu: () => ({ + onMenuClose: jest.fn(), + }), +})); describe('EntityContextMenuItemBlueprint', () => { const data = [ @@ -49,7 +62,131 @@ describe('EntityContextMenuItemBlueprint', () => { "id": "page:catalog/entity", "input": "contextMenuItems", }, - "configSchema": undefined, + "configSchema": { + "parse": [Function], + "schema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "filter": { + "anyOf": [ + { + "anyOf": [ + { + "type": [ + "string", + "number", + "boolean", + ], + }, + { + "items": { + "$ref": "#/properties/filter/anyOf/0/anyOf/0", + }, + "type": "array", + }, + ], + }, + { + "additionalProperties": false, + "properties": { + "$all": { + "items": { + "$ref": "#/properties/filter", + }, + "type": "array", + }, + }, + "required": [ + "$all", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$any": { + "items": { + "$ref": "#/properties/filter", + }, + "type": "array", + }, + }, + "required": [ + "$any", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$not": { + "$ref": "#/properties/filter", + }, + }, + "required": [ + "$not", + ], + "type": "object", + }, + { + "additionalProperties": { + "anyOf": [ + { + "$ref": "#/properties/filter/anyOf/0", + }, + { + "additionalProperties": false, + "properties": { + "$exists": { + "type": "boolean", + }, + }, + "required": [ + "$exists", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$in": { + "items": { + "$ref": "#/properties/filter/anyOf/0/anyOf/0", + }, + "type": "array", + }, + }, + "required": [ + "$in", + ], + "type": "object", + }, + { + "additionalProperties": false, + "properties": { + "$contains": { + "$ref": "#/properties/filter", + }, + }, + "required": [ + "$contains", + ], + "type": "object", + }, + ], + }, + "propertyNames": { + "pattern": "^(?!\\$).*$", + }, + "type": "object", + }, + ], + }, + }, + "type": "object", + }, + }, "disabled": false, "factory": [Function], "inputs": {}, @@ -57,6 +194,15 @@ describe('EntityContextMenuItemBlueprint', () => { "name": "test", "output": [ [Function], + { + "$$type": "@backstage/ExtensionDataRef", + "config": { + "optional": true, + }, + "id": "catalog.entity-filter-function", + "optional": [Function], + "toString": [Function], + }, ], "override": [Function], "toString": [Function], @@ -64,4 +210,57 @@ describe('EntityContextMenuItemBlueprint', () => { } `); }); + + it('should render a menu item', async () => { + const extension = EntityContextMenuItemBlueprint.make({ + name: 'test', + params: { + icon: Icon, + useProps: () => ({ + title: 'Test', + onClick: () => {}, + }), + }, + }); + + renderInTestApp( + +
    {createExtensionTester(extension).reactElement()}
+
, + ); + + await waitFor(() => { + expect(screen.getByText('Test')).toBeInTheDocument(); + }); + }); + + it.each([ + { filter: { kind: 'Api' } }, + { filter: (e: Entity) => e.kind.toLowerCase() === 'api' }, + ])('should return a filter function', async ({ filter }) => { + const extension = EntityContextMenuItemBlueprint.make({ + name: 'test', + params: { + icon: Icon, + useProps: () => ({ title: 'Test', onClick: () => {} }), + filter, + }, + }); + + const tester = createExtensionTester(extension); + + const filterFn = tester.get( + EntityContextMenuItemBlueprint.dataRefs.filterFunction, + ); + + expect(filterFn).toBeDefined(); + expect(filterFn?.({ kind: 'Api' } as Entity)).toBe(true); + expect(filterFn?.({ kind: 'Component' } as Entity)).toBe(false); + }); }); diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx index f43dd6a6d9..470aa13646 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx @@ -24,7 +24,13 @@ import MenuItem from '@material-ui/core/MenuItem'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; import { useEntityContextMenu } from '../../hooks/useEntityContextMenu'; - +import { + EntityPredicate, + entityPredicateToFilterFunction, +} from '../predicates'; +import type { Entity } from '@backstage/catalog-model'; +import { entityFilterFunctionDataRef } from './extensionData'; +import { createEntityPredicateSchema } from '../predicates/createEntityPredicateSchema'; /** @alpha */ export type UseProps = () => | { @@ -42,14 +48,26 @@ export type UseProps = () => export type EntityContextMenuItemParams = { useProps: UseProps; icon: JSX.Element; + filter?: EntityPredicate | ((entity: Entity) => boolean); }; /** @alpha */ export const EntityContextMenuItemBlueprint = createExtensionBlueprint({ kind: 'entity-context-menu-item', attachTo: { id: 'page:catalog/entity', input: 'contextMenuItems' }, - output: [coreExtensionData.reactElement], - *factory(params: EntityContextMenuItemParams, { node }) { + output: [ + coreExtensionData.reactElement, + entityFilterFunctionDataRef.optional(), + ], + dataRefs: { + filterFunction: entityFilterFunctionDataRef, + }, + config: { + schema: { + filter: z => createEntityPredicateSchema(z).optional(), + }, + }, + *factory(params: EntityContextMenuItemParams, { node, config }) { const loader = async () => { const Component = () => { const { onMenuClose } = useEntityContextMenu(); @@ -79,5 +97,17 @@ export const EntityContextMenuItemBlueprint = createExtensionBlueprint({ }; yield coreExtensionData.reactElement(ExtensionBoundary.lazy(node, loader)); + + if (config.filter) { + yield entityFilterFunctionDataRef( + entityPredicateToFilterFunction(config.filter), + ); + } else if (typeof params.filter === 'function') { + yield entityFilterFunctionDataRef(params.filter); + } else if (params.filter) { + yield entityFilterFunctionDataRef( + entityPredicateToFilterFunction(params.filter), + ); + } }, }); diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index e7b747928f..6f974e361b 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.2.17-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + ## 0.2.17-next.2 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index c6663150c0..8b810df81a 100644 --- a/plugins/catalog-unprocessed-entities/package.json +++ b/plugins/catalog-unprocessed-entities/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities", - "version": "0.2.17-next.2", + "version": "0.2.17-next.3", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-unprocessed-entities", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index b410114940..57e4467030 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-catalog +## 1.30.0-next.3 + +### Minor Changes + +- 970cb48: Show the pagination text for the offset-paginated catalog table, and remove the pagination bar from the top of the `CatalogTable` when pagination is enabled. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.9.0-next.2 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-permission-react@0.4.34-next.1 + - @backstage/plugin-scaffolder-common@1.5.11-next.0 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 1.29.1-next.2 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 8eda14596b..816f3fffd2 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.29.1-next.2", + "version": "1.30.0-next.3", "description": "The Backstage plugin for browsing the Backstage catalog", "backstage": { "role": "frontend-plugin", diff --git a/plugins/catalog/report-alpha.api.md b/plugins/catalog/report-alpha.api.md index 54d4187536..7ef20d8228 100644 --- a/plugins/catalog/report-alpha.api.md +++ b/plugins/catalog/report-alpha.api.md @@ -863,39 +863,63 @@ const _default: FrontendPlugin< 'entity-context-menu-item:catalog/copy-entity-url': ExtensionDefinition<{ kind: 'entity-context-menu-item'; name: 'copy-entity-url'; - config: {}; - configInput: {}; - output: ConfigurableExtensionDataRef< - JSX_2.Element, - 'core.reactElement', - {} - >; + config: { + filter: EntityPredicate | undefined; + }; + configInput: { + filter?: EntityPredicate | undefined; + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + >; inputs: {}; params: EntityContextMenuItemParams; }>; 'entity-context-menu-item:catalog/inspect-entity': ExtensionDefinition<{ kind: 'entity-context-menu-item'; name: 'inspect-entity'; - config: {}; - configInput: {}; - output: ConfigurableExtensionDataRef< - JSX_2.Element, - 'core.reactElement', - {} - >; + config: { + filter: EntityPredicate | undefined; + }; + configInput: { + filter?: EntityPredicate | undefined; + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + >; inputs: {}; params: EntityContextMenuItemParams; }>; 'entity-context-menu-item:catalog/unregister-entity': ExtensionDefinition<{ kind: 'entity-context-menu-item'; name: 'unregister-entity'; - config: {}; - configInput: {}; - output: ConfigurableExtensionDataRef< - JSX_2.Element, - 'core.reactElement', - {} - >; + config: { + filter: EntityPredicate | undefined; + }; + configInput: { + filter?: EntityPredicate | undefined; + }; + output: + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + >; inputs: {}; params: EntityContextMenuItemParams; }>; @@ -1045,7 +1069,14 @@ const _default: FrontendPlugin< } >; contextMenuItems: ExtensionInput< - ConfigurableExtensionDataRef, + | ConfigurableExtensionDataRef + | ConfigurableExtensionDataRef< + (entity: Entity) => boolean, + 'catalog.entity-filter-function', + { + optional: true; + } + >, { singleton: false; optional: false; diff --git a/plugins/catalog/src/alpha/pages.test.tsx b/plugins/catalog/src/alpha/pages.test.tsx index f5aeb7d196..47b3efbd0a 100644 --- a/plugins/catalog/src/alpha/pages.test.tsx +++ b/plugins/catalog/src/alpha/pages.test.tsx @@ -36,6 +36,7 @@ import { } from '@backstage/plugin-catalog-react'; import { convertLegacyRouteRef } from '@backstage/core-compat-api'; import { rootRouteRef } from '../routes'; +import { Entity } from '@backstage/catalog-model'; describe('Entity page', () => { const entityMock = { @@ -733,5 +734,91 @@ describe('Entity page', () => { expect(onClickMock).toHaveBeenCalledTimes(disabled ? 0 : 1); }); }); + + it.each([ + { + positive: { params: {} }, + negative: { params: { filter: { kind: 'api' } } }, + }, + { + positive: { params: { filter: { kind: 'component' } } }, + negative: { params: { filter: { kind: 'api' } } }, + }, + { + positive: { + params: { + filter: (e: Entity) => e.kind.toLowerCase() === 'component', + }, + }, + negative: { + params: { filter: (e: Entity) => e.kind.toLowerCase() === 'api' }, + }, + }, + ])( + 'should render menu items according to filters', + async ({ positive, negative }) => { + const menuItem = EntityContextMenuItemBlueprint.make({ + name: 'should-render-menu-item', + params: { + icon: Test Icon, + useProps: () => ({ + onClick: onClickMock, + title: 'Should Render', + }), + ...positive.params, + }, + }); + + const filteredMenuItem = EntityContextMenuItemBlueprint.make({ + name: 'should-not-render-menu-item', + params: { + icon: Test Icon, + useProps: () => ({ + onClick: onClickMock, + title: 'Should Not Render', + }), + ...negative.params, + }, + }); + + const tester = createExtensionTester( + Object.assign({ namespace: 'catalog' }, catalogEntityPage), + ) + .add(menuItem) + .add(filteredMenuItem); + + renderInTestApp( + + {tester.reactElement()} + , + { + config: { + app: { + title: 'Custom app', + }, + backend: { baseUrl: 'http://localhost:7000' }, + }, + mountedRoutes: { + '/catalog': convertLegacyRouteRef(rootRouteRef), + '/catalog/:namespace/:kind/:name': + convertLegacyRouteRef(entityRouteRef), + }, + }, + ); + + await waitFor(async () => { + await userEvent.click(screen.getByTestId('menu-button')); + expect(screen.getByText('Should Render')).toBeInTheDocument(); + expect( + screen.queryByText('Should Not Render'), + ).not.toBeInTheDocument(); + }); + }, + ); }); }); diff --git a/plugins/catalog/src/alpha/pages.tsx b/plugins/catalog/src/alpha/pages.tsx index b39fe510e6..c1f5b55341 100644 --- a/plugins/catalog/src/alpha/pages.tsx +++ b/plugins/catalog/src/alpha/pages.tsx @@ -31,11 +31,11 @@ import { EntityHeaderBlueprint, EntityContentBlueprint, defaultEntityContentGroups, + EntityContextMenuItemBlueprint, } from '@backstage/plugin-catalog-react/alpha'; import { rootRouteRef } from '../routes'; import { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl'; import { buildFilterFn } from './filter/FilterWrapper'; -import { EntityHeader } from './components/EntityHeader'; export const catalogPage = PageBlueprint.makeWithOverrides({ inputs: { @@ -72,7 +72,10 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({ EntityContentBlueprint.dataRefs.filterExpression.optional(), EntityContentBlueprint.dataRefs.group.optional(), ]), - contextMenuItems: createExtensionInput([coreExtensionData.reactElement]), + contextMenuItems: createExtensionInput([ + coreExtensionData.reactElement, + EntityContextMenuItemBlueprint.dataRefs.filterFunction.optional(), + ]), }, config: { schema: { @@ -89,9 +92,12 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({ loader: async () => { const { EntityLayout } = await import('./components/EntityLayout'); - const menuItems = inputs.contextMenuItems.map(item => - item.get(coreExtensionData.reactElement), - ); + const menuItems = inputs.contextMenuItems.map(item => ({ + element: item.get(coreExtensionData.reactElement), + filter: + item.get(EntityContextMenuItemBlueprint.dataRefs.filterFunction) ?? + (() => true), + })); type Groups = Record< string, @@ -100,7 +106,7 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({ const header = inputs.header?.get( EntityHeaderBlueprint.dataRefs.element, - ) ?? ; + ); let groups = Object.entries(defaultEntityContentGroups).reduce( (rest, group) => { @@ -137,9 +143,18 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({ } const Component = () => { + const entityFromUrl = useEntityFromUrl(); + const { entity } = entityFromUrl; + const filteredMenuItems = entity + ? menuItems.filter(i => i.filter(entity)).map(i => i.element) + : []; + return ( - - + + {Object.values(groups).flatMap(({ title, items }) => items.map(output => ( + >; +}; + // @public (undocumented) export type CardConfig = { layout?: CardLayout; @@ -52,6 +62,15 @@ export type ComponentRenderer = { Renderer?: (props: RendererProps) => JSX.Element; }; +// @public +export const ContentModal: (props: ContentModalProps) => JSX_2.Element; + +// @public +export type ContentModalProps = { + modalContent: JSX_3.Element; + linkContent: string | JSX_3.Element; +}; + // @public export function createCardExtension(options: { title?: string; @@ -62,6 +81,14 @@ export function createCardExtension(options: { settings?: CardSettings; }): Extension<(props: CardExtensionProps) => JSX_2.Element>; +// @public (undocumented) +export type PluginHomeComponentsNameToClassKey = { + PluginHomeContentModal: PluginHomeContentModalClassKey; +}; + +// @public (undocumented) +export type PluginHomeContentModalClassKey = 'contentModal' | 'linkText'; + // @public (undocumented) export type RendererProps = { title?: string; diff --git a/plugins/home/src/homePageComponents/QuickStart/ContentModal.tsx b/plugins/home-react/src/components/ContentModal.tsx similarity index 71% rename from plugins/home/src/homePageComponents/QuickStart/ContentModal.tsx rename to plugins/home-react/src/components/ContentModal.tsx index ff917f367c..cde317e248 100644 --- a/plugins/home/src/homePageComponents/QuickStart/ContentModal.tsx +++ b/plugins/home-react/src/components/ContentModal.tsx @@ -18,14 +18,43 @@ import { JSX, useState } from 'react'; import { Link } from '@backstage/core-components'; import Modal from '@material-ui/core/Modal'; import Box from '@material-ui/core/Box'; +import { makeStyles, Theme } from '@material-ui/core/styles'; -import { useStyles } from './styles'; +/** @public */ +export type PluginHomeContentModalClassKey = 'contentModal' | 'linkText'; +export const useStyles = makeStyles( + (theme: Theme) => ({ + contentModal: { + position: 'absolute', + top: '50%', + left: '50%', + transform: 'translate(-50%, -50%)', + width: '80%', + height: 'auto', + }, + linkText: { + marginBottom: theme.spacing(1.5), + }, + }), + { name: 'PluginHomeContentModal' }, +); + +/** + * Props customizing the component. + * + * @public + */ export type ContentModalProps = { modalContent: JSX.Element; linkContent: string | JSX.Element; }; +/** + * A component to expand given content into a full screen modal. + * + * @public + */ export const ContentModal = (props: ContentModalProps) => { const { modalContent, linkContent } = props; const styles = useStyles(); diff --git a/plugins/home-react/src/components/index.ts b/plugins/home-react/src/components/index.ts index dce6968b36..8a982075eb 100644 --- a/plugins/home-react/src/components/index.ts +++ b/plugins/home-react/src/components/index.ts @@ -15,3 +15,7 @@ */ export { SettingsModal } from './SettingsModal'; +export { ContentModal } from './ContentModal'; + +export type { PluginHomeContentModalClassKey } from './ContentModal'; +export type { ContentModalProps } from './ContentModal'; diff --git a/plugins/home-react/src/index.ts b/plugins/home-react/src/index.ts index 1f02ded1c6..54602dc814 100644 --- a/plugins/home-react/src/index.ts +++ b/plugins/home-react/src/index.ts @@ -30,3 +30,4 @@ export type { CardSettings, CardConfig, } from './extensions'; +export * from './overridableComponents'; diff --git a/plugins/home-react/src/overridableComponents.ts b/plugins/home-react/src/overridableComponents.ts new file mode 100644 index 0000000000..8a88fd5c94 --- /dev/null +++ b/plugins/home-react/src/overridableComponents.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2025 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 { Overrides } from '@material-ui/core/styles/overrides'; +import { StyleRules } from '@material-ui/core/styles/withStyles'; +import { PluginHomeContentModalClassKey } from './'; + +/** @public */ +export type PluginHomeComponentsNameToClassKey = { + PluginHomeContentModal: PluginHomeContentModalClassKey; +}; + +/** @public */ +export type BackstageOverrides = Overrides & { + [Name in keyof PluginHomeComponentsNameToClassKey]?: Partial< + StyleRules + >; +}; + +declare module '@backstage/theme' { + interface OverrideComponentNameToClassKeys + extends PluginHomeComponentsNameToClassKey {} +} diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 73eb07ffd7..1a1eba2dae 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-home +## 0.8.8-next.3 + +### Patch Changes + +- f7ca0fe: Added the Catalog presentation API to the HomePageRecentlyVisited and HomePageTopVisited components +- eddd96c: Added optional title prop to `customHomePageGrid` +- 16eb4bf: Export ContentModal from `@backstage/plugin-home-react` so people can use this in other scenarios. + Renamed `CatalogReactComponentsNameToClassKey` to `PluginHomeComponentsNameToClassKey` in `overridableComponents.ts` + + Made QuickStartCard `docsLinkTitle` prop more flexible to allow for any React.JSX.Element instead of just a string. + Added QuickStartCard prop `additionalContent` which can eventually replace the prop `video`. + +- 195323f: Export root page route from the home plugin to enable adding links/nav to it from outside the plugin +- d710d74: docs: Update default for `preventCollision` prop +- Updated dependencies + - @backstage/core-app-api@1.17.0-next.1 + - @backstage/plugin-home-react@0.1.26-next.2 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/theme@0.6.6-next.0 + ## 0.8.8-next.2 ### Patch Changes diff --git a/plugins/home/README.md b/plugins/home/README.md index 50b422a3dc..3498d6700f 100644 --- a/plugins/home/README.md +++ b/plugins/home/README.md @@ -107,6 +107,9 @@ export const homePage = ( ); ``` +> [!NOTE] +> You can provide a title to the grid by passing it as a prop: ``. This will be displayed as a header above the grid layout. + ### Creating Customizable Components The custom home page can use the default components created by using the default `createCardExtension` method but if you diff --git a/plugins/home/package.json b/plugins/home/package.json index c832e8f888..30cd16529a 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.8.8-next.2", + "version": "0.8.8-next.3", "description": "A Backstage plugin that helps you build a home page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/home/report-alpha.api.md b/plugins/home/report-alpha.api.md index 6033cb47f8..5e43163e31 100644 --- a/plugins/home/report-alpha.api.md +++ b/plugins/home/report-alpha.api.md @@ -13,7 +13,9 @@ import { RouteRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) const _default: FrontendPlugin< - {}, + { + root: RouteRef; + }, {}, { 'page:home': ExtensionDefinition<{ diff --git a/plugins/home/report.api.md b/plugins/home/report.api.md index 7c7f55fd47..61ce9a77f8 100644 --- a/plugins/home/report.api.md +++ b/plugins/home/report.api.md @@ -89,6 +89,7 @@ export const CustomHomepageGrid: ( export type CustomHomepageGridProps = { children?: ReactNode; config?: LayoutConfiguration[]; + title?: string; rowHeight?: number; breakpoints?: Record; cols?: Record; @@ -194,9 +195,10 @@ export const QuickStartCard: ( // @public export type QuickStartCardProps = { modalTitle?: string | JSX_3.Element; - docsLinkTitle?: string; + docsLinkTitle?: string | JSX_3.Element; docsLink?: string; video?: JSX_3.Element; + additionalContent?: JSX_3.Element; image: JSX_3.Element; cardDescription?: string; downloadImage?: JSX_3.Element; diff --git a/plugins/home/src/alpha.tsx b/plugins/home/src/alpha.tsx index 74f96a2195..04d612ef1c 100644 --- a/plugins/home/src/alpha.tsx +++ b/plugins/home/src/alpha.tsx @@ -69,4 +69,7 @@ const homePage = PageBlueprint.makeWithOverrides({ export default createFrontendPlugin({ pluginId: 'home', extensions: [homePage], + routes: { + root: rootRouteRef, + }, }); diff --git a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx index da55ccc5f4..675e17357e 100644 --- a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx +++ b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx @@ -321,7 +321,7 @@ export const CustomHomepageGrid = (props: CustomHomepageGridProps) => { return ( <> - + ({ +const useStyles = makeStyles(theme => ({ name: { marginLeft: '0.8rem', marginRight: '0.8rem', + fontSize: theme.typography.body1.fontSize, }, })); export const ItemName = ({ visit }: { visit: Visit }) => { const classes = useStyles(); + if (visit.entityRef) + return ( + + ); + return ( { - {props.video && props.video} + {(props.additionalContent && props.additionalContent) || + (props.video && props.video)} ); }; diff --git a/plugins/home/src/homePageComponents/QuickStart/QuickStart.stories.tsx b/plugins/home/src/homePageComponents/QuickStart/QuickStart.stories.tsx index 18375c9c27..c0963b88a1 100644 --- a/plugins/home/src/homePageComponents/QuickStart/QuickStart.stories.tsx +++ b/plugins/home/src/homePageComponents/QuickStart/QuickStart.stories.tsx @@ -18,6 +18,7 @@ import { QuickStartCard } from '../../plugin'; import { ComponentType, PropsWithChildren } from 'react'; import { wrapInTestApp } from '@backstage/test-utils'; import Grid from '@material-ui/core/Grid'; +import OpenInNewIcon from '@material-ui/icons/OpenInNew'; import ContentImage from './static/backstageSystemModel.png'; export default { @@ -61,6 +62,40 @@ export const Customized = () => { /> } cardDescription="Backstage system model will help you create new entities" + additionalContent={ +

+ This is a custom description for the Quick Start card. It can be + used to provide additional information or context about the Quick + Start process. +

+ } + /> + + ); +}; + +export const CustomDocLink = () => { + return ( + + + + Learn more with getting started docs + + } + docsLink="https://backstage.io/docs/getting-started" + image={ + quick start + } + cardDescription="Backstage system model will help you create new entities" /> ); diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 4147c855f8..c162bcb8cc 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-kubernetes-backend +## 0.19.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.16-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-kubernetes-common@0.9.5-next.0 + - @backstage/plugin-kubernetes-node@0.3.0-next.2 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-node@0.10.0-next.2 + ## 0.19.6-next.2 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index 95854d8e88..9f7dc6fd31 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-backend", - "version": "0.19.6-next.2", + "version": "0.19.6-next.3", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index aeaf6868ed..4343e9f5a2 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.25-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/plugin-kubernetes-react@0.5.7-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-kubernetes-common@0.9.5-next.0 + - @backstage/plugin-permission-react@0.4.34-next.1 + ## 0.0.25-next.2 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index c7d0013ad2..02e6253c2e 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-cluster", - "version": "0.0.25-next.2", + "version": "0.0.25-next.3", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 43e94e275a..6cbd464a9f 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-kubernetes-node +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.5-next.0 + ## 0.3.0-next.1 ### Minor Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index ef81490e0a..d4842f9030 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-node", - "version": "0.3.0-next.1", + "version": "0.3.0-next.2", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index b2c602a222..685e751982 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-kubernetes +## 0.12.7-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/plugin-kubernetes-react@0.5.7-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/plugin-kubernetes-common@0.9.5-next.0 + - @backstage/plugin-permission-react@0.4.34-next.1 + ## 0.12.7-next.2 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index 60c232bc51..6dc09d3c6a 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.7-next.2", + "version": "0.12.7-next.3", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md index f0baa3195d..8b39ef8055 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.9-next.3 + +### Patch Changes + +- aa3a63a: Enable the ability to configure the endpoint for the SES connection used in the notifications email module. This enables the configuration of alternate endpoints as required, for example for local testing or alternative stacks. +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.15-next.2 + ## 0.3.9-next.2 ### Patch Changes diff --git a/plugins/notifications-backend-module-email/config.d.ts b/plugins/notifications-backend-module-email/config.d.ts index bce7a4b332..97a6495246 100644 --- a/plugins/notifications-backend-module-email/config.d.ts +++ b/plugins/notifications-backend-module-email/config.d.ts @@ -64,6 +64,10 @@ export interface Config { * AWS account ID to use */ accountId?: string; + /** + * AWS endpoint to use, defaults to standard AWS endpoint based on region + */ + endpoint?: string; /** * AWS region to use */ diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index 566a446aa3..94e953d480 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-email", - "version": "0.3.9-next.2", + "version": "0.3.9-next.3", "description": "The email backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend-module-email/src/processor/transports/ses.ts b/plugins/notifications-backend-module-email/src/processor/transports/ses.ts index 0be23078ab..3ac351307e 100644 --- a/plugins/notifications-backend-module-email/src/processor/transports/ses.ts +++ b/plugins/notifications-backend-module-email/src/processor/transports/ses.ts @@ -30,6 +30,7 @@ export const createSesTransport = async ( apiVersion: config.getOptionalString('apiVersion') ?? '2010-12-01', credentials: credentials.sdkCredentialProvider, region: config.getOptionalString('region'), + endpoint: config.getOptionalString('endpoint'), }, ]); return createTransport({ diff --git a/plugins/notifications-backend-module-slack/CHANGELOG.md b/plugins/notifications-backend-module-slack/CHANGELOG.md index 2cdd8848a4..6b8aede3b6 100644 --- a/plugins/notifications-backend-module-slack/CHANGELOG.md +++ b/plugins/notifications-backend-module-slack/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-notifications-backend-module-slack +## 0.1.1-next.3 + +### Patch Changes + +- f6480c7: Fix dataloader caching, and use the proper catalog service ref +- e099d0a: Notifications which mention user entity refs are now replaced with Slack compatible mentions. + + Example: `Welcome <@user:default/billy>!` -> `Welcome <@U123456890>!` + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.15-next.2 + ## 0.1.1-next.2 ### Patch Changes diff --git a/plugins/notifications-backend-module-slack/package.json b/plugins/notifications-backend-module-slack/package.json index 33770bdeac..33bfcc55bc 100644 --- a/plugins/notifications-backend-module-slack/package.json +++ b/plugins/notifications-backend-module-slack/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend-module-slack", - "version": "0.1.1-next.2", + "version": "0.1.1-next.3", "description": "The slack backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend/CHANGELOG.md b/plugins/notifications-backend/CHANGELOG.md index 34435c659f..03f8828ea3 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-notifications-backend +## 0.5.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.15-next.2 + - @backstage/plugin-signals-node@0.1.20-next.2 + ## 0.5.6-next.1 ### Patch Changes diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index 6929b362b9..1f1c328abe 100644 --- a/plugins/notifications-backend/package.json +++ b/plugins/notifications-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-backend", - "version": "0.5.6-next.1", + "version": "0.5.6-next.2", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index 3b0c21e7d7..2191de49ad 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-notifications-node +## 0.2.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-node@0.1.20-next.2 + ## 0.2.15-next.1 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index ca66d819b9..02775f2030 100644 --- a/plugins/notifications-node/package.json +++ b/plugins/notifications-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-node", - "version": "0.2.15-next.1", + "version": "0.2.15-next.2", "description": "Node.js library for the notifications plugin", "backstage": { "role": "node-library", diff --git a/plugins/notifications/CHANGELOG.md b/plugins/notifications/CHANGELOG.md index f171b8a148..9527fe373f 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-notifications +## 0.5.5-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/theme@0.6.6-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-signals-react@0.0.13-next.0 + ## 0.5.5-next.2 ### Patch Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index 0a9d383056..9b5df4bfe2 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.5.5-next.2", + "version": "0.5.5-next.3", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 766eb1b7ca..8ad4f304ba 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-org-react +## 0.1.38-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + ## 0.1.38-next.2 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 8659c22098..99d7f2b1b5 100644 --- a/plugins/org-react/package.json +++ b/plugins/org-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org-react", - "version": "0.1.38-next.2", + "version": "0.1.38-next.3", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 5dd3fe3463..e77c76c618 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-org +## 0.6.39-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/plugin-catalog-common@1.1.4-next.0 + ## 0.6.39-next.2 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index 5337c5ec0f..78e0dad480 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.39-next.2", + "version": "0.6.39-next.3", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 71dbc1bfe5..fd25849e16 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.2.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-node@0.10.0-next.2 + ## 0.2.8-next.1 ### Patch Changes diff --git a/plugins/permission-backend-module-policy-allow-all/package.json b/plugins/permission-backend-module-policy-allow-all/package.json index fb56a68093..63c1d3e98d 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", - "version": "0.2.8-next.1", + "version": "0.2.8-next.2", "description": "Allow all policy backend module for the permission plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index 6205e7e819..f6e675c843 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-backend +## 0.7.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-node@0.10.0-next.2 + ## 0.7.0-next.1 ### Minor Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index 2a2348a263..88ca6ad884 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.7.0-next.1", + "version": "0.7.0-next.2", "backstage": { "role": "backend-plugin", "pluginId": "permission", diff --git a/plugins/permission-common/src/PermissionClient.test.ts b/plugins/permission-common/src/PermissionClient.test.ts index b55e5da94d..4ddfadadda 100644 --- a/plugins/permission-common/src/PermissionClient.test.ts +++ b/plugins/permission-common/src/PermissionClient.test.ts @@ -407,7 +407,7 @@ describe('PermissionClient', () => { }, { id: req.body.items[1].id, - result: [AuthorizeResult.DENY], + result: AuthorizeResult.DENY, }, { id: req.body.items[2].id, diff --git a/plugins/permission-common/src/PermissionClient.ts b/plugins/permission-common/src/PermissionClient.ts index 93c3d5152f..61adc9279a 100644 --- a/plugins/permission-common/src/PermissionClient.ts +++ b/plugins/permission-common/src/PermissionClient.ts @@ -60,14 +60,16 @@ const authorizePermissionResponseSchema: z.ZodSchema = z.union([ @@ -240,9 +242,13 @@ export class PermissionClient implements PermissionEvaluator { const { id } = request[query.permission.name]; const item = responsesById[id]; - return { - result: query.resourceRef ? item.result.shift()! : item.result[0], - }; + + if (Array.isArray(item.result)) { + return { + result: query.resourceRef ? item.result.shift()! : item.result[0], + }; + } + return { result: item.result }; }); } diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index 13c3f7907d..0b7c03bac1 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-permission-node +## 0.10.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.0-next.0 + ## 0.10.0-next.1 ### Minor Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index f60f50f305..7e388faf29 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-node", - "version": "0.10.0-next.1", + "version": "0.10.0-next.2", "description": "Common permission and authorization utilities for backend plugins", "backstage": { "role": "node-library", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index c7c9c22f77..573f50ff60 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-proxy-backend +## 0.6.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/types@1.2.1 + - @backstage/plugin-proxy-node@0.1.4-next.2 + ## 0.6.2-next.1 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 60d0464c4a..3ad9442d97 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.6.2-next.1", + "version": "0.6.2-next.2", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", "backstage": { "role": "backend-plugin", diff --git a/plugins/proxy-node/CHANGELOG.md b/plugins/proxy-node/CHANGELOG.md index 6d1a68beb7..db7df34df3 100644 --- a/plugins/proxy-node/CHANGELOG.md +++ b/plugins/proxy-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-proxy-node +## 0.1.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + ## 0.1.4-next.1 ### Patch Changes diff --git a/plugins/proxy-node/package.json b/plugins/proxy-node/package.json index 20db83410b..5318593350 100644 --- a/plugins/proxy-node/package.json +++ b/plugins/proxy-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-proxy-node", - "version": "0.1.4-next.1", + "version": "0.1.4-next.2", "description": "The plugin-proxy-node module for @backstage/plugin-proxy-backend", "backstage": { "role": "node-library", diff --git a/plugins/scaffolder-backend-module-azure/CHANGELOG.md b/plugins/scaffolder-backend-module-azure/CHANGELOG.md index 4575bae2c7..58f802e4fd 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 9507c527d7..f16c53d00d 100644 --- a/plugins/scaffolder-backend-module-azure/package.json +++ b/plugins/scaffolder-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-azure", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "description": "The azure module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md index cd1207a738..8138ca43aa 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-bitbucket-cloud-common@0.3.0-next.3 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index 79ea0f1131..d6a5761fa0 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "description": "The Bitbucket Cloud module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md index ad54b35865..cc84fff174 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index c092f10207..f78ebfec25 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/package.json +++ b/plugins/scaffolder-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket-server", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "description": "The Bitbucket Server module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md index c8bfd2fc7b..0e7d33210b 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.3.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.9-next.3 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.9-next.3 + ## 0.3.10-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index 699858702c..27ac5f6bc0 100644 --- a/plugins/scaffolder-backend-module-bitbucket/package.json +++ b/plugins/scaffolder-backend-module-bitbucket/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-bitbucket", - "version": "0.3.10-next.2", + "version": "0.3.10-next.3", "description": "The bitbucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 06756b19fd..2b161776cb 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.3.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.3.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index de6045689f..d28013752e 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", - "version": "0.3.9-next.2", + "version": "0.3.9-next.3", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index c1a9ce091f..7b6b0c3dc8 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.3.10-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 404bad2b94..4618c01653 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", - "version": "0.3.10-next.2", + "version": "0.3.10-next.3", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index 9f7f9b1631..563d5f588c 100644 --- a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gcp +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 7c2fc0a624..dde1a8ba87 100644 --- a/plugins/scaffolder-backend-module-gcp/package.json +++ b/plugins/scaffolder-backend-module-gcp/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gcp", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "description": "The GCP Bucket module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md index 7c0367d1b0..23ea33f17a 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index aa6e7478ef..32678efabf 100644 --- a/plugins/scaffolder-backend-module-gerrit/package.json +++ b/plugins/scaffolder-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gerrit", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "description": "The gerrit module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md index c7e65d176e..72d4cbe1d7 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 1be15b9164..c3b14f0ba1 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitea", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/CHANGELOG.md b/plugins/scaffolder-backend-module-github/CHANGELOG.md index 6170587864..67b338beac 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.7.1-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.7.1-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 0217d0b48d..1e6b265412 100644 --- a/plugins/scaffolder-backend-module-github/package.json +++ b/plugins/scaffolder-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-github", - "version": "0.7.1-next.2", + "version": "0.7.1-next.3", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index aa4a43c22c..dc7c180b59 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.9.1-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.9.1-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index d2f699b219..54c6d5efec 100644 --- a/plugins/scaffolder-backend-module-gitlab/package.json +++ b/plugins/scaffolder-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitlab", - "version": "0.9.1-next.2", + "version": "0.9.1-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index 4db8585828..3923382d94 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.15-next.2 + ## 0.1.10-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index ea6c416a9e..c49531786f 100644 --- a/plugins/scaffolder-backend-module-notifications/package.json +++ b/plugins/scaffolder-backend-module-notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-notifications", - "version": "0.1.10-next.2", + "version": "0.1.10-next.3", "description": "The notifications backend module for the scaffolder plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 9c9117bfb3..d4a8d0f82b 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.5.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.5.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index c4f58e24f0..6cd2e344cd 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", - "version": "0.5.9-next.2", + "version": "0.5.9-next.3", "description": "A module for the scaffolder backend that lets you template projects using Rails", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 886803e1f0..829c30ef6a 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.2.9-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.9-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index c59a288861..bebe43c2f5 100644 --- a/plugins/scaffolder-backend-module-sentry/package.json +++ b/plugins/scaffolder-backend-module-sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-sentry", - "version": "0.2.9-next.2", + "version": "0.2.9-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index a319388ed3..df48b09214 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.4.10-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-node-test-utils@0.2.2-next.3 + ## 0.4.10-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 577e6735a0..44bdb6d120 100644 --- a/plugins/scaffolder-backend-module-yeoman/package.json +++ b/plugins/scaffolder-backend-module-yeoman/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend-module-yeoman", - "version": "0.4.10-next.2", + "version": "0.4.10-next.3", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index bfcea59479..8c42f7a26a 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-scaffolder-backend +## 1.33.0-next.3 + +### Patch Changes + +- ec42f8e: Generating new tokens on each Scaffolder Task Retry +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.1-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-bitbucket-cloud-common@0.3.0-next.3 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.8-next.2 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-events-node@0.4.11-next.2 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-node@0.10.0-next.2 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.9-next.3 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.10-next.3 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.9-next.3 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.9-next.3 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.9-next.3 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.9-next.3 + - @backstage/plugin-scaffolder-backend-module-github@0.7.1-next.3 + - @backstage/plugin-scaffolder-common@1.5.11-next.0 + ## 1.33.0-next.2 ### Patch Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 8a36adfd7a..30cc5ffbf3 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "1.33.0-next.2", + "version": "1.33.0-next.3", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index 0438922de3..41c228e3f1 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -502,7 +502,10 @@ export class DatabaseTaskStore implements TaskStore { targetPath: string; }): Promise; // (undocumented) - retryTask?(options: { taskId: string }): Promise; + retryTask?(options: { + secrets?: TaskSecrets_2; + taskId: string; + }): Promise; // (undocumented) saveTaskState(options: { taskId: string; state?: JsonObject }): Promise; // (undocumented) @@ -769,7 +772,10 @@ export interface TaskStore { targetPath: string; }): Promise; // (undocumented) - retryTask?(options: { taskId: string }): Promise; + retryTask?(options: { + secrets?: TaskSecrets_2; + taskId: string; + }): Promise; // (undocumented) saveTaskState?(options: { taskId: string; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index 4dbe938679..51a30a20ec 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -654,12 +654,18 @@ export class DatabaseTaskStore implements TaskStore { }); } - async retryTask?(options: { taskId: string }): Promise { + async retryTask?(options: { + secrets?: TaskSecrets; + taskId: string; + }): Promise { + const { secrets, taskId } = options; + await this.db.transaction(async tx => { const result = await tx('tasks') - .where('id', options.taskId) + .where('id', taskId) .update( { + ...(secrets && { secrets: JSON.stringify(secrets) }), status: 'open', last_heartbeat_at: this.db.fn.now(), }, diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts index 0fd18a9ee5..27a4499772 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts @@ -503,8 +503,11 @@ export class StorageTaskBroker implements TaskBroker { }); } - async retry?(taskId: string): Promise { - await this.storage.retryTask?.({ taskId }); + async retry?(options: { + secrets?: TaskSecrets; + taskId: string; + }): Promise { + await this.storage.retryTask?.(options); this.signalDispatch(); } } diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index 874793922f..1556c4838c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -171,7 +171,7 @@ export interface TaskStore { options: TaskStoreCreateTaskOptions, ): Promise; - retryTask?(options: { taskId: string }): Promise; + retryTask?(options: { secrets?: TaskSecrets; taskId: string }): Promise; recoverTasks?( options: TaskStoreRecoverTaskOptions, diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index e5c384987a..f2e323e831 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -828,7 +828,22 @@ export async function createRouter( await auditorEvent?.success(); - await taskBroker.retry?.(taskId); + const { token } = await auth.getPluginRequestToken({ + onBehalfOf: credentials, + targetPluginId: 'catalog', + }); + + const secrets: InternalTaskSecrets = { + ...req.body.secrets, + backstageToken: token, + __initiatorCredentials: JSON.stringify({ + ...credentials, + // credentials.token is nonenumerable and will not be serialized, so we need to add it explicitly + token: (credentials as any).token, + }), + }; + + await taskBroker.retry?.({ secrets, taskId }); res.status(201).json({ id: taskId }); } catch (err) { await auditorEvent?.fail({ error: err }); diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index 02f9c91a97..e867e35a94 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.2.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.8.2-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/backend-test-utils@1.5.0-next.3 + - @backstage/types@1.2.1 + ## 0.2.2-next.2 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 9473450114..87874d23c4 100644 --- a/plugins/scaffolder-node-test-utils/package.json +++ b/plugins/scaffolder-node-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node-test-utils", - "version": "0.2.2-next.2", + "version": "0.2.2-next.3", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 9654b575de..c978c94944 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-node +## 0.8.2-next.3 + +### Patch Changes + +- 16e2e9c: trim leading and trailing slashes from parseRepoUrl query parameters +- ec42f8e: Generating new tokens on each Scaffolder Task Retry +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.11-next.0 + ## 0.8.2-next.2 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 011b09c91f..61557aa4a8 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-node", - "version": "0.8.2-next.2", + "version": "0.8.2-next.3", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", @@ -66,6 +66,7 @@ "globby": "^11.0.0", "isomorphic-git": "^1.23.0", "jsonschema": "^1.5.0", + "lodash": "^4.17.21", "p-limit": "^3.1.0", "tar": "^6.1.12", "winston": "^3.2.1", @@ -76,6 +77,7 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/config": "workspace:^" + "@backstage/config": "workspace:^", + "@types/lodash": "^4.14.151" } } diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index 6dabcbf346..f2e8501527 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -414,7 +414,7 @@ export interface TaskBroker { // (undocumented) recoverTasks?(): Promise; // (undocumented) - retry?(taskId: string): Promise; + retry?(options: { secrets?: TaskSecrets; taskId: string }): Promise; // (undocumented) vacuumTasks(options: { timeoutS: number }): Promise; } diff --git a/plugins/scaffolder-node/src/actions/util.test.ts b/plugins/scaffolder-node/src/actions/util.test.ts new file mode 100644 index 0000000000..4b1e787bbc --- /dev/null +++ b/plugins/scaffolder-node/src/actions/util.test.ts @@ -0,0 +1,247 @@ +/* + * Copyright 2025 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 { ScmIntegrationRegistry } from '@backstage/integration'; +import { parseRepoUrl } from './util'; +import { mapValues } from 'lodash'; + +const queryString = ( + params: Partial< + Record<'owner' | 'organization' | 'workspace' | 'project' | 'repo', string> + > = {}, +): string => { + const pEntries = Object.entries(params); + if (pEntries.length) { + return `?${pEntries + .map(([k, v]) => `${k}=${encodeURIComponent(v)}`) + .join('&')}`; + } + return ''; +}; + +describe('scaffolder action utils', () => { + describe('parseRepoUrl', () => { + const byHost = jest.fn(); + const integrations = { + byHost, + } as unknown as ScmIntegrationRegistry; + + describe('rejects url when', () => { + it('empty', () => + expect(() => parseRepoUrl('', integrations)).toThrow( + /Invalid repo URL passed/, + )); + it('blank', () => + expect(() => parseRepoUrl(' ', integrations)).toThrow( + /Invalid repo URL passed/, + )); + }); + it('requires that host match an integration type', () => { + byHost.mockClear(); + expect(() => parseRepoUrl('foo', integrations)).toThrow( + /No matching integration configuration for host/, + ); + }); + describe('bitbucket', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'bitbucket' })); + describe('cloud', () => { + const [host, workspace, project, repo] = [ + 'www.bitbucket.org', + 'foo', + 'bar', + 'baz', + ]; + it('requires workspace', () => + expect(() => + parseRepoUrl( + `${host}${queryString({ project, repo })}`, + integrations, + ), + ).toThrow(/missing workspace/)); + it('requires project', () => + expect(() => + parseRepoUrl( + `${host}${queryString({ workspace, repo })}`, + integrations, + ), + ).toThrow(/missing project/)); + it('requires repo', () => + expect(() => + parseRepoUrl( + `${host}${queryString({ workspace, project })}`, + integrations, + ), + ).toThrow(/missing repo/)); + it('happy path', () => + expect( + parseRepoUrl( + `${host}${queryString({ workspace, project, repo })}`, + integrations, + ), + ).toMatchObject({ + host, + workspace, + project, + repo, + })); + }); + describe('other', () => { + const [host, project, repo] = ['bitbucket.other', 'foo', 'bar']; + it('requires project', () => + expect(() => + parseRepoUrl(`${host}${queryString({ repo })}`, integrations), + ).toThrow(/missing project/)); + it('requires repo', () => + expect(() => + parseRepoUrl(`${host}${queryString({ project })}`, integrations), + ).toThrow(/missing repo/)); + it('happy path', () => + expect( + parseRepoUrl( + `${host}${queryString({ project, repo })}`, + integrations, + ), + ).toMatchObject({ + host, + project, + repo, + })); + }); + }); + describe('azure', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'azure' })); + const [host, project, repo] = ['az.ure', 'foo', 'bar']; + it('requires project', () => + expect(() => + parseRepoUrl(`${host}${queryString({ repo })}`, integrations), + ).toThrow(/missing project/)); + it('requires repo', () => + expect(() => + parseRepoUrl(`${host}${queryString({ project })}`, integrations), + ).toThrow(/missing repo/)); + it('happy path', () => + expect( + parseRepoUrl( + `${host}${queryString({ project, repo })}`, + integrations, + ), + ).toMatchObject({ + host, + project, + repo, + })); + }); + describe('gitlab', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'gitlab' })); + const [host, owner, repo, project] = ['gitl.ab', 'foo', 'bar', '123456']; + it('requires owner', () => + expect(() => + parseRepoUrl(`${host}${queryString({ repo })}`, integrations), + ).toThrow(/missing owner/)); + it('requires repo', () => + expect(() => + parseRepoUrl(`${host}${queryString({ owner })}`, integrations), + ).toThrow(/missing repo/)); + it('unless project specified', () => + expect( + parseRepoUrl(`${host}${queryString({ project })}`, integrations), + ).toMatchObject({ host, project })); + it('happy path', () => + expect( + parseRepoUrl(`${host}${queryString({ owner, repo })}`, integrations), + ).toMatchObject({ + host, + owner, + repo, + })); + }); + describe('gitea', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'gitea' })); + const [host, repo] = ['git.ea', 'foo']; + it('requires repo', () => + expect(() => parseRepoUrl(host, integrations)).toThrow(/missing repo/)); + it('happy path', () => + expect( + parseRepoUrl(`${host}${queryString({ repo })}`, integrations), + ).toMatchObject({ host, repo })); + }); + describe('gerrit', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'gerrit' })); + const [host, repo] = ['gerr.it', 'foo']; + it('requires repo', () => + expect(() => parseRepoUrl(host, integrations)).toThrow(/missing repo/)); + it('happy path', () => + expect( + parseRepoUrl(`${host}${queryString({ repo })}`, integrations), + ).toMatchObject({ + host, + repo, + })); + }); + describe('generic type', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'generic' })); + const [host, owner, repo] = ['oth.er', 'foo', 'bar']; + it('requires owner', () => + expect(() => + parseRepoUrl(`${host}${queryString({ repo })}`, integrations), + ).toThrow(/missing owner/)); + it('requires repo', () => + expect(() => + parseRepoUrl(`${host}${queryString({ owner })}`, integrations), + ).toThrow(/missing repo/)); + it('happy path', () => + expect( + parseRepoUrl(`${host}${queryString({ owner, repo })}`, integrations), + ).toMatchObject({ + host, + owner, + repo, + })); + }); + describe('facilitates naive URL construction', () => { + beforeEach(() => byHost.mockReturnValue({ type: 'irrelevant' })); + it('decodes encoded params', () => { + const [host, owner, repo] = ['with_the_most', 'foo/bar/baz', 'blah']; + expect( + parseRepoUrl(`${host}${queryString({ owner, repo })}`, integrations), + ).toMatchObject({ host, owner, repo }); + }); + it('trims leading and trailing / from params', () => { + const [host, owner, organization, workspace, project, repo] = [ + 'anywhere', + 'anyone', + 'anything', + 'anyway', + 'anyhow', + 'any', + ]; + const junkedUp = mapValues( + { owner, organization, workspace, project, repo }, + v => `//${v}//`, + ); + return expect( + parseRepoUrl(`${host}${queryString(junkedUp)}`, integrations), + ).toMatchObject({ + host, + owner, + organization, + workspace, + project, + repo, + }); + }); + }); + }); +}); diff --git a/plugins/scaffolder-node/src/actions/util.ts b/plugins/scaffolder-node/src/actions/util.ts index f1775bb318..12f73b0c6c 100644 --- a/plugins/scaffolder-node/src/actions/util.ts +++ b/plugins/scaffolder-node/src/actions/util.ts @@ -22,6 +22,7 @@ import { TemplateActionOptions } from './createTemplateAction'; import zodToJsonSchema from 'zod-to-json-schema'; import { z } from 'zod'; import { Schema } from 'jsonschema'; +import { trim } from 'lodash'; /** * @public @@ -67,11 +68,6 @@ export const parseRepoUrl = ( ); } const host = parsed.host; - const owner = parsed.searchParams.get('owner') ?? undefined; - const organization = parsed.searchParams.get('organization') ?? undefined; - const workspace = parsed.searchParams.get('workspace') ?? undefined; - const project = parsed.searchParams.get('project') ?? undefined; - const type = integrations.byHost(host)?.type; if (!type) { @@ -79,8 +75,14 @@ export const parseRepoUrl = ( `No matching integration configuration for host ${host}, please check your integrations config`, ); } - - const repo: string = parsed.searchParams.get('repo')!; + const { owner, organization, workspace, project, repo } = Object.fromEntries( + ['owner', 'organization', 'workspace', 'project', 'repo'].map(param => [ + param, + parsed.searchParams.has(param) + ? trim(parsed.searchParams.get(param)!, '/') + : undefined, + ]), + ); switch (type) { case 'bitbucket': { if (host === 'www.bitbucket.org') { @@ -113,8 +115,7 @@ export const parseRepoUrl = ( break; } } - - return { host, owner, repo, organization, workspace, project }; + return { host, owner, repo: repo!, organization, workspace, project }; }; function checkRequiredParams(repoUrl: URL, ...params: string[]) { diff --git a/plugins/scaffolder-node/src/tasks/types.ts b/plugins/scaffolder-node/src/tasks/types.ts index fdfeb49a0b..d8cfd24530 100644 --- a/plugins/scaffolder-node/src/tasks/types.ts +++ b/plugins/scaffolder-node/src/tasks/types.ts @@ -165,7 +165,7 @@ export interface TaskContext { export interface TaskBroker { cancel?(taskId: string): Promise; - retry?(taskId: string): Promise; + retry?(options: { secrets?: TaskSecrets; taskId: string }): Promise; claim(): Promise; diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 97925ee2a1..9cddcfefca 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-scaffolder-react +## 1.16.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/theme@0.6.6-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.34-next.1 + - @backstage/plugin-scaffolder-common@1.5.11-next.0 + ## 1.16.0-next.2 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 514da4fe93..81f9dad6dc 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-react", - "version": "1.16.0-next.2", + "version": "1.16.0-next.3", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index ba8d5db71b..c315185b92 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-scaffolder +## 1.31.0-next.3 + +### Patch Changes + +- a274e0a: Migrate custom fields to new schema factory function; + standardize field descriptions to prefer ui:description and present consistently, + utilizing ScaffolderField component where possible. +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/plugin-scaffolder-react@1.16.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-permission-react@0.4.34-next.1 + - @backstage/plugin-scaffolder-common@1.5.11-next.0 + ## 1.31.0-next.2 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 9c71f584ed..47e092112f 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.31.0-next.2", + "version": "1.31.0-next.3", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/scaffolder/report-alpha.api.md b/plugins/scaffolder/report-alpha.api.md index de088224b8..69fcec11e6 100644 --- a/plugins/scaffolder/report-alpha.api.md +++ b/plugins/scaffolder/report-alpha.api.md @@ -289,6 +289,8 @@ export const scaffolderTranslationRef: TranslationRef< readonly 'fields.entityPicker.description': 'An entity from the catalog'; readonly 'fields.entityTagsPicker.title': 'Tags'; readonly 'fields.entityTagsPicker.description': "Add any relevant tags, hit 'Enter' to add new tags. Valid format: [a-z0-9+#] separated by [-], at most 63 characters"; + readonly 'fields.multiEntityPicker.title': 'Entity'; + readonly 'fields.multiEntityPicker.description': 'An entity from the catalog'; readonly 'fields.myGroupsPicker.title': 'Entity'; readonly 'fields.myGroupsPicker.description': 'An entity from the catalog'; readonly 'fields.ownedEntityPicker.title': 'Entity'; diff --git a/plugins/scaffolder/report.api.md b/plugins/scaffolder/report.api.md index b929d7be5b..b44889cded 100644 --- a/plugins/scaffolder/report.api.md +++ b/plugins/scaffolder/report.api.md @@ -71,7 +71,7 @@ export type CustomFieldValidator = // @public export const EntityNamePickerFieldExtension: FieldExtensionComponent_2< string, - {} + any >; // @public @@ -104,7 +104,7 @@ export const EntityPickerFieldExtension: FieldExtensionComponent_2< >; // @public (undocumented) -export const EntityPickerFieldSchema: FieldSchema< +export const EntityPickerFieldSchema: FieldSchema_2< string, { defaultKind?: string | undefined; @@ -133,8 +133,9 @@ export const EntityPickerFieldSchema: FieldSchema< >; // @public -export type EntityPickerUiOptions = - typeof EntityPickerFieldSchema.uiOptionsType; +export type EntityPickerUiOptions = NonNullable< + (typeof EntityPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; // @public export const EntityTagsPickerFieldExtension: FieldExtensionComponent_2< @@ -147,7 +148,7 @@ export const EntityTagsPickerFieldExtension: FieldExtensionComponent_2< >; // @public (undocumented) -export const EntityTagsPickerFieldSchema: FieldSchema< +export const EntityTagsPickerFieldSchema: FieldSchema_2< string[], { helperText?: string | undefined; @@ -157,8 +158,9 @@ export const EntityTagsPickerFieldSchema: FieldSchema< >; // @public -export type EntityTagsPickerUiOptions = - typeof EntityTagsPickerFieldSchema.uiOptionsType; +export type EntityTagsPickerUiOptions = NonNullable< + (typeof EntityTagsPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; // @public @deprecated (undocumented) export type FieldExtensionComponent<_TReturnValue, _TInputProps> = @@ -233,27 +235,19 @@ export const MultiEntityPickerFieldExtension: FieldExtensionComponent_2< // @public export const MyGroupsPickerFieldExtension: FieldExtensionComponent_2< string, - { - title?: string | undefined; - description?: string | undefined; - } + any >; // @public -export const MyGroupsPickerFieldSchema: FieldSchema< - string, - { - title?: string | undefined; - description?: string | undefined; - } ->; +export const MyGroupsPickerFieldSchema: FieldSchema_2; // @public export const MyGroupsPickerSchema: CustomFieldExtensionSchema_2; // @public -export type MyGroupsPickerUiOptions = - typeof MyGroupsPickerFieldSchema.uiOptionsType; +export type MyGroupsPickerUiOptions = NonNullable< + (typeof MyGroupsPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; // @public export const OwnedEntityPickerFieldExtension: FieldExtensionComponent_2< @@ -285,7 +279,7 @@ export const OwnedEntityPickerFieldExtension: FieldExtensionComponent_2< >; // @public (undocumented) -export const OwnedEntityPickerFieldSchema: FieldSchema< +export const OwnedEntityPickerFieldSchema: FieldSchema_2< string, { defaultKind?: string | undefined; @@ -314,8 +308,9 @@ export const OwnedEntityPickerFieldSchema: FieldSchema< >; // @public -export type OwnedEntityPickerUiOptions = - typeof OwnedEntityPickerFieldSchema.uiOptionsType; +export type OwnedEntityPickerUiOptions = NonNullable< + (typeof OwnedEntityPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; // @public export const OwnerPickerFieldExtension: FieldExtensionComponent_2< @@ -346,7 +341,7 @@ export const OwnerPickerFieldExtension: FieldExtensionComponent_2< >; // @public (undocumented) -export const OwnerPickerFieldSchema: FieldSchema< +export const OwnerPickerFieldSchema: FieldSchema_2< string, { defaultNamespace?: string | false | undefined; @@ -374,7 +369,9 @@ export const OwnerPickerFieldSchema: FieldSchema< >; // @public -export type OwnerPickerUiOptions = typeof OwnerPickerFieldSchema.uiOptionsType; +export type OwnerPickerUiOptions = NonNullable< + (typeof OwnerPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; // @public export const RepoBranchPickerFieldExtension: FieldExtensionComponent_2< @@ -462,8 +459,9 @@ export const RepoUrlPickerFieldSchema: FieldSchema_2< >; // @public @deprecated -export type RepoUrlPickerUiOptions = - typeof RepoUrlPickerFieldSchema.uiOptionsType; +export type RepoUrlPickerUiOptions = NonNullable< + (typeof RepoUrlPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; export { ReviewStepProps }; diff --git a/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts index 27e33befb5..fe4765b8b8 100644 --- a/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityNamePicker/schema.ts @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { z } from 'zod'; -import { makeFieldSchemaFromZod } from '../utils'; +import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; -const EntityNamePickerFieldSchema = makeFieldSchemaFromZod(z.string()); +const EntityNamePickerFieldSchema = makeFieldSchema({ + output: z => z.string(), +}); export const EntityNamePickerSchema = EntityNamePickerFieldSchema.schema; -export type EntityNamePickerProps = typeof EntityNamePickerFieldSchema.type; +export type EntityNamePickerProps = typeof EntityNamePickerFieldSchema.TProps; diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx index 2098e3e07d..97c90b2aa5 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.test.tsx @@ -28,6 +28,8 @@ import { EntityPickerProps } from './schema'; import { ScaffolderRJSFFieldProps as FieldProps } from '@backstage/plugin-scaffolder-react'; import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { scaffolderTranslationRef } from '../../../translation'; const makeEntity = (kind: string, namespace: string, name: string): Entity => ({ apiVersion: 'scaffolder.backstage.io/v1beta3', @@ -891,4 +893,128 @@ describe('', () => { expect(onChange).toHaveBeenCalledWith(undefined); }); }); + + describe('EntityPicker description', () => { + const description = { + fromSchema: 'EntityPicker description from schema', + fromUiSchema: 'EntityPicker description from uiSchema', + } as { fromSchema: string; fromUiSchema: string; default?: string }; + + beforeEach(() => { + const RealWrapper = Wrapper; + Wrapper = ({ children }: { children?: ReactNode }) => { + const { t } = useTranslationRef(scaffolderTranslationRef); + description.default = t('fields.entityPicker.description'); + return {children}; + }; + }); + it('presents default description', async () => { + uiSchema = { + 'ui:options': { + catalogFilter: [ + { + kind: ['Group'], + 'metadata.name': 'test-entity', + }, + { + kind: ['User'], + 'metadata.name': 'test-entity', + }, + ], + }, + }; + props = { + onChange, + schema, + required: true, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(getByText(description.default!)).toBeInTheDocument(); + expect(queryByText(description.fromSchema)).toBe(null); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents schema description', async () => { + uiSchema = { + 'ui:options': { + catalogFilter: [ + { + kind: ['Group'], + 'metadata.name': 'test-entity', + }, + { + kind: ['User'], + 'metadata.name': 'test-entity', + }, + ], + }, + }; + props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(getByText(description.fromSchema)).toBeInTheDocument(); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents uiSchema description', async () => { + uiSchema = { + 'ui:options': { + catalogFilter: [ + { + kind: ['Group'], + 'metadata.name': 'test-entity', + }, + { + kind: ['User'], + 'metadata.name': 'test-entity', + }, + ], + }, + 'ui:description': description.fromUiSchema, + }; + props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(queryByText(description.fromSchema)).toBe(null); + expect(getByText(description.fromUiSchema)).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx index 26f88ea85d..d2197d4426 100644 --- a/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityPicker/EntityPicker.tsx @@ -185,7 +185,7 @@ export const EntityPicker = (props: EntityPickerProps) => { return ( + z.record( + z .string() - .optional() - .describe( - 'The default entity kind. Options of this kind will not be prefixed.', - ), - allowArbitraryValues: z - .boolean() - .optional() - .describe('Whether to allow arbitrary user input. Defaults to true'), - defaultNamespace: z - .union([z.string(), z.literal(false)]) - .optional() - .describe( - 'The default namespace. Options with this namespace will not be prefixed.', - ), - catalogFilter: z - .array(entityQueryFilterExpressionSchema) - .or(entityQueryFilterExpressionSchema) - .optional() - .describe('List of key-value filter expression for entities'), - }), -); + .or(z.object({ exists: z.boolean().optional() })) + .or(z.array(z.string())), + ); + +/** + * @public + */ +export const EntityPickerFieldSchema = makeFieldSchema({ + output: z => z.string(), + uiOptions: z => + z.object({ + /** + * @deprecated Use `catalogFilter` instead. + */ + allowedKinds: z + .array(z.string()) + .optional() + .describe( + 'DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from', + ), + defaultKind: z + .string() + .optional() + .describe( + 'The default entity kind. Options of this kind will not be prefixed.', + ), + allowArbitraryValues: z + .boolean() + .optional() + .describe('Whether to allow arbitrary user input. Defaults to true'), + defaultNamespace: z + .union([z.string(), z.literal(false)]) + .optional() + .describe( + 'The default namespace. Options with this namespace will not be prefixed.', + ), + catalogFilter: (t => t.or(t.array()))( + createEntityQueryFilterExpressionSchema(z), + ) + .optional() + .describe('List of key-value filter expression for entities'), + }), +}); /** * The input props that can be specified under `ui:options` for the @@ -71,15 +70,16 @@ export const EntityPickerFieldSchema = makeFieldSchemaFromZod( * * @public */ -export type EntityPickerUiOptions = - typeof EntityPickerFieldSchema.uiOptionsType; +export type EntityPickerUiOptions = NonNullable< + (typeof EntityPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; -export type EntityPickerProps = typeof EntityPickerFieldSchema.type; +export type EntityPickerProps = typeof EntityPickerFieldSchema.TProps; export const EntityPickerSchema = EntityPickerFieldSchema.schema; -export type EntityPickerFilterQuery = z.TypeOf< - typeof entityQueryFilterExpressionSchema +export type EntityPickerFilterQuery = zod.TypeOf< + ReturnType >; export type EntityPickerFilterQueryValue = diff --git a/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.test.tsx b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.test.tsx new file mode 100644 index 0000000000..98091f5098 --- /dev/null +++ b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.test.tsx @@ -0,0 +1,148 @@ +/* + * Copyright 2025 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 { Entity } from '@backstage/catalog-model'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; +import { ScaffolderRJSFFieldProps as FieldProps } from '@backstage/plugin-scaffolder-react'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { ComponentType, PropsWithChildren, ReactNode } from 'react'; +import { EntityTagsPicker } from './EntityTagsPicker'; +import { EntityTagsPickerProps } from './schema'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { scaffolderTranslationRef } from '../../../translation'; + +const makeEntity = (kind: string, namespace: string, name: string): Entity => ({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind, + metadata: { namespace, name }, +}); + +describe('', () => { + const entities: Entity[] = [ + makeEntity('Group', 'default', 'team-a'), + makeEntity('Group', 'default', 'squad-b'), + ]; + const onChange = jest.fn(); + const schema = { type: 'array', items: { type: 'string' } }; + const uiSchema: EntityTagsPickerProps['uiSchema'] = { + 'ui:options': {}, + }; + const rawErrors: string[] = []; + const formData = undefined; + + let props: FieldProps; + + const catalogApi = catalogApiMock.mock({ + getEntities: jest.fn(async () => ({ items: entities })), + }); + + let Wrapper: ComponentType>; + + beforeEach(() => { + Wrapper = ({ children }: { children?: ReactNode }) => ( + + {children} + + ); + }); + + afterEach(() => jest.resetAllMocks()); + + describe('EntityTagsPicker description', () => { + const description = { + fromSchema: 'EntityTagsPicker description from schema', + fromUiSchema: 'EntityTagsPicker description from uiSchema', + } as { fromSchema: string; fromUiSchema: string; default?: string }; + + beforeEach(() => { + const RealWrapper = Wrapper; + Wrapper = ({ children }: { children?: ReactNode }) => { + const { t } = useTranslationRef(scaffolderTranslationRef); + description.default = t('fields.entityTagsPicker.description'); + return {children}; + }; + }); + it('presents default description', async () => { + props = { + onChange, + schema, + required: true, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(getByText(description.default!)).toBeInTheDocument(); + expect(queryByText(description.fromSchema)).toBe(null); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents schema description', async () => { + props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(getByText(description.fromSchema)).toBeInTheDocument(); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents uiSchema description', async () => { + props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema: { + ...uiSchema, + 'ui:description': description.fromUiSchema, + }, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(queryByText(description.fromSchema)).toBe(null); + expect(getByText(description.fromUiSchema)).toBeInTheDocument(); + }); + }); +}); diff --git a/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx index 3cfa16de23..7d63526fe4 100644 --- a/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx +++ b/plugins/scaffolder/src/components/fields/EntityTagsPicker/EntityTagsPicker.tsx @@ -20,12 +20,12 @@ import { GetEntityFacetsRequest } from '@backstage/catalog-client'; import { makeValidator } from '@backstage/catalog-model'; import { useApi } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import FormControl from '@material-ui/core/FormControl'; import TextField from '@material-ui/core/TextField'; import Autocomplete from '@material-ui/lab/Autocomplete'; import { EntityTagsPickerProps } from './schema'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { scaffolderTranslationRef } from '../../../translation'; +import { ScaffolderField } from '@backstage/plugin-scaffolder-react/alpha'; export { EntityTagsPickerSchema } from './schema'; @@ -36,7 +36,19 @@ export { EntityTagsPickerSchema } from './schema'; * @public */ export const EntityTagsPicker = (props: EntityTagsPickerProps) => { - const { formData, onChange, uiSchema } = props; + const { t } = useTranslationRef(scaffolderTranslationRef); + const { + formData, + onChange, + schema: { + title = t('fields.entityTagsPicker.title'), + description = t('fields.entityTagsPicker.description'), + }, + uiSchema, + rawErrors, + required, + errors, + } = props; const catalogApi = useApi(catalogApiRef); const [tagOptions, setTagOptions] = useState([]); const [inputValue, setInputValue] = useState(''); @@ -47,8 +59,6 @@ export const EntityTagsPicker = (props: EntityTagsPickerProps) => { const helperText = uiSchema['ui:options']?.helperText; const isDisabled = uiSchema?.['ui:disabled'] ?? false; - const { t } = useTranslationRef(scaffolderTranslationRef); - const { loading, value: existingTags } = useAsync(async () => { const facet = 'metadata.tags'; const tagsRequest: GetEntityFacetsRequest = { facets: [facet] }; @@ -97,7 +107,13 @@ export const EntityTagsPicker = (props: EntityTagsPickerProps) => { useEffectOnce(() => onChange(formData || [])); return ( - + { renderInput={params => ( setInputValue(e.target.value)} error={inputError} - helperText={helperText ?? t('fields.entityTagsPicker.description')} FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }} /> )} /> - + ); }; diff --git a/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts b/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts index 6677d16e98..d1a300f5c8 100644 --- a/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/EntityTagsPicker/schema.ts @@ -13,30 +13,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { z } from 'zod'; -import { makeFieldSchemaFromZod } from '../utils'; +import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; /** * @public */ -export const EntityTagsPickerFieldSchema = makeFieldSchemaFromZod( - z.array(z.string()), - z.object({ - kinds: z - .array(z.string()) - .optional() - .describe('List of kinds of entities to derive tags from'), - showCounts: z - .boolean() - .optional() - .describe('Whether to show usage counts per tag'), - helperText: z.string().optional().describe('Helper text to display'), - }), -); +export const EntityTagsPickerFieldSchema = makeFieldSchema({ + output: z => z.array(z.string()), + uiOptions: z => + z.object({ + kinds: z + .array(z.string()) + .optional() + .describe('List of kinds of entities to derive tags from'), + showCounts: z + .boolean() + .optional() + .describe('Whether to show usage counts per tag'), + helperText: z + .string() + .optional() + .describe( + 'Helper text to display; DEPRECATED, simply use ui:description', + ), + }), +}); export const EntityTagsPickerSchema = EntityTagsPickerFieldSchema.schema; -export type EntityTagsPickerProps = typeof EntityTagsPickerFieldSchema.type; +export type EntityTagsPickerProps = typeof EntityTagsPickerFieldSchema.TProps; /** * The input props that can be specified under `ui:options` for the @@ -44,5 +49,6 @@ export type EntityTagsPickerProps = typeof EntityTagsPickerFieldSchema.type; * * @public */ -export type EntityTagsPickerUiOptions = - typeof EntityTagsPickerFieldSchema.uiOptionsType; +export type EntityTagsPickerUiOptions = NonNullable< + (typeof EntityTagsPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx index eb80d47e78..acae3418b9 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx @@ -30,6 +30,8 @@ import { MultiEntityPickerProps } from './schema'; import { ScaffolderRJSFFieldProps as FieldProps } from '@backstage/plugin-scaffolder-react'; import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { scaffolderTranslationRef } from '../../../translation'; const makeEntity = (kind: string, namespace: string, name: string): Entity => ({ apiVersion: 'scaffolder.backstage.io/v1beta3', @@ -43,7 +45,7 @@ describe('', () => { makeEntity('Group', 'default', 'squad-b'), ]; const onChange = jest.fn(); - const schema = {}; + const schema = { type: 'array', items: { type: 'string' } }; const required = false; let uiSchema: MultiEntityPickerProps['uiSchema']; const rawErrors: string[] = []; @@ -847,4 +849,102 @@ describe('', () => { ]); }); }); + + describe('MultiEntityPicker description', () => { + const description = { + fromSchema: 'MultiEntityPicker description from schema', + fromUiSchema: 'MultiEntityPicker description from uiSchema', + } as { fromSchema: string; fromUiSchema: string; default?: string }; + + beforeEach(() => { + const RealWrapper = Wrapper; + Wrapper = ({ children }: { children?: ReactNode }) => { + const { t } = useTranslationRef(scaffolderTranslationRef); + description.default = t('fields.multiEntityPicker.description'); + return {children}; + }; + uiSchema = { + 'ui:options': { + catalogFilter: [ + { + kind: ['Group'], + 'metadata.name': 'test-entity', + }, + { + kind: ['User'], + 'metadata.name': 'test-entity', + }, + ], + }, + }; + }); + it('presents default description', async () => { + props = { + onChange, + schema, + required: true, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(getByText(description.default!)).toBeInTheDocument(); + expect(queryByText(description.fromSchema)).toBe(null); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents schema description', async () => { + props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(getByText(description.fromSchema)).toBeInTheDocument(); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents uiSchema description', async () => { + props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema: { + ...uiSchema, + 'ui:description': description.fromUiSchema, + }, + rawErrors, + formData, + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(queryByText(description.fromSchema)).toBe(null); + expect(getByText(description.fromUiSchema)).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx index e6a535af41..e7dcf0f996 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx @@ -30,7 +30,6 @@ import { EntityRefPresentationSnapshot, } from '@backstage/plugin-catalog-react'; import TextField from '@material-ui/core/TextField'; -import FormControl from '@material-ui/core/FormControl'; import Autocomplete, { AutocompleteChangeReason, } from '@material-ui/lab/Autocomplete'; @@ -44,6 +43,9 @@ import { MultiEntityPickerFilterQuery, } from './schema'; import { VirtualizedListbox } from '../VirtualizedListbox'; +import { ScaffolderField } from '@backstage/plugin-scaffolder-react/alpha'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { scaffolderTranslationRef } from '../../../translation'; export { MultiEntityPickerSchema } from './schema'; @@ -52,14 +54,19 @@ export { MultiEntityPickerSchema } from './schema'; * field extension. */ export const MultiEntityPicker = (props: MultiEntityPickerProps) => { + const { t } = useTranslationRef(scaffolderTranslationRef); const { onChange, - schema: { title = 'Entity', description = 'An entity from the catalog' }, + schema: { + title = t('fields.multiEntityPicker.title'), + description = t('fields.multiEntityPicker.description'), + }, required, uiSchema, rawErrors, formData, idSchema, + errors, } = props; const catalogFilter = buildCatalogFilter(uiSchema); @@ -144,10 +151,12 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => { }, [entities, onChange, required, allowArbitraryValues]); return ( - 0 && !formData} + disabled={isDisabled} + errors={errors} > { label={title} disabled={isDisabled} margin="dense" - helperText={description} FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 }, @@ -197,7 +205,7 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => { )} ListboxComponent={VirtualizedListbox} /> - + ); }; diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts index 2621157d02..667b4638b8 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/schema.ts @@ -13,55 +13,57 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { z } from 'zod'; -import { makeFieldSchemaFromZod } from '../utils'; +import { z as zod } from 'zod'; +import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; -export const entityQueryFilterExpressionSchema = z.record( - z +export const entityQueryFilterExpressionSchema = zod.record( + zod .string() - .or(z.object({ exists: z.boolean().optional() })) - .or(z.array(z.string())), + .or(zod.object({ exists: zod.boolean().optional() })) + .or(zod.array(zod.string())), ); -export const MultiEntityPickerFieldSchema = makeFieldSchemaFromZod( - z.array(z.string()), - z.object({ - defaultKind: z - .string() - .optional() - .describe( - 'The default entity kind. Options of this kind will not be prefixed.', - ), - allowArbitraryValues: z - .boolean() - .optional() - .describe('Whether to allow arbitrary user input. Defaults to true'), - defaultNamespace: z - .union([z.string(), z.literal(false)]) - .optional() - .describe( - 'The default namespace. Options with this namespace will not be prefixed.', - ), - catalogFilter: z - .array(entityQueryFilterExpressionSchema) - .or(entityQueryFilterExpressionSchema) - .optional() - .describe('List of key-value filter expression for entities'), - }), -); +export const MultiEntityPickerFieldSchema = makeFieldSchema({ + output: z => z.array(z.string()), + uiOptions: z => + z.object({ + defaultKind: z + .string() + .optional() + .describe( + 'The default entity kind. Options of this kind will not be prefixed.', + ), + allowArbitraryValues: z + .boolean() + .optional() + .describe('Whether to allow arbitrary user input. Defaults to true'), + defaultNamespace: z + .union([z.string(), z.literal(false)]) + .optional() + .describe( + 'The default namespace. Options with this namespace will not be prefixed.', + ), + catalogFilter: z + .array(entityQueryFilterExpressionSchema) + .or(entityQueryFilterExpressionSchema) + .optional() + .describe('List of key-value filter expression for entities'), + }), +}); /** * The input props that can be specified under `ui:options` for the * `EntityPicker` field extension. */ -export type MultiEntityPickerUiOptions = - typeof MultiEntityPickerFieldSchema.uiOptionsType; +export type MultiEntityPickerUiOptions = NonNullable< + (typeof MultiEntityPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; -export type MultiEntityPickerProps = typeof MultiEntityPickerFieldSchema.type; +export type MultiEntityPickerProps = typeof MultiEntityPickerFieldSchema.TProps; export const MultiEntityPickerSchema = MultiEntityPickerFieldSchema.schema; -export type MultiEntityPickerFilterQuery = z.TypeOf< +export type MultiEntityPickerFilterQuery = zod.TypeOf< typeof entityQueryFilterExpressionSchema >; diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx index 6e0a3b81bc..dcba013093 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx @@ -35,6 +35,9 @@ import { import userEvent from '@testing-library/user-event'; import { ScaffolderRJSFFieldProps as FieldProps } from '@backstage/plugin-scaffolder-react'; import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog'; +import { ComponentType, PropsWithChildren, ReactNode } from 'react'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { scaffolderTranslationRef } from '../../../translation'; const mockIdentityApi = mockApis.identity({ userEntityRef: 'user:default/bob', @@ -99,6 +102,7 @@ describe('', () => { onChange, schema, required, + uiSchema: {}, } as unknown as FieldProps; await renderInTestApp( @@ -175,6 +179,7 @@ describe('', () => { onChange, schema, required, + uiSchema: {}, } as unknown as FieldProps; const { queryByText, getByRole } = await renderInTestApp( @@ -236,6 +241,7 @@ describe('', () => { onChange, schema, required, + uiSchema: {}, } as unknown as FieldProps; const { getByRole } = await renderInTestApp( @@ -299,6 +305,7 @@ describe('', () => { onChange, schema, required, + uiSchema: {}, formData: 'group:default/group1', } as unknown as FieldProps; @@ -327,4 +334,99 @@ describe('', () => { expect(inputFieldValue).toEqual(userGroups[0].metadata.title); }); + + describe('MyGroupsPicker description', () => { + const description = { + fromSchema: 'MyGroupsPicker description from schema', + fromUiSchema: 'MyGroupsPicker description from uiSchema', + } as { fromSchema: string; fromUiSchema: string; default?: string }; + + let Wrapper: ComponentType>; + + beforeEach(() => { + Wrapper = ({ children }: { children?: ReactNode }) => { + const { t } = useTranslationRef(scaffolderTranslationRef); + description.default = t('fields.myGroupsPicker.description'); + return ( + + {children} + + ); + }; + }); + it('presents default description', async () => { + const props = { + onChange, + schema, + required: true, + uiSchema: {}, + formData: 'group:default/group1', + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(getByText(description.default!)).toBeInTheDocument(); + expect(queryByText(description.fromSchema)).toBe(null); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents schema description', async () => { + const props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema: {}, + formData: 'group:default/group1', + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(getByText(description.fromSchema)).toBeInTheDocument(); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents uiSchema description', async () => { + const props = { + onChange, + schema: { + ...schema, + description: description.fromSchema, + }, + required: true, + uiSchema: { + 'ui:description': description.fromUiSchema, + }, + formData: 'group:default/group1', + } as unknown as FieldProps; + + const { getByText, queryByText } = await renderInTestApp( + + + , + ); + expect(queryByText(description.default!)).toBe(null); + expect(queryByText(description.fromSchema)).toBe(null); + expect(getByText(description.fromUiSchema)).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx index b49a1a1531..e92057c713 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.tsx @@ -21,7 +21,6 @@ import { useApi, } from '@backstage/core-plugin-api'; import TextField from '@material-ui/core/TextField'; -import FormControl from '@material-ui/core/FormControl'; import { MyGroupsPickerProps, MyGroupsPickerSchema } from './schema'; import Autocomplete, { createFilterOptions, @@ -38,6 +37,7 @@ import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { VirtualizedListbox } from '../VirtualizedListbox'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { scaffolderTranslationRef } from '../../../translation'; +import { ScaffolderField } from '@backstage/plugin-scaffolder-react/alpha'; export { MyGroupsPickerSchema }; @@ -52,12 +52,15 @@ export const MyGroupsPicker = (props: MyGroupsPickerProps) => { rawErrors, onChange, formData, + uiSchema, + errors, } = props; const identityApi = useApi(identityApiRef); const catalogApi = useApi(catalogApiRef); const errorApi = useApi(errorApiRef); const entityPresentationApi = useApi(entityPresentationApiRef); + const isDisabled = uiSchema?.['ui:disabled'] ?? false; const { value: groups, loading } = useAsync(async () => { const { userEntityRef } = await identityApi.getBackstageIdentity(); @@ -108,10 +111,12 @@ export const MyGroupsPicker = (props: MyGroupsPickerProps) => { }, [groups, onChange, selectedEntity, required]); return ( - 0} + disabled={isDisabled} + errors={errors} > { {...params} label={title} margin="dense" - helperText={description} FormHelperTextProps={{ margin: 'dense', style: { marginLeft: 0 } }} variant="outlined" required={required} @@ -145,6 +149,6 @@ export const MyGroupsPicker = (props: MyGroupsPickerProps) => { })} ListboxComponent={VirtualizedListbox} /> - + ); }; diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts b/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts index d5f8af7c56..6ccda91afb 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/schema.ts @@ -14,42 +14,34 @@ * limitations under the License. */ -import { z } from 'zod'; -import { makeFieldSchemaFromZod } from '../utils'; +import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; /** * Field schema for the MyGroupsPicker. * @public */ -export const MyGroupsPickerFieldSchema = makeFieldSchemaFromZod( - z.string(), - z.object({ - title: z.string().default('Group').describe('Group'), - description: z - .string() - .default('A group you are part of') - .describe('The group to which the entity belongs'), - }), -); +export const MyGroupsPickerFieldSchema = makeFieldSchema({ + output: z => z.string(), +}); /** * UI options for the MyGroupsPicker. * @public */ -export type MyGroupsPickerUiOptions = - typeof MyGroupsPickerFieldSchema.uiOptionsType; +export type MyGroupsPickerUiOptions = NonNullable< + (typeof MyGroupsPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; + /** * Props for the MyGroupsPicker. * @public */ - -export type MyGroupsPickerProps = typeof MyGroupsPickerFieldSchema.type; +export type MyGroupsPickerProps = typeof MyGroupsPickerFieldSchema.TProps; /** * Schema for the MyGroupsPicker. * @public */ - export const MyGroupsPickerSchema = MyGroupsPickerFieldSchema.schema; diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx index a7c29c081e..0651066cfb 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/OwnedEntityPicker.tsx @@ -24,6 +24,7 @@ import { OwnedEntityPickerProps } from './schema'; import { EntityPickerProps } from '../EntityPicker/schema'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { scaffolderTranslationRef } from '../../../translation'; +import { ScaffolderField } from '@backstage/plugin-scaffolder-react/alpha'; export { OwnedEntityPickerSchema } from './schema'; @@ -52,22 +53,30 @@ export const OwnedEntityPicker = (props: OwnedEntityPickerProps) => { if (loading) return ( - ( - - )} - options={[]} - /> + + ( + + )} + options={[]} + /> + ); const entityPickerUISchema = buildEntityPickerUISchema( diff --git a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts index 2bb75db263..6dd00df49d 100644 --- a/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/OwnedEntityPicker/schema.ts @@ -13,45 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { z } from 'zod'; -import { makeFieldSchemaFromZod } from '../utils'; -import { entityQueryFilterExpressionSchema } from '../EntityPicker/schema'; +import { EntityPickerFieldSchema } from '../EntityPicker/schema'; /** * @public */ -export const OwnedEntityPickerFieldSchema = makeFieldSchemaFromZod( - z.string(), - z.object({ - allowedKinds: z - .array(z.string()) - .optional() - .describe( - 'DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from', - ), - defaultKind: z - .string() - .optional() - .describe( - 'The default entity kind. Options of this kind will not be prefixed.', - ), - allowArbitraryValues: z - .boolean() - .optional() - .describe('Whether to allow arbitrary user input. Defaults to true'), - defaultNamespace: z - .union([z.string(), z.literal(false)]) - .optional() - .describe( - 'The default namespace. Options with this namespace will not be prefixed.', - ), - catalogFilter: z - .array(entityQueryFilterExpressionSchema) - .or(entityQueryFilterExpressionSchema) - .optional() - .describe('List of key-value filter expression for entities'), - }), -); +export const OwnedEntityPickerFieldSchema = EntityPickerFieldSchema; /** * The input props that can be specified under `ui:options` for the @@ -59,9 +26,10 @@ export const OwnedEntityPickerFieldSchema = makeFieldSchemaFromZod( * * @public */ -export type OwnedEntityPickerUiOptions = - typeof OwnedEntityPickerFieldSchema.uiOptionsType; +export type OwnedEntityPickerUiOptions = NonNullable< + (typeof OwnedEntityPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; -export type OwnedEntityPickerProps = typeof OwnedEntityPickerFieldSchema.type; +export type OwnedEntityPickerProps = typeof OwnedEntityPickerFieldSchema.TProps; export const OwnedEntityPickerSchema = OwnedEntityPickerFieldSchema.schema; diff --git a/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts b/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts index 0dbe2fa74f..76389216ba 100644 --- a/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/OwnerPicker/schema.ts @@ -13,43 +13,43 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { z } from 'zod'; -import { makeFieldSchemaFromZod } from '../utils'; -import { entityQueryFilterExpressionSchema } from '../EntityPicker/schema'; +import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; +import { createEntityQueryFilterExpressionSchema } from '../EntityPicker/schema'; /** * @public */ -export const OwnerPickerFieldSchema = makeFieldSchemaFromZod( - z.string(), - z.object({ - /** - * @deprecated Use `catalogFilter` instead. - */ - allowedKinds: z - .array(z.string()) - .default(['Group', 'User']) - .optional() - .describe( - 'DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from. Defaults to Group and User', - ), - allowArbitraryValues: z - .boolean() - .optional() - .describe('Whether to allow arbitrary user input. Defaults to true'), - defaultNamespace: z - .union([z.string(), z.literal(false)]) - .optional() - .describe( - 'The default namespace. Options with this namespace will not be prefixed.', - ), - catalogFilter: z - .array(entityQueryFilterExpressionSchema) - .or(entityQueryFilterExpressionSchema) - .optional() - .describe('List of key-value filter expression for entities'), - }), -); +export const OwnerPickerFieldSchema = makeFieldSchema({ + output: z => z.string(), + uiOptions: z => + z.object({ + /** + * @deprecated Use `catalogFilter` instead. + */ + allowedKinds: z + .array(z.string()) + .default(['Group', 'User']) + .optional() + .describe( + 'DEPRECATED: Use `catalogFilter` instead. List of kinds of entities to derive options from. Defaults to Group and User', + ), + allowArbitraryValues: z + .boolean() + .optional() + .describe('Whether to allow arbitrary user input. Defaults to true'), + defaultNamespace: z + .union([z.string(), z.literal(false)]) + .optional() + .describe( + 'The default namespace. Options with this namespace will not be prefixed.', + ), + catalogFilter: (t => t.or(t.array()))( + createEntityQueryFilterExpressionSchema(z), + ) + .optional() + .describe('List of key-value filter expression for entities'), + }), +}); /** * The input props that can be specified under `ui:options` for the @@ -57,8 +57,10 @@ export const OwnerPickerFieldSchema = makeFieldSchemaFromZod( * * @public */ -export type OwnerPickerUiOptions = typeof OwnerPickerFieldSchema.uiOptionsType; +export type OwnerPickerUiOptions = NonNullable< + (typeof OwnerPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; -export type OwnerPickerProps = typeof OwnerPickerFieldSchema.type; +export type OwnerPickerProps = typeof OwnerPickerFieldSchema.TProps; export const OwnerPickerSchema = OwnerPickerFieldSchema.schema; diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx index ba1b9642fd..2d479d346a 100644 --- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.test.tsx @@ -29,9 +29,11 @@ import { scaffolderApiRef, useTemplateSecrets, ScaffolderRJSFField, + ScaffolderRJSFFormProps as FormProps, } from '@backstage/plugin-scaffolder-react'; import { act, fireEvent, screen } from '@testing-library/react'; import { RepoBranchPicker } from './RepoBranchPicker'; +import { ComponentType, PropsWithChildren, ReactNode } from 'react'; describe('RepoBranchPicker', () => { const mockIntegrationsApi: Partial = { @@ -333,4 +335,104 @@ describe('RepoBranchPicker', () => { expect(getByText('abc123')).toBeInTheDocument(); }); }); + + describe('RepoBranchPicker description', () => { + const description = { + fromSchema: 'RepoBranchPicker description from schema', + fromUiSchema: 'RepoBranchPicker description from uiSchema', + } as { fromSchema: string; fromUiSchema: string }; + + let Wrapper: ComponentType>; + + beforeEach(() => { + Wrapper = ({ children }: { children?: ReactNode }) => { + return ( + + {children} + + ); + }; + }); + it('omits description', async () => { + const props = { + validator, + schema: { type: 'string' }, + uiSchema: { 'ui:field': 'RepoBranchPicker' }, + fields: { + RepoBranchPicker: RepoBranchPicker as ScaffolderRJSFField, + }, + formContext: { + formData: {}, + }, + } as unknown as FormProps; + + const { container } = await renderInTestApp( + +
+ , + ); + expect( + container.getElementsByClassName('MuiTypography-body1'), + ).toHaveLength(0); + }); + + it('presents schema description', async () => { + const props = { + validator, + schema: { type: 'string', description: description.fromSchema }, + uiSchema: { 'ui:field': 'RepoBranchPicker' }, + fields: { + RepoBranchPicker: RepoBranchPicker as ScaffolderRJSFField, + }, + formContext: { + formData: {}, + }, + } as unknown as FormProps; + + const { container, getByText, queryByText } = await renderInTestApp( + + + , + ); + expect( + container.getElementsByClassName('MuiTypography-body1'), + ).toHaveLength(1); + expect(getByText(description.fromSchema)).toBeInTheDocument(); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents uiSchema description', async () => { + const props = { + validator, + schema: { type: 'string', description: description.fromSchema }, + uiSchema: { + 'ui:field': 'RepoBranchPicker', + 'ui:description': description.fromUiSchema, + }, + fields: { + RepoBranchPicker: RepoBranchPicker as ScaffolderRJSFField, + }, + formContext: { + formData: {}, + }, + } as unknown as FormProps; + + const { container, getByText, queryByText } = await renderInTestApp( + + + , + ); + expect( + container.getElementsByClassName('MuiTypography-body1'), + ).toHaveLength(1); + expect(queryByText(description.fromSchema)).toBe(null); + expect(getByText(description.fromUiSchema)).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.tsx b/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.tsx index f81091f115..34f968d0de 100644 --- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/RepoBranchPicker.tsx @@ -31,6 +31,7 @@ import { RepoBranchPickerState } from './types'; import { BitbucketRepoBranchPicker } from './BitbucketRepoBranchPicker'; import { DefaultRepoBranchPicker } from './DefaultRepoBranchPicker'; import { GitHubRepoBranchPicker } from './GitHubRepoBranchPicker'; +import { MarkdownContent } from '@backstage/core-components'; /** * The underlying component that is rendered in the form for the `RepoBranchPicker` @@ -164,6 +165,8 @@ export const RepoBranchPicker = (props: RepoBranchPickerProps) => { } }; + const description = uiSchema['ui:description'] ?? schema.description; + return ( <> {schema.title && ( @@ -172,8 +175,10 @@ export const RepoBranchPicker = (props: RepoBranchPickerProps) => { )} - {schema.description && ( - {schema.description} + {description && ( + + + )} {renderRepoBranchPicker()} diff --git a/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts b/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts index fae5e6435e..62a3b66e14 100644 --- a/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts +++ b/plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts @@ -13,58 +13,58 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { z } from 'zod'; -import { makeFieldSchemaFromZod } from '../utils'; +import { makeFieldSchema } from '@backstage/plugin-scaffolder-react'; /** * @public */ -export const RepoBranchPickerFieldSchema = makeFieldSchemaFromZod( - z.string(), - z.object({ - requestUserCredentials: z - .object({ - secretsKey: z - .string() - .describe( - 'Key used within the template secrets context to store the credential', - ), - additionalScopes: z - .object({ - gitea: z - .array(z.string()) - .optional() - .describe('Additional Gitea scopes to request'), - gerrit: z - .array(z.string()) - .optional() - .describe('Additional Gerrit scopes to request'), - github: z - .array(z.string()) - .optional() - .describe('Additional GitHub scopes to request'), - gitlab: z - .array(z.string()) - .optional() - .describe('Additional GitLab scopes to request'), - bitbucket: z - .array(z.string()) - .optional() - .describe('Additional BitBucket scopes to request'), - azure: z - .array(z.string()) - .optional() - .describe('Additional Azure scopes to request'), - }) - .optional() - .describe('Additional permission scopes to request'), - }) - .optional() - .describe( - 'If defined will request user credentials to auth against the given SCM platform', - ), - }), -); +export const RepoBranchPickerFieldSchema = makeFieldSchema({ + output: z => z.string(), + uiOptions: z => + z.object({ + requestUserCredentials: z + .object({ + secretsKey: z + .string() + .describe( + 'Key used within the template secrets context to store the credential', + ), + additionalScopes: z + .object({ + gitea: z + .array(z.string()) + .optional() + .describe('Additional Gitea scopes to request'), + gerrit: z + .array(z.string()) + .optional() + .describe('Additional Gerrit scopes to request'), + github: z + .array(z.string()) + .optional() + .describe('Additional GitHub scopes to request'), + gitlab: z + .array(z.string()) + .optional() + .describe('Additional GitLab scopes to request'), + bitbucket: z + .array(z.string()) + .optional() + .describe('Additional BitBucket scopes to request'), + azure: z + .array(z.string()) + .optional() + .describe('Additional Azure scopes to request'), + }) + .optional() + .describe('Additional permission scopes to request'), + }) + .optional() + .describe( + 'If defined will request user credentials to auth against the given SCM platform', + ), + }), +}); /** * The input props that can be specified under `ui:options` for the @@ -72,10 +72,11 @@ export const RepoBranchPickerFieldSchema = makeFieldSchemaFromZod( * * @public */ -export type RepoBranchPickerUiOptions = - typeof RepoBranchPickerFieldSchema.uiOptionsType; +export type RepoBranchPickerUiOptions = NonNullable< + (typeof RepoBranchPickerFieldSchema.TProps.uiSchema)['ui:options'] +>; -export type RepoBranchPickerProps = typeof RepoBranchPickerFieldSchema.type; +export type RepoBranchPickerProps = typeof RepoBranchPickerFieldSchema.TProps; // This has been duplicated from /plugins/scaffolder/src/components/fields/RepoUrlPicker/schema.ts // NOTE: There is a bug with this failing validation in the custom field explorer due diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx index c5d88275f6..f23d8356ac 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx @@ -30,9 +30,11 @@ import { ScaffolderApi, useTemplateSecrets, ScaffolderRJSFField, + ScaffolderRJSFFormProps as FormProps, } from '@backstage/plugin-scaffolder-react'; import { act, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { ComponentType, PropsWithChildren, ReactNode } from 'react'; describe('RepoUrlPicker', () => { const mockScaffolderApi: Partial = { @@ -417,4 +419,106 @@ describe('RepoUrlPicker', () => { }); }); }); + + describe('RepoUrlPicker description', () => { + const description = { + fromSchema: 'RepoUrlPicker description from schema', + fromUiSchema: 'RepoUrlPicker description from uiSchema', + } as { fromSchema: string; fromUiSchema: string }; + + let Wrapper: ComponentType>; + + beforeEach(() => { + Wrapper = ({ children }: { children?: ReactNode }) => { + return ( + + {children} + + ); + }; + }); + it('omits description', async () => { + const props = { + validator, + schema: { type: 'string' }, + uiSchema: { 'ui:field': 'RepoUrlPicker' }, + fields: { + RepoUrlPicker: RepoUrlPicker as ScaffolderRJSFField, + }, + formContext: { + formData: {}, + }, + } as unknown as FormProps; + + const { container } = await renderInTestApp( + + + , + ); + expect( + container.getElementsByClassName('MuiTypography-body1'), + ).toHaveLength(0); + }); + + it('presents schema description', async () => { + const props = { + validator, + schema: { type: 'string', description: description.fromSchema }, + uiSchema: { + 'ui:field': 'RepoUrlPicker', + }, + fields: { + RepoUrlPicker: RepoUrlPicker as ScaffolderRJSFField, + }, + formContext: { + formData: {}, + }, + } as unknown as FormProps; + + const { container, getByText, queryByText } = await renderInTestApp( + + + , + ); + expect( + container.getElementsByClassName('MuiTypography-body1'), + ).toHaveLength(1); + expect(getByText(description.fromSchema)).toBeInTheDocument(); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents uiSchema description', async () => { + const props = { + validator, + schema: { type: 'string', description: description.fromSchema }, + uiSchema: { + 'ui:field': 'RepoUrlPicker', + 'ui:description': description.fromUiSchema, + }, + fields: { + RepoUrlPicker: RepoUrlPicker as ScaffolderRJSFField, + }, + formContext: { + formData: {}, + }, + } as unknown as FormProps; + + const { container, getByText, queryByText } = await renderInTestApp( + + + , + ); + expect( + container.getElementsByClassName('MuiTypography-body1'), + ).toHaveLength(1); + expect(queryByText(description.fromSchema)).toBe(null); + expect(getByText(description.fromUiSchema)).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx index 953b607b07..8f1e3a67b7 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx @@ -35,6 +35,7 @@ import { RepoUrlPickerRepoName } from './RepoUrlPickerRepoName'; import { RepoUrlPickerFieldSchema } from './schema'; import { RepoUrlPickerState } from './types'; import { parseRepoPickerUrl, serializeRepoPickerUrl } from './utils'; +import { MarkdownContent } from '@backstage/core-components'; export { RepoUrlPickerSchema } from './schema'; @@ -166,6 +167,9 @@ export const RepoUrlPicker = ( const hostType = (state.host && integrationApi.byHost(state.host)?.type) ?? null; + + const description = uiSchema['ui:description'] ?? schema.description; + return ( <> {schema.title && ( @@ -174,8 +178,10 @@ export const RepoUrlPicker = ( )} - {schema.description && ( - {schema.description} + {description && ( + + + )} ; -export type RepoUrlPickerProps = typeof RepoUrlPickerFieldSchema.type; +export type RepoUrlPickerProps = typeof RepoUrlPickerFieldSchema.TProps; // This has been duplicated to /plugins/scaffolder/src/components/fields/RepoBranchPicker/schema.ts // NOTE: There is a bug with this failing validation in the custom field explorer due diff --git a/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.test.tsx b/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.test.tsx index f1619aacba..d4ddaea537 100644 --- a/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.test.tsx +++ b/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.test.tsx @@ -19,6 +19,7 @@ import { } from '@backstage/plugin-scaffolder-react'; import { SecretInput } from './SecretInput'; import { renderInTestApp } from '@backstage/test-utils'; +import { ScaffolderRJSFFormProps as FormProps } from '@backstage/plugin-scaffolder-react'; import { Form } from '@backstage/plugin-scaffolder-react/alpha'; import validator from '@rjsf/validator-ajv8'; import { fireEvent, act, waitFor } from '@testing-library/react'; @@ -73,4 +74,92 @@ describe('', () => { { timeout: 500 }, ); }); + + describe('SecretInput description', () => { + const description = { + fromSchema: 'MyGroupsPicker description from schema', + fromUiSchema: 'MyGroupsPicker description from uiSchema', + } as { fromSchema: string; fromUiSchema: string }; + + it('omits description', async () => { + const props = { + validator, + schema: { + properties: { myKey: { type: 'string', title: 'secret' } }, + }, + uiSchema: { + myKey: { + 'ui:field': 'Secret', + }, + }, + fields: { + Secret: SecretInput, + }, + } as unknown as FormProps; + + const { queryByText } = await renderInTestApp( + + + + , + ); + expect(queryByText(description.fromSchema)).toBe(null); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents schema description', async () => { + const props = { + validator, + schema: { + properties: { myKey: { type: 'string', title: 'secret' } }, + description: description.fromSchema, + }, + uiSchema: { + myKey: { + 'ui:field': 'Secret', + }, + }, + fields: { + Secret: SecretInput, + }, + } as unknown as FormProps; + + const { getByText, queryByText } = await renderInTestApp( + + + + , + ); + expect(getByText(description.fromSchema)).toBeInTheDocument(); + expect(queryByText(description.fromUiSchema)).toBe(null); + }); + + it('presents uiSchema description', async () => { + const props = { + validator, + schema: { + properties: { myKey: { type: 'string', title: 'secret' } }, + description: description.fromSchema, + }, + uiSchema: { + myKey: { + 'ui:field': 'Secret', + }, + 'ui:description': description.fromUiSchema, + }, + fields: { + Secret: SecretInput, + }, + } as unknown as FormProps; + + const { getByText, queryByText } = await renderInTestApp( + + + + , + ); + expect(queryByText(description.fromSchema)).toBe(null); + expect(getByText(description.fromUiSchema)).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.tsx b/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.tsx index d3bfd09502..b21356d6a8 100644 --- a/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.tsx +++ b/plugins/scaffolder/src/components/fields/SecretInput/SecretInput.tsx @@ -26,12 +26,13 @@ export const SecretInput = (props: ScaffolderRJSFFieldProps) => { disabled, errors, required, + uiSchema, } = props; return ( ( 'search.elasticsearch.highlightOptions', ), + config.getOptional( + 'search.elasticsearch.queryOptions', + ), ); for (const indexTemplate of this.readIndexTemplateConfig( diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index b2fa21aa3b..fc0f405a24 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-explore +## 0.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-backend-node@1.3.11-next.2 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 0.3.2-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index e57ea07e00..92ef1a18e7 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-explore", - "version": "0.3.2-next.1", + "version": "0.3.2-next.2", "description": "A module for the search backend that exports explore modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index def19bf30b..47c2a65de4 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.44-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-backend-node@1.3.11-next.2 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 0.5.44-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 0eb67810d4..0686bac80d 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-pg", - "version": "0.5.44-next.1", + "version": "0.5.44-next.2", "description": "A module for the search backend that implements search using PostgreSQL", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md index 3aa92b12ec..99ee569733 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.3.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-search-backend-node@1.3.11-next.2 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 0.3.9-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index 465627c118..b5d0258dcf 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", - "version": "0.3.9-next.1", + "version": "0.3.9-next.2", "description": "A module for the search backend that exports stack overflow modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index 4af1906d57..62d6f6a1d8 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.4.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-search-backend-node@1.3.11-next.2 + - @backstage/plugin-search-common@1.2.18-next.0 + - @backstage/plugin-techdocs-node@1.13.3-next.3 + ## 0.4.2-next.2 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 8d088fe1b7..22683c1179 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", - "version": "0.4.2-next.2", + "version": "0.4.2-next.3", "description": "A module for the search backend that exports techdocs modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index 28e245a5ef..7cd55d993c 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-node +## 1.3.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 1.3.11-next.1 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index ab6f99462c..cd16c32b8b 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "1.3.11-next.1", + "version": "1.3.11-next.2", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", "backstage": { "role": "node-library", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 9f51eb47ea..1a9875b526 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-search-backend +## 2.0.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/backend-openapi-utils@0.5.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-permission-node@0.10.0-next.2 + - @backstage/plugin-search-backend-node@1.3.11-next.2 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 2.0.2-next.2 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 57b212d906..a45345bbe5 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "2.0.2-next.2", + "version": "2.0.2-next.3", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index a6c9421949..1551669adc 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-search-react +## 1.9.0-next.2 + +### Patch Changes + +- 2c76614: Fix memoization of `filterValue` in `SearchFilter.Autocomplete` to prevent unintended resets +- fa48594: search plugin support i18n +- Updated dependencies + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/theme@0.6.6-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 1.9.0-next.1 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index 59bc492e10..3e4e7ba821 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.9.0-next.1", + "version": "1.9.0-next.2", "backstage": { "role": "web-library", "pluginId": "search", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 8a4d42569e..66b7f98985 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-search +## 1.4.26-next.3 + +### Patch Changes + +- fa48594: search plugin support i18n +- Updated dependencies + - @backstage/plugin-search-react@1.9.0-next.2 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18-next.0 + ## 1.4.26-next.2 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 8a872978e2..c9bff12da7 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.26-next.2", + "version": "1.4.26-next.3", "description": "The Backstage plugin that provides your backstage app with search", "backstage": { "role": "frontend-plugin", diff --git a/plugins/signals-backend/CHANGELOG.md b/plugins/signals-backend/CHANGELOG.md index 93c0bfc099..d53dc508e8 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-signals-backend +## 0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.11-next.2 + - @backstage/plugin-signals-node@0.1.20-next.2 + ## 0.3.4-next.1 ### Patch Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index 39ca69672f..65d7e3ab4a 100644 --- a/plugins/signals-backend/package.json +++ b/plugins/signals-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-backend", - "version": "0.3.4-next.1", + "version": "0.3.4-next.2", "backstage": { "role": "backend-plugin", "pluginId": "signals", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index 23fa9ade45..bebb00934c 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-signals-node +## 0.1.20-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.11-next.2 + ## 0.1.20-next.1 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index b3510d3d5a..1b1f235cf7 100644 --- a/plugins/signals-node/package.json +++ b/plugins/signals-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-node", - "version": "0.1.20-next.1", + "version": "0.1.20-next.2", "description": "Node.js library for the signals plugin", "backstage": { "role": "node-library", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index b59f735816..2dc7e1ba49 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.48-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.17.0-next.1 + - @backstage/plugin-catalog@1.30.0-next.3 + - @backstage/plugin-search-react@1.9.0-next.2 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/test-utils@1.7.8-next.2 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/plugin-techdocs@1.12.6-next.3 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/plugin-techdocs-react@1.2.17-next.1 + ## 1.0.48-next.2 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 0b6ee4f297..aaa9864a08 100644 --- a/plugins/techdocs-addons-test-utils/package.json +++ b/plugins/techdocs-addons-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-addons-test-utils", - "version": "1.0.48-next.2", + "version": "1.0.48-next.3", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 8ba2abcdb2..8d1dd79d28 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-techdocs-backend +## 2.0.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-permission-common@0.9.0-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.2-next.3 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-node@1.13.3-next.3 + ## 2.0.2-next.2 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 380d37f79a..ab2f3dd17f 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "2.0.2-next.2", + "version": "2.0.2-next.3", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index df7bb86451..cc6d00815d 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.24-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/plugin-techdocs-react@1.2.17-next.1 + ## 1.1.24-next.2 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index 8896409a09..14d5280b55 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", - "version": "1.1.24-next.2", + "version": "1.1.24-next.3", "description": "Plugin module for contributed TechDocs Addons", "backstage": { "role": "frontend-plugin-module", diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index b7c474a1e9..76b9836cd3 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-techdocs-node +## 1.13.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.16-next.0 + - @backstage/plugin-search-common@1.2.18-next.0 + - @backstage/plugin-techdocs-common@0.1.0 + ## 1.13.3-next.2 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 2cabc2b3c2..ff5986a5d4 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-node", - "version": "1.13.3-next.2", + "version": "1.13.3-next.3", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", "backstage": { "role": "node-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index bcc2f88efa..747eccd17b 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-techdocs +## 1.12.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-search-react@1.9.0-next.2 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/theme@0.6.6-next.0 + - @backstage/plugin-auth-react@0.1.15-next.1 + - @backstage/plugin-search-common@1.2.18-next.0 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.17-next.1 + ## 1.12.6-next.2 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 8ddeaa597f..a488863a25 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.12.6-next.2", + "version": "1.12.6-next.3", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 5c3a22f044..7920484835 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-user-settings-backend +## 0.3.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/plugin-auth-node@0.6.3-next.2 + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.20-next.2 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.3.2-next.2 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index b216e566a5..70c77f740b 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings-backend", - "version": "0.3.2-next.2", + "version": "0.3.2-next.3", "description": "The Backstage backend plugin to manage user settings", "backstage": { "role": "backend-plugin", diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index f5170c340f..1d2d5aa0dc 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-user-settings +## 0.8.22-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.17.0-next.1 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/theme@0.6.6-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.13-next.0 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.8.22-next.2 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 54dfcc0d26..66d768d102 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-user-settings", - "version": "0.8.22-next.2", + "version": "0.8.22-next.3", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", diff --git a/storybook/package.json b/storybook/package.json index 58cce5e57e..c122639045 100644 --- a/storybook/package.json +++ b/storybook/package.json @@ -34,7 +34,7 @@ "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "eslint": "^9.11.1", - "eslint-plugin-storybook": "^0.11.0", + "eslint-plugin-storybook": "^0.12.0", "globals": "^15.9.0", "react-router-dom": "^6.3.0", "storybook": "^8.3.5", diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 09cd7357fa..96cf1e99bb 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -632,10 +632,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.25.1": - version: 9.25.1 - resolution: "@eslint/js@npm:9.25.1" - checksum: 10/ad5812889598de32d674ef60c0e61468ac5c7f3b6ecf98b0e29d1e88d7af8ba3aab255b8c0a46bbaf654047bbd2ee5aa033db9b53e330f97615093fcccde4cbb +"@eslint/js@npm:9.26.0": + version: 9.26.0 + resolution: "@eslint/js@npm:9.26.0" + checksum: 10/863d35df8f6675250bb5a917037e0f6833965437eba4c4649633fd0b55a93e8d727bcd36e9b5cc82047898ee9348cb40363e196f333914ae3a6bb36159495212 languageName: node linkType: hard @@ -779,6 +779,24 @@ __metadata: languageName: node linkType: hard +"@modelcontextprotocol/sdk@npm:^1.8.0": + version: 1.11.0 + resolution: "@modelcontextprotocol/sdk@npm:1.11.0" + dependencies: + content-type: "npm:^1.0.5" + cors: "npm:^2.8.5" + cross-spawn: "npm:^7.0.3" + eventsource: "npm:^3.0.2" + express: "npm:^5.0.1" + express-rate-limit: "npm:^7.5.0" + pkce-challenge: "npm:^5.0.0" + raw-body: "npm:^3.0.0" + zod: "npm:^3.23.8" + zod-to-json-schema: "npm:^3.24.1" + checksum: 10/527413fd2b18f75e031cda7f73a662098f3c5f1224b9c6b0b903d5a1f79e23e23a4f4b8e6971bac7eb46a74ed65ae05e8e548f7b7a3f7f6d179c3f6d10825fbc + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -1525,21 +1543,21 @@ __metadata: linkType: hard "@types/react-dom@npm:^18.0.0": - version: 18.3.6 - resolution: "@types/react-dom@npm:18.3.6" + version: 18.3.7 + resolution: "@types/react-dom@npm:18.3.7" peerDependencies: "@types/react": ^18.0.0 - checksum: 10/ae179355401c64423d39946eda22c5f7f74c94ce61c21505024d4d82c33853ec40bc9b370f75e4a7750b0524aba4d95a43fcc328d8d14684dc2abb41ba529de8 + checksum: 10/317569219366d487a3103ba1e5e47154e95a002915fdcf73a44162c48fe49c3a57fcf7f57fc6979e70d447112681e6b13c6c3c1df289db8b544df4aab2d318f3 languageName: node linkType: hard "@types/react@npm:^18.0.0": - version: 18.3.20 - resolution: "@types/react@npm:18.3.20" + version: 18.3.21 + resolution: "@types/react@npm:18.3.21" dependencies: "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10/020c51e63b60862e6d772f0cdea0b9441182eedab6289dabd8add0708ded62003834c4e7c6f23a1ccd3ca9486b46296057c3f881c34261a0483765351f8d0bc3 + checksum: 10/89adc2fe391fd63b620db0aaa10b2be89fad338039415fd748e60c962e47bf2b0f1897cb5226f181eda963f37a1c8ac1bf0b3b29a0e35313bacd0355e5d6c736 languageName: node linkType: hard @@ -1866,6 +1884,16 @@ __metadata: languageName: node linkType: hard +"accepts@npm:^2.0.0": + version: 2.0.0 + resolution: "accepts@npm:2.0.0" + dependencies: + mime-types: "npm:^3.0.0" + negotiator: "npm:^1.0.0" + checksum: 10/ea1343992b40b2bfb3a3113fa9c3c2f918ba0f9197ae565c48d3f84d44b174f6b1d5cd9989decd7655963eb03a272abc36968cc439c2907f999bd5ef8653d5a7 + languageName: node + linkType: hard + "acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -2036,7 +2064,7 @@ __metadata: "@typescript-eslint/eslint-plugin": "npm:^5.0.0" "@typescript-eslint/parser": "npm:^5.0.0" eslint: "npm:^9.11.1" - eslint-plugin-storybook: "npm:^0.11.0" + eslint-plugin-storybook: "npm:^0.12.0" globals: "npm:^15.9.0" react: "npm:^18.0.2" react-dom: "npm:^18.0.2" @@ -2064,6 +2092,23 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:^2.2.0": + version: 2.2.0 + resolution: "body-parser@npm:2.2.0" + dependencies: + bytes: "npm:^3.1.2" + content-type: "npm:^1.0.5" + debug: "npm:^4.4.0" + http-errors: "npm:^2.0.0" + iconv-lite: "npm:^0.6.3" + on-finished: "npm:^2.4.1" + qs: "npm:^6.14.0" + raw-body: "npm:^3.0.0" + type-is: "npm:^2.0.0" + checksum: 10/e9d844b036bd15970df00a16f373c7ed28e1ef870974a0a1d4d6ef60d70e01087cc20a0dbb2081c49a88e3c08ce1d87caf1e2898c615dffa193f63e8faa8a84e + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -2113,6 +2158,13 @@ __metadata: languageName: node linkType: hard +"bytes@npm:3.1.2, bytes@npm:^3.1.2": + version: 3.1.2 + resolution: "bytes@npm:3.1.2" + checksum: 10/a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388 + languageName: node + linkType: hard + "cacache@npm:^18.0.0": version: 18.0.4 resolution: "cacache@npm:18.0.4" @@ -2133,6 +2185,16 @@ __metadata: languageName: node linkType: hard +"call-bind-apply-helpers@npm:^1.0.1, call-bind-apply-helpers@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind-apply-helpers@npm:1.0.2" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + checksum: 10/00482c1f6aa7cfb30fb1dbeb13873edf81cfac7c29ed67a5957d60635a56b2a4a480f1016ddbdb3395cc37900d46037fb965043a51c5c789ffeab4fc535d18b5 + languageName: node + linkType: hard + "call-bind@npm:^1.0.2, call-bind@npm:^1.0.7": version: 1.0.7 resolution: "call-bind@npm:1.0.7" @@ -2146,6 +2208,16 @@ __metadata: languageName: node linkType: hard +"call-bound@npm:^1.0.2": + version: 1.0.4 + resolution: "call-bound@npm:1.0.4" + dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + get-intrinsic: "npm:^1.3.0" + checksum: 10/ef2b96e126ec0e58a7ff694db43f4d0d44f80e641370c21549ed911fecbdbc2df3ebc9bddad918d6bbdefeafb60bb3337902006d5176d72bcd2da74820991af7 + languageName: node + linkType: hard + "callsites@npm:^3.0.0": version: 3.1.0 resolution: "callsites@npm:3.1.0" @@ -2283,6 +2355,22 @@ __metadata: languageName: node linkType: hard +"content-disposition@npm:^1.0.0": + version: 1.0.0 + resolution: "content-disposition@npm:1.0.0" + dependencies: + safe-buffer: "npm:5.2.1" + checksum: 10/0dcc1a2d7874526b0072df3011b134857b49d97a3bc135bb464a299525d4972de6f5f464fd64da6c4d8406d26a1ffb976f62afaffef7723b1021a44498d10e08 + languageName: node + linkType: hard + +"content-type@npm:^1.0.5": + version: 1.0.5 + resolution: "content-type@npm:1.0.5" + checksum: 10/585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662 + languageName: node + linkType: hard + "convert-source-map@npm:^2.0.0": version: 2.0.0 resolution: "convert-source-map@npm:2.0.0" @@ -2290,7 +2378,31 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.6": +"cookie-signature@npm:^1.2.1": + version: 1.2.2 + resolution: "cookie-signature@npm:1.2.2" + checksum: 10/be44a3c9a56f3771aea3a8bd8ad8f0a8e2679bcb967478267f41a510b4eb5ec55085386ba79c706c4ac21605ca76f4251973444b90283e0eb3eeafe8a92c7708 + languageName: node + linkType: hard + +"cookie@npm:^0.7.1": + version: 0.7.2 + resolution: "cookie@npm:0.7.2" + checksum: 10/24b286c556420d4ba4e9bc09120c9d3db7d28ace2bd0f8ccee82422ce42322f73c8312441271e5eefafbead725980e5996cc02766dbb89a90ac7f5636ede608f + languageName: node + linkType: hard + +"cors@npm:^2.8.5": + version: 2.8.5 + resolution: "cors@npm:2.8.5" + dependencies: + object-assign: "npm:^4" + vary: "npm:^1" + checksum: 10/66e88e08edee7cbce9d92b4d28a2028c88772a4c73e02f143ed8ca76789f9b59444eed6b1c167139e76fa662998c151322720093ba229f9941365ada5a6fc2c6 + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -2315,15 +2427,15 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": - version: 4.3.7 - resolution: "debug@npm:4.3.7" +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.4.0": + version: 4.4.0 + resolution: "debug@npm:4.4.0" dependencies: ms: "npm:^2.1.3" peerDependenciesMeta: supports-color: optional: true - checksum: 10/71168908b9a78227ab29d5d25fe03c5867750e31ce24bf2c44a86efc5af041758bb56569b0a3d48a9b5344c00a24a777e6f4100ed6dfd9534a42c1dde285125a + checksum: 10/1847944c2e3c2c732514b93d11886575625686056cd765336212dc15de2d2b29612b6cd80e1afba767bb8e1803b778caf9973e98169ef1a24a7a7009e1820367 languageName: node linkType: hard @@ -2359,6 +2471,13 @@ __metadata: languageName: node linkType: hard +"depd@npm:2.0.0, depd@npm:^2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: 10/c0c8ff36079ce5ada64f46cc9d6fd47ebcf38241105b6e0c98f412e8ad91f084bcf906ff644cc3a4bd876ca27a62accb8b0fff72ea6ed1a414b89d8506f4a5ca + languageName: node + linkType: hard + "dequal@npm:^2.0.2, dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" @@ -2398,6 +2517,17 @@ __metadata: languageName: node linkType: hard +"dunder-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "dunder-proto@npm:1.0.1" + dependencies: + call-bind-apply-helpers: "npm:^1.0.1" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.2.0" + checksum: 10/5add88a3d68d42d6e6130a0cac450b7c2edbe73364bbd2fc334564418569bea97c6943a8fcd70e27130bf32afc236f30982fc4905039b703f23e9e0433c29934 + languageName: node + linkType: hard + "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -2405,6 +2535,13 @@ __metadata: languageName: node linkType: hard +"ee-first@npm:1.1.1": + version: 1.1.1 + resolution: "ee-first@npm:1.1.1" + checksum: 10/1b4cac778d64ce3b582a7e26b218afe07e207a0f9bfe13cc7395a6d307849cfe361e65033c3251e00c27dd060cab43014c2d6b2647676135e18b77d2d05b3f4f + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.5.28": version: 1.5.41 resolution: "electron-to-chromium@npm:1.5.41" @@ -2426,6 +2563,13 @@ __metadata: languageName: node linkType: hard +"encodeurl@npm:^2.0.0": + version: 2.0.0 + resolution: "encodeurl@npm:2.0.0" + checksum: 10/abf5cd51b78082cf8af7be6785813c33b6df2068ce5191a40ca8b1afe6a86f9230af9a9ce694a5ce4665955e5c1120871826df9c128a642e09c58d592e2807fe + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -2449,12 +2593,10 @@ __metadata: languageName: node linkType: hard -"es-define-property@npm:^1.0.0": - version: 1.0.0 - resolution: "es-define-property@npm:1.0.0" - dependencies: - get-intrinsic: "npm:^1.2.4" - checksum: 10/f66ece0a887b6dca71848fa71f70461357c0e4e7249696f81bad0a1f347eed7b31262af4a29f5d726dc026426f085483b6b90301855e647aa8e21936f07293c6 +"es-define-property@npm:^1.0.0, es-define-property@npm:^1.0.1": + version: 1.0.1 + resolution: "es-define-property@npm:1.0.1" + checksum: 10/f8dc9e660d90919f11084db0a893128f3592b781ce967e4fccfb8f3106cb83e400a4032c559184ec52ee1dbd4b01e7776c7cd0b3327b1961b1a4a7008920fe78 languageName: node linkType: hard @@ -2465,6 +2607,15 @@ __metadata: languageName: node linkType: hard +"es-object-atoms@npm:^1.0.0, es-object-atoms@npm:^1.1.1": + version: 1.1.1 + resolution: "es-object-atoms@npm:1.1.1" + dependencies: + es-errors: "npm:^1.3.0" + checksum: 10/54fe77de288451dae51c37bfbfe3ec86732dc3778f98f3eb3bdb4bf48063b2c0b8f9c93542656986149d08aa5be3204286e2276053d19582b76753f1a2728867 + languageName: node + linkType: hard + "es-toolkit@npm:^1.22.0": version: 1.26.1 resolution: "es-toolkit@npm:1.26.1" @@ -2661,6 +2812,13 @@ __metadata: languageName: node linkType: hard +"escape-html@npm:^1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 10/6213ca9ae00d0ab8bccb6d8d4e0a98e76237b2410302cf7df70aaa6591d509a2a37ce8998008cbecae8fc8ffaadf3fb0229535e6a145f3ce0b211d060decbb24 + languageName: node + linkType: hard + "escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" @@ -2675,17 +2833,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-storybook@npm:^0.11.0": - version: 0.11.4 - resolution: "eslint-plugin-storybook@npm:0.11.4" +"eslint-plugin-storybook@npm:^0.12.0": + version: 0.12.0 + resolution: "eslint-plugin-storybook@npm:0.12.0" dependencies: "@storybook/csf": "npm:^0.1.11" "@typescript-eslint/utils": "npm:^8.8.1" ts-dedent: "npm:^2.2.0" peerDependencies: eslint: ">=8" - typescript: ">=4.8.4 <5.8.0" - checksum: 10/f0fbeef2686e399861db644fd3ffa2759ea50e99a84dc25cf43ff6dd12ac8d12d34b90722e56139a43b58f65a4ea69c36d6919c5224943998a902d2b1d5d13cc + checksum: 10/278ea59565e30b74ee1d57f0a8f704906eaf40973b13999ec2c44872bb90c7505dfb12777b264940e2b480e81ace85c0532af69666e76a783b8ffa898a1d49ad languageName: node linkType: hard @@ -2724,8 +2881,8 @@ __metadata: linkType: hard "eslint@npm:^9.11.1": - version: 9.25.1 - resolution: "eslint@npm:9.25.1" + version: 9.26.0 + resolution: "eslint@npm:9.26.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.12.1" @@ -2733,11 +2890,12 @@ __metadata: "@eslint/config-helpers": "npm:^0.2.1" "@eslint/core": "npm:^0.13.0" "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.25.1" + "@eslint/js": "npm:9.26.0" "@eslint/plugin-kit": "npm:^0.2.8" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" "@humanwhocodes/retry": "npm:^0.4.2" + "@modelcontextprotocol/sdk": "npm:^1.8.0" "@types/estree": "npm:^1.0.6" "@types/json-schema": "npm:^7.0.15" ajv: "npm:^6.12.4" @@ -2762,6 +2920,7 @@ __metadata: minimatch: "npm:^3.1.2" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" + zod: "npm:^3.24.2" peerDependencies: jiti: "*" peerDependenciesMeta: @@ -2769,7 +2928,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10/037bbdc5cba6f72199976dcdce115b1b479b9425ee1116c08bcaf25e0de4a74a0ffe696d48610ade79c91b04ef3e707a7215a42dfba9c7d3a0b85747d5902e67 + checksum: 10/b87092cb7e87f1d0963475c1a1e15e551842ea122925cf13231e742fae565bf3582029a5b0b4aecf793f25c26ee0be3ee1f32190bc361e0c3f3633b9cbace948 languageName: node linkType: hard @@ -2849,6 +3008,29 @@ __metadata: languageName: node linkType: hard +"etag@npm:^1.8.1": + version: 1.8.1 + resolution: "etag@npm:1.8.1" + checksum: 10/571aeb3dbe0f2bbd4e4fadbdb44f325fc75335cd5f6f6b6a091e6a06a9f25ed5392f0863c5442acb0646787446e816f13cbfc6edce5b07658541dff573cab1ff + languageName: node + linkType: hard + +"eventsource-parser@npm:^3.0.1": + version: 3.0.1 + resolution: "eventsource-parser@npm:3.0.1" + checksum: 10/2730c54c3cb47d55d2967f2ece843f9fc95d8a11c2fef6fece8d17d9080193cbe3cd9ac7b04a325977f63cbf8c1664fdd0512dec1aec601666a5c5bd8564b61f + languageName: node + linkType: hard + +"eventsource@npm:^3.0.2": + version: 3.0.6 + resolution: "eventsource@npm:3.0.6" + dependencies: + eventsource-parser: "npm:^3.0.1" + checksum: 10/ac08c7d1b21e454c7685693fe4ace53fc0b84f3cf752699a556876f2a7f33b7a12972ae33d1c407fb920d6d4aed10de52fdf0dd01902ccdf45cd5da8d55e7f88 + languageName: node + linkType: hard + "exponential-backoff@npm:^3.1.1": version: 3.1.1 resolution: "exponential-backoff@npm:3.1.1" @@ -2856,6 +3038,50 @@ __metadata: languageName: node linkType: hard +"express-rate-limit@npm:^7.5.0": + version: 7.5.0 + resolution: "express-rate-limit@npm:7.5.0" + peerDependencies: + express: ^4.11 || 5 || ^5.0.0-beta.1 + checksum: 10/eff34c83bf586789933a332a339b66649e2cca95c8e977d193aa8bead577d3182ac9f0e9c26f39389287539b8038890ff023f910b54ebb506a26a2ce135b92ca + languageName: node + linkType: hard + +"express@npm:^5.0.1": + version: 5.1.0 + resolution: "express@npm:5.1.0" + dependencies: + accepts: "npm:^2.0.0" + body-parser: "npm:^2.2.0" + content-disposition: "npm:^1.0.0" + content-type: "npm:^1.0.5" + cookie: "npm:^0.7.1" + cookie-signature: "npm:^1.2.1" + debug: "npm:^4.4.0" + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + etag: "npm:^1.8.1" + finalhandler: "npm:^2.1.0" + fresh: "npm:^2.0.0" + http-errors: "npm:^2.0.0" + merge-descriptors: "npm:^2.0.0" + mime-types: "npm:^3.0.0" + on-finished: "npm:^2.4.1" + once: "npm:^1.4.0" + parseurl: "npm:^1.3.3" + proxy-addr: "npm:^2.0.7" + qs: "npm:^6.14.0" + range-parser: "npm:^1.2.1" + router: "npm:^2.2.0" + send: "npm:^1.1.0" + serve-static: "npm:^2.2.0" + statuses: "npm:^2.0.1" + type-is: "npm:^2.0.1" + vary: "npm:^1.1.2" + checksum: 10/6dba00bbdf308f43a84ed3f07a7e9870d5208f2a0b8f60f39459dda089750379747819863fad250849d3c9163833f33f94ce69d73938df31e0c5a430800d7e56 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -2924,6 +3150,20 @@ __metadata: languageName: node linkType: hard +"finalhandler@npm:^2.1.0": + version: 2.1.0 + resolution: "finalhandler@npm:2.1.0" + dependencies: + debug: "npm:^4.4.0" + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + on-finished: "npm:^2.4.1" + parseurl: "npm:^1.3.3" + statuses: "npm:^2.0.1" + checksum: 10/b2bd68c310e2c463df0ab747ab05f8defbc540b8c3f2442f86e7d084ac8acbc31f8cae079931b7f5a406521501941e3395e963de848a0aaf45dd414adeb5ff4e + languageName: node + linkType: hard + "find-up@npm:^5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" @@ -2970,6 +3210,20 @@ __metadata: languageName: node linkType: hard +"forwarded@npm:0.2.0": + version: 0.2.0 + resolution: "forwarded@npm:0.2.0" + checksum: 10/29ba9fd347117144e97cbb8852baae5e8b2acb7d1b591ef85695ed96f5b933b1804a7fac4a15dd09ca7ac7d0cdc104410e8102aae2dd3faa570a797ba07adb81 + languageName: node + linkType: hard + +"fresh@npm:^2.0.0": + version: 2.0.0 + resolution: "fresh@npm:2.0.0" + checksum: 10/44e1468488363074641991c1340d2a10c5a6f6d7c353d89fd161c49d120c58ebf9890720f7584f509058385836e3ce50ddb60e9f017315a4ba8c6c3461813bfc + languageName: node + linkType: hard + "fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" @@ -3021,16 +3275,31 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4": - version: 1.2.4 - resolution: "get-intrinsic@npm:1.2.4" +"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.3.0": + version: 1.3.0 + resolution: "get-intrinsic@npm:1.3.0" dependencies: + call-bind-apply-helpers: "npm:^1.0.2" + es-define-property: "npm:^1.0.1" es-errors: "npm:^1.3.0" + es-object-atoms: "npm:^1.1.1" function-bind: "npm:^1.1.2" - has-proto: "npm:^1.0.1" - has-symbols: "npm:^1.0.3" - hasown: "npm:^2.0.0" - checksum: 10/85bbf4b234c3940edf8a41f4ecbd4e25ce78e5e6ad4e24ca2f77037d983b9ef943fd72f00f3ee97a49ec622a506b67db49c36246150377efcda1c9eb03e5f06d + get-proto: "npm:^1.0.1" + gopd: "npm:^1.2.0" + has-symbols: "npm:^1.1.0" + hasown: "npm:^2.0.2" + math-intrinsics: "npm:^1.1.0" + checksum: 10/6e9dd920ff054147b6f44cb98104330e87caafae051b6d37b13384a45ba15e71af33c3baeac7cb630a0aaa23142718dcf25b45cfdd86c184c5dcb4e56d953a10 + languageName: node + linkType: hard + +"get-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "get-proto@npm:1.0.1" + dependencies: + dunder-proto: "npm:^1.0.1" + es-object-atoms: "npm:^1.0.0" + checksum: 10/4fc96afdb58ced9a67558698b91433e6b037aaa6f1493af77498d7c85b141382cf223c0e5946f334fb328ee85dfe6edd06d218eaf09556f4bc4ec6005d7f5f7b languageName: node linkType: hard @@ -3103,12 +3372,10 @@ __metadata: languageName: node linkType: hard -"gopd@npm:^1.0.1": - version: 1.0.1 - resolution: "gopd@npm:1.0.1" - dependencies: - get-intrinsic: "npm:^1.1.3" - checksum: 10/5fbc7ad57b368ae4cd2f41214bd947b045c1a4be2f194a7be1778d71f8af9dbf4004221f3b6f23e30820eb0d052b4f819fe6ebe8221e2a3c6f0ee4ef173421ca +"gopd@npm:^1.0.1, gopd@npm:^1.2.0": + version: 1.2.0 + resolution: "gopd@npm:1.2.0" + checksum: 10/94e296d69f92dc1c0768fcfeecfb3855582ab59a7c75e969d5f96ce50c3d201fd86d5a2857c22565764d5bb8a816c7b1e58f133ec318cd56274da36c5e3fb1a1 languageName: node linkType: hard @@ -3149,17 +3416,10 @@ __metadata: languageName: node linkType: hard -"has-proto@npm:^1.0.1": - version: 1.0.3 - resolution: "has-proto@npm:1.0.3" - checksum: 10/0b67c2c94e3bea37db3e412e3c41f79d59259875e636ba471e94c009cdfb1fa82bf045deeffafc7dbb9c148e36cae6b467055aaa5d9fad4316e11b41e3ba551a - languageName: node - linkType: hard - -"has-symbols@npm:^1.0.3": - version: 1.0.3 - resolution: "has-symbols@npm:1.0.3" - checksum: 10/464f97a8202a7690dadd026e6d73b1ceeddd60fe6acfd06151106f050303eaa75855aaa94969df8015c11ff7c505f196114d22f7386b4a471038da5874cf5e9b +"has-symbols@npm:^1.0.3, has-symbols@npm:^1.1.0": + version: 1.1.0 + resolution: "has-symbols@npm:1.1.0" + checksum: 10/959385c98696ebbca51e7534e0dc723ada325efa3475350951363cce216d27373e0259b63edb599f72eb94d6cde8577b4b2375f080b303947e560f85692834fa languageName: node linkType: hard @@ -3172,7 +3432,7 @@ __metadata: languageName: node linkType: hard -"hasown@npm:^2.0.0, hasown@npm:^2.0.2": +"hasown@npm:^2.0.2": version: 2.0.2 resolution: "hasown@npm:2.0.2" dependencies: @@ -3188,6 +3448,19 @@ __metadata: languageName: node linkType: hard +"http-errors@npm:2.0.0, http-errors@npm:^2.0.0": + version: 2.0.0 + resolution: "http-errors@npm:2.0.0" + dependencies: + depd: "npm:2.0.0" + inherits: "npm:2.0.4" + setprototypeof: "npm:1.2.0" + statuses: "npm:2.0.1" + toidentifier: "npm:1.0.1" + checksum: 10/0e7f76ee8ff8a33e58a3281a469815b893c41357378f408be8f6d4aa7d1efafb0da064625518e7078381b6a92325949b119dc38fcb30bdbc4e3a35f78c44c439 + languageName: node + linkType: hard + "http-proxy-agent@npm:^7.0.0": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" @@ -3208,7 +3481,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:^0.6.2": +"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" dependencies: @@ -3248,7 +3521,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:^2.0.3": +"inherits@npm:2.0.4, inherits@npm:^2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 @@ -3265,6 +3538,13 @@ __metadata: languageName: node linkType: hard +"ipaddr.js@npm:1.9.1": + version: 1.9.1 + resolution: "ipaddr.js@npm:1.9.1" + checksum: 10/864d0cced0c0832700e9621913a6429ccdc67f37c1bd78fb8c6789fff35c9d167cb329134acad2290497a53336813ab4798d2794fd675d5eb33b5fdf0982b9ca + languageName: node + linkType: hard + "is-arguments@npm:^1.0.4": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" @@ -3346,6 +3626,13 @@ __metadata: languageName: node linkType: hard +"is-promise@npm:^4.0.0": + version: 4.0.0 + resolution: "is-promise@npm:4.0.0" + checksum: 10/0b46517ad47b00b6358fd6553c83ec1f6ba9acd7ffb3d30a0bf519c5c69e7147c132430452351b8a9fc198f8dd6c4f76f8e6f5a7f100f8c77d57d9e0f4261a8a + languageName: node + linkType: hard + "is-typed-array@npm:^1.1.3": version: 1.1.13 resolution: "is-typed-array@npm:1.1.13" @@ -3605,6 +3892,20 @@ __metadata: languageName: node linkType: hard +"math-intrinsics@npm:^1.1.0": + version: 1.1.0 + resolution: "math-intrinsics@npm:1.1.0" + checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd + languageName: node + linkType: hard + +"media-typer@npm:^1.1.0": + version: 1.1.0 + resolution: "media-typer@npm:1.1.0" + checksum: 10/a58dd60804df73c672942a7253ccc06815612326dc1c0827984b1a21704466d7cde351394f47649e56cf7415e6ee2e26e000e81b51b3eebb5a93540e8bf93cbd + languageName: node + linkType: hard + "memoizerific@npm:^1.11.3": version: 1.11.3 resolution: "memoizerific@npm:1.11.3" @@ -3614,6 +3915,13 @@ __metadata: languageName: node linkType: hard +"merge-descriptors@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-descriptors@npm:2.0.0" + checksum: 10/e383332e700a94682d0125a36c8be761142a1320fc9feeb18e6e36647c9edf064271645f5669b2c21cf352116e561914fd8aa831b651f34db15ef4038c86696a + languageName: node + linkType: hard + "merge2@npm:^1.3.0, merge2@npm:^1.4.1": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -3631,6 +3939,22 @@ __metadata: languageName: node linkType: hard +"mime-db@npm:^1.54.0": + version: 1.54.0 + resolution: "mime-db@npm:1.54.0" + checksum: 10/9e7834be3d66ae7f10eaa69215732c6d389692b194f876198dca79b2b90cbf96688d9d5d05ef7987b20f749b769b11c01766564264ea5f919c88b32a29011311 + languageName: node + linkType: hard + +"mime-types@npm:^3.0.0, mime-types@npm:^3.0.1": + version: 3.0.1 + resolution: "mime-types@npm:3.0.1" + dependencies: + mime-db: "npm:^1.54.0" + checksum: 10/fa1d3a928363723a8046c346d87bf85d35014dae4285ad70a3ff92bd35957992b3094f8417973cfe677330916c6ef30885109624f1fb3b1e61a78af509dba120 + languageName: node + linkType: hard + "min-indent@npm:^1.0.0, min-indent@npm:^1.0.1": version: 1.0.1 resolution: "min-indent@npm:1.0.1" @@ -3793,6 +4117,13 @@ __metadata: languageName: node linkType: hard +"negotiator@npm:^1.0.0": + version: 1.0.0 + resolution: "negotiator@npm:1.0.0" + checksum: 10/b5734e87295324fabf868e36fb97c84b7d7f3156ec5f4ee5bf6e488079c11054f818290fc33804cef7b1ee21f55eeb14caea83e7dafae6492a409b3e573153e5 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 10.2.0 resolution: "node-gyp@npm:10.2.0" @@ -3831,6 +4162,38 @@ __metadata: languageName: node linkType: hard +"object-assign@npm:^4": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10/fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.3": + version: 1.13.4 + resolution: "object-inspect@npm:1.13.4" + checksum: 10/aa13b1190ad3e366f6c83ad8a16ed37a19ed57d267385aa4bfdccda833d7b90465c057ff6c55d035a6b2e52c1a2295582b294217a0a3a1ae7abdd6877ef781fb + languageName: node + linkType: hard + +"on-finished@npm:^2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: "npm:1.1.1" + checksum: 10/8e81472c5028125c8c39044ac4ab8ba51a7cdc19a9fbd4710f5d524a74c6d8c9ded4dd0eed83f28d3d33ac1d7a6a439ba948ccb765ac6ce87f30450a26bfe2ea + languageName: node + linkType: hard + +"once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10/cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + "open@npm:^8.0.4": version: 8.4.2 resolution: "open@npm:8.4.2" @@ -3899,6 +4262,13 @@ __metadata: languageName: node linkType: hard +"parseurl@npm:^1.3.3": + version: 1.3.3 + resolution: "parseurl@npm:1.3.3" + checksum: 10/407cee8e0a3a4c5cd472559bca8b6a45b82c124e9a4703302326e9ab60fc1081442ada4e02628efef1eb16197ddc7f8822f5a91fd7d7c86b51f530aedb17dfa2 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -3930,6 +4300,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:^8.0.0": + version: 8.2.0 + resolution: "path-to-regexp@npm:8.2.0" + checksum: 10/23378276a172b8ba5f5fb824475d1818ca5ccee7bbdb4674701616470f23a14e536c1db11da9c9e6d82b82c556a817bbf4eee6e41b9ed20090ef9427cbb38e13 + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -3958,6 +4335,13 @@ __metadata: languageName: node linkType: hard +"pkce-challenge@npm:^5.0.0": + version: 5.0.0 + resolution: "pkce-challenge@npm:5.0.0" + checksum: 10/e60c06a0e0481cb82f80072053d5c479a7490758541c4226460450285dd5d72a995c44b3c553731ca7c2f64cc34b35f1d2e5f9de08d276b59899298f9efe1ddf + languageName: node + linkType: hard + "polished@npm:^4.2.2": version: 4.3.1 resolution: "polished@npm:4.3.1" @@ -4036,6 +4420,16 @@ __metadata: languageName: node linkType: hard +"proxy-addr@npm:^2.0.7": + version: 2.0.7 + resolution: "proxy-addr@npm:2.0.7" + dependencies: + forwarded: "npm:0.2.0" + ipaddr.js: "npm:1.9.1" + checksum: 10/f24a0c80af0e75d31e3451398670d73406ec642914da11a2965b80b1898ca6f66a0e3e091a11a4327079b2b268795f6fa06691923fef91887215c3d0e8ea3f68 + languageName: node + linkType: hard + "punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -4043,6 +4437,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.14.0": + version: 6.14.0 + resolution: "qs@npm:6.14.0" + dependencies: + side-channel: "npm:^1.1.0" + checksum: 10/a60e49bbd51c935a8a4759e7505677b122e23bf392d6535b8fc31c1e447acba2c901235ecb192764013cd2781723dc1f61978b5fdd93cc31d7043d31cdc01974 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -4050,6 +4453,25 @@ __metadata: languageName: node linkType: hard +"range-parser@npm:^1.2.1": + version: 1.2.1 + resolution: "range-parser@npm:1.2.1" + checksum: 10/ce21ef2a2dd40506893157970dc76e835c78cf56437e26e19189c48d5291e7279314477b06ac38abd6a401b661a6840f7b03bd0b1249da9b691deeaa15872c26 + languageName: node + linkType: hard + +"raw-body@npm:^3.0.0": + version: 3.0.0 + resolution: "raw-body@npm:3.0.0" + dependencies: + bytes: "npm:3.1.2" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.6.3" + unpipe: "npm:1.0.0" + checksum: 10/2443429bbb2f9ae5c50d3d2a6c342533dfbde6b3173740b70fa0302b30914ff400c6d31a46b3ceacbe7d0925dc07d4413928278b494b04a65736fc17ca33e30c + languageName: node + linkType: hard + "react-confetti@npm:^6.1.0": version: 6.1.0 resolution: "react-confetti@npm:6.1.0" @@ -4298,6 +4720,19 @@ __metadata: languageName: node linkType: hard +"router@npm:^2.2.0": + version: 2.2.0 + resolution: "router@npm:2.2.0" + dependencies: + debug: "npm:^4.4.0" + depd: "npm:^2.0.0" + is-promise: "npm:^4.0.0" + parseurl: "npm:^1.3.3" + path-to-regexp: "npm:^8.0.0" + checksum: 10/8949bd1d3da5403cc024e2989fee58d7fda0f3ffe9f2dc5b8a192f295f400b3cde307b0b554f7d44851077640f36962ca469a766b3d57410d7d96245a7ba6c91 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -4307,6 +4742,13 @@ __metadata: languageName: node linkType: hard +"safe-buffer@npm:5.2.1": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451 + languageName: node + linkType: hard + "safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" @@ -4348,6 +4790,37 @@ __metadata: languageName: node linkType: hard +"send@npm:^1.1.0, send@npm:^1.2.0": + version: 1.2.0 + resolution: "send@npm:1.2.0" + dependencies: + debug: "npm:^4.3.5" + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + etag: "npm:^1.8.1" + fresh: "npm:^2.0.0" + http-errors: "npm:^2.0.0" + mime-types: "npm:^3.0.1" + ms: "npm:^2.1.3" + on-finished: "npm:^2.4.1" + range-parser: "npm:^1.2.1" + statuses: "npm:^2.0.1" + checksum: 10/9fa3b1a3b9a06b7b4ab00c25e8228326d9665a9745753a34d1ffab8ac63c7c206727331d1dc5be73647f1b658d259a1aa8e275b0e0eee51349370af02e9da506 + languageName: node + linkType: hard + +"serve-static@npm:^2.2.0": + version: 2.2.0 + resolution: "serve-static@npm:2.2.0" + dependencies: + encodeurl: "npm:^2.0.0" + escape-html: "npm:^1.0.3" + parseurl: "npm:^1.3.3" + send: "npm:^1.2.0" + checksum: 10/9f1a900738c5bb02258275ce3bd1273379c4c3072b622e15d44e8f47d89a1ba2d639ec2d63b11c263ca936096b40758acb7a0d989cd6989018a65a12f9433ada + languageName: node + linkType: hard + "set-function-length@npm:^1.2.1": version: 1.2.2 resolution: "set-function-length@npm:1.2.2" @@ -4362,6 +4835,13 @@ __metadata: languageName: node linkType: hard +"setprototypeof@npm:1.2.0": + version: 1.2.0 + resolution: "setprototypeof@npm:1.2.0" + checksum: 10/fde1630422502fbbc19e6844346778f99d449986b2f9cdcceb8326730d2f3d9964dbcb03c02aaadaefffecd0f2c063315ebea8b3ad895914bf1afc1747fc172e + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -4378,6 +4858,54 @@ __metadata: languageName: node linkType: hard +"side-channel-list@npm:^1.0.0": + version: 1.0.0 + resolution: "side-channel-list@npm:1.0.0" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.3" + checksum: 10/603b928997abd21c5a5f02ae6b9cc36b72e3176ad6827fab0417ead74580cc4fb4d5c7d0a8a2ff4ead34d0f9e35701ed7a41853dac8a6d1a664fcce1a044f86f + languageName: node + linkType: hard + +"side-channel-map@npm:^1.0.1": + version: 1.0.1 + resolution: "side-channel-map@npm:1.0.1" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + checksum: 10/5771861f77feefe44f6195ed077a9e4f389acc188f895f570d56445e251b861754b547ea9ef73ecee4e01fdada6568bfe9020d2ec2dfc5571e9fa1bbc4a10615 + languageName: node + linkType: hard + +"side-channel-weakmap@npm:^1.0.2": + version: 1.0.2 + resolution: "side-channel-weakmap@npm:1.0.2" + dependencies: + call-bound: "npm:^1.0.2" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.5" + object-inspect: "npm:^1.13.3" + side-channel-map: "npm:^1.0.1" + checksum: 10/a815c89bc78c5723c714ea1a77c938377ea710af20d4fb886d362b0d1f8ac73a17816a5f6640f354017d7e292a43da9c5e876c22145bac00b76cfb3468001736 + languageName: node + linkType: hard + +"side-channel@npm:^1.1.0": + version: 1.1.0 + resolution: "side-channel@npm:1.1.0" + dependencies: + es-errors: "npm:^1.3.0" + object-inspect: "npm:^1.13.3" + side-channel-list: "npm:^1.0.0" + side-channel-map: "npm:^1.0.1" + side-channel-weakmap: "npm:^1.0.2" + checksum: 10/7d53b9db292c6262f326b6ff3bc1611db84ece36c2c7dc0e937954c13c73185b0406c56589e2bb8d071d6fee468e14c39fb5d203ee39be66b7b8174f179afaba + languageName: node + linkType: hard + "signal-exit@npm:^4.0.1": version: 4.1.0 resolution: "signal-exit@npm:4.1.0" @@ -4450,6 +4978,13 @@ __metadata: languageName: node linkType: hard +"statuses@npm:2.0.1, statuses@npm:^2.0.1": + version: 2.0.1 + resolution: "statuses@npm:2.0.1" + checksum: 10/18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb + languageName: node + linkType: hard + "storybook@npm:^8.3.5": version: 8.6.12 resolution: "storybook@npm:8.6.12" @@ -4616,6 +5151,13 @@ __metadata: languageName: node linkType: hard +"toidentifier@npm:1.0.1": + version: 1.0.1 + resolution: "toidentifier@npm:1.0.1" + checksum: 10/952c29e2a85d7123239b5cfdd889a0dde47ab0497f0913d70588f19c53f7e0b5327c95f4651e413c74b785147f9637b17410ac8c846d5d4a20a5a33eb6dc3a45 + languageName: node + linkType: hard + "ts-api-utils@npm:^2.0.1": version: 2.0.1 resolution: "ts-api-utils@npm:2.0.1" @@ -4691,6 +5233,17 @@ __metadata: languageName: node linkType: hard +"type-is@npm:^2.0.0, type-is@npm:^2.0.1": + version: 2.0.1 + resolution: "type-is@npm:2.0.1" + dependencies: + content-type: "npm:^1.0.5" + media-typer: "npm:^1.1.0" + mime-types: "npm:^3.0.0" + checksum: 10/bacdb23c872dacb7bd40fbd9095e6b2fca2895eedbb689160c05534d7d4810a7f4b3fd1ae87e96133c505958f6d602967a68db5ff577b85dd6be76eaa75d58af + languageName: node + linkType: hard + "typescript-eslint@npm:^8.7.0": version: 8.26.0 resolution: "typescript-eslint@npm:8.26.0" @@ -4750,6 +5303,13 @@ __metadata: languageName: node linkType: hard +"unpipe@npm:1.0.0": + version: 1.0.0 + resolution: "unpipe@npm:1.0.0" + checksum: 10/4fa18d8d8d977c55cb09715385c203197105e10a6d220087ec819f50cb68870f02942244f1017565484237f1f8c5d3cd413631b1ae104d3096f24fdfde1b4aa2 + languageName: node + linkType: hard + "unplugin@npm:^1.3.1": version: 1.14.1 resolution: "unplugin@npm:1.14.1" @@ -4810,6 +5370,13 @@ __metadata: languageName: node linkType: hard +"vary@npm:^1, vary@npm:^1.1.2": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: 10/31389debef15a480849b8331b220782230b9815a8e0dbb7b9a8369559aed2e9a7800cd904d4371ea74f4c3527db456dc8e7ac5befce5f0d289014dbdf47b2242 + languageName: node + linkType: hard + "vite@npm:^5.4.8": version: 5.4.19 resolution: "vite@npm:5.4.19" @@ -4924,6 +5491,13 @@ __metadata: languageName: node linkType: hard +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10/159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + "ws@npm:^8.2.3": version: 8.18.0 resolution: "ws@npm:8.18.0" @@ -4959,3 +5533,19 @@ __metadata: checksum: 10/f77b3d8d00310def622123df93d4ee654fc6a0096182af8bd60679ddcdfb3474c56c6c7190817c84a2785648cdee9d721c0154eb45698c62176c322fb46fc700 languageName: node linkType: hard + +"zod-to-json-schema@npm:^3.24.1": + version: 3.24.5 + resolution: "zod-to-json-schema@npm:3.24.5" + peerDependencies: + zod: ^3.24.1 + checksum: 10/1af291b4c429945c9568c2e924bdb7c66ab8d139cbeb9a99b6e9fc9e1b02863f85d07759b9303714f07ceda3993dcaf0ebcb80d2c18bb2aaf5502b2c1016affd + languageName: node + linkType: hard + +"zod@npm:^3.23.8, zod@npm:^3.24.2": + version: 3.24.4 + resolution: "zod@npm:3.24.4" + checksum: 10/3d545792fa54bb27ee5dbc34a5709e81f603185fcc94c8204b5d95c20dc4c81d870ff9c51f3884a30ef05cdc601449f4c4df254ac4783f0827b1faed7c1cdb48 + languageName: node + linkType: hard diff --git a/yarn.lock b/yarn.lock index f700a3af06..337604b388 100644 --- a/yarn.lock +++ b/yarn.lock @@ -204,13 +204,13 @@ __metadata: linkType: hard "@apidevtools/json-schema-ref-parser@npm:^11.0.0, @apidevtools/json-schema-ref-parser@npm:^11.7.0": - version: 11.7.3 - resolution: "@apidevtools/json-schema-ref-parser@npm:11.7.3" + version: 11.9.3 + resolution: "@apidevtools/json-schema-ref-parser@npm:11.9.3" dependencies: "@jsdevtools/ono": "npm:^7.1.3" "@types/json-schema": "npm:^7.0.15" js-yaml: "npm:^4.1.0" - checksum: 10/ab496d84f8deca4e5e3ab4adb28825fb1e90655561cf7b70b6dc7b8cf95e49ea9fa3440c265d1d115a43e673b94a79005af05b5d76f77a8934ed1c2fae6762c9 + checksum: 10/3d3618dbb611d1296b99bdee4ff0dde664dad47632d30e0310c6d10de8081f6378ccb58329ea4e03103eca9347d5143671d03f0527b1c3f0916d95f8c09215e2 languageName: node linkType: hard @@ -324,25 +324,25 @@ __metadata: languageName: node linkType: hard -"@asyncapi/protobuf-schema-parser@npm:^3.4.0": - version: 3.4.0 - resolution: "@asyncapi/protobuf-schema-parser@npm:3.4.0" +"@asyncapi/protobuf-schema-parser@npm:^3.5.1": + version: 3.5.1 + resolution: "@asyncapi/protobuf-schema-parser@npm:3.5.1" dependencies: "@asyncapi/parser": "npm:^3.4.0" "@types/protocol-buffers-schema": "npm:^3.4.3" protobufjs: "npm:^7.4.0" - checksum: 10/e04ab346e0145d735a4d4238d7adc74ea469b588be10507363e6e216822b3501b786f2c33d0451ace20ac9e6dace411458b6b8045388bed0300278e7fa2e3822 + checksum: 10/dbef0c14080f0894e2d2ca1f5f233485e3cce3f37bf82e3412be50322bd16366812ab933f35e38c35ee453a29ae20e2d8a811a6921fc5631cb5caf0b59fd839a languageName: node linkType: hard "@asyncapi/react-component@npm:^2.3.3": - version: 2.5.1 - resolution: "@asyncapi/react-component@npm:2.5.1" + version: 2.6.3 + resolution: "@asyncapi/react-component@npm:2.6.3" dependencies: "@asyncapi/avro-schema-parser": "npm:^3.0.24" "@asyncapi/openapi-schema-parser": "npm:^3.0.24" "@asyncapi/parser": "npm:^3.3.0" - "@asyncapi/protobuf-schema-parser": "npm:^3.4.0" + "@asyncapi/protobuf-schema-parser": "npm:^3.5.1" highlight.js: "npm:^10.7.2" isomorphic-dompurify: "npm:^2.14.0" marked: "npm:^4.0.14" @@ -352,7 +352,7 @@ __metadata: peerDependencies: react: ">=18.0.0" react-dom: ">=18.0.0" - checksum: 10/590b9e8e4326a803149bd5c99662cd75acf4e469cb3c829588f2b8a2b2b85bf3d342a81daccf3f974d9fbdb89e30a6bfed2d870826abaf7366b1017389dbd5c5 + checksum: 10/7105385f8f806200638f10b799ff1a5d1838041d20d14c6e64b1e1a411933727b91cd3957c2057bc7946524be01c8ab7bb03946886534b15f4767c277da38445 languageName: node linkType: hard @@ -3587,6 +3587,7 @@ __metadata: "@types/cors": "npm:^2.8.6" "@types/express": "npm:^4.17.6" "@types/http-errors": "npm:^2.0.0" + "@types/is-glob": "npm:^4.0.2" "@types/node-forge": "npm:^1.3.0" "@types/pg-format": "npm:^1.0.5" "@types/yauzl": "npm:^2.10.0" @@ -3605,6 +3606,7 @@ __metadata: git-url-parse: "npm:^15.0.0" helmet: "npm:^6.0.0" http-errors: "npm:^2.0.0" + is-glob: "npm:^4.0.3" jose: "npm:^5.0.0" keyv: "npm:^5.2.1" knex: "npm:^3.0.0" @@ -3944,9 +3946,9 @@ __metadata: "@rollup/plugin-json": "npm:^6.0.0" "@rollup/plugin-node-resolve": "npm:^15.0.0" "@rollup/plugin-yaml": "npm:^4.0.0" - "@rspack/core": "npm:^1.0.10" - "@rspack/dev-server": "npm:^1.0.9" - "@rspack/plugin-react-refresh": "npm:^1.0.0" + "@rspack/core": "npm:^1.3.9" + "@rspack/dev-server": "npm:^1.1.1" + "@rspack/plugin-react-refresh": "npm:^1.4.2" "@spotify/eslint-config-base": "npm:^15.0.0" "@spotify/eslint-config-react": "npm:^15.0.0" "@spotify/eslint-config-typescript": "npm:^15.0.0" @@ -4060,7 +4062,7 @@ __metadata: zod: "npm:^3.22.4" zod-validation-error: "npm:^3.4.0" peerDependencies: - "@rspack/core": ^1.0.10 + "@rspack/core": ^1.2.8 "@rspack/dev-server": ^1.0.9 "@rspack/plugin-react-refresh": ^1.0.0 peerDependenciesMeta: @@ -5275,6 +5277,7 @@ __metadata: passport: "npm:^0.7.0" passport-atlassian-oauth2: "npm:^2.1.0" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5314,6 +5317,7 @@ __metadata: jose: "npm:^5.0.0" msw: "npm:^2.0.8" node-cache: "npm:^5.1.2" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5332,6 +5336,7 @@ __metadata: express: "npm:^4.19.2" jose: "npm:^5.0.0" passport: "npm:^0.7.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5350,6 +5355,7 @@ __metadata: passport: "npm:^0.7.0" passport-bitbucket-oauth2: "npm:^0.1.2" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5368,6 +5374,7 @@ __metadata: passport: "npm:^0.7.0" passport-oauth2: "npm:^1.6.1" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5389,6 +5396,7 @@ __metadata: msw: "npm:^2.0.0" node-mocks-http: "npm:^1.0.0" uuid: "npm:^11.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5404,6 +5412,7 @@ __metadata: "@backstage/types": "workspace:^" express: "npm:^4.18.2" google-auth-library: "npm:^9.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5421,6 +5430,7 @@ __metadata: "@types/passport-github2": "npm:^1.2.4" passport-github2: "npm:^0.1.12" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5439,6 +5449,7 @@ __metadata: passport: "npm:^0.7.0" passport-gitlab2: "npm:^5.0.0" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5456,6 +5467,7 @@ __metadata: google-auth-library: "npm:^9.0.0" passport-google-oauth20: "npm:^2.0.0" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5493,6 +5505,7 @@ __metadata: msw: "npm:^1.0.0" passport-microsoft: "npm:^1.0.0" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5510,6 +5523,7 @@ __metadata: passport: "npm:^0.7.0" passport-oauth2: "npm:^1.6.1" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5523,6 +5537,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" jose: "npm:^5.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5547,6 +5562,7 @@ __metadata: openid-client: "npm:^5.5.0" passport: "npm:^0.7.0" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5565,6 +5581,7 @@ __metadata: express: "npm:^4.18.2" passport: "npm:^0.7.0" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5583,6 +5600,7 @@ __metadata: passport: "npm:^0.7.0" passport-onelogin-oauth: "npm:^0.0.1" supertest: "npm:^7.0.0" + zod: "npm:^3.22.4" languageName: unknown linkType: soft @@ -5942,6 +5960,23 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-catalog-backend-module-gitea@workspace:plugins/catalog-backend-module-gitea": + version: 0.0.0-use.local + resolution: "@backstage/plugin-catalog-backend-module-gitea@workspace:plugins/catalog-backend-module-gitea" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/integration": "workspace:^" + "@backstage/plugin-catalog-common": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" + p-limit: "npm:^3.0.2" + uuid: "npm:^11.0.0" + languageName: unknown + linkType: soft + "@backstage/plugin-catalog-backend-module-github-org@workspace:plugins/catalog-backend-module-github-org": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-backend-module-github-org@workspace:plugins/catalog-backend-module-github-org" @@ -8020,11 +8055,13 @@ __metadata: "@backstage/plugin-scaffolder-common": "workspace:^" "@backstage/types": "workspace:^" "@isomorphic-git/pgp-plugin": "npm:^0.0.7" + "@types/lodash": "npm:^4.14.151" concat-stream: "npm:^2.0.0" fs-extra: "npm:^11.2.0" globby: "npm:^11.0.0" isomorphic-git: "npm:^1.23.0" jsonschema: "npm:^1.5.0" + lodash: "npm:^4.17.21" p-limit: "npm:^3.1.0" tar: "npm:^6.1.12" winston: "npm:^3.2.1" @@ -8942,7 +8979,7 @@ __metadata: portfinder: "npm:^1.0.32" tar: "npm:^6.1.12" ts-morph: "npm:^24.0.0" - typedoc: "npm:^0.27.6" + typedoc: "npm:^0.28.0" yaml-diff-patch: "npm:^2.0.0" peerDependencies: "@microsoft/api-extractor-model": "*" @@ -8950,7 +8987,7 @@ __metadata: "@microsoft/tsdoc-config": "*" "@useoptic/optic": ^1.0.0 prettier: ^2.8.1 - typedoc: ^0.27.0 + typedoc: ^0.28.0 typescript: "> 3.0.0" peerDependenciesMeta: prettier: @@ -9214,9 +9251,9 @@ __metadata: languageName: node linkType: hard -"@changesets/assemble-release-plan@npm:^6.0.7": - version: 6.0.7 - resolution: "@changesets/assemble-release-plan@npm:6.0.7" +"@changesets/assemble-release-plan@npm:^6.0.8": + version: 6.0.8 + resolution: "@changesets/assemble-release-plan@npm:6.0.8" dependencies: "@changesets/errors": "npm:^0.2.0" "@changesets/get-dependents-graph": "npm:^2.1.3" @@ -9224,7 +9261,7 @@ __metadata: "@changesets/types": "npm:^6.1.0" "@manypkg/get-packages": "npm:^1.1.3" semver: "npm:^7.5.3" - checksum: 10/61e0962c8116b802de11c7eddc34aa0a827166dba84686b69705da7c8e57bf16101f062b4de784a622f511e967554685b050d6f54fffe953da04f6564e65e414 + checksum: 10/5d01fc42c67229874cc70b93fbdc971e11909aa7a72f1909c585ecb3fdc69f3ac105d243e1341cd5b07c02dee133be461fa48138125f00d137e71f8b7e8f428e languageName: node linkType: hard @@ -9238,16 +9275,16 @@ __metadata: linkType: hard "@changesets/cli@npm:^2.14.0": - version: 2.29.3 - resolution: "@changesets/cli@npm:2.29.3" + version: 2.29.4 + resolution: "@changesets/cli@npm:2.29.4" dependencies: "@changesets/apply-release-plan": "npm:^7.0.12" - "@changesets/assemble-release-plan": "npm:^6.0.7" + "@changesets/assemble-release-plan": "npm:^6.0.8" "@changesets/changelog-git": "npm:^0.2.1" "@changesets/config": "npm:^3.1.1" "@changesets/errors": "npm:^0.2.0" "@changesets/get-dependents-graph": "npm:^2.1.3" - "@changesets/get-release-plan": "npm:^4.0.11" + "@changesets/get-release-plan": "npm:^4.0.12" "@changesets/git": "npm:^3.0.4" "@changesets/logger": "npm:^0.1.1" "@changesets/pre": "npm:^2.0.2" @@ -9271,7 +9308,7 @@ __metadata: term-size: "npm:^2.1.0" bin: changeset: bin.js - checksum: 10/9ab528d026651b8544d071000d7c26a9f6ac157299050ca67279b001ef6215f9647b83906aa5a4c761215696d898e704e5613df16c81c4bab7895ef1fabea9e0 + checksum: 10/fc325447b81a811464107e72a687f6c0414c5f928e518cb122d1efde1d71c205b1972464795ab97fb26087900ea55b99a551e9a010c9681def3d7561fd1c3f0b languageName: node linkType: hard @@ -9311,17 +9348,17 @@ __metadata: languageName: node linkType: hard -"@changesets/get-release-plan@npm:^4.0.11": - version: 4.0.11 - resolution: "@changesets/get-release-plan@npm:4.0.11" +"@changesets/get-release-plan@npm:^4.0.12": + version: 4.0.12 + resolution: "@changesets/get-release-plan@npm:4.0.12" dependencies: - "@changesets/assemble-release-plan": "npm:^6.0.7" + "@changesets/assemble-release-plan": "npm:^6.0.8" "@changesets/config": "npm:^3.1.1" "@changesets/pre": "npm:^2.0.2" "@changesets/read": "npm:^0.6.5" "@changesets/types": "npm:^6.1.0" "@manypkg/get-packages": "npm:^1.1.3" - checksum: 10/4a05b4474847a60604dc6158d0c8204a400a98fcb4593dc46b897abca3a1ce1eea40af208100e119e50ad693e34f061cc9b84912aa5ff3eb1560d4db59a4af59 + checksum: 10/d6482ecb6f1c2c47266493a36d05b484f0950d0a4472820649e953d073e3fdd612cdd8a4df9e3d7e00756d4e446dae639f9d6e0dab8a25f76bb6df77cd91c21c languageName: node linkType: hard @@ -9523,13 +9560,13 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.23.0": - version: 6.36.7 - resolution: "@codemirror/view@npm:6.36.7" + version: 6.36.8 + resolution: "@codemirror/view@npm:6.36.8" dependencies: "@codemirror/state": "npm:^6.5.0" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/0ba49a3025fbb381a8a35022135ea40363a3a3c298d41e3083bb5e263dfa4f11ff9419a46f2b78ef8e853376384ad6c7e48d2a7887e10d366e878d10b53e3b54 + checksum: 10/cb2cbfb8a41ee1d6cfb17c2ea91b9763e692f85d523b55276cc46ab7fda3f6ba0bcc03e2194ad6cf6b413ec34d309ab408300c19b62d5d6d6f9e2071fb99d489 languageName: node linkType: hard @@ -10328,14 +10365,16 @@ __metadata: languageName: node linkType: hard -"@gerrit0/mini-shiki@npm:^1.24.0": - version: 1.27.2 - resolution: "@gerrit0/mini-shiki@npm:1.27.2" +"@gerrit0/mini-shiki@npm:^3.2.2": + version: 3.4.0 + resolution: "@gerrit0/mini-shiki@npm:3.4.0" dependencies: - "@shikijs/engine-oniguruma": "npm:^1.27.2" - "@shikijs/types": "npm:^1.27.2" - "@shikijs/vscode-textmate": "npm:^10.0.1" - checksum: 10/ee4c80d94da587bd0dfe262f92ce81be0a1aa86dd0ef88e708839751af552451dd113f139b556be4f18747bf883d81ee85a119e535852114a71efda1cb3c5be5 + "@shikijs/engine-oniguruma": "npm:^3.4.0" + "@shikijs/langs": "npm:^3.4.0" + "@shikijs/themes": "npm:^3.4.0" + "@shikijs/types": "npm:^3.4.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + checksum: 10/2e116b8a6cb4d63db0fc8d96ba696befb4b4d66d4eaf4673c2585e798ae871e7e7cc581252cdc08169e5c19ae1aa496d93286da7626a36f7b0d298c660178855 languageName: node linkType: hard @@ -10427,15 +10466,15 @@ __metadata: linkType: hard "@google-cloud/firestore@npm:^7.0.0": - version: 7.11.0 - resolution: "@google-cloud/firestore@npm:7.11.0" + version: 7.11.1 + resolution: "@google-cloud/firestore@npm:7.11.1" dependencies: "@opentelemetry/api": "npm:^1.3.0" fast-deep-equal: "npm:^3.1.1" functional-red-black-tree: "npm:^1.0.1" google-gax: "npm:^4.3.3" protobufjs: "npm:^7.2.6" - checksum: 10/ae0e42ffee1e5cf7160149e1a581c0ceee702ea34d87d7212f7ffa326d9101f0aa2e9c5d37527a7f75864ac174cf32b50f1c593a81d02476ca4c8b8e85cba3bc + checksum: 10/c793718c5ecc6c4e2f7affabaeaab42af3d382c40c6b748f17bd3724232583073a4d7236f448b24c6b1a9e8bf5d485fb62d1ffb10588b0fe54a0d105fc2b3f40 languageName: node linkType: hard @@ -12283,10 +12322,10 @@ __metadata: languageName: node linkType: hard -"@module-federation/error-codes@npm:0.13.0": - version: 0.13.0 - resolution: "@module-federation/error-codes@npm:0.13.0" - checksum: 10/42a826d374f8451c320236f73f1337f69449d1875ee1141e70a0150c4820cd2c0a2997bb134aa01f39e405fe7142d5998b008c8ea32d493e9a507ae7c29c3ec9 +"@module-federation/error-codes@npm:0.13.1": + version: 0.13.1 + resolution: "@module-federation/error-codes@npm:0.13.1" + checksum: 10/fa9bbc0936ad9d037116c05f741bfb0ff741c3e2eaa682fa0132f4825d66ef0c63da0c20ec931ca16c8e7b660c1fdc60101a24ccab8951f19e07fba642aa1c6a languageName: node linkType: hard @@ -12354,13 +12393,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-core@npm:0.13.0": - version: 0.13.0 - resolution: "@module-federation/runtime-core@npm:0.13.0" +"@module-federation/runtime-core@npm:0.13.1": + version: 0.13.1 + resolution: "@module-federation/runtime-core@npm:0.13.1" dependencies: - "@module-federation/error-codes": "npm:0.13.0" - "@module-federation/sdk": "npm:0.13.0" - checksum: 10/04d23270be780dc3626a2391d14fe7b794aed9dc2e31b02339d399e9369b21cdc4c80b86edd14a06b0142fe711539284b381a2ea4894898923d3b457a472e569 + "@module-federation/error-codes": "npm:0.13.1" + "@module-federation/sdk": "npm:0.13.1" + checksum: 10/81b52eb7866f623115922af325bcc1dba6c85f95216dfa2cbf2f777b37ec11ba856846892a4a5ab446105a18837c495b0a05cb5d574f7d2e50f340d1e57413f4 languageName: node linkType: hard @@ -12374,13 +12413,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-tools@npm:0.13.0": - version: 0.13.0 - resolution: "@module-federation/runtime-tools@npm:0.13.0" +"@module-federation/runtime-tools@npm:0.13.1": + version: 0.13.1 + resolution: "@module-federation/runtime-tools@npm:0.13.1" dependencies: - "@module-federation/runtime": "npm:0.13.0" - "@module-federation/webpack-bundler-runtime": "npm:0.13.0" - checksum: 10/deb7abf5e44714a19766ec6009efd656e9e4da8e1ddeea775fa8d2d4b3d2e165629163ed86c83a003a4f4e76f17649b9d889582ce0c5d35a7a2eacf8da5bfb2e + "@module-federation/runtime": "npm:0.13.1" + "@module-federation/webpack-bundler-runtime": "npm:0.13.1" + checksum: 10/821f32979ef2235cd043b3ef561fd63f7554211bd86d3af7e17a57dd5c709c6ee2ae57935222318694732115d7771c122cf51fb9f71f879269d452bc5ab0a8d2 languageName: node linkType: hard @@ -12394,14 +12433,14 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime@npm:0.13.0": - version: 0.13.0 - resolution: "@module-federation/runtime@npm:0.13.0" +"@module-federation/runtime@npm:0.13.1": + version: 0.13.1 + resolution: "@module-federation/runtime@npm:0.13.1" dependencies: - "@module-federation/error-codes": "npm:0.13.0" - "@module-federation/runtime-core": "npm:0.13.0" - "@module-federation/sdk": "npm:0.13.0" - checksum: 10/a79accc2a44e2a8b5d6f5025582520c8904776c1b8fabe62554c20baf4602937a0bfdde2bf69c6b29cefb3e108ba8cc6b00c2dc78751da3f9dfd75722cede4af + "@module-federation/error-codes": "npm:0.13.1" + "@module-federation/runtime-core": "npm:0.13.1" + "@module-federation/sdk": "npm:0.13.1" + checksum: 10/2536ea17d0704bf4d093fe5791720e61fa3f4f4e83b6927b33f1d2ee132bf0cc9232e960cffefe32a6c79f3aedd864738b1be79b3790ad0c9dbd924f15ef2841 languageName: node linkType: hard @@ -12416,10 +12455,10 @@ __metadata: languageName: node linkType: hard -"@module-federation/sdk@npm:0.13.0": - version: 0.13.0 - resolution: "@module-federation/sdk@npm:0.13.0" - checksum: 10/aa08ca793fa6f0912d06f02592337340d973672a79f632b278527f72cc76ee19d3a95579ecb930ac355120b89c13c0260dc37fe08de1854f99626a796e5a6a7f +"@module-federation/sdk@npm:0.13.1": + version: 0.13.1 + resolution: "@module-federation/sdk@npm:0.13.1" + checksum: 10/7cfc98a6ee09aa338a9d76df146d064b5f4c6163b985daf3aa641e3f0e52d7cc40116b449cd1265b1333a66eeec31ce8bb40e1944f3ac62797d87323ce0e91ec languageName: node linkType: hard @@ -12441,13 +12480,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:0.13.0": - version: 0.13.0 - resolution: "@module-federation/webpack-bundler-runtime@npm:0.13.0" +"@module-federation/webpack-bundler-runtime@npm:0.13.1": + version: 0.13.1 + resolution: "@module-federation/webpack-bundler-runtime@npm:0.13.1" dependencies: - "@module-federation/runtime": "npm:0.13.0" - "@module-federation/sdk": "npm:0.13.0" - checksum: 10/692fdbd6d99d5dac9420691f581eebbb7d0538a7b01e6f7d6d2e4c3ec23798962f693775a6239298e53daf4b7cb4b5cac4775ce15625257c7c12aa7d6aa5b398 + "@module-federation/runtime": "npm:0.13.1" + "@module-federation/sdk": "npm:0.13.1" + checksum: 10/bebccaa4411c00355bb399e2dddb946586967642b90da4d4436554093fecb614fdff6c65dc73cef944c7344f169d2c0ecc22ed0b5b899e21d4535507806145e0 languageName: node linkType: hard @@ -12742,23 +12781,25 @@ __metadata: languageName: node linkType: hard -"@nestjs/axios@npm:3.1.1": - version: 3.1.1 - resolution: "@nestjs/axios@npm:3.1.1" +"@nestjs/axios@npm:4.0.0": + version: 4.0.0 + resolution: "@nestjs/axios@npm:4.0.0" peerDependencies: - "@nestjs/common": ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + "@nestjs/common": ^10.0.0 || ^11.0.0 axios: ^1.3.1 - rxjs: ^6.0.0 || ^7.0.0 - checksum: 10/2022e63016577f9448837591ec5c700c724bf48763ec3006f41acd3edba4e18d5508bcf5f513dfdb8c0f25eb8c6c84aa6378ed6e3ed6abb4aab3f8b8e851db96 + rxjs: ^7.0.0 + checksum: 10/9a61ac8a2fdbf304961696148945ba9e19c0ed73256767b0a643bbafe77106b2f738cb2f35f2fc63bff09a8abcd18365d2f8c772484e2fdd70b455bceb7b5822 languageName: node linkType: hard -"@nestjs/common@npm:10.4.6": - version: 10.4.6 - resolution: "@nestjs/common@npm:10.4.6" +"@nestjs/common@npm:11.0.20": + version: 11.0.20 + resolution: "@nestjs/common@npm:11.0.20" dependencies: + file-type: "npm:20.4.1" iterare: "npm:1.2.1" - tslib: "npm:2.7.0" + load-esm: "npm:1.0.2" + tslib: "npm:2.8.1" uid: "npm:2.0.2" peerDependencies: class-transformer: "*" @@ -12770,25 +12811,25 @@ __metadata: optional: true class-validator: optional: true - checksum: 10/bf1887715a8612e207c621a00761c0caed5c32f3fce9f6abd7de6a9c6f0217f825696f4dde218952017967493e47bd6fcc5b85d6f5d7fe82e31be9811179d2f5 + checksum: 10/ab06f3d910a86351e895829773b28236dcb4a80a3eba1ed902c51dbc44db9d40f619d5f8713c5ce42518fc95633bbe9c80b1c7188ffc4fd3d5d934b561e2f495 languageName: node linkType: hard -"@nestjs/core@npm:10.4.6": - version: 10.4.6 - resolution: "@nestjs/core@npm:10.4.6" +"@nestjs/core@npm:11.0.20": + version: 11.0.20 + resolution: "@nestjs/core@npm:11.0.20" dependencies: - "@nuxtjs/opencollective": "npm:0.3.2" + "@nuxt/opencollective": "npm:0.4.1" fast-safe-stringify: "npm:2.1.1" iterare: "npm:1.2.1" - path-to-regexp: "npm:3.3.0" - tslib: "npm:2.7.0" + path-to-regexp: "npm:8.2.0" + tslib: "npm:2.8.1" uid: "npm:2.0.2" peerDependencies: - "@nestjs/common": ^10.0.0 - "@nestjs/microservices": ^10.0.0 - "@nestjs/platform-express": ^10.0.0 - "@nestjs/websockets": ^10.0.0 + "@nestjs/common": ^11.0.0 + "@nestjs/microservices": ^11.0.0 + "@nestjs/platform-express": ^11.0.0 + "@nestjs/websockets": ^11.0.0 reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: @@ -12798,7 +12839,7 @@ __metadata: optional: true "@nestjs/websockets": optional: true - checksum: 10/09dd2b2a44a4c3cb546db78027ec76fbd6b12510b3013217d012b670abcbf6f7f021a91b0a59fe65eb5bc0cd53634edb9daebb61341d1d10e44476087759d21a + checksum: 10/0e18e6744eaa912442d43d6a8d5543bc4b7e09d350943881d76110aa53a0d26876818898d1ddb71f92d24e6f7154ab3264c6c5604c9670221a5f63552e61d431 languageName: node linkType: hard @@ -13085,6 +13126,17 @@ __metadata: languageName: node linkType: hard +"@nuxt/opencollective@npm:0.4.1": + version: 0.4.1 + resolution: "@nuxt/opencollective@npm:0.4.1" + dependencies: + consola: "npm:^3.2.3" + bin: + opencollective: bin/opencollective.js + checksum: 10/37739657e87196c7f1019a76bc33dc6e33b028eeeec43ffbf29c821e89bf5c170514e9e224456e1da85d95859ba63a3a36bd7ce1b82f2d366f7be3d6299e7631 + languageName: node + linkType: hard + "@nuxtjs/opencollective@npm:0.3.2": version: 0.3.2 resolution: "@nuxtjs/opencollective@npm:0.3.2" @@ -13852,30 +13904,30 @@ __metadata: linkType: hard "@openapitools/openapi-generator-cli@npm:^2.4.26, @openapitools/openapi-generator-cli@npm:^2.7.0": - version: 2.15.3 - resolution: "@openapitools/openapi-generator-cli@npm:2.15.3" + version: 2.20.0 + resolution: "@openapitools/openapi-generator-cli@npm:2.20.0" dependencies: - "@nestjs/axios": "npm:3.1.1" - "@nestjs/common": "npm:10.4.6" - "@nestjs/core": "npm:10.4.6" + "@nestjs/axios": "npm:4.0.0" + "@nestjs/common": "npm:11.0.20" + "@nestjs/core": "npm:11.0.20" "@nuxtjs/opencollective": "npm:0.3.2" - axios: "npm:1.7.7" + axios: "npm:1.8.4" chalk: "npm:4.1.2" commander: "npm:8.3.0" compare-versions: "npm:4.1.4" concurrently: "npm:6.5.1" console.table: "npm:0.10.0" - fs-extra: "npm:10.1.0" + fs-extra: "npm:11.3.0" glob: "npm:9.3.5" inquirer: "npm:8.2.6" lodash: "npm:4.17.21" - proxy-agent: "npm:6.4.0" - reflect-metadata: "npm:0.1.13" - rxjs: "npm:7.8.1" + proxy-agent: "npm:6.5.0" + reflect-metadata: "npm:0.2.2" + rxjs: "npm:7.8.2" tslib: "npm:2.8.1" bin: openapi-generator-cli: main.js - checksum: 10/8956772c697b11205c577a3f77161853a00cfdc405189fdc4d71cc20a1bc2d2d07a28ae50b0e2d7c43dee89ad57af1dab2b4675599d9ce2e223811542c8b5cd0 + checksum: 10/b5114d7811505e4fd3e7fa050c1262a76510b1d9e0c1a0e24a417f9570ca986a2edc0c7234eea0e6e30aae2ed698d2e9815ccdd8a5d1663b5794e9e505868edb languageName: node linkType: hard @@ -16544,82 +16596,82 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-darwin-arm64@npm:1.3.8" +"@rspack/binding-darwin-arm64@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-darwin-arm64@npm:1.3.10" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-darwin-x64@npm:1.3.8" +"@rspack/binding-darwin-x64@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-darwin-x64@npm:1.3.10" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.3.8" +"@rspack/binding-linux-arm64-gnu@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.3.10" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.3.8" +"@rspack/binding-linux-arm64-musl@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.3.10" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.3.8" +"@rspack/binding-linux-x64-gnu@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.3.10" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-linux-x64-musl@npm:1.3.8" +"@rspack/binding-linux-x64-musl@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-linux-x64-musl@npm:1.3.10" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.3.8" +"@rspack/binding-win32-arm64-msvc@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.3.10" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.3.8" +"@rspack/binding-win32-ia32-msvc@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.3.10" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.3.8" +"@rspack/binding-win32-x64-msvc@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.3.10" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.3.8": - version: 1.3.8 - resolution: "@rspack/binding@npm:1.3.8" +"@rspack/binding@npm:1.3.10": + version: 1.3.10 + resolution: "@rspack/binding@npm:1.3.10" dependencies: - "@rspack/binding-darwin-arm64": "npm:1.3.8" - "@rspack/binding-darwin-x64": "npm:1.3.8" - "@rspack/binding-linux-arm64-gnu": "npm:1.3.8" - "@rspack/binding-linux-arm64-musl": "npm:1.3.8" - "@rspack/binding-linux-x64-gnu": "npm:1.3.8" - "@rspack/binding-linux-x64-musl": "npm:1.3.8" - "@rspack/binding-win32-arm64-msvc": "npm:1.3.8" - "@rspack/binding-win32-ia32-msvc": "npm:1.3.8" - "@rspack/binding-win32-x64-msvc": "npm:1.3.8" + "@rspack/binding-darwin-arm64": "npm:1.3.10" + "@rspack/binding-darwin-x64": "npm:1.3.10" + "@rspack/binding-linux-arm64-gnu": "npm:1.3.10" + "@rspack/binding-linux-arm64-musl": "npm:1.3.10" + "@rspack/binding-linux-x64-gnu": "npm:1.3.10" + "@rspack/binding-linux-x64-musl": "npm:1.3.10" + "@rspack/binding-win32-arm64-msvc": "npm:1.3.10" + "@rspack/binding-win32-ia32-msvc": "npm:1.3.10" + "@rspack/binding-win32-x64-msvc": "npm:1.3.10" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -16639,28 +16691,28 @@ __metadata: optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/2b4427240e97899845b08f592af4f806884b49e192fb2b5a93f450f11c97a3ecbef581bb32b9cf1b2bfca48fd65426a978011f04fc7484307e84005ffd174211 + checksum: 10/10328e405c6708f7d1eea7e8f83d7f4453bcc3e4ca77b99eb29819d9f66c51e85b8cf46daab797bea59fdafc266dab6091deb9b5e2ec3007482da11ab10dc62c languageName: node linkType: hard -"@rspack/core@npm:^1.0.10": - version: 1.3.8 - resolution: "@rspack/core@npm:1.3.8" +"@rspack/core@npm:^1.3.9": + version: 1.3.10 + resolution: "@rspack/core@npm:1.3.10" dependencies: - "@module-federation/runtime-tools": "npm:0.13.0" - "@rspack/binding": "npm:1.3.8" + "@module-federation/runtime-tools": "npm:0.13.1" + "@rspack/binding": "npm:1.3.10" "@rspack/lite-tapable": "npm:1.0.1" - caniuse-lite: "npm:^1.0.30001715" + caniuse-lite: "npm:^1.0.30001717" peerDependencies: "@swc/helpers": ">=0.5.1" peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/e2f3fd21d512faa3865a784f219f92b78497713e0e9dd2fd03c4ec6c9bcfa33c53c301f9d5c5546dd7e86e8e46309b0aef3ab6f1661fed10c622482b8717b588 + checksum: 10/5471ced4f461936c723199006182ce9fc54a5840aa7ba46f56f137d46b1839b994a9f6e4539e1376eaaf2956cbbbe5a7846d06ab61246e38711a805f7dd56df4 languageName: node linkType: hard -"@rspack/dev-server@npm:^1.0.9": +"@rspack/dev-server@npm:^1.1.1": version: 1.1.1 resolution: "@rspack/dev-server@npm:1.1.1" dependencies: @@ -16685,9 +16737,9 @@ __metadata: languageName: node linkType: hard -"@rspack/plugin-react-refresh@npm:^1.0.0": - version: 1.4.1 - resolution: "@rspack/plugin-react-refresh@npm:1.4.1" +"@rspack/plugin-react-refresh@npm:^1.4.2": + version: 1.4.3 + resolution: "@rspack/plugin-react-refresh@npm:1.4.3" dependencies: error-stack-parser: "npm:^2.1.4" html-entities: "npm:^2.6.0" @@ -16697,7 +16749,7 @@ __metadata: peerDependenciesMeta: webpack-hot-middleware: optional: true - checksum: 10/0d6ee27a6577947399b7d457b54dbc65dc2c1199cacd257baf9b16ecd4621b288ae4c73a47e753ee8ff62a51b11fe2cac4ac0d168543299593cb59fa35c38119 + checksum: 10/ddbe4268f0c5eb1e6c4384db16cad42628596ff7ca97707b5075be894f60bb87de2483fe2735129dc624380e46b7999735af7c418123a6bb2d01280ca5fea2c1 languageName: node linkType: hard @@ -16894,30 +16946,48 @@ __metadata: languageName: node linkType: hard -"@shikijs/engine-oniguruma@npm:^1.27.2": - version: 1.29.2 - resolution: "@shikijs/engine-oniguruma@npm:1.29.2" +"@shikijs/engine-oniguruma@npm:^3.4.0": + version: 3.4.0 + resolution: "@shikijs/engine-oniguruma@npm:3.4.0" dependencies: - "@shikijs/types": "npm:1.29.2" - "@shikijs/vscode-textmate": "npm:^10.0.1" - checksum: 10/bb3e2c01da84d573251ebc289b1ecf815261024dea5bddb93ad56c3504a71cde3630db070be401ed3bbcd23a8a839ec78984a82317f9c9d0bba58daed935b781 + "@shikijs/types": "npm:3.4.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + checksum: 10/3f6c33a1f49e6a8235b79029e3e95f0bc95398f299e53b96a23942ceb4648be51071f48b8632e2f244b2b2477d152035fd18bc4b7e34abe885d84e94698f7e94 languageName: node linkType: hard -"@shikijs/types@npm:1.29.2, @shikijs/types@npm:^1.27.2": - version: 1.29.2 - resolution: "@shikijs/types@npm:1.29.2" +"@shikijs/langs@npm:^3.4.0": + version: 3.4.0 + resolution: "@shikijs/langs@npm:3.4.0" dependencies: - "@shikijs/vscode-textmate": "npm:^10.0.1" + "@shikijs/types": "npm:3.4.0" + checksum: 10/355065accab578a8a017ee238f13ee0b6adf007b514f78d7ea126f07ee89cd682f1f377f49d923688c6787b474b5b3a640aa3b083ac11ea11ea4edd7e8c50ac7 + languageName: node + linkType: hard + +"@shikijs/themes@npm:^3.4.0": + version: 3.4.0 + resolution: "@shikijs/themes@npm:3.4.0" + dependencies: + "@shikijs/types": "npm:3.4.0" + checksum: 10/74e5911548d77a63375125575db77dd62b44c40afd39f168bc3fc4d7fefb4d2922f49cf76d21139d850a02a5e207659a247a417be27b438d3d5ea80abe450e52 + languageName: node + linkType: hard + +"@shikijs/types@npm:3.4.0, @shikijs/types@npm:^3.4.0": + version: 3.4.0 + resolution: "@shikijs/types@npm:3.4.0" + dependencies: + "@shikijs/vscode-textmate": "npm:^10.0.2" "@types/hast": "npm:^3.0.4" - checksum: 10/579e64b6e8cb83023232b8060b08f51cff3909b199d0d1a0c58ed500c898dd34b74bf0457336fa2e6bee1005889e198d7d924347ad616eee30c6ae4c89a67ab8 + checksum: 10/bca759ac0972f9ab673b199d44fb4655eb75a1af68b024f05126ff6ef0ce1fd7d316ca16da8eb7c383de8b058f358f90e6172be3783a1f04a3871f518fb0fd72 languageName: node linkType: hard -"@shikijs/vscode-textmate@npm:^10.0.1": - version: 10.0.1 - resolution: "@shikijs/vscode-textmate@npm:10.0.1" - checksum: 10/8bb005efbf472a9cac19bb5fb0bb1a2b612e128122e5a9d7b2bb2af674c29912db0e59c547ec458e5333f8f7ac7a8f054a24cb653f11dc20951e299933b416e4 +"@shikijs/vscode-textmate@npm:^10.0.2": + version: 10.0.2 + resolution: "@shikijs/vscode-textmate@npm:10.0.2" + checksum: 10/d924cba8a01cd9ca12f56ba097d628fdb81455abb85884c8d8a5ae85b628a37dd5907e7691019b97107bd6608c866adf91ba04a1c3bba391281c88e386c044ea languageName: node linkType: hard @@ -19399,90 +19469,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-darwin-arm64@npm:1.11.21" +"@swc/core-darwin-arm64@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-darwin-arm64@npm:1.11.24" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-darwin-x64@npm:1.11.21" +"@swc/core-darwin-x64@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-darwin-x64@npm:1.11.24" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.11.21" +"@swc/core-linux-arm-gnueabihf@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.11.24" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-linux-arm64-gnu@npm:1.11.21" +"@swc/core-linux-arm64-gnu@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-linux-arm64-gnu@npm:1.11.24" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-linux-arm64-musl@npm:1.11.21" +"@swc/core-linux-arm64-musl@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-linux-arm64-musl@npm:1.11.24" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-linux-x64-gnu@npm:1.11.21" +"@swc/core-linux-x64-gnu@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-linux-x64-gnu@npm:1.11.24" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-linux-x64-musl@npm:1.11.21" +"@swc/core-linux-x64-musl@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-linux-x64-musl@npm:1.11.24" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-win32-arm64-msvc@npm:1.11.21" +"@swc/core-win32-arm64-msvc@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-win32-arm64-msvc@npm:1.11.24" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-win32-ia32-msvc@npm:1.11.21" +"@swc/core-win32-ia32-msvc@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-win32-ia32-msvc@npm:1.11.24" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.11.21": - version: 1.11.21 - resolution: "@swc/core-win32-x64-msvc@npm:1.11.21" +"@swc/core-win32-x64-msvc@npm:1.11.24": + version: 1.11.24 + resolution: "@swc/core-win32-x64-msvc@npm:1.11.24" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.10.8, @swc/core@npm:^1.3.46": - version: 1.11.21 - resolution: "@swc/core@npm:1.11.21" + version: 1.11.24 + resolution: "@swc/core@npm:1.11.24" dependencies: - "@swc/core-darwin-arm64": "npm:1.11.21" - "@swc/core-darwin-x64": "npm:1.11.21" - "@swc/core-linux-arm-gnueabihf": "npm:1.11.21" - "@swc/core-linux-arm64-gnu": "npm:1.11.21" - "@swc/core-linux-arm64-musl": "npm:1.11.21" - "@swc/core-linux-x64-gnu": "npm:1.11.21" - "@swc/core-linux-x64-musl": "npm:1.11.21" - "@swc/core-win32-arm64-msvc": "npm:1.11.21" - "@swc/core-win32-ia32-msvc": "npm:1.11.21" - "@swc/core-win32-x64-msvc": "npm:1.11.21" + "@swc/core-darwin-arm64": "npm:1.11.24" + "@swc/core-darwin-x64": "npm:1.11.24" + "@swc/core-linux-arm-gnueabihf": "npm:1.11.24" + "@swc/core-linux-arm64-gnu": "npm:1.11.24" + "@swc/core-linux-arm64-musl": "npm:1.11.24" + "@swc/core-linux-x64-gnu": "npm:1.11.24" + "@swc/core-linux-x64-musl": "npm:1.11.24" + "@swc/core-win32-arm64-msvc": "npm:1.11.24" + "@swc/core-win32-ia32-msvc": "npm:1.11.24" + "@swc/core-win32-x64-msvc": "npm:1.11.24" "@swc/counter": "npm:^0.1.3" "@swc/types": "npm:^0.1.21" peerDependencies: @@ -19511,7 +19581,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/98a0f201a0a4aa026b0b07c61c8da49d94d4ac3b059b466416c90b9b2cb4f457d836ae55ccfe90b0a89c7bdde36526ba5b7747c4e616c0d556c733e3728e9dd4 + checksum: 10/0b3e883f8a5652a7ab221a777386ccc8a65fc5b53d533bad15b703b22984eb3b449efd907b1872263f1a9990a9a50612f3c6deb619894a43f03cd974ec9bd1b7 languageName: node linkType: hard @@ -19532,15 +19602,15 @@ __metadata: linkType: hard "@swc/jest@npm:^0.2.22": - version: 0.2.37 - resolution: "@swc/jest@npm:0.2.37" + version: 0.2.38 + resolution: "@swc/jest@npm:0.2.38" dependencies: "@jest/create-cache-key-function": "npm:^29.7.0" "@swc/counter": "npm:^0.1.3" jsonc-parser: "npm:^3.2.0" peerDependencies: "@swc/core": "*" - checksum: 10/bbec37079b4f5c1ff1c95aeec07d08277c646a0c5e16e057ea3a8fe5c6e2bd59bbfc4312e53ddd05d25fa4de20a03607be274f560f28bb5e229dd08124780e16 + checksum: 10/3aaf557425e806890ebefea35334b7795e9f8ddf6f82d634d865ef917333cca4208190af1a9610c134c0e3b7a6a1aea4ec77a659e3ca5965be7aace65ce80c97 languageName: node linkType: hard @@ -19727,6 +19797,17 @@ __metadata: languageName: node linkType: hard +"@tokenizer/inflate@npm:^0.2.6": + version: 0.2.7 + resolution: "@tokenizer/inflate@npm:0.2.7" + dependencies: + debug: "npm:^4.4.0" + fflate: "npm:^0.8.2" + token-types: "npm:^6.0.0" + checksum: 10/6cee1857e47ca0fc053d6cd87773b7c21857ab84cb847c7d9437a76d923e265c88f8e99a4ac9643c2f989f4b9791259ca17128f0480191449e2b412821a1b9a7 + languageName: node + linkType: hard + "@tokenizer/token@npm:^0.3.0": version: 0.3.0 resolution: "@tokenizer/token@npm:0.3.0" @@ -20132,11 +20213,11 @@ __metadata: linkType: hard "@types/cors@npm:^2.8.6": - version: 2.8.17 - resolution: "@types/cors@npm:2.8.17" + version: 2.8.18 + resolution: "@types/cors@npm:2.8.18" dependencies: "@types/node": "npm:*" - checksum: 10/469bd85e29a35977099a3745c78e489916011169a664e97c4c3d6538143b0a16e4cc72b05b407dc008df3892ed7bf595f9b7c0f1f4680e169565ee9d64966bde + checksum: 10/6e49b741345e67834cd19d766228509e4b37d6d5c272355bb059502b4787f5adf58776d9114ac5f0f407966e0347ae8d1f995d7ea41e6a24f716d36b3010401b languageName: node linkType: hard @@ -21115,11 +21196,11 @@ __metadata: linkType: hard "@types/react-dom@npm:^18.0.0": - version: 18.3.6 - resolution: "@types/react-dom@npm:18.3.6" + version: 18.3.7 + resolution: "@types/react-dom@npm:18.3.7" peerDependencies: "@types/react": ^18.0.0 - checksum: 10/ae179355401c64423d39946eda22c5f7f74c94ce61c21505024d4d82c33853ec40bc9b370f75e4a7750b0524aba4d95a43fcc328d8d14684dc2abb41ba529de8 + checksum: 10/317569219366d487a3103ba1e5e47154e95a002915fdcf73a44162c48fe49c3a57fcf7f57fc6979e70d447112681e6b13c6c3c1df289db8b544df4aab2d318f3 languageName: node linkType: hard @@ -21217,12 +21298,12 @@ __metadata: linkType: hard "@types/react@npm:^18.0.0": - version: 18.3.20 - resolution: "@types/react@npm:18.3.20" + version: 18.3.21 + resolution: "@types/react@npm:18.3.21" dependencies: "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10/020c51e63b60862e6d772f0cdea0b9441182eedab6289dabd8add0708ded62003834c4e7c6f23a1ccd3ca9486b46296057c3f881c34261a0483765351f8d0bc3 + checksum: 10/89adc2fe391fd63b620db0aaa10b2be89fad338039415fd748e60c962e47bf2b0f1897cb5226f181eda963f37a1c8ac1bf0b3b29a0e35313bacd0355e5d6c736 languageName: node linkType: hard @@ -22016,24 +22097,24 @@ __metadata: languageName: node linkType: hard -"@useoptic/json-pointer-helpers@npm:1.0.6": - version: 1.0.6 - resolution: "@useoptic/json-pointer-helpers@npm:1.0.6" +"@useoptic/json-pointer-helpers@npm:1.0.8": + version: 1.0.8 + resolution: "@useoptic/json-pointer-helpers@npm:1.0.8" dependencies: jsonpointer: "npm:^5.0.1" minimatch: "npm:9.0.3" - checksum: 10/a4a0b2175a7a95c1c658f384d26aed2bc0e9db53aba9d44e03e4be239d3fce8b5e20b089dbb3a3bdfcd9cdf3951d46eeec662f6c47156c14ac8dc0a81463b30e + checksum: 10/075142e33ab89de448283ef2199438e84942ad285600df33d6a8f3553e4d3ab60d139c080c0dcd667f60ee607cadedeceb904739b6d7b03d99c2558413f1b223 languageName: node linkType: hard -"@useoptic/openapi-io@npm:1.0.6": - version: 1.0.6 - resolution: "@useoptic/openapi-io@npm:1.0.6" +"@useoptic/openapi-io@npm:1.0.8": + version: 1.0.8 + resolution: "@useoptic/openapi-io@npm:1.0.8" dependencies: "@apidevtools/json-schema-ref-parser": "npm:9.0.9" "@jsdevtools/ono": "npm:^7.1.3" - "@useoptic/json-pointer-helpers": "npm:1.0.6" - "@useoptic/openapi-utilities": "npm:1.0.6" + "@useoptic/json-pointer-helpers": "npm:1.0.8" + "@useoptic/openapi-utilities": "npm:1.0.8" ajv: "npm:8.17.1" ajv-errors: "npm:~3.0.0" ajv-formats: "npm:~2.1.0" @@ -22051,15 +22132,15 @@ __metadata: upath: "npm:^2.0.1" yaml: "npm:^2.3.2" yaml-ast-parser: "npm:^0.0.43" - checksum: 10/6c91f614d49840c7913a70d60bbdd9b43271765da3c109ea4efcf8191d01206e0ac3729d111bf76dfa5287a3be0a75b5f63b5c88d3c5aca97e8556ba002b6920 + checksum: 10/1d228bf7191ed8496b1ddcb5478ff051f5d7d2014e8dfa4d0e60e3d2def1284dc607e35f6ccba3b36a2d6263ce5fdea0b573bf9a7164f122036f2d54831db159 languageName: node linkType: hard -"@useoptic/openapi-utilities@npm:1.0.6": - version: 1.0.6 - resolution: "@useoptic/openapi-utilities@npm:1.0.6" +"@useoptic/openapi-utilities@npm:1.0.8": + version: 1.0.8 + resolution: "@useoptic/openapi-utilities@npm:1.0.8" dependencies: - "@useoptic/json-pointer-helpers": "npm:1.0.6" + "@useoptic/json-pointer-helpers": "npm:1.0.8" ajv: "npm:8.17.1" ajv-errors: "npm:^3.0.0" ajv-formats: "npm:^3.0.1" @@ -22076,7 +22157,7 @@ __metadata: ts-invariant: "npm:^0.9.3" url-join: "npm:^4.0.1" yaml-ast-parser: "npm:^0.0.43" - checksum: 10/e7aebd5fb1548da8d10512aaf9c54ca2e398ceaca4d3f754393082aa9f6b301823f0a0e24cf6369a12eb13dd520a9e0ad9ade1839820de06a9319c6611f3aae4 + checksum: 10/0c0b7ec345ca9b13553c096dbf39dc90e719fc4c21855134783b7c98896bd16b2bd7e19c5419a345e4e44705e928e8dafea574d20a075028ae50a5871042a5ea languageName: node linkType: hard @@ -22106,8 +22187,8 @@ __metadata: linkType: hard "@useoptic/optic@npm:^1.0.0": - version: 1.0.6 - resolution: "@useoptic/optic@npm:1.0.6" + version: 1.0.8 + resolution: "@useoptic/optic@npm:1.0.8" dependencies: "@babel/runtime": "npm:^7.20.6" "@httptoolkit/httpolyglot": "npm:^2.0.1" @@ -22117,10 +22198,10 @@ __metadata: "@sentry/node": "npm:^7.74.0" "@sinclair/typebox": "npm:0.31.28" "@stoplight/spectral-core": "npm:^1.8.1" - "@useoptic/openapi-io": "npm:1.0.6" - "@useoptic/openapi-utilities": "npm:1.0.6" - "@useoptic/rulesets-base": "npm:1.0.6" - "@useoptic/standard-rulesets": "npm:1.0.6" + "@useoptic/openapi-io": "npm:1.0.8" + "@useoptic/openapi-utilities": "npm:1.0.8" + "@useoptic/rulesets-base": "npm:1.0.8" + "@useoptic/standard-rulesets": "npm:1.0.8" ajv: "npm:8.17.1" ajv-formats: "npm:~2.1.0" async-exit-hook: "npm:^2.0.1" @@ -22177,34 +22258,34 @@ __metadata: yaml: "npm:^2.3.4" bin: optic: build/index.js - checksum: 10/16dd6b98a84eff68cefde74688c4dfc9a219d8b9d960945b4da3a893cf7113f80a05ee9a12a6dd9f907bd4c653bfc40a57c998062dda482595f23ab443c38548 + checksum: 10/7d9275697e068ffe72656bf38044d5724cc18b707e9507c65babe28f1b84017cc7be33abef4fa696fa9290e843342778d0131004aee76417fc902aa25d68038a languageName: node linkType: hard -"@useoptic/rulesets-base@npm:1.0.6": - version: 1.0.6 - resolution: "@useoptic/rulesets-base@npm:1.0.6" +"@useoptic/rulesets-base@npm:1.0.8": + version: 1.0.8 + resolution: "@useoptic/rulesets-base@npm:1.0.8" dependencies: "@stoplight/spectral-core": "npm:^1.8.1" "@stoplight/spectral-rulesets": "npm:^1.14.1" - "@useoptic/json-pointer-helpers": "npm:1.0.6" - "@useoptic/openapi-utilities": "npm:1.0.6" + "@useoptic/json-pointer-helpers": "npm:1.0.8" + "@useoptic/openapi-utilities": "npm:1.0.8" ajv: "npm:^8.6.0" lodash.pick: "npm:^4.4.0" node-fetch: "npm:^2.6.7" semver: "npm:^7.5.4" bin: rulesets-base: build/index.js - checksum: 10/ecf1844f569aa52ece6ae118f1d2df89f1b2d19f28f620bad2dfaa99d8e477d64b9b884718c5e8f62c41a7768129c54e13aae6117b3dd289d458a6edc213591a + checksum: 10/d92a2f72dca642f61ea0a4f0318b0f9e689c647b30c90896c49cfd294ab635b096906c697e57cb28486cc647154025ede0328c72dfc785dd7e873429e316a5db languageName: node linkType: hard -"@useoptic/standard-rulesets@npm:1.0.6": - version: 1.0.6 - resolution: "@useoptic/standard-rulesets@npm:1.0.6" +"@useoptic/standard-rulesets@npm:1.0.8": + version: 1.0.8 + resolution: "@useoptic/standard-rulesets@npm:1.0.8" dependencies: - "@useoptic/openapi-utilities": "npm:1.0.6" - "@useoptic/rulesets-base": "npm:1.0.6" + "@useoptic/openapi-utilities": "npm:1.0.8" + "@useoptic/rulesets-base": "npm:1.0.8" ajv: "npm:^8.6.0" ajv-draft-04: "npm:^1.0.0" ajv-formats: "npm:~2.1.0" @@ -22215,7 +22296,7 @@ __metadata: whatwg-mimetype: "npm:^3.0.0" bin: standard-rulesets: build/index.js - checksum: 10/e1e7d59f5cdd953486583cc9cb9a4a06e3821bb2ff21506a27a261c1c3173ed91f35409297a4f5fed102bd084486c36b60358dbacf3a3c45f6cb59dbf143070e + checksum: 10/63f60ac25fb8cccd0ea89097336cf5bc7ad59022ad6ef49fd382e7555f5ea9d88ca144991315eb07646aa309f5f38f3d056c4b7c869ab6b5f66fa9e93d96f4ef languageName: node linkType: hard @@ -23415,7 +23496,7 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": +"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.3 resolution: "agent-base@npm:7.1.3" checksum: 10/3db6d8d4651f2aa1a9e4af35b96ab11a7607af57a24f3bc721a387eaa3b5f674e901f0a648b0caefd48f3fd117c7761b79a3b55854e2aebaa96c3f32cf76af84 @@ -24360,14 +24441,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:1.7.7": - version: 1.7.7 - resolution: "axios@npm:1.7.7" +"axios@npm:1.8.4": + version: 1.8.4 + resolution: "axios@npm:1.8.4" dependencies: follow-redirects: "npm:^1.15.6" form-data: "npm:^4.0.0" proxy-from-env: "npm:^1.1.0" - checksum: 10/7f875ea13b9298cd7b40fd09985209f7a38d38321f1118c701520939de2f113c4ba137832fe8e3f811f99a38e12c8225481011023209a77b0c0641270e20cde1 + checksum: 10/a10f0dd836613924e48cf03dc2eff3fd21b14f764807aedaee4880a70c0f142aaebdb21da7ce27104d4c16ca00d0e452a20a20851f60e385a8d5bad1ae909d46 languageName: node linkType: hard @@ -25518,10 +25599,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001669, caniuse-lite@npm:^1.0.30001715": - version: 1.0.30001715 - resolution: "caniuse-lite@npm:1.0.30001715" - checksum: 10/5608cdaf609eb5fe3a86ab6c1c2f3943dbdab813041725f4747f5432b05e6e19fc606faa8a9b75c329b37b772c91c47e8db483e76a6b715b59c289ce53dcba68 +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001669, caniuse-lite@npm:^1.0.30001717": + version: 1.0.30001717 + resolution: "caniuse-lite@npm:1.0.30001717" + checksum: 10/e47dfd8707ea305baa177f3d3d531df614f5a9ac6335363fc8f86f0be4caf79f5734f3f68b601fee4edd9d79f1e5ffc0931466bb894bf955ed6b1dd5a1c34b1d languageName: node linkType: hard @@ -26555,6 +26636,13 @@ __metadata: languageName: node linkType: hard +"consola@npm:^3.2.3": + version: 3.4.2 + resolution: "consola@npm:3.4.2" + checksum: 10/32192c9f50d7cac27c5d7c4ecd3ff3679aea863e6bf5bd6a9cc2b05d1cd78addf5dae71df08c54330c142be8e7fbd46f051030129b57c6aacdd771efe409c4b2 + languageName: node + linkType: hard + "console-browserify@npm:^1.1.0": version: 1.2.0 resolution: "console-browserify@npm:1.2.0" @@ -29702,11 +29790,11 @@ __metadata: linkType: hard "eventsource@npm:^3.0.6": - version: 3.0.6 - resolution: "eventsource@npm:3.0.6" + version: 3.0.7 + resolution: "eventsource@npm:3.0.7" dependencies: eventsource-parser: "npm:^3.0.1" - checksum: 10/ac08c7d1b21e454c7685693fe4ace53fc0b84f3cf752699a556876f2a7f33b7a12972ae33d1c407fb920d6d4aed10de52fdf0dd01902ccdf45cd5da8d55e7f88 + checksum: 10/e034915bc97068d1d38617951afd798e6776d6a3a78e36a7569c235b177c7afc2625c9fe82656f7341ab72c7eeecb3fd507b7f88e9328f2448872ff9c4742bb6 languageName: node linkType: hard @@ -30396,6 +30484,13 @@ __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" @@ -30414,6 +30509,18 @@ __metadata: languageName: node linkType: hard +"file-type@npm:20.4.1": + version: 20.4.1 + resolution: "file-type@npm:20.4.1" + dependencies: + "@tokenizer/inflate": "npm:^0.2.6" + strtok3: "npm:^10.2.0" + token-types: "npm:^6.0.0" + uint8array-extras: "npm:^1.4.0" + checksum: 10/efbb81c69c84ea4d83ea86dc1c95a45bc6830d5455deb7666e0dbefde2dbfe2bcee4e5398bfc5a8fc1ac55a80a27a3ec4f2bff4b53d4af20be799db4fae324b6 + languageName: node + linkType: hard + "file-type@npm:3.9.0": version: 3.9.0 resolution: "file-type@npm:3.9.0" @@ -30859,7 +30966,7 @@ __metadata: languageName: node linkType: hard -"formidable@npm:^3.5.1": +"formidable@npm:^3.5.4": version: 3.5.4 resolution: "formidable@npm:3.5.4" dependencies: @@ -30945,14 +31052,14 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:10.1.0, fs-extra@npm:^10.0.0": - version: 10.1.0 - resolution: "fs-extra@npm:10.1.0" +"fs-extra@npm:11.3.0, fs-extra@npm:^11.0.0, fs-extra@npm:^11.1.0, fs-extra@npm:^11.2.0": + version: 11.3.0 + resolution: "fs-extra@npm:11.3.0" dependencies: graceful-fs: "npm:^4.2.0" jsonfile: "npm:^6.0.1" universalify: "npm:^2.0.0" - checksum: 10/05ce2c3b59049bcb7b52001acd000e44b3c4af4ec1f8839f383ef41ec0048e3cfa7fd8a637b1bddfefad319145db89be91f4b7c1db2908205d38bf91e7d1d3b7 + checksum: 10/c9fe7b23dded1efe7bbae528d685c3206477e20cc60e9aaceb3f024f9b9ff2ee1f62413c161cb88546cc564009ab516dec99e9781ba782d869bb37e4fe04a97f languageName: node linkType: hard @@ -30968,14 +31075,14 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.0.0, fs-extra@npm:^11.1.0, fs-extra@npm:^11.2.0": - version: 11.2.0 - resolution: "fs-extra@npm:11.2.0" +"fs-extra@npm:^10.0.0": + version: 10.1.0 + resolution: "fs-extra@npm:10.1.0" dependencies: graceful-fs: "npm:^4.2.0" jsonfile: "npm:^6.0.1" universalify: "npm:^2.0.0" - checksum: 10/0579bf6726a4cd054d4aa308f10b483f52478bb16284f32cf60b4ce0542063d551fca1a08a2af365e35db21a3fa5a06cf2a6ed614004b4368982bc754cb816b3 + checksum: 10/05ce2c3b59049bcb7b52001acd000e44b3c4af4ec1f8839f383ef41ec0048e3cfa7fd8a637b1bddfefad319145db89be91f4b7c1db2908205d38bf91e7d1d3b7 languageName: node linkType: hard @@ -31475,7 +31582,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": +"glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -32558,13 +32665,13 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.3, https-proxy-agent@npm:^7.0.5": - version: 7.0.5 - resolution: "https-proxy-agent@npm:7.0.5" +"https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" dependencies: - agent-base: "npm:^7.0.2" + agent-base: "npm:^7.1.2" debug: "npm:4" - checksum: 10/6679d46159ab3f9a5509ee80c3a3fc83fba3a920a5e18d32176c3327852c3c00ad640c0c4210a8fd70ea3c4a6d3a1b375bf01942516e7df80e2646bdc77658ab + checksum: 10/784b628cbd55b25542a9d85033bdfd03d4eda630fb8b3c9477959367f3be95dc476ed2ecbb9836c359c7c698027fc7b45723a302324433590f45d6c1706e8c13 languageName: node linkType: hard @@ -32592,9 +32699,9 @@ __metadata: linkType: hard "humanize-duration@npm:^3.25.1": - version: 3.32.1 - resolution: "humanize-duration@npm:3.32.1" - checksum: 10/5909107485c33d0c025e5d15a45b2700f91c9efc1e88510867926b3d1ef24d2d0c8bf31f52abef92da53b29e69410c5acb3a4d6d72429bd8b61d82ac25739ce4 + version: 3.32.2 + resolution: "humanize-duration@npm:3.32.2" + checksum: 10/2dfe847085c7586d2cc83613b8643a4cca70966a16522e3786be07f0283087502e5340e98a79867c7b019ea22ae85c3fa9d1ca1be587eb19ccf03710c986d41d languageName: node linkType: hard @@ -35982,6 +36089,13 @@ __metadata: languageName: node linkType: hard +"load-esm@npm:1.0.2": + version: 1.0.2 + resolution: "load-esm@npm:1.0.2" + checksum: 10/1b4adb40c28c6fdbd4ca8c97942c04debddb3c93ae91413540ff5a21ca3511a651988c835cb80cad7288d1ecb869c4794b8a787ab02e09cc07ec951ad1eefcf9 + languageName: node + linkType: hard + "load-yaml-file@npm:^0.2.0": version: 0.2.0 resolution: "load-yaml-file@npm:0.2.0" @@ -38253,8 +38367,8 @@ __metadata: linkType: hard "msw@npm:^2.0.0, msw@npm:^2.0.8": - version: 2.7.6 - resolution: "msw@npm:2.7.6" + version: 2.8.2 + resolution: "msw@npm:2.8.2" dependencies: "@bundled-es-modules/cookie": "npm:^2.0.1" "@bundled-es-modules/statuses": "npm:^1.0.1" @@ -38281,7 +38395,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 10/23f3907b487102b395e2405ab8ae69c8cc74413485805a1039e1a3ab0b54b56947aa77b5ef5a15befad02a69aaa21a204ecfafd2e81d4fb1fd284cb99dfe2c0b + checksum: 10/7579a8dccb8cc8eb0f13d0bf3a232a3d50a478511d95bc2a4b70778e78ffa28fd0949a855fbceb6ec381bbf76f6331a6d36fcb4a3197ff5bf55d0f14a7f3b35c languageName: node linkType: hard @@ -38764,8 +38878,8 @@ __metadata: linkType: hard "node-mocks-http@npm:^1.0.0": - version: 1.17.0 - resolution: "node-mocks-http@npm:1.17.0" + version: 1.17.2 + resolution: "node-mocks-http@npm:1.17.2" dependencies: accepts: "npm:^1.3.7" content-disposition: "npm:^0.5.3" @@ -38785,7 +38899,7 @@ __metadata: optional: true "@types/node": optional: true - checksum: 10/7b8e0db30df8dacc957b352e4553635ed9c212a0e307ed486b5cb93cb7653c82a8b5bbe6a99ba016e1f390b3a2f5ef9388831b6cf1a4e43f014c0070c3292df3 + checksum: 10/3ab97b5a7b2dba0032fc5090983e4ca4be0597f1c645b34dff99d465feb17ef1ee7ea4829a9b4303495399c1ac0f12f8964b878e0dda43cda798bb97254ad571 languageName: node linkType: hard @@ -39859,19 +39973,19 @@ __metadata: languageName: node linkType: hard -"pac-proxy-agent@npm:^7.0.0, pac-proxy-agent@npm:^7.0.1": - version: 7.0.2 - resolution: "pac-proxy-agent@npm:7.0.2" +"pac-proxy-agent@npm:^7.0.0, pac-proxy-agent@npm:^7.1.0": + version: 7.2.0 + resolution: "pac-proxy-agent@npm:7.2.0" dependencies: "@tootallnate/quickjs-emscripten": "npm:^0.23.0" - agent-base: "npm:^7.0.2" + agent-base: "npm:^7.1.2" debug: "npm:^4.3.4" get-uri: "npm:^6.0.1" http-proxy-agent: "npm:^7.0.0" - https-proxy-agent: "npm:^7.0.5" + https-proxy-agent: "npm:^7.0.6" pac-resolver: "npm:^7.0.1" - socks-proxy-agent: "npm:^8.0.4" - checksum: 10/bb9b53b32ba98f085fd98ad0ea5e4201498585bf8d9390b3365c057b692b8562997be166d44224878ac216a81f1016c1f55f4e1dec52a6d92e5aa659eba9124c + socks-proxy-agent: "npm:^8.0.5" + checksum: 10/187656be62d5a6b983d90a86d64106a38b1a9ee78f591fabb27b3cf0d51e5d528456a9faaaf981c93dd54dc9c9ee8d33e35a51072b73a19ec1a8e0d0c36a2b99 languageName: node linkType: hard @@ -40343,6 +40457,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:8.2.0, path-to-regexp@npm:^8.0.0, path-to-regexp@npm:^8.1.0": + version: 8.2.0 + resolution: "path-to-regexp@npm:8.2.0" + checksum: 10/23378276a172b8ba5f5fb824475d1818ca5ccee7bbdb4674701616470f23a14e536c1db11da9c9e6d82b82c556a817bbf4eee6e41b9ed20090ef9427cbb38e13 + languageName: node + linkType: hard + "path-to-regexp@npm:^6.3.0": version: 6.3.0 resolution: "path-to-regexp@npm:6.3.0" @@ -40350,13 +40471,6 @@ __metadata: languageName: node linkType: hard -"path-to-regexp@npm:^8.0.0, path-to-regexp@npm:^8.1.0": - version: 8.2.0 - resolution: "path-to-regexp@npm:8.2.0" - checksum: 10/23378276a172b8ba5f5fb824475d1818ca5ccee7bbdb4674701616470f23a14e536c1db11da9c9e6d82b82c556a817bbf4eee6e41b9ed20090ef9427cbb38e13 - languageName: node - linkType: hard - "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -40412,6 +40526,13 @@ __metadata: languageName: node linkType: hard +"peek-readable@npm:^7.0.0": + version: 7.0.0 + resolution: "peek-readable@npm:7.0.0" + checksum: 10/e14d533c6a43f3991bb98b644101a55631e307f7fe70e26bd0fd0341bde1e6cdd60e205a66ec17c226b32cb8a4ad5c4e76c1a6173d37201f0bf466cbb617485c + languageName: node + linkType: hard + "pend@npm:~1.2.0": version: 1.2.0 resolution: "pend@npm:1.2.0" @@ -41708,19 +41829,19 @@ __metadata: languageName: node linkType: hard -"proxy-agent@npm:6.4.0": - version: 6.4.0 - resolution: "proxy-agent@npm:6.4.0" +"proxy-agent@npm:6.5.0": + version: 6.5.0 + resolution: "proxy-agent@npm:6.5.0" dependencies: - agent-base: "npm:^7.0.2" + agent-base: "npm:^7.1.2" debug: "npm:^4.3.4" http-proxy-agent: "npm:^7.0.1" - https-proxy-agent: "npm:^7.0.3" + https-proxy-agent: "npm:^7.0.6" lru-cache: "npm:^7.14.1" - pac-proxy-agent: "npm:^7.0.1" + pac-proxy-agent: "npm:^7.1.0" proxy-from-env: "npm:^1.1.0" - socks-proxy-agent: "npm:^8.0.2" - checksum: 10/a22f202b74cc52f093efd9bfe52de8db08eda8bbc16b9d3d73acda2acc1b40223966e5521b1706788b06adf9265f093ed554d989b354e81b2d6ad482e5bd4d23 + socks-proxy-agent: "npm:^8.0.5" + checksum: 10/56d5a494d96dafad94868870af776939e7b9aaca172465a5c251d2523496a8353b029c32d2a72a012bd62622cdc9a43ba3df59b5738ab7b740bc6b362e9f9477 languageName: node linkType: hard @@ -43076,10 +43197,10 @@ __metadata: languageName: node linkType: hard -"reflect-metadata@npm:0.1.13": - version: 0.1.13 - resolution: "reflect-metadata@npm:0.1.13" - checksum: 10/732570da35d2d96f8fdd5aac60fb263aa92f6512eaded5962b052bd9e90f22a9dec5aaf0d7ff4bfe97646c9530e8444e8435c2d80b24d0bdf938b5d47f6f5b83 +"reflect-metadata@npm:0.2.2": + version: 0.2.2 + resolution: "reflect-metadata@npm:0.2.2" + checksum: 10/1c93f9ac790fea1c852fde80c91b2760420069f4862f28e6fae0c00c6937a56508716b0ed2419ab02869dd488d123c4ab92d062ae84e8739ea7417fae10c4745 languageName: node linkType: hard @@ -43909,10 +44030,10 @@ __metadata: node-gyp: "npm:^10.0.0" prettier: "npm:^2.2.1" semver: "npm:^7.5.3" - shx: "npm:^0.3.2" + shx: "npm:^0.4.0" sloc: "npm:^0.3.1" sort-package-json: "npm:^2.8.0" - typedoc: "npm:^0.27.6" + typedoc: "npm:^0.28.0" typescript: "npm:~5.6.0" yaml: "npm:^2.7.0" languageName: unknown @@ -43959,12 +44080,12 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:7.8.1": - version: 7.8.1 - resolution: "rxjs@npm:7.8.1" +"rxjs@npm:7.8.2, rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": + version: 7.8.2 + resolution: "rxjs@npm:7.8.2" dependencies: tslib: "npm:^2.1.0" - checksum: 10/b10cac1a5258f885e9dd1b70d23c34daeb21b61222ee735d2ec40a8685bdca40429000703a44f0e638c27a684ac139e1c37e835d2a0dc16f6fc061a138ae3abb + checksum: 10/03dff09191356b2b87d94fbc1e97c4e9eb3c09d4452399dddd451b09c2f1ba8d56925a40af114282d7bc0c6fe7514a2236ca09f903cf70e4bbf156650dddb49d languageName: node linkType: hard @@ -43977,15 +44098,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.2.0, rxjs@npm:^7.5.5": - version: 7.8.2 - resolution: "rxjs@npm:7.8.2" - dependencies: - tslib: "npm:^2.1.0" - checksum: 10/03dff09191356b2b87d94fbc1e97c4e9eb3c09d4452399dddd451b09c2f1ba8d56925a40af114282d7bc0c6fe7514a2236ca09f903cf70e4bbf156650dddb49d - languageName: node - linkType: hard - "sade@npm:^1.7.3": version: 1.8.1 resolution: "sade@npm:1.8.1" @@ -44281,11 +44393,11 @@ __metadata: linkType: hard "semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2": - version: 7.7.1 - resolution: "semver@npm:7.7.1" + version: 7.7.2 + resolution: "semver@npm:7.7.2" bin: semver: bin/semver.js - checksum: 10/4cfa1eb91ef3751e20fc52e47a935a0118d56d6f15a837ab814da0c150778ba2ca4f1a4d9068b33070ea4273629e615066664c2cfcd7c272caf7a8a0f6518b2c + checksum: 10/7a24cffcaa13f53c09ce55e05efe25cd41328730b2308678624f8b9f5fc3093fc4d189f47950f0b811ff8f3c3039c24a2c36717ba7961615c682045bf03e1dda languageName: node linkType: hard @@ -44531,16 +44643,17 @@ __metadata: languageName: node linkType: hard -"shelljs@npm:^0.8.5": - version: 0.8.5 - resolution: "shelljs@npm:0.8.5" +"shelljs@npm:^0.9.2": + version: 0.9.2 + resolution: "shelljs@npm:0.9.2" dependencies: - glob: "npm:^7.0.0" + execa: "npm:^1.0.0" + fast-glob: "npm:^3.3.2" interpret: "npm:^1.0.0" rechoir: "npm:^0.6.2" bin: shjs: bin/shjs - checksum: 10/f2178274b97b44332bbe9ddb78161137054f55ecf701c7a99db9552cb5478fe279ad5f5131d8a7c2f0730e01ccf0c629d01094143f0541962ce1a3d0243d23f7 + checksum: 10/d264239d4b5f2cb075270a8de5f6c8f8ace1c8320efc8fa05b9a0c94bd9cb8aabb296f4942772b2c225d32431ead0b45a40861b2be5ace9f0d09b50d6a5261cd languageName: node linkType: hard @@ -44561,15 +44674,15 @@ __metadata: languageName: node linkType: hard -"shx@npm:^0.3.2": - version: 0.3.4 - resolution: "shx@npm:0.3.4" +"shx@npm:^0.4.0": + version: 0.4.0 + resolution: "shx@npm:0.4.0" dependencies: - minimist: "npm:^1.2.3" - shelljs: "npm:^0.8.5" + minimist: "npm:^1.2.8" + shelljs: "npm:^0.9.2" bin: shx: lib/cli.js - checksum: 10/5271b60f7e322540799102ad6935121f10c857a995a1321357a7bffd1628674a4758a602153dc5cc126d8dc94d3489586587d3dee54dc3cac0222ca08a78e33a + checksum: 10/8df78b1f2e099de11d4ed44846878cb811cee02b40a08232302d59a8cb130e7be8ef71698e70e0984c345202bab39c35d5e0202cc0f0d0ea7b0d0501fb81ac00 languageName: node linkType: hard @@ -44881,7 +44994,7 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^8.0.1, socks-proxy-agent@npm:^8.0.2, socks-proxy-agent@npm:^8.0.3, socks-proxy-agent@npm:^8.0.4": +"socks-proxy-agent@npm:^8.0.1, socks-proxy-agent@npm:^8.0.3, socks-proxy-agent@npm:^8.0.4, socks-proxy-agent@npm:^8.0.5": version: 8.0.5 resolution: "socks-proxy-agent@npm:8.0.5" dependencies: @@ -45769,6 +45882,16 @@ __metadata: languageName: node linkType: hard +"strtok3@npm:^10.2.0": + version: 10.2.2 + resolution: "strtok3@npm:10.2.2" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + peek-readable: "npm:^7.0.0" + checksum: 10/964e60d76576232803fc75572adfb8778b55a7ef021ecfa3a507dcdb426f1e2c42580e545ab18f882da8f2e9bfca05fb0659f56f71b18723e95c31c0512301fc + languageName: node + linkType: hard + "strtok3@npm:^6.2.4": version: 6.2.4 resolution: "strtok3@npm:6.2.4" @@ -45901,30 +46024,30 @@ __metadata: languageName: node linkType: hard -"superagent@npm:^9.0.1": - version: 9.0.2 - resolution: "superagent@npm:9.0.2" +"superagent@npm:^10.2.1": + version: 10.2.1 + resolution: "superagent@npm:10.2.1" dependencies: component-emitter: "npm:^1.3.0" cookiejar: "npm:^2.1.4" debug: "npm:^4.3.4" fast-safe-stringify: "npm:^2.1.1" form-data: "npm:^4.0.0" - formidable: "npm:^3.5.1" + formidable: "npm:^3.5.4" methods: "npm:^1.1.2" mime: "npm:2.6.0" qs: "npm:^6.11.0" - checksum: 10/d3c0c9051ceec84d5b431eaa410ad81bcd53255cea57af1fc66d683a24c34f3ba4761b411072a9bf489a70e3d5b586a78a0e6f2eac6a561067e7d196ddab0907 + checksum: 10/fb91785361955e45d3be7d69f11b64b072678385acbd50ae38f713d6f4862384f138bd9842aff96f475c139b3afda96ea6b016dd544e9a984cf0bcc9e0c103b7 languageName: node linkType: hard "supertest@npm:^7.0.0": - version: 7.1.0 - resolution: "supertest@npm:7.1.0" + version: 7.1.1 + resolution: "supertest@npm:7.1.1" dependencies: methods: "npm:^1.1.2" - superagent: "npm:^9.0.1" - checksum: 10/20069f739a44821dfa4f7f397b9086ef31a358366331138f97945eedb2e231796e7c55b032125d3bd12f9839f089fbb809893dbc0f98edc57e12333b9f42b726 + superagent: "npm:^10.2.1" + checksum: 10/17e2db5d7aef36599f7fe526972e86ceccf3f52300d5d5ba56770a79a85b55172662d80b88591c6d7468e2018b4b75c418c4ffcd9a16d08645ce8f8ddf5f0c90 languageName: node linkType: hard @@ -46645,6 +46768,16 @@ __metadata: languageName: node linkType: hard +"token-types@npm:^6.0.0": + version: 6.0.0 + resolution: "token-types@npm:6.0.0" + dependencies: + "@tokenizer/token": "npm:^0.3.0" + ieee754: "npm:^1.2.1" + checksum: 10/b541b605d602e8e6495745badb35f90ee8f997e43dc29bc51aee7e9a0bc3c6bc7372a305bd45f3e80d75223c2b6a5c7e65cb5159d8c4e49fa25cdbaae531fad4 + languageName: node + linkType: hard + "tosource@npm:^2.0.0-alpha.3": version: 2.0.0-alpha.3 resolution: "tosource@npm:2.0.0-alpha.3" @@ -46951,13 +47084,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.7.0": - version: 2.7.0 - resolution: "tslib@npm:2.7.0" - checksum: 10/9a5b47ddac65874fa011c20ff76db69f97cf90c78cff5934799ab8894a5342db2d17b4e7613a087046bc1d133d21547ddff87ac558abeec31ffa929c88b7fce6 - languageName: node - linkType: hard - "tslib@npm:^1.11.1, tslib@npm:^1.13.0, tslib@npm:^1.14.1, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" @@ -47218,20 +47344,20 @@ __metadata: languageName: node linkType: hard -"typedoc@npm:^0.27.6": - version: 0.27.9 - resolution: "typedoc@npm:0.27.9" +"typedoc@npm:^0.28.0": + version: 0.28.4 + resolution: "typedoc@npm:0.28.4" dependencies: - "@gerrit0/mini-shiki": "npm:^1.24.0" + "@gerrit0/mini-shiki": "npm:^3.2.2" lunr: "npm:^2.3.9" markdown-it: "npm:^14.1.0" minimatch: "npm:^9.0.5" - yaml: "npm:^2.6.1" + yaml: "npm:^2.7.1" peerDependencies: typescript: 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x bin: typedoc: bin/typedoc - checksum: 10/fb1e4b54849cad1628543fb24863358320b737aabba83194562360cfbcaac8684b2b18824fd623bb27a9e8dbbc0e01c0b88fedd9a642d78e7a3c108387e44d25 + checksum: 10/0ccab46519e4be1aa9bd4defd55e920cbefe8825e0ba7ca93b8d6fd3867300e8b60260d2182033b46121f9040251366f6a496a3500da458c3031b224d7bdadd7 languageName: node linkType: hard @@ -47384,6 +47510,13 @@ __metadata: languageName: node linkType: hard +"uint8array-extras@npm:^1.4.0": + version: 1.4.0 + resolution: "uint8array-extras@npm:1.4.0" + checksum: 10/4d2955d67c112e5ebaa4901272a75fc9ad14902c40f05a178b01e32387aa2702b6840472d931a1ca16e068ac59013c7d9ee2b4b2f141c4e73ba4bc7456490599 + languageName: node + linkType: hard + "unbox-primitive@npm:^1.1.0": version: 1.1.0 resolution: "unbox-primitive@npm:1.1.0" @@ -47439,18 +47572,18 @@ __metadata: linkType: hard "undici@npm:^5.28.4": - version: 5.28.4 - resolution: "undici@npm:5.28.4" + version: 5.29.0 + resolution: "undici@npm:5.29.0" dependencies: "@fastify/busboy": "npm:^2.0.0" - checksum: 10/a666a9f5ac4270c659fafc33d78b6b5039a0adbae3e28f934774c85dcc66ea91da907896f12b414bd6f578508b44d5dc206fa636afa0e49a4e1c9e99831ff065 + checksum: 10/0ceca8924a32acdcc0cfb8dd2d368c217840970aa3f5e314fc169608474be6341c5b8e50cad7bd257dbe3b4e432bc5d0a0d000f83644b54fa11a48735ec52b93 languageName: node linkType: hard "undici@npm:^7.2.3": - version: 7.2.3 - resolution: "undici@npm:7.2.3" - checksum: 10/92870d186682c19f4b9909e4b6229a88b4e73e6e12d7a3e151033cefb66579bdc1d36e46d97e386161ba0afcd84b718e917fca7b7f08d409d918689343034c09 + version: 7.9.0 + resolution: "undici@npm:7.9.0" + checksum: 10/fa92d3d9106612be566e79942174e00426c2e1f9a688fb86c8d1809c84a032c02fc57bd601d2051a45d94bbd312c50221644b58c8186c4f0d64f8ecc7f18b806 languageName: node linkType: hard @@ -48917,8 +49050,8 @@ __metadata: linkType: hard "ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.2.3, ws@npm:^8.8.0": - version: 8.18.1 - resolution: "ws@npm:8.18.1" + version: 8.18.2 + resolution: "ws@npm:8.18.2" peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ">=5.0.2" @@ -48927,7 +49060,7 @@ __metadata: optional: true utf-8-validate: optional: true - checksum: 10/3f38e9594f2af5b6324138e86b74df7d77bbb8e310bf8188679dd80bac0d1f47e51536a1923ac3365f31f3d8b25ea0b03e4ade466aa8292a86cd5defca64b19b + checksum: 10/018e04ec95561d88248d53a2eaf094b4ae131e9b062f2679e6e8a62f04649bc543448f1e038125225ac6bbb25f54c1e65d7a2cc9dbc1e28b43e5e6b7162ad88e languageName: node linkType: hard @@ -49101,7 +49234,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2, yaml@npm:^2.3.3, yaml@npm:^2.3.4, yaml@npm:^2.6.1, yaml@npm:^2.7.0": +"yaml@npm:^2.0.0, yaml@npm:^2.0.0-10, yaml@npm:^2.1.1, yaml@npm:^2.2.1, yaml@npm:^2.2.2, yaml@npm:^2.3.2, yaml@npm:^2.3.3, yaml@npm:^2.3.4, yaml@npm:^2.7.0, yaml@npm:^2.7.1": version: 2.7.1 resolution: "yaml@npm:2.7.1" bin: